Author Topic: FPGA VGA Controller for 8-bit computer  (Read 510863 times)

0 Members and 8 Guests are viewing this topic.

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8143
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #975 on: January 25, 2020, 10:34:24 pm »
Okay, well it looks like the pad in the footprint I'm using is a little larger than the actual ground pad on the IC, and I'd just finished the attached update to the PCB before I read your post about putting vias around the edge - hopefully this will be alright?  I've moved the 2.5 line on the bottom side down, so it goes around the edge of the centre of U1 and there's now a nice ground plane under U1.
Is it just me, or, do the pads on your new footprint appear to be small?

Make a high quality 1:1 paper print of both versions of the PCB and make sure you can solder the FPGA in place by placing a FPGA on top and looking that there is exposed copper both under the pins + a little extra outside of the pins.  Without that little extra, you wont be able to do a clean proper solder with an iron.

Make sure your print copy doe not contain the extra mask around the IC pads, just the copper underneath.
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #976 on: January 25, 2020, 11:33:01 pm »
Okay, well it looks like the pad in the footprint I'm using is a little larger than the actual ground pad on the IC, and I'd just finished the attached update to the PCB before I read your post about putting vias around the edge - hopefully this will be alright?  I've moved the 2.5 line on the bottom side down, so it goes around the edge of the centre of U1 and there's now a nice ground plane under U1.
Is it just me, or, do the pads on your new footprint appear to be small?

No, it's not just you.  I was concerned about that myself - it seems the footprint I selected for the chip wasn't precisely correct.  I'd chosen a TQFP-144 package footprint, as it was the only one I could find at the time with a ground pad under the chip.  It turns out that the pads are smaller, too - I've since done some more hunting (I'm no expert with EasyEDA and creating new symbols) and found an EQFP-144 package which is the real deal and matches the chip much more precisely in terms of the ground pad underneath.  It also comes with a grid of vias already and pads that match the original.

 :phew:

Next step is to see about fitting the CE15 into this design for the backwards compatibility...
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #977 on: January 26, 2020, 11:05:35 am »
Question:  Does it matter much if signal lines don't cross each other at right-angles?

Previously, whilst making my auto-routed PCBs I'd made for my CPU, memory, CompactFlash and power/serial cards, I've been a bit OCD about making sure signals on different layers cross each other at 90-degree angles to minimise cross-talk.  I don't even know where I got that idea from - or if it's even a thing I need to worry about. :-//

Just thought I'd ask and put that demon to bed once and for all.  >:D

 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #978 on: January 26, 2020, 12:13:38 pm »
You still haven't considered the additional GND connections on the edge connectors which I mentioned earlier.

There's not a lot I can do about those unless/until I redesign the entire computer and its system bus.  Those headers were designed over two years ago, before I knew anything about multiple grounds etc.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8143
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #979 on: January 26, 2020, 02:53:26 pm »
Question:  Does it matter much if signal lines don't cross each other at right-angles?

Previously, whilst making my auto-routed PCBs I'd made for my CPU, memory, CompactFlash and power/serial cards, I've been a bit OCD about making sure signals on different layers cross each other at 90-degree angles to minimise cross-talk.  I don't even know where I got that idea from - or if it's even a thing I need to worry about. :-//

Just thought I'd ask and put that demon to bed once and for all.  >:D

(Attachment Link)
Too low frequency/power design to worry bout.  Long parallel trace above and below is a larger problem.

90 degree turns on a thin trace is also a problem as they are a weak etching point.  (You have 1 below a resistor by the PS2 keyboard connector on the top layer.)

Also, you have so many unused IOs on those connectors, you cannot add just 2-3 GNDs on each side?


 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #980 on: January 26, 2020, 04:30:10 pm »
90 degree turns on a thin trace is also a problem as they are a weak etching point.  (You have 1 below a resistor by the PS2 keyboard connector on the top layer.)

Ah, missed that one - but yes, was aware of the no-right-angles rule (although I wasn't sure why - now I know!)  ;D

Also, you have so many unused IOs on those connectors, you cannot add just 2-3 GNDs on each side?

They're not used for this card (only the pins that are used have net labels in this design), but they are pretty much all used elsewhere on other boards.  There's a couple I could re-purpose as GNDs, but would have to bodge them into the same GND pins as I'm using currently when they get to the 'utility' card that handles the power supply.
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #981 on: January 26, 2020, 08:21:45 pm »
Can't seem to find a solution to this issue I'm having.

I'm trying to return a value from a parameter array, defined as below:

Code: [Select]
parameter int BANK_ID[16] = '{9,3,71,80,85,0,255,255,255,255,255,255,255,255,255,255}; // The BANK_ID data to return
... based upon the address being read, like below:

Code: [Select]
Z80_rData[7:0] <= BANK_ID[addr_hold[3:0]]; // Return the appropriate value
addr_hold is an 8-bit register, addr_hold[7:0], consisting of the lowest 8-bits of a memory address.

I'm getting this error, though:

Error (10687): SystemVerilog error at z80_bridge.sv(115): indexing addr_hold returns an aggregate value

I can't seem to bottom out the meaning of this error or how to fix it.  :-//
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8143
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #982 on: January 27, 2020, 12:33:38 am »

Code: [Select]
parameter int BANK_ID[16] = '{9,3,71,80,85,0,255,255,255,255,255,255,255,255,255,255}; // The BANK_ID data to return
Code: [Select]
Z80_rData[7:0] <= BANK_ID[addr_hold[3:0]][7:0]; // Return the appropriate value
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #983 on: January 27, 2020, 08:21:50 am »

Code: [Select]
parameter int BANK_ID[16] = '{9,3,71,80,85,0,255,255,255,255,255,255,255,255,255,255}; // The BANK_ID data to return

Code: [Select]
Z80_rData[7:0] <= BANK_ID[addr_hold[3:0]][7:0]; // Return the appropriate value

The one thing I didn't try - specifying the width of BANK_ID.  ::)  I was thinking the problem was with addr_hold itself..

... but it returns the same error again.  :(

Full code:

Code: [Select]
module Z80_bridge (

   // input
   input wire  reset,            // GPU reset signal
   input wire  GPU_CLK,          // GPU clock (125 MHz)
   input wire  Z80_CLK,          // Microcom clock signal (8 MHz)
   input wire  Z80_M1n,          // Z80 M1 - active LOW
   input wire  Z80_MREQn,        // Z80 MREQ - active LOW
   input wire  Z80_WRn,          // Z80 WR - active LOW
   input wire  Z80_RDn,          // Z80 RD - active LOW
   input wire  [21:0] Z80_addr,  // Microcom 22-bit address bus
   input wire  [7:0]  Z80_wData, // Z80 DATA bus to pass incoming data to GPU RAM
   input wire  [7:0]  gpu_rData,
   input wire  gpu_rd_rdy,       // one-shot signal from mux that data is ready
input wire  sel_pclk,         // make HIGH to trigger the Z80 bus on the positive edge of Z80_CLK
   input wire  sel_nclk,         // make LOW  to trigger the Z80 bus on the negative edge of Z80_CLK
input wire PS2_RDY, // goes HIGH when data is ready from PS2 keyboard on PS2_DAT
input wire [7:0] PS2_DAT, // data from keyboard
input wire Z80_IORQ, // Z80 IORQ - active LOW
input wire Z80_IEI, // if HIGH, Z80_bridge can request interrupt immediately
   
   // output
   output reg  Z80_245data_dir,  // control level converter direction for data flow - HIGH = A -> B (toward FPGA)
   output reg  [7:0]  Z80_rData, // Z80 DATA bus to return data from GPU RAM to Z80
   output reg  Z80_rData_ena,    // flag HIGH to write data back to Z80
   output reg  Z80_245_oe,       // OE for 245 level translator *** ACTIVE LOW ***
   output reg  gpu_wr_ena,       // flag HIGH for 1 clock when writing to GPU RAM
   output reg  gpu_rd_req,       // flag HIGH for 1 clock when reading from GPU RAM
   output reg  [19:0] gpu_addr,  // connect to Z80_addr in vid_osd_generator to address GPU RAM
   output reg  [7:0]  gpu_wdata, // 8-bit data bus to GPU RAM in vid_osd_generator
output reg Z80_INT_REQ, // flag HIGH to signal to host for an interrupt request
output reg Z80_IEO // flagged LOW when GPU is requesting an interrupt
   
);

// TODO:
//
// 1) Respond with appropriate data to any requests from Microcom ROM identification routines
//
// 2) Implement PS2 keyboard interface and interrupt handling (INTACK occurs when M1 goes LOW, then IORQ goes LOW, ends when both go HIGH)
//

parameter MEMORY_RANGE  = 3'b010; // Z80_addr[21:19] == 3'b010 targets the 512KB 'window' at 0x100000-0x17FFFF (Socket 3 on the Microcom)
parameter MEM_SIZE_BITS = 15;    // Specifies maximum address size for the GPU RAM (anything above this returns $FF)
parameter BANK_RESPONSE = 1; // 1 - respond to reads at BANK_ID_ADDR with appropriate data, 0 - ignore reads to that address
parameter BANK_ID_ADDR = 17'b10111111111111111; // Address to respond to BANK_ID queries with data (lowest 4 bits left off)

parameter byte BANK_ID[16] = '{9,3,71,80,85,0,255,255,255,255,255,255,255,255,255,255}; // The BANK_ID data to return

wire Z80_mreq, Z80_write, Z80_read, Write_GPU_RAM;
wire Read_GPU_RAM;
wire Z80_clk_pos,Z80_clk_neg,Z80_clk_trig;

reg Z80_clk_delay, last_Z80_WR, last_Z80_RD, mem_valid_range, mem_window, last_Z80_WR2, last_Z80_WR3, last_Z80_RD2, last_Z80_RD3, bank_id_access;
reg addr_hold[7:0];

assign Z80_clk_pos    = ~Z80_clk_delay &&  Z80_CLK;
assign Z80_clk_neg    =  Z80_clk_delay && ~Z80_CLK;
assign Z80_clk_trig   = (Z80_clk_pos && sel_pclk) || (Z80_clk_neg && ~sel_nclk);

assign Z80_mreq       = ~Z80_MREQn && Z80_M1n;  // Define a bus memory access state
assign Z80_write      = ~Z80_WRn;               // write transaction
assign Z80_read       = ~Z80_RDn;               // read transaction

// Only allow RD/WR to valid GPU RAM space
assign Write_GPU_RAM  =  mem_window && Z80_mreq && Z80_write && mem_valid_range; // Define a GPU Write action - only write to address within GPU RAM bounds
//assign Read_GPU_RAM   =  mem_window && Z80_mreq && Z80_read  && mem_valid_range; // Define the beginning of a Z80 read request of GPU Ram.

// Allow RD/WR to entire 512KB memory window
//assign Write_GPU_RAM  =  mem_window && Z80_mreq && Z80_write; // Define a GPU Write action - only write to address within GPU RAM bounds
assign Read_GPU_RAM   =  mem_window && Z80_mreq && Z80_read; // Define the beginning of a Z80 read request of GPU Ram.

// **********************************************************************************************************

always @ (posedge GPU_CLK) begin

   gpu_addr         <=  Z80_addr[18:0];                        // Latch address bus onto GPU address bus
   mem_valid_range  <= (Z80_addr[18:0]  <  2**MEM_SIZE_BITS);  // Define GPU addressable memory space
   mem_window       <= (Z80_addr[21:19] == MEMORY_RANGE);      // Define an active memory range (this decides which socket the GPU replaces)
bank_id_access   <= (Z80_addr[21:4]  == BANK_ID_ADDR); // Define access to BANK_ID area

   if (Write_GPU_RAM) begin

      Z80_245data_dir  <= 1'b1;       // Set 245 DIR to FPGA
      Z80_rData_ena    <= 1'b0;       // Set FPGA pins to input (should be by default)
      Z80_245_oe       <= 1'b0;       // Enable 245 output
      gpu_wdata        <= Z80_wData;  // Latch data bus onto GPU data bus

   end

   if (Read_GPU_RAM) begin

      Z80_245data_dir  <= 1'b0;       // Set 245 DIR TO Z80
      Z80_245_oe       <= 1'b0;       // Enable 245 output
      Z80_rData_ena    <= 1'b1;       // Set bidir pins to output
addr_hold   <= Z80_addr[7:0];

   end else begin

      Z80_245data_dir  <= 1'b1;       // Set 245 dir toward FPGA
      Z80_rData_ena    <= 1'b0;       // Re-set bidir pins to input

   end

   if (gpu_rd_rdy) begin

      if (mem_valid_range) begin

Z80_rData[7:0] <= gpu_rData[7:0];  // Latch the GPU RAM read into the output register for the Z80

end else begin

if (BANK_RESPONSE && bank_id_access) begin

Z80_rData[7:0] <= BANK_ID[addr_hold[3:0]][7:0]; // Return the appropriate value

end else begin

Z80_rData[7:0] <= 8'b11111111; // return $FF if addressed byte is outside the GPU's upper RAM limit

end

end

   end
   
   Z80_clk_delay  <= Z80_CLK;          // find the rising clock edge

   last_Z80_WR    <= Write_GPU_RAM;
   last_Z80_WR2   <= last_Z80_WR;
   last_Z80_WR3   <= last_Z80_WR2;

   last_Z80_RD    <= Read_GPU_RAM;
   last_Z80_RD2   <= last_Z80_RD;
   last_Z80_RD3   <= last_Z80_RD2;

   gpu_wr_ena     <= Write_GPU_RAM && ~last_Z80_WR2 ; // Pulse the GPU ram's write enable only once after the Z80 write cycle has completed
   gpu_rd_req     <= Read_GPU_RAM  && ~last_Z80_RD2 ; // Pulse the read request only once at the beginning of a read cycle.
   
end

// **********************************************************************************************************

endmodule
« Last Edit: January 27, 2020, 08:26:07 am by nockieboy »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8143
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #984 on: January 27, 2020, 06:59:00 pm »
Why arent you using :

Code: [Select]
Z80_rData[7:0] <= BANK_ID[Z80_addr[3:0]]; // Return the appropriate value
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #985 on: January 27, 2020, 07:01:27 pm »
Why arent you using :

Code: [Select]
Z80_rData[7:0] <= BANK_ID[Z80_addr[3:0]]; // Return the appropriate value

I seem to recall having problems using Z80_addr previously.  The bit of code that assigns the value to Z80_rData executes a couple of clock cycles later, after Z80_addr has been 'captured'.  I'm operating under the assumption that the address may have changed between the read being initiated and the value being returned to the data bus.  Thinking about it, however, this was back when we were having timing troubles with the whole Z80_bridge, so maybe I'm labouring under a false assumption...  :-//

It still doesn't compile, though - even though it successfully compiles symbol files...  ???
« Last Edit: January 27, 2020, 07:02:58 pm by nockieboy »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8143
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #986 on: January 27, 2020, 07:09:29 pm »
Why arent you using :

Code: [Select]
Z80_rData[7:0] <= BANK_ID[Z80_addr[3:0]]; // Return the appropriate value

I seem to recall having problems using Z80_addr previously.  The bit of code that assigns the value to Z80_rData executes a couple of clock cycles later, after Z80_addr has been 'captured'.  I'm operating under the assumption that the address may have changed between the read being initiated and the value being returned to the data bus.  Thinking about it, however, this was back when we were having timing troubles with the whole Z80_bridge, so maybe I'm labouring under a false assumption...  :-//

It still doesn't compile, though - even though it successfully compiles symbol files...  ???
Yes it does compile.  I checked...  Showing your problem lies somewhere else, like with the line:
---------------------------------
reg addr_hold[7:0];
---------------------------------

 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #987 on: January 27, 2020, 07:33:06 pm »
Yes it does compile.  I checked...  Showing your problem lies somewhere else, like with the line:
---------------------------------
reg addr_hold[7:0];
---------------------------------

 |O

Project compiles now.  Silly mistake on my part, as usual..  ;)
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #988 on: January 29, 2020, 10:51:18 am »
Okay, I seem to have another head-scratcher.  :o

I'm working on the PS2 keyboard interface.  I figured it would be easier to test using IO polling initially, rather than the whole interrupt setup as there are more unknowns there.  So the Z80 would be polling the GPU regularly to see if there is a character (or keycode) in the PS2 interface.  To do this, the GPU needs to intercept IO from the host - for the Z80, this is when IORQ and RD go LOW together, much like a memory cycle (but with IORQ instead of MREQ).

Here's the timing diagram for an IO cycle straight from the Z80 datasheet:



It's pretty much identical to a memory cycle, except the Z80 automatically inserts 1 WAIT state into the cycle to give whatever is being addressed time to get its ducks in order. How generous.

So, I've set about modifying the Z80_bridge module to intercept IO cycles targeting a user-specified IO address ($F0 in this case), only looking at the lower half of the address bus as (officially, at least) the Z80 only uses the lower half of the address bus for IO addressing.  Yes, I know about IN A,(C)/OUT (C),A placing the full BC register on the address bus, just in case anyone is thinking about that.

Anyhow, should be simple, right?  I should be able to use the same process as for the memory read cycle in Z80_bridge?

Seems not.  I'm sometimes getting the right value back, but more often than not just getting $78 returned, which is effectively a null return on my system.

I've tried varying the delay between the IORQ cycle being detected, 'arming' the 245 in the right direction and putting the data on the bus, but a 2 cycle delay is a logical minimum to allow the 245 to change direction before driving the data bus, as proven with the previous memory cycle work.

I've tried adding a delay at the end of the IORQ cycle, despite the Z80 datasheets stating zero hold time is required after IORQ/RD goes HIGH at the end of the cycle.

With the code in its current state, I'm rarely getting the desired value returned (should get $AA returned for a valid IO read).

So I'm wondering if there's something stupid I've done in the code that is causing this issue?  It's fairly likely, after all.  :-[

I've also tried to break down the various functions within the always block to make it more readable (for me, anyway!)

I appreciate this sort of issue is a tough one to pin down if it's hardware-related, but I suspect it could be an issue with my HDL or the way I'm trying to do it.

 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #989 on: January 29, 2020, 10:52:19 am »
It won't let me post the code AND an in-line image at the same time...  |O

Code: [Select]
module Z80_bridge (

   // input
   input wire  reset,            // GPU reset signal
   input wire  GPU_CLK,          // GPU clock (125 MHz)
   input wire  Z80_CLK,          // Microcom clock signal (8 MHz)
   input wire  Z80_M1n,          // Z80 M1 - active LOW
   input wire  Z80_MREQn,        // Z80 MREQ - active LOW
   input wire  Z80_WRn,          // Z80 WR - active LOW
   input wire  Z80_RDn,          // Z80 RD - active LOW
   input wire  [21:0] Z80_addr,  // Microcom 22-bit address bus
   input wire  [7:0]  Z80_wData, // Z80 DATA bus to pass incoming data to GPU RAM
   input wire  [7:0]  gpu_rData,
   input wire  gpu_rd_rdy,       // one-shot signal from mux that data is ready
   input wire  sel_pclk,         // make HIGH to trigger the Z80 bus on the positive edge of Z80_CLK
   input wire  sel_nclk,         // make LOW  to trigger the Z80 bus on the negative edge of Z80_CLK
   input wire  PS2_RDY,          // goes HIGH when data is ready from PS2 keyboard on PS2_DAT
   input wire  [7:0] PS2_DAT,    // data from keyboard
   input wire  Z80_IORQn,        // Z80 IORQ - active LOW
   input wire  Z80_IEI,          // if HIGH, Z80_bridge can request interrupt immediately
   
   // output
   output reg  Z80_245data_dir,  // control level converter direction for data flow - HIGH = A -> B (toward FPGA)
   output reg  [7:0]  Z80_rData, // Z80 DATA bus to return data from GPU RAM to Z80
   output reg  Z80_rData_ena,    // flag HIGH to write data back to Z80
   output reg  Z80_245_oe,       // OE for 245 level translator *** ACTIVE LOW ***
   output reg  gpu_wr_ena,       // flag HIGH for 1 clock when writing to GPU RAM
   output reg  gpu_rd_req,       // flag HIGH for 1 clock when reading from GPU RAM
   output reg  [19:0] gpu_addr,  // connect to Z80_addr in vid_osd_generator to address GPU RAM
   output reg  [7:0]  gpu_wdata, // 8-bit data bus to GPU RAM in vid_osd_generator
   output reg  Z80_INT_REQ,      // flag HIGH to signal to host for an interrupt request
   output reg  Z80_IEO           // flag HIGH when GPU is requesting an interrupt to pull IEO LOW
   
);

// TODO:
//
// 1) Implement PS2 keyboard interface and IO-polled / interrupt handling
//

parameter MEMORY_RANGE  = 3'b010;      // Z80_addr[21:19] == 3'b010 targets the 512KB 'window' at 0x100000-0x17FFFF (Socket 3 on the Microcom)
parameter MEM_SIZE_BITS = 15;          // Specifies maximum address size for the GPU RAM (anything above this returns $FF)
parameter BANK_RESPONSE = 1;           // 1 - respond to reads at BANK_ID_ADDR with appropriate data, 0 - ignore reads to that address
parameter BANK_ID_ADDR  = 17'b10111111111111111;      // Address to respond to BANK_ID queries with data (lowest 4 bits left off)

parameter int BANK_ID[16] = '{9,3,71,80,85,32,69,80,52,67,69,54,0,255,255,255};  // The BANK_ID data to return

wire Z80_mreq, Z80_write, Z80_read, Write_GPU_RAM;
wire Read_GPU_RAM;
wire Z80_clk_pos,Z80_clk_neg,Z80_clk_trig;

reg Z80_clk_delay, last_Z80_WR, last_Z80_RD, mem_valid_range, mem_window, last_Z80_WR2, last_Z80_WR3, last_Z80_RD2, last_Z80_RD3, bank_id_access;


// ********************** PS2 Keyboard interface stuff **********************
//
parameter int INT_TYP  = 0;            // 0 = polled (IO), 1 = interrupt
parameter byte INT_VEC = 'h30;         // INTerrupt VECtor to be passed to host in event of an interrupt acknowledge
parameter int IO_DATA  = 240;          // IO address for keyboard polling
parameter int IO_CTRL  = 241;          // IO address for keyboard status
parameter int IO_HOLD  = 2;            // CLK cycles to hold IO data on bus after end of Z80 cycle

wire PS2_READY, IO_DATA_RQ, IO_STAT_RQ, INTA_start, INTA_end, Z80_INTACK;

reg [7:0] PS2_CHAR;
reg PS2_prev, INTACK_prev, IO_DATA_prev, IO_STAT_prev;
reg [4:0] INT_DELAY;
reg [4:0] IO_245_DLY;      // Delay to allow 245 to change direction before putting data onto bus
//reg [6:0] dly_io = 7'b0;
reg IO_hold_clk = 1'b0;       // used to hold the IO cycle a little longer past the end of the real cycle

assign PS2_READY     = PS2_RDY && ~PS2_prev;                                  // HIGH for 1 CLK when valid data is available on PS2_DAT
assign IO_DATA_RQ    = ~Z80_IORQn && (IO_DATA == Z80_addr[7:0]) & ~Z80_RDn;   // HIGH when host is reading data via appropriate IO address
assign IO_STAT_RQ    = ~Z80_IORQn && (IO_CTRL == Z80_addr[7:0]) & ~Z80_RDn;   // HIGH when host is reading status via appropriate IO address

assign Z80_INTACK    = INT_TYP && ~Z80_M1n && ~Z80_IORQn;                     // HIGH when host is acknowledging interrupt
assign INTA_start    = INT_TYP && Z80_INT_REQ && Z80_INTACK && ~INTACK_prev;  // HIGH for 1 CLK when interrupt acknowledge detected
assign INTA_end      = INT_TYP && Z80_INT_REQ && ~Z80_INTACK && INTACK_prev;  // HIGH for 1 CLK when interrupt acknowledge ends
//
// ********************************************************************

assign Z80_clk_pos   = ~Z80_clk_delay &&  Z80_CLK;
assign Z80_clk_neg   =  Z80_clk_delay && ~Z80_CLK;
assign Z80_clk_trig  = (Z80_clk_pos && sel_pclk) || (Z80_clk_neg && ~sel_nclk);

assign Z80_mreq      = ~Z80_MREQn && Z80_M1n;   // Define a bus memory access state
assign Z80_write     = ~Z80_WRn;                // write transaction
assign Z80_read      = ~Z80_RDn;                // read transaction

// Only allow RD/WR to valid GPU RAM space
assign Write_GPU_RAM  =  mem_window && Z80_mreq && Z80_write && mem_valid_range; // Define a GPU Write action - only write to address within GPU RAM bounds
//assign Read_GPU_RAM   =  mem_window && Z80_mreq && Z80_read  && mem_valid_range; // Define the beginning of a Z80 read request of GPU Ram.

// Allow RD/WR to entire 512KB memory window
//assign Write_GPU_RAM  =  mem_window && Z80_mreq && Z80_write;   // Define a GPU Write action - only write to address within GPU RAM bounds
assign Read_GPU_RAM   =  mem_window && Z80_mreq && Z80_read;   // Define the beginning of a Z80 read request of GPU Ram.

// **********************************************************************************************************

always @ (posedge GPU_CLK) begin

   gpu_addr             <=  Z80_addr[18:0];                       // Latch address bus onto GPU address bus
   mem_valid_range      <= (Z80_addr[18:0]  <  2**MEM_SIZE_BITS); // Define GPU addressable memory space
   mem_window           <= (Z80_addr[21:19] == MEMORY_RANGE);     // Define an active memory range (this decides which socket the GPU replaces)
   bank_id_access       <= (Z80_addr[21:4]  == BANK_ID_ADDR);     // Define access to BANK_ID area

   if (PS2_READY) begin       // valid data on PS2_DAT
     
      PS2_CHAR          <= PS2_DAT; // Latch the character into Ps2_char register
     
      // Check IEI here to make sure we can request an interrupt
     
      if (INT_TYP) begin
         Z80_INT_REQ    <= 1'b1;    // Fire off an INTerrupt REQuest
         Z80_IEO        <= 1'b1;    // Pull IEO LOW to prevent anything downstream from requesting interrupts
      end
     
   end
   
   if (Write_GPU_RAM) begin   // *** WRITING TO GPU RAM
     
      Z80_245data_dir   <= 1'b1;       // Set 245 DIR to FPGA
      Z80_rData_ena     <= 1'b0;       // Set FPGA pins to input (should be by default)
      Z80_245_oe        <= 1'b0;       // Enable 245 output
      gpu_wdata         <= Z80_wData;  // Latch data bus onto GPU data bus
     
   end
   
   if (Read_GPU_RAM) begin    // *** READING FROM GPU RAM
     
      Z80_245data_dir   <= 1'b0;       // Set 245 DIR TO Z80
      Z80_245_oe        <= 1'b0;       // Enable 245 output
      Z80_rData_ena     <= 1'b1;       // Set bidir pins to output
     
   end
   
   if (INT_TYP && INT_DELAY[2]) begin  // *** RESPONDING TO INTERRUPT ACKNOWLEDGE
     
      Z80_rData[7:0]    <= INT_VEC;    // Put INT_VEC onto data bus after delay to allow 245 to get itself sorted
     
   end

   if (INTA_start) begin      // *** Valid INTerrupt ACKnowledge detected
     
      Z80_245data_dir   <= 1'b0;       // Set 245 DIR TO Z80
      Z80_245_oe        <= 1'b0;       // Enable 245 output
      Z80_rData_ena     <= 1'b1;       // Set bidir pins to output
      INT_DELAY[0]      <= 1'b1;       // Start delay timer
     
   end
   else if (INTA_end) begin   // *** End of valid INTerrupt cycle
     
      Z80_245data_dir   <= 1'b1;       // Set 245 dir toward FPGA
      Z80_rData_ena     <= 1'b0;       // Re-set bidir pins to input
      Z80_INT_REQ       <= 1'b0;       // Cancel INTerrupt REQuest
      Z80_IEO           <= 1'b0;       // Allow downstream interrupt requests again
     
   end

   if (gpu_rd_rdy) begin
     
      if (mem_valid_range) begin
         
         Z80_rData[7:0] <= gpu_rData[7:0];  // Latch the GPU RAM read into the output register for the Z80
         
      end else begin
         
         if (BANK_RESPONSE && bank_id_access) begin
           
            Z80_rData[7:0] <= BANK_ID[Z80_addr[3:0]]; // Return the appropriate value
           
         end else begin
           
            Z80_rData[7:0] <= 8'b11111111;   // return $FF if addressed byte is outside the GPU's upper RAM limit
           
         end
         
      end
     
   end
   
   // *** START of IO cycle requesting PS2_CHAR byte
   if (~INT_TYP && IO_DATA_RQ && ~IO_DATA_prev) begin
     
      Z80_245data_dir   <= 1'b0;       // Set 245 DIR TO Z80
      Z80_245_oe        <= 1'b0;       // Enable 245 output
      Z80_rData_ena     <= 1'b1;       // Set bidir pins to output
      IO_245_DLY[0]     <= 1'b1;       // Start delay timer before putting data on bus
      IO_hold_clk       <= 1'b1;       // Enable IO hold
     
   end
   
   // *** RESPONDING TO IO READ
   if (~INT_TYP && IO_DATA_RQ && IO_245_DLY[2]) begin
     
      //Z80_rData[7:0]     <= PS2_CHAR;   // Put PS2_CHAR onto data bus after delay to allow 245 to get itself sorted
      Z80_rData[7:0]    <= 'hAA;       // Put static value on bus to test
     
   end
   
   // *** END of IO cycle
   if (~INT_TYP && ~IO_DATA_RQ && IO_DATA_prev) begin
     
      Z80_245data_dir   <= 1'b1;       // Set 245 dir toward FPGA
      Z80_rData_ena     <= 1'b0;       // Re-set bidir pins to input
      PS2_CHAR          <= 8'b0;       // Reset PS_CHAR value
      //dly_io[0]          <= 1'b1;       // Trigger IO shutoff delay
      IO_hold_clk       <= 1'b0;       // Release IO hold
     
   end
   
   // *** DELAYED END of IO cycle
   /*if (~INT_TYP && IO_hold_clk && dly_io[IO_HOLD]) begin
     
      Z80_245data_dir   <= 1'b1;       // Set 245 dir toward FPGA
      Z80_rData_ena     <= 1'b0;       // Re-set bidir pins to input
      PS2_CHAR          <= 8'b0;       // Reset PS_CHAR value
      IO_hold_clk       <= 1'b0;       // Release IO hold
     
   end*/
   
   //
   // *** IF NO INTERRUPT, MEMORY OR IO READ OPERATIONS ARE ONGOING, ENFORCE DATA BUS DEFAULTS ***
   //
   if ((~INT_TYP || ~Z80_INT_REQ) && ~Read_GPU_RAM && ~IO_hold_clk) begin
     
      Z80_245data_dir   <= 1'b1;       // Set 245 dir toward FPGA
      Z80_rData_ena     <= 1'b0;       // Re-set bidir pins to input
      Z80_IEO           <= ~Z80_IEI;   // Pass-through Interrupt Enable flag from upstream devices
     
   end
   
   //
   // *** FLAG & SIGNAL MAINTENANCE ***
   //
   
   // CLOCK EDGE DETECT
   Z80_clk_delay  <= Z80_CLK;

   // WRITE EDGE DETECT
   last_Z80_WR    <= Write_GPU_RAM;
   last_Z80_WR2   <= last_Z80_WR;
   last_Z80_WR3   <= last_Z80_WR2;

   // READ PROCESSING
   last_Z80_RD    <= Read_GPU_RAM;
   last_Z80_RD2   <= last_Z80_RD;
   last_Z80_RD3   <= last_Z80_RD2;
   
   // IO EDGE DETECT & PIPELINE
   IO_DATA_prev   <= IO_DATA_RQ;
   IO_STAT_prev   <= IO_STAT_RQ;
   IO_245_DLY[4:1]<= IO_245_DLY[3:0];
   //dly_io[6:1]     <= dly_io[5:0];
   
   // INTERRUPT EDGE DETECT & PIPELINE
   INTACK_prev    <= Z80_INTACK;
   INT_DELAY[4:1] <= INT_DELAY[3:0];
   
   // PS2 KEYBOARD EDGE DETECT
   PS2_prev       <= PS2_RDY;

   // GPU RAM FLAGS
   gpu_wr_ena     <= Write_GPU_RAM && ~last_Z80_WR2 ; // Pulse the GPU ram's write enable only once after the Z80 write cycle has completed
   gpu_rd_req     <= Read_GPU_RAM  && ~last_Z80_RD2 ; // Pulse the read request only once at the beginning of a read cycle.
   
end

// **********************************************************************************************************

endmodule

 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #990 on: January 29, 2020, 12:13:49 pm »
Okay, I've made an improvement.  ;D

I'm now getting mostly the intended value back, but it's still not 100%.



Now I'm getting an error in 1 in 7 results, approximately.  I've just cleaned up some of the IF... conditionals and completely removed any delay holding data on the bus after the end of the IORQ cycle.  Code in next post.
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #991 on: January 29, 2020, 12:14:21 pm »
Code: [Select]
module Z80_bridge (

   // input
   input wire  reset,            // GPU reset signal
   input wire  GPU_CLK,          // GPU clock (125 MHz)
   input wire  Z80_CLK,          // Microcom clock signal (8 MHz)
   input wire  Z80_M1n,          // Z80 M1 - active LOW
   input wire  Z80_MREQn,        // Z80 MREQ - active LOW
   input wire  Z80_WRn,          // Z80 WR - active LOW
   input wire  Z80_RDn,          // Z80 RD - active LOW
   input wire  [21:0] Z80_addr,  // Microcom 22-bit address bus
   input wire  [7:0]  Z80_wData, // Z80 DATA bus to pass incoming data to GPU RAM
   input wire  [7:0]  gpu_rData,
   input wire  gpu_rd_rdy,       // one-shot signal from mux that data is ready
   input wire  sel_pclk,         // make HIGH to trigger the Z80 bus on the positive edge of Z80_CLK
   input wire  sel_nclk,         // make LOW  to trigger the Z80 bus on the negative edge of Z80_CLK
   input wire  PS2_RDY,          // goes HIGH when data is ready from PS2 keyboard on PS2_DAT
   input wire  [7:0] PS2_DAT,    // data from keyboard
   input wire  Z80_IORQn,        // Z80 IORQ - active LOW
   input wire  Z80_IEI,          // if HIGH, Z80_bridge can request interrupt immediately
   
   // output
   output reg  Z80_245data_dir,  // control level converter direction for data flow - HIGH = A -> B (toward FPGA)
   output reg  [7:0]  Z80_rData, // Z80 DATA bus to return data from GPU RAM to Z80
   output reg  Z80_rData_ena,    // flag HIGH to write data back to Z80
   output reg  Z80_245_oe,       // OE for 245 level translator *** ACTIVE LOW ***
   output reg  gpu_wr_ena,       // flag HIGH for 1 clock when writing to GPU RAM
   output reg  gpu_rd_req,       // flag HIGH for 1 clock when reading from GPU RAM
   output reg  [19:0] gpu_addr,  // connect to Z80_addr in vid_osd_generator to address GPU RAM
   output reg  [7:0]  gpu_wdata, // 8-bit data bus to GPU RAM in vid_osd_generator
   output reg  Z80_INT_REQ,      // flag HIGH to signal to host for an interrupt request
   output reg  Z80_IEO           // flag HIGH when GPU is requesting an interrupt to pull IEO LOW
   
);

// TODO:
//
// 1) Implement PS2 keyboard interface and IO-polled / interrupt handling
//

parameter MEMORY_RANGE  = 3'b010;      // Z80_addr[21:19] == 3'b010 targets the 512KB 'window' at 0x100000-0x17FFFF (Socket 3 on the Microcom)
parameter MEM_SIZE_BITS = 15;          // Specifies maximum address size for the GPU RAM (anything above this returns $FF)
parameter BANK_RESPONSE = 1;           // 1 - respond to reads at BANK_ID_ADDR with appropriate data, 0 - ignore reads to that address
parameter BANK_ID_ADDR  = 17'b10111111111111111;      // Address to respond to BANK_ID queries with data (lowest 4 bits left off)

parameter int BANK_ID[16] = '{9,3,71,80,85,32,69,80,52,67,69,54,0,255,255,255};  // The BANK_ID data to return

wire Z80_mreq, Z80_write, Z80_read, Write_GPU_RAM;
wire Read_GPU_RAM;
wire Z80_clk_pos,Z80_clk_neg,Z80_clk_trig;

reg Z80_clk_delay, last_Z80_WR, last_Z80_RD, mem_valid_range, mem_window, last_Z80_WR2, last_Z80_WR3, last_Z80_RD2, last_Z80_RD3, bank_id_access;


// ********************** PS2 Keyboard interface stuff **********************
//
parameter int INT_TYP  = 0;            // 0 = polled (IO), 1 = interrupt
parameter byte INT_VEC = 'h30;         // INTerrupt VECtor to be passed to host in event of an interrupt acknowledge
parameter int IO_DATA  = 240;          // IO address for keyboard polling
parameter int IO_CTRL  = 241;          // IO address for keyboard status
parameter int IO_HOLD  = 2;            // CLK cycles to hold IO data on bus after end of Z80 cycle

wire PS2_READY, IO_DATA_RQ, IO_STAT_RQ, INTA_start, INTA_end, Z80_INTACK;

reg [7:0] PS2_CHAR;
reg PS2_prev, INTACK_prev, IO_DATA_prev, IO_STAT_prev;
reg [4:0] INT_DELAY;
reg [4:0] IO_245_DLY;      // Delay to allow 245 to change direction before putting data onto bus
//reg [6:0] dly_io = 7'b0;
reg IO_hold_clk = 1'b0;       // used to hold the IO cycle a little longer past the end of the real cycle

assign PS2_READY     = PS2_RDY && ~PS2_prev;                                  // HIGH for 1 CLK when valid data is available on PS2_DAT
assign IO_DATA_RQ    = ~Z80_IORQn && (IO_DATA == Z80_addr[7:0]) & ~Z80_RDn;   // HIGH when host is reading data via appropriate IO address
assign IO_STAT_RQ    = ~Z80_IORQn && (IO_CTRL == Z80_addr[7:0]) & ~Z80_RDn;   // HIGH when host is reading status via appropriate IO address

assign Z80_INTACK    = INT_TYP && ~Z80_M1n && ~Z80_IORQn;                     // HIGH when host is acknowledging interrupt
assign INTA_start    = INT_TYP && Z80_INT_REQ && Z80_INTACK && ~INTACK_prev;  // HIGH for 1 CLK when interrupt acknowledge detected
assign INTA_end      = INT_TYP && Z80_INT_REQ && ~Z80_INTACK && INTACK_prev;  // HIGH for 1 CLK when interrupt acknowledge ends
//
// ********************************************************************

assign Z80_clk_pos   = ~Z80_clk_delay &&  Z80_CLK;
assign Z80_clk_neg   =  Z80_clk_delay && ~Z80_CLK;
assign Z80_clk_trig  = (Z80_clk_pos && sel_pclk) || (Z80_clk_neg && ~sel_nclk);

assign Z80_mreq      = ~Z80_MREQn && Z80_M1n;   // Define a bus memory access state
assign Z80_write     = ~Z80_WRn;                // write transaction
assign Z80_read      = ~Z80_RDn;                // read transaction

// Only allow RD/WR to valid GPU RAM space
assign Write_GPU_RAM  =  mem_window && Z80_mreq && Z80_write && mem_valid_range; // Define a GPU Write action - only write to address within GPU RAM bounds
//assign Read_GPU_RAM   =  mem_window && Z80_mreq && Z80_read  && mem_valid_range; // Define the beginning of a Z80 read request of GPU Ram.

// Allow RD/WR to entire 512KB memory window
//assign Write_GPU_RAM  =  mem_window && Z80_mreq && Z80_write;   // Define a GPU Write action - only write to address within GPU RAM bounds
assign Read_GPU_RAM   =  mem_window && Z80_mreq && Z80_read;   // Define the beginning of a Z80 read request of GPU Ram.

// **********************************************************************************************************

always @ (posedge GPU_CLK) begin

   gpu_addr             <=  Z80_addr[18:0];                       // Latch address bus onto GPU address bus
   mem_valid_range      <= (Z80_addr[18:0]  <  2**MEM_SIZE_BITS); // Define GPU addressable memory space
   mem_window           <= (Z80_addr[21:19] == MEMORY_RANGE);     // Define an active memory range (this decides which socket the GPU replaces)
   bank_id_access       <= (Z80_addr[21:4]  == BANK_ID_ADDR);     // Define access to BANK_ID area

   if (PS2_READY) begin       // valid data on PS2_DAT
     
      PS2_CHAR          <= PS2_DAT; // Latch the character into Ps2_char register
     
      // Check IEI here to make sure we can request an interrupt
     
      if (INT_TYP) begin
         Z80_INT_REQ    <= 1'b1;    // Fire off an INTerrupt REQuest
         Z80_IEO        <= 1'b1;    // Pull IEO LOW to prevent anything downstream from requesting interrupts
      end
     
   end
   
   if (Write_GPU_RAM) begin   // *** WRITING TO GPU RAM
     
      Z80_245data_dir   <= 1'b1;       // Set 245 DIR to FPGA
      Z80_rData_ena     <= 1'b0;       // Set FPGA pins to input (should be by default)
      Z80_245_oe        <= 1'b0;       // Enable 245 output
      gpu_wdata         <= Z80_wData;  // Latch data bus onto GPU data bus
     
   end
   
   if (Read_GPU_RAM) begin    // *** READING FROM GPU RAM
     
      Z80_245data_dir   <= 1'b0;       // Set 245 DIR TO Z80
      Z80_245_oe        <= 1'b0;       // Enable 245 output
      Z80_rData_ena     <= 1'b1;       // Set bidir pins to output
     
   end
   
   if (INT_TYP && INT_DELAY[2]) begin  // *** RESPONDING TO INTERRUPT ACKNOWLEDGE
     
      Z80_rData[7:0]    <= INT_VEC;    // Put INT_VEC onto data bus after delay to allow 245 to get itself sorted
     
   end

   if (INTA_start) begin      // *** Valid INTerrupt ACKnowledge detected
     
      Z80_245data_dir   <= 1'b0;       // Set 245 DIR TO Z80
      Z80_245_oe        <= 1'b0;       // Enable 245 output
      Z80_rData_ena     <= 1'b1;       // Set bidir pins to output
      INT_DELAY[0]      <= 1'b1;       // Start delay timer
     
   end
   else if (INTA_end) begin   // *** End of valid INTerrupt cycle
     
      Z80_245data_dir   <= 1'b1;       // Set 245 dir toward FPGA
      Z80_rData_ena     <= 1'b0;       // Re-set bidir pins to input
      Z80_INT_REQ       <= 1'b0;       // Cancel INTerrupt REQuest
      Z80_IEO           <= 1'b0;       // Allow downstream interrupt requests again
     
   end

   if (gpu_rd_rdy) begin
     
      if (mem_valid_range) begin
         
         Z80_rData[7:0] <= gpu_rData[7:0];  // Latch the GPU RAM read into the output register for the Z80
         
      end else begin
         
         if (BANK_RESPONSE && bank_id_access) begin
           
            Z80_rData[7:0] <= BANK_ID[Z80_addr[3:0]]; // Return the appropriate value
           
         end else begin
           
            Z80_rData[7:0] <= 8'b11111111;   // return $FF if addressed byte is outside the GPU's upper RAM limit
           
         end
         
      end
     
   end
   
   // *** START of IO cycle requesting PS2_CHAR byte
   if (IO_DATA_RQ && ~IO_DATA_prev) begin
     
      Z80_245data_dir   <= 1'b0;       // Set 245 DIR TO Z80
      Z80_245_oe        <= 1'b0;       // Enable 245 output
      Z80_rData_ena     <= 1'b1;       // Set bidir pins to output
      IO_245_DLY[0]     <= 1'b1;       // Start delay timer before putting data on bus
      //IO_hold_clk       <= 1'b1;       // Enable IO hold
     
   end
   
   // *** RESPONDING TO IO READ
   if (IO_DATA_RQ && IO_245_DLY[2]) begin
     
      //Z80_rData[7:0]     <= PS2_CHAR;   // Put PS2_CHAR onto data bus after delay to allow 245 to get itself sorted
      Z80_rData[7:0]    <= 'hAA;       // Put static value on bus to test
     
   end
   
   // *** END of IO cycle
   if (~IO_DATA_RQ && IO_DATA_prev) begin
     
      Z80_245data_dir   <= 1'b1;       // Set 245 dir toward FPGA
      Z80_rData_ena     <= 1'b0;       // Re-set bidir pins to input
      PS2_CHAR          <= 8'b0;       // Reset PS_CHAR value
      //dly_io[0]          <= 1'b1;       // Trigger IO shutoff delay
      //IO_hold_clk       <= 1'b0;       // Release IO hold
     
   end
   
   // *** DELAYED END of IO cycle
   /*if (~INT_TYP && IO_hold_clk && dly_io[IO_HOLD]) begin
     
      Z80_245data_dir   <= 1'b1;       // Set 245 dir toward FPGA
      Z80_rData_ena     <= 1'b0;       // Re-set bidir pins to input
      PS2_CHAR          <= 8'b0;       // Reset PS_CHAR value
      IO_hold_clk       <= 1'b0;       // Release IO hold
     
   end*/
   
   //
   // *** IF NO INTERRUPT, MEMORY OR IO READ OPERATIONS ARE ONGOING, ENFORCE DATA BUS DEFAULTS ***
   //
   if (~Z80_INT_REQ && ~Read_GPU_RAM && ~IO_DATA_RQ) begin
     
      Z80_245data_dir   <= 1'b1;       // Set 245 dir toward FPGA
      Z80_rData_ena     <= 1'b0;       // Re-set bidir pins to input
      Z80_IEO           <= ~Z80_IEI;   // Pass-through Interrupt Enable flag from upstream devices
     
   end
   
   //
   // *** FLAG & SIGNAL MAINTENANCE ***
   //
   
   // CLOCK EDGE DETECT
   Z80_clk_delay  <= Z80_CLK;

   // WRITE EDGE DETECT
   last_Z80_WR    <= Write_GPU_RAM;
   last_Z80_WR2   <= last_Z80_WR;
   last_Z80_WR3   <= last_Z80_WR2;

   // READ PROCESSING
   last_Z80_RD    <= Read_GPU_RAM;
   last_Z80_RD2   <= last_Z80_RD;
   last_Z80_RD3   <= last_Z80_RD2;
   
   // IO EDGE DETECT & PIPELINE
   IO_DATA_prev   <= IO_DATA_RQ;
   IO_STAT_prev   <= IO_STAT_RQ;
   IO_245_DLY[4:1]<= IO_245_DLY[3:0];
   //dly_io[6:1]     <= dly_io[5:0];
   
   // INTERRUPT EDGE DETECT & PIPELINE
   INTACK_prev    <= Z80_INTACK;
   INT_DELAY[4:1] <= INT_DELAY[3:0];
   
   // PS2 KEYBOARD EDGE DETECT
   PS2_prev       <= PS2_RDY;

   // GPU RAM FLAGS
   gpu_wr_ena     <= Write_GPU_RAM && ~last_Z80_WR2 ; // Pulse the GPU ram's write enable only once after the Z80 write cycle has completed
   gpu_rd_req     <= Read_GPU_RAM  && ~last_Z80_RD2 ; // Pulse the read request only once at the beginning of a read cycle.
   
end

// **********************************************************************************************************

endmodule
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #992 on: January 29, 2020, 12:35:20 pm »
^-^ So I seem to have sorted it myself, though I'm not sure why.  I've removed all the terms from the IF... conditionals checking for IO cycle start, data release, end with wires that do the comparisons, as that was the only difference I could see between my IO conditionals and the memory ones.

So:

Code: [Select]
IF (IO_DATA_REQ && ~IO_DATA_prev) begin

has now changed to (only relevant lines shown - obviously the WIRE assignment doesn't appear in the ALWAYS block!):

Code: [Select]
assign IO_DATA_ST = IO_DATA_RQ && ~IO_DATA_prev; // HIGH for 1 CLK when valid IO_DATA cycle starts

if (IO_DATA_ST) begin

Now I seem to be getting reliable values returned every time.  If anyone can explain why, I'd like to know what the difference is!  :popcorn:
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8143
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #993 on: April 25, 2020, 02:46:13 am »
So, has anything new progressed?

Or, are you on covid - save all your pennies/spend all of your time with the family lockdown?
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #994 on: April 25, 2020, 10:15:26 am »
So, has anything new progressed?

Or, are you on covid - save all your pennies/spend all of your time with the family lockdown?

Hey!

Well, no news to report in terms of features or software developments (other than getting CP/M drivers working nicely so I can use all the various programs there with proper screen output).  Yes, lockdown has been in force here since mid-March, which has affected our post and sanity, but I got all the parts together to make the video card in the last few days, so I'm incrementally building it and testing as I go to make sure there's no problems.  Amazingly, the cheap EP4CE10's I got from China actually appear to be EP4CE10's...  :phew:

I have it running the GPU HDL, communicating nicely with the rs232_debugger software and playing tunes on the buzzer/speaker via a single IO.  I soldered the 245's onto the board last night to connect the FPGA up to the Z80's bus to see if I could read the GPU's RAM from the Z80, but all I'm getting is $7E's where I should be getting the first page of video RAM.  I didn't have time to take it any further last night, but whilst sleeping(!) wondered if it's because the test system I'm using is running at 4 MHz instead of 8?  Don't know if that would mess with the z80_bridge timings at all and cause the Z80 to miss the data the FPGA is putting back onto the data bus?

Anyhow, I'm going to look at that today if I get the chance - switch the uCOM over to 8 MHz and test it again, and if that still doesn't work then I'll test all the connections between the FPGA chip itself and the address & control buses to see if there's any bad connections preventing the FPGA detecting RD/WRs to it.  Will update as soon as I can with developments.  :-+

EDIT:
No - bumping the system clock up to 8 MHz makes no difference.  I'm able to turn on/off the sound output via an OUT command to the appropriate IO port, so it would appear the lower (A0-A7) part of the address bus and data bus is working properly, but it seems to me that getting no data back when trying to read the GPU RAM could be an issue with the middle/upper addresses not getting through properly.  Will continue looking at these.

EDIT 2:
The Z80 is able to write data to the GPU RAM - I've been POKE-ing values to the GPU RAM and can see the changes in the RS232_debugger, but the Z80 cannot read the values back - all I'm getting back is $7E, which is what I get when there's no chip in the socket where the memory address resides.  Could this be a timing issue?
« Last Edit: April 25, 2020, 07:08:05 pm by nockieboy »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8143
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #995 on: April 26, 2020, 04:13:15 am »

EDIT 2:
The Z80 is able to write data to the GPU RAM - I've been POKE-ing values to the GPU RAM and can see the changes in the RS232_debugger, but the Z80 cannot read the values back - all I'm getting back is $7E, which is what I get when there's no chip in the socket where the memory address resides.  Could this be a timing issue?

1. Check with a magnifying glass your soldering.
2. Check with ohm meter for continuity on the OE and DIR lines of the 245s.
3. Go with an ohm meter and make sure your IO buffer's OE/DIR aren't shorted to adjacent pins on the FPGA, or shorted to GND/VCC.
4. Double check the schematic by pin number to the Quartus pinout file since I think we moved some IOs to adapt to the PCB layout.
5.  Verify that the new IOs on the larger CycloneIV are valid free IO.  (You only need to concern yourself with the OE and DIR signals for the 245s as the write function works.
6.  Make sure during pin number swapping, you didn't accidentally label/number the OE and DIR controls coming out of the FPGA backwards.

This type of possible problem is normal with a first time brand new PCB.

Lastly, you will need to scope the 245's OE/DIR as the 'Chinese Cyclone' may have a damaged IO pin.  Make sure the signal has a good 3v high and 0v low.
« Last Edit: April 26, 2020, 04:17:49 am by BrianHG »
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #996 on: April 26, 2020, 10:40:11 am »
1. Check with a magnifying glass your soldering.
2. Check with ohm meter for continuity on the OE and DIR lines of the 245s.
3. Go with an ohm meter and make sure your IO buffer's OE/DIR aren't shorted to adjacent pins on the FPGA, or shorted to GND/VCC.
4. Double check the schematic by pin number to the Quartus pinout file since I think we moved some IOs to adapt to the PCB layout.

Have done these already - there's no issues with continuity between the FPGA and 245 for OE or DIR signals, no shorts that I can find.

5.  Verify that the new IOs on the larger CycloneIV are valid free IO.  (You only need to concern yourself with the OE and DIR signals for the 245s as the write function works.

Hmm.. well they appear to be valid IO according to the Pin Planner.  Where else do I need to look to check these?  I'm wading through datasheets and handbooks...

6.  Make sure during pin number swapping, you didn't accidentally label/number the OE and DIR controls coming out of the FPGA backwards.

Have checked and double-checked this, all is good.

This type of possible problem is normal with a first time brand new PCB.

That's reassuring, but it's still frustrating!

Lastly, you will need to scope the 245's OE/DIR as the 'Chinese Cyclone' may have a damaged IO pin.  Make sure the signal has a good 3v high and 0v low.

Yes, so this is the next thing - will require some setup with my logic analyser and I'll check it with that first.  My scope won't touch this sort of issue unless I specifically write some HDL to repeatedly switch the DIR pin at a frequency that the scope can see and allow me to get a stable trace on the screen.

I've included the latest version of the project in a zip below in case it's useful.
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #997 on: April 26, 2020, 11:12:07 am »
Hang on...

Code: [Select]
   if (Write_GPU_RAM) begin   // *** WRITING TO GPU RAM
     
      Z80_245data_dir   <= 1'b1;       // Set 245 DIR to FPGA
      Z80_rData_ena     <= 1'b0;       // Set FPGA pins to input (should be by default)
      Z80_245_oe        <= 1'b0;       // Enable 245 output
      gpu_wdata         <= Z80_wData;  // Latch data bus onto GPU data bus
     
   end
   
   if (Read_GPU_RAM) begin    // *** READING FROM GPU RAM
     
      Z80_245data_dir   <= 1'b0;       // Set 245 DIR TO Z80
      Z80_245_oe        <= 1'b0;       // Enable 245 output
      Z80_rData_ena     <= 1'b1;       // Set bidir pins to output
     
   end

Above code is from z80_bridge.sv.

For a GPU RAM WRITE from the Z80, it's setting Z80_245data_dir HIGH.  That's setting the 245 to A>B direction.  This is fine for the prototype, because I've got the Z80 data bus on the A side, HOWEVER on the PCB I've got the FPGA on the A side.  :palm:

So if I'm reading it right, Z80_245data_dir is being set to the opposite value of what is required.  Amazing that I was able to write data to the GPU RAM at all...  :wtf:  Unless the 245's DIR pin never goes HIGH at all, in which case it won't matter what value Z80_245data_dir is set to, it'll always be sending data TO the FPGA.

I've reversed the values for Z80_245data_dir in the HDL to accommodate the change in layout of the PCB, but still not able to read anything from GPU RAM.  Strangely, I'm able to write to GPU RAM (and I can see the uCOM's bootstrap setting up the GPU for text display mode) but any block copies (using LDIR) just seem to write 0xB0 to every address, rather than 0x00 or any other value.

EDIT: Updated Z80_bridge.sv attached.
« Last Edit: April 26, 2020, 11:17:42 am by nockieboy »
 

Offline lintweaker

  • Contributor
  • Posts: 23
  • Country: nl
Re: FPGA VGA Controller for 8-bit computer
« Reply #998 on: April 27, 2020, 11:49:36 am »
Oh, yes. Those level converters with DIR can make things a lot harder...
It took me months to get a Z80 to read stuff back from peripherals living in the FPGA. After going back to the basics (simple LED with status readback) I've managed to get it working.
I am using TI SN74LVC8T245DWR or SN74LVC8T245PWRs as level converters with the 5V Z80 stuff on the 'B' side.

Did you put your project on github? I am interested in using it.

 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #999 on: April 27, 2020, 12:24:50 pm »
Did you put your project on github? I am interested in using it.

Yes - here it is: https://github.com/nockieboy/gpu  :)
 
The following users thanked this post: oPossum


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf