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

0 Members and 5 Guests are viewing this topic.

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8144
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #425 on: November 21, 2019, 04:41:36 pm »

I've just thrown this code together really, but the more I think about it the more I worry that there could be timing problems etc.  Should I run the code off of the Z80's 8 MHz clock or the much faster GPU clock?  Are there issues with how I'm setting/resetting h_wr_ena? etc..  :-//

Code: [Select]
module host_handler (

// input
input wire reset, // GPU reset signal
input wire host_CLK, // Microcom clock signal (8 MHz)
input wire host_M1, // Z80 M1 - active LOW
input wire host_MREQ, // Z80 MREQ - active LOW
input wire host_WR, // Z80 WR - active LOW
input wire host_RD, // Z80 RD - active LOW
input wire [21:0] host_addr, // Microcom 22-bit address bus
input wire [7:0] host_wr_data, // Z80 DATA bus to pass incoming data to GPU RAM

// output
output wire [7:0] h_rd_data, // Z80 DATA bus to return data from GPU RAM to Z80
output reg          h_rd_req, //
output reg          h_wr_ena, // flag HIGH when writing to GPU RAM
output wire [19:0] h_addr, // connect to host_addr in vid_osd_generator to address GPU RAM
output reg  [7:0]  h_wdata // 8-bit data bus to GPU RAM in vid_osd_generator

);

always @ (posedge host_CLK)
begin

// is a WR memory cycle being executed?
if (!host_M1 && host_MREQ && host_WR) begin

// is the GPU being addressed?
if (host_addr[21:19] == 3'b011) begin // host_addr[21:19] == 3'b011 targets the 512KB 'window' at 0x180000-0x1FFFFF

h_wdata <= host_wr_data;
h_addr <= host_addr[18:0];
h_wr_ena <= 1;

end // gpu addressed
else begin
h_wr_ena <= 0;
end // gpu not addressed

end // memory cycle
else begin

h_wr_ena <= 0;

end

end

Show me a Z80 data sheet write and read cycle waveform example.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8144
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #426 on: November 21, 2019, 05:10:57 pm »
LOL, here you go:

876342-0

876346-1

I shoved your GPU project into my 10 year old 15K$  video scaler:

876350-2

Your GPU is driving a 30bit color HDMI output, with HDCP encryption... LOL, talk about overkill.  It's using around 2% of the onboard FPGA + -110% of the 2 x 1GB DDR2 sodim modules mounted under the PCB.  My project went from this:

876354-3

To now this:

876358-4
« Last Edit: November 21, 2019, 05:15:09 pm by BrianHG »
 

Offline lawrence11

  • Frequent Contributor
  • **
  • !
  • Posts: 322
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #427 on: November 21, 2019, 07:14:10 pm »
NOTE: This message has been deleted by the forum moderator Simon for being against the forum rules and/or at the discretion of the moderator as being in the best interests of the forum community and the nature of the thread.
If you believe this to be in error, please contact the moderator involved.
An optional additional explanation is:
« Last Edit: November 21, 2019, 09:42:08 pm by Simon »
 

Offline jhpadjustable

  • Frequent Contributor
  • **
  • Posts: 295
  • Country: us
  • Salt 'n' pepper beard
Re: FPGA VGA Controller for 8-bit computer
« Reply #428 on: November 21, 2019, 09:36:47 pm »
Shoved? Shouldn't that be more like thrown? :)

Show me a Z80 data sheet write and read cycle waveform example.

Aloow me to play librarian for a sec...



~M1 (not shown) is an active low instruction fetch cycle type identifier signal. It is asserted during an instruction fetch and negated otherwise.

nockieboy's logic looks alright, without even looking at timing.
"There are more things in heaven and earth, Arduino, than are dreamt of in your philosophy."
 
The following users thanked this post: BrianHG, nockieboy

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8144
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #429 on: November 21, 2019, 11:26:09 pm »
Shoved? Shouldn't that be more like thrown? :)

Show me a Z80 data sheet write and read cycle waveform example.

Aloow me to play librarian for a sec...

(Attachment Link)

~M1 (not shown) is an active low instruction fetch cycle type identifier signal. It is asserted during an instruction fetch and negated otherwise.

nockieboy's logic looks alright, without even looking at timing.
Oh boy, that's a joke easy.  What I was getting at looking at that illustration, create a synchronous 125mhz core clocked interface which looks like a static ram access to the host Z80 cpu, but, only eats a single 125mhz cycle from the FPGA 125mhz ram.  Hint, it pretty much looks like my 5 port design, but without the 5 cycle pixel phase.  Since the Z80 only cycles once every 3 8Mhz clock cycles, there is no need to eat up the FPGA at all.


Write Example:
Code: [Select]
-------------------------------------------------------------------------
output regs fpga_mem_addr/data/write_ena... ect

Always @ (posedge 125MHz clock ...) begin

     last_Z80wr <= Z80wr;  // This is part 1 of the secret sauce

//           *** This is part 2 of the secret sauce ***
          if (   (~Z80nwr && last_Z80nwr)    &&  ~Z80mreq  && (good page address)  ) begin    // trap the high to low transition of the Z80wr, for a single 125MHz clock event as soon as the Z80wr input falls

               fpga_mem_addr       <= Z80addr;    // register latch the z80 address and
               fpga_write_data     <= Z80data;    // data only on that falling edge of the Z80wr
               fpga_write_ena      <= 1;           // turn on the ram write request

          end else fpga_write_ena <= 0;      // at the next 125mhz clock cycle, turn off the FPGA memory write request, IE the write pulse is only a 1 clock pulsed cycle

end
-------------------------------------------------------------
(Now, this code works good since 125Mhz runs at least 4 fold faster than the Z80 8MHz buss clock.)

No Z80 clock.  FPGA ram and interface module runs at 125Mhz.  This also leaves the FPGA ram empty for around 45 additional reads or writes in between each Z80 access which will be put to use by my RS232 Debugger and the graphics accelerated drawing hardware lateron.

Now, just like I trapped the falling edge of the Z80we, you can also trap the rising or falling edge of the Z80clk inside the 'IF' as well, or, you can double register ALL the Z80 inputs.  The first set using the Z80 clock, then trigger from those registered inputs at the 125mhz clock with the single pulse transition detection trick.

The read would be a second 'IF()begin' after the write, except you would pulse out a read request to the FPGA ram, latch the data coming back from the FPGA ram.  Only the MREQ and RD, plus valid address range would switch the Z80 data port on the FPGA IO's output enable.
« Last Edit: November 22, 2019, 08:03:11 am by BrianHG »
 
The following users thanked this post: nockieboy

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #430 on: November 22, 2019, 09:39:59 am »
This is the final RS232_Debugger update before I make it's own dedicated thread.  I've added page read bursts into the communications protocol.  The read and write the the GPU now operate at full speed unhinged.  This means with 128 kilobytes in the GPU, reading or writing the full memory contents should take under 2 seconds.

Let me know if it loads and sends the full 16k any faster...

I've also made the VGA 8x16 font default.

Works fine - yes, seems to load faster than previous version and is pretty darn quick in any case.  :-+

Your doing ok, and for now, maybe you should keep working like this.
But, you are going to need to begin to eventually think along operating in 2 different clock domains.

1) You have a Z80 operating at 8Mhz.  It is sending you impossibly slow read and write requests.
2) Your static ram in an FPGA can operating at 125 Mhz.
3) How can I keep it running that fast and still run the Z80's 8Mhz requests?


This is not difficult.  Working/thinking like this will allow you to keep running the ram's host port at 125MHz.

Well, I was thinking of acting on the Z80 write as soon as it came in, setting a flag so as not to act on the write more than once, and writing the data to the GPU RAM, resetting the flag when the Z80's write action completes.  That way, the GPU and its internals would all still run at full speed.

I shoved your GPU project into my 10 year old 15K$  video scaler:
...
Your GPU is driving a 30bit color HDMI output, with HDCP encryption... LOL, talk about overkill.  It's using around 2% of the onboard FPGA + -110% of the 2 x 1GB DDR2 sodim modules mounted under the PCB.

 :wtf:   Aye carumba... :o


Shoved? Shouldn't that be more like thrown? :)

Yeah, I can't imagine Brian broke out much of a sweat trying to squeeze my project in there..!  :-DD

(Attachment Link)

~M1 (not shown) is an active low instruction fetch cycle type identifier signal. It is asserted during an instruction fetch and negated otherwise.

nockieboy's logic looks alright, without even looking at timing.

Yep - I have to check for the state of ~M1 as I don't want the GPU responding to an instruction fetch cycle - the Z80 won't be running code from the GPU RAM.  Although that's an interesting point.  Maybe I should get the GPU to return NOPs (8'b00000000) in the edge-case that the Z80 does try to read instructions from the GPU memory space?

Oh boy, that's a joke easy.  What I was getting at looking at that illustration, create a synchronous 125mhz core clocked interface which looks like a static ram access to the host Z80 cpu, but, only eats a single 125mhz cycle from the FPGA 125mhz ram.  Hint, it pretty much looks like my 5 port design, but without the 5 cycle pixel phase.  Since the Z80 only cycles once every 3 8Mhz clock cycles, there is no need to eat up the FPGA at all.

I guess the only consideration I have to make is that it needs to share the access to the RAM with the RS232 debugger module?

Write Example:
Code: [Select]
-------------------------------------------------------------------------
output regs fpga_mem_addr/data/write_ena... ect

Always @ (posedge 125MHz clock ...) begin

     last_Z80wr <= Z80wr;  // This is part 1 of the secret sauce

//           *** This is part 2 of the secret sauce ***
          if (   (~Z80nwr && last_Z80nwr)    &&  ~Z80mreq  && (good page address)  ) begin    // trap the high to low transition of the Z80wr, for a single 125MHz clock event as soon as the Z80wr input falls

               fpga_mem_addr       <= Z80addr;    // register latch the z80 address and
               fpga_write_data     <= Z80data;    // data only on that falling edge of the Z80wr
               fpga_write_ena      <= 1;           // turn on the ram write request

          end else fpga_write_ena <= 0;      // at the next 125mhz clock cycle, turn off the FPGA memory write request, IE the write pulse is only a 1 clock pulsed cycle

end
-------------------------------------------------------------
(Now, this code works good since 125Mhz runs at least 4 fold faster than the Z80 8MHz buss clock.)

Yes - that's kinda what I was planning to do, but yours is a bit more straightforward.  ;)

No Z80 clock.  FPGA ram and interface module runs at 125Mhz.  This also leaves the FPGA ram empty for around 45 additional reads or writes in between each Z80 access which will be put to use by my RS232 Debugger and the graphics accelerated drawing hardware lateron.

So I'll just need to create a bus mux for the two write inputs to the host port in the vid_osd_generator, like the multiport_gpu_ram module we have currently, to gate the writes to the second port of the GPU RAM?

Now, just like I trapped the falling edge of the Z80we, you can also trap the rising or falling edge of the Z80clk inside the 'IF' as well, or, you can double register ALL the Z80 inputs.  The first set using the Z80 clock, then trigger from those registered inputs at the 125mhz clock with the single pulse transition detection trick.

That might be necessary to return data to the Z80 for a read request, but I'm liking the simplicity of your example above for the writes.

The read would be a second 'IF()begin' after the write, except you would pulse out a read request to the FPGA ram, latch the data coming back from the FPGA ram.  Only the MREQ and RD, plus valid address range would switch the Z80 data port on the FPGA IO's output enable.

The Z80 data port on the FPGA is going to need to be bidirectional - bearing in mind my extremely limited experience with FPGAs, will that be a problem or require any special considerations?
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #431 on: November 22, 2019, 10:45:33 am »
Okay, latest update to host_handler below:

Code: [Select]
module host_handler (

// input
input wire reset, // GPU reset signal
input wire GPU_CLK, // GPU clock (125 MHz)
input wire host_CLK, // Microcom clock signal (8 MHz)
input wire host_M1, // Z80 M1 - active LOW
input wire host_MREQ, // Z80 MREQ - active LOW
input wire host_WR, // Z80 WR - active LOW
input wire host_RD, // Z80 RD - active LOW
input wire [21:0] host_addr, // Microcom 22-bit address bus
input wire [7:0] host_wr_data, // Z80 DATA bus to pass incoming data to GPU RAM

// output
//output wire [7:0]  h_rd_data, // Z80 DATA bus to return data from GPU RAM to Z80
//output reg          h_rd_req, //
output reg          gpu_wr_ena, // flag HIGH when writing to GPU RAM
output reg [19:0] gpu_addr, // connect to host_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

);

reg last_Z80_WR = 1'b1;

always @ (posedge GPU_CLK)
begin

// is this the start of a WR memory cycle being executed within the GPU's memory window?
// host_addr[21:19] == 3'b011 targets the 512KB 'window' at 0x180000-0x1FFFFF
if ((~host_WR && last_Z80_WR) && host_M1 && ~host_MREQ && (host_addr[21:19] == 3'b011))
begin
// yes - pass the address (limited to 512 KB) and data through
gpu_wdata <= host_wr_data;
gpu_addr <= host_addr[18:0];
gpu_wr_ena <= 1'b1;

end // memory cycle
else begin

gpu_wr_ena <= 1'b0;

end

last_Z80_WR <= host_WR;

end

endmodule

I think this is looking quite usable - I moved the last_Z80_WR assignment to the end, otherwise the GPU would always be comparing the current Z80_WR signal with itself (or a few nanoseconds old).  Hope that's right and I haven't misunderstood something.  ???
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8144
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #432 on: November 22, 2019, 12:06:09 pm »
Got it right.

     As for the bidirectional port, there is the 'bidir' pin and 'tri' block diagram symbols in Quartus block diagram entry.  Just make add a 'bidir' pin on you block diagram.  Label it 'host_data[7..0]'.  Then add a 'tri' and tie it to the 'bidir' pin.  On the 'bidir' pin side, label that wire 'host_data_in[7..0].  On the other side of the 'tri' symbol, add a buss wire and label it 'host_data_out[7..0]'.  On the top of the 'tri' symbol, just add a single wire and label it 'host_data_oe'.  The rest should be self explanatory.

     I would also make an output pin or 2 from your GPU host interface to control the direction and output enables of the TTL 3.3v to 5v level shifters if you are using them.

« Last Edit: November 22, 2019, 12:08:02 pm by BrianHG »
 
The following users thanked this post: nockieboy

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8144
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #433 on: November 22, 2019, 12:15:07 pm »
I think this is looking quite usable - I moved the last_Z80_WR assignment to the end, otherwise the GPU would always be comparing the current Z80_WR signal with itself (or a few nanoseconds old).  Hope that's right and I haven't misunderstood something.  ???
     This is wrong, all the compares inside the 'IF()' are only finalized at the next 'always @ (posedge GPU_CLK)'.  So, as long as the GPU clock isn't faster than the combination of logic in the FPGA, which is spelled out to you by the compiler's FMAX in the compilation report, it doesn't matter where you place the 'last_Z80_WR <= host_WR;' line.  Remember, the compiler is wiring a breadboard with your description.  You aren't telling it where to place the wires and IC logic on that breadboard, being the FPGA fabric.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8144
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #434 on: November 22, 2019, 01:33:55 pm »
Some code cleaning tips:

Quote from: nockieboy on Today at 05:45:33 am

 
Code: [Select]
  module host_handler (

    // input
    input wire reset, // GPU reset signal
    input wire GPU_CLK, // GPU clock (125 MHz)
    input wire host_CLK, // Microcom clock signal (8 MHz)
    input wire host_M1, // Z80 M1 - active LOW
    input wire host_MREQ, // Z80 MREQ - active LOW
    input wire host_WR, // Z80 WR - active LOW
    input wire host_RD, // Z80 RD - active LOW
    input wire [21:0] host_addr, // Microcom 22-bit address bus
    input wire [7:0] host_wr_data, // Z80 DATA bus to pass incoming data to GPU RAM
   
    // output
    //output wire [7:0]  h_rd_data, // Z80 DATA bus to return data from GPU RAM to Z80
    //output reg          h_rd_req, //
    output reg          gpu_wr_ena, // flag HIGH when writing to GPU RAM
    output reg [19:0] gpu_addr, // connect to host_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

    );

    parameter MEMORY_RANGE = 3'b011;  // host_addr[21:19] == 3'b011 targets the 512KB 'window' at 0x180000-0x1FFFFF

    reg last_Z80_WR = 1'b0;  // keep these low on power-up, otherwise a read or write pulse may be triggered.
    reg last_Z80_RD = 1'b0;

    assign mem_window = (host_addr[21:19] == MEMORY_RANGE) ; // Define an active memory range
    assign Z80_mreq = ~host_MREQ && host_M1 ; // Define a bus memory access state
    assign Z80_write = ~host_WR && last_Z80_WR ; // Isolate a single write transaction
    assign Z80_read = ~host_RD && last_Z80_RD ; // Isolate a single read transaction

    assign Write_GPU_RAM = mem_window && Z80_mreq && Z80_write ; // Define a GPU Write action
    assign Read_GPU_RAM = mem_window && Z80_mreq && Z80_read ; // Define a GPU Read action
    assign GPU_data_oe = mem_window && Z80_mreq && ~host_RD ; // Define the time the GPU ouputs data onto the Z80 data bus.

    always @ (posedge GPU_CLK)
    begin

    // is this the start of a WR memory cycle being executed within the GPU's memory window?
   
    if ( Write_GPU_RAM )
    begin
    // yes - pass the address (limited to 512 KB) and data through
    gpu_wdata <= host_wr_data;
    gpu_addr <= host_addr[18:0];
    gpu_wr_ena <= 1'b1;

    end // memory cycle
    else begin
   
    gpu_wr_ena <= 1'b0;
   
    end
   
    last_Z80_WR <= host_WR;

    end

    endmodule

You should be able to workout the rest.
« Last Edit: November 22, 2019, 04:49:42 pm by BrianHG »
 
The following users thanked this post: nockieboy

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8144
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #435 on: November 22, 2019, 04:48:37 pm »
Ooops, to prevent compiler warnings, add this 1 line of code to the above code:

Code: [Select]
wire  mem_window, Z80_mreq, Z80_write, Z80_read, Write_GPU_RAM, Read_GPU_RAM, GPU_data_oe ;
 
The following users thanked this post: nockieboy

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #436 on: November 22, 2019, 05:12:05 pm »
Some code cleaning tips:

Quote from: nockieboy on Today at 05:45:33 am

 
Code: [Select]
  module host_handler (

    // input
    input wire reset, // GPU reset signal
    input wire GPU_CLK, // GPU clock (125 MHz)
    input wire host_CLK, // Microcom clock signal (8 MHz)
    input wire host_M1, // Z80 M1 - active LOW
    input wire host_MREQ, // Z80 MREQ - active LOW
    input wire host_WR, // Z80 WR - active LOW
    input wire host_RD, // Z80 RD - active LOW
    input wire [21:0] host_addr, // Microcom 22-bit address bus
    input wire [7:0] host_wr_data, // Z80 DATA bus to pass incoming data to GPU RAM
   
    // output
    //output wire [7:0]  h_rd_data, // Z80 DATA bus to return data from GPU RAM to Z80
    //output reg          h_rd_req, //
    output reg          gpu_wr_ena, // flag HIGH when writing to GPU RAM
    output reg [19:0] gpu_addr, // connect to host_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

    );

    parameter MEMORY_RANGE = 3'b011;  // host_addr[21:19] == 3'b011 targets the 512KB 'window' at 0x180000-0x1FFFFF

    reg last_Z80_WR = 1'b0;  // keep these low on power-up, otherwise a read or write pulse may be triggered.
    reg last_Z80_RD = 1'b0;

    assign mem_window = (host_addr[21:19] == MEMORY_RANGE) ; // Define an active memory range
    assign Z80_mreq = ~host_MREQ && host_M1 ; // Define a bus memory access state
    assign Z80_write = ~host_WR && last_Z80_WR ; // Isolate a single write transaction
    assign Z80_read = ~host_RD && last_Z80_RD ; // Isolate a single read transaction

    assign Write_GPU_RAM = mem_window && Z80_mreq && Z80_write ; // Define a GPU Write action
    assign Read_GPU_RAM = mem_window && Z80_mreq && Z80_read ; // Define a GPU Read action
    assign GPU_data_oe = mem_window && Z80_mreq && ~host_RD ; // Define the time the GPU ouputs data onto the Z80 data bus.

    always @ (posedge GPU_CLK)
    begin

    // is this the start of a WR memory cycle being executed within the GPU's memory window?
   
    if ( Write_GPU_RAM )
    begin
    // yes - pass the address (limited to 512 KB) and data through
    gpu_wdata <= host_wr_data;
    gpu_addr <= host_addr[18:0];
    gpu_wr_ena <= 1'b1;

    end // memory cycle
    else begin
   
    gpu_wr_ena <= 1'b0;
   
    end
   
    last_Z80_WR <= host_WR;

    end

    endmodule

You should be able to workout the rest.

Awesome, thank you.   ;D
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #437 on: November 22, 2019, 07:47:18 pm »
As for the bidirectional port, there is the 'bidir' pin and 'tri' block diagram symbols in Quartus block diagram entry.  Just make add a 'bidir' pin on you block diagram.  Label it 'host_data[7..0]'.  Then add a 'tri' and tie it to the 'bidir' pin.  On the 'bidir' pin side, label that wire 'host_data_in[7..0].  On the other side of the 'tri' symbol, add a buss wire and label it 'host_data_out[7..0]'.  On the top of the 'tri' symbol, just add a single wire and label it 'host_data_oe'.

So, something like this?

 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8144
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #438 on: November 22, 2019, 08:49:16 pm »
As for the bidirectional port, there is the 'bidir' pin and 'tri' block diagram symbols in Quartus block diagram entry.  Just make add a 'bidir' pin on you block diagram.  Label it 'host_data[7..0]'.  Then add a 'tri' and tie it to the 'bidir' pin.  On the 'bidir' pin side, label that wire 'host_data_in[7..0].  On the other side of the 'tri' symbol, add a buss wire and label it 'host_data_out[7..0]'.  On the top of the 'tri' symbol, just add a single wire and label it 'host_data_oe'.

So, something like this?

(Attachment Link)
LOL..., that's backwards, like this: (Hint, take a look ad the direction of the 'tri' buffer.)
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #439 on: November 22, 2019, 10:20:20 pm »
LOL..., that's backwards, like this: (Hint, take a look ad the direction of the 'tri' buffer.)
(Attachment Link)

 :palm:

Attempt 2:



h_data_oe should be HIGH to output data, right?
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8144
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #440 on: November 23, 2019, 06:34:00 am »
Yes, always positive logic.

Also, when naming you Z80 ports, or host ports, use caps for the signal control  names an add a lowercase 'n' in front so you know those are deliberate negative logic.  EG:

h_nWR
h_nRD
h_nMREQ

Keep the 'n' wherever the logic is negative, so, inside the verilog module, you may make wires:
input h_nWR...

wire h_wr;
assign h_wr = ~h_nWR;
....
If a verilog/hdl friendly way of having the '!' or '/' in front of the signal, or having the bar on top of the signal name.
« Last Edit: November 23, 2019, 08:38:35 am by BrianHG »
 
The following users thanked this post: nockieboy

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #441 on: November 23, 2019, 10:13:48 am »
Okay, this is wrong as I'm not sure about delaying the output of data back to the Z80 whilst waiting for the read data to come from the GPU, but this is my first attempt at a 'read' function:

Code: [Select]
if (Read_GPU_RAM)
begin

gpu_addr <= host_addr[18:0]; // pass address to GPU RAM

// wait a clock cycle?

data_dir <= 1'b1; // switch level converter to -> Z80
host_data_ena <= 1'b1; // drive data outputs
host_data[7:0] <= gpu_r_data[7:0]; // set data to output

end
else begin

data_dir <= 1'b0; // ensure data pins aren't driven when not reading

end
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8144
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #442 on: November 23, 2019, 10:32:33 am »
Okay, this is wrong as I'm not sure about delaying the output of data back to the Z80 whilst waiting for the read data to come from the GPU, but this is my first attempt at a 'read' function:

Code: [Select]
if (Read_GPU_RAM)
begin

gpu_addr <= host_addr[18:0]; // pass address to GPU RAM

// wait a clock cycle?

data_dir <= 1'b1; // switch level converter to -> Z80
host_data_ena <= 1'b1; // drive data outputs
host_data[7:0] <= gpu_r_data[7:0]; // set data to output

end
else begin

data_dir <= 1'b0; // ensure data pins aren't driven when not reading

end

Missing too much stuff.  Let's see the whole program...
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8144
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #443 on: November 23, 2019, 12:30:45 pm »
I've attached the GPU project with the final RS232_Debugger setup.  This version passes the 'ADDR_SIZE' parameter to the RS232_Debugger client on the PC.  I've also attached the updated client as it expects this reading within one of the commands it uses.  Let me know if they work.
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #444 on: November 23, 2019, 04:19:14 pm »
Missing too much stuff.  Let's see the whole program...

Code: [Select]
module host_handler (

// input
input wire reset, // GPU reset signal
input wire GPU_CLK, // GPU clock (125 MHz)
//input wire host_CLK, // Microcom clock signal (8 MHz)
input wire host_M1, // Z80 M1 - active LOW
input wire host_MREQ, // Z80 MREQ - active LOW
input wire host_WR, // Z80 WR - active LOW
input wire host_RD, // Z80 RD - active LOW
input wire [21:0] host_addr, // Microcom 22-bit address bus
input wire [7:0] host_wr_data, // Z80 DATA bus to pass incoming data to GPU RAM
input wire [7:0] gpu_r_data,

// output
//output wire [7:0]  h_rd_data, // Z80 DATA bus to return data from GPU RAM to Z80
//output reg          h_rd_req, //
output reg gpu_wr_ena, // flag HIGH when writing to GPU RAM
output reg data_dir, // control level converter direction for data flow
output reg [19:0] gpu_addr, // connect to host_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 [7:0] host_data,
output reg host_data_ena // flag HIGH to write data back to Z80

);

parameter MEMORY_RANGE = 3'b011; // host_addr[21:19] == 3'b011 targets the 512KB 'window' at 0x180000-0x1FFFFF

wire mem_window, Z80_mreq, Z80_write, Z80_read, Write_GPU_RAM, Read_GPU_RAM, GPU_data_oe;

reg last_Z80_WR = 1'b0;  // keep these low on power-up, otherwise a read or write pulse may be triggered
reg last_Z80_RD = 1'b0;

assign mem_window = (host_addr[21:19] == MEMORY_RANGE); // Define an active memory range
assign Z80_mreq = ~host_MREQ && host_M1 ; // Define a bus memory access state
assign Z80_write = ~host_WR && last_Z80_WR ; // Isolate a single write transaction
assign Z80_read = ~host_RD && last_Z80_RD ; // Isolate a single read transaction

assign Write_GPU_RAM = mem_window && Z80_mreq && Z80_write; // Define a GPU Write action
assign Read_GPU_RAM = mem_window && Z80_mreq && Z80_read ; // Define a GPU Read action
assign GPU_data_oe = mem_window && Z80_mreq && ~host_RD ; // Define the time the GPU ouputs data onto the Z80 data bus

always @ (posedge GPU_CLK)
begin

// is this the start of a WR memory cycle being executed within the GPU's memory window?
if (Write_GPU_RAM) // these compares are finalised on the next GPU_CLK
begin
// yes - pass the address (limited to 512 KB) and data through
gpu_wdata <= host_wr_data;
gpu_addr <= host_addr[18:0];
gpu_wr_ena <= 1'b1;

end // memory cycle
else begin

gpu_wr_ena <= 1'b0;

end

if (Read_GPU_RAM)
begin

gpu_addr <= host_addr[18:0]; // pass address to GPU RAM

// wait a clock cycle?

data_dir <= 1'b1; // switch level converter to -> Z80
host_data_ena <= 1'b1; // drive data outputs
host_data[7:0] <= gpu_r_data[7:0]; // set data to output


end
else begin

data_dir <= 1'b0; // ensure data pins aren't driven when not reading

end

last_Z80_WR <= host_WR;

end

endmodule

There ya go.  :-+

Oh - apologies for the tabs - I've no idea what's going on with my editors and pasting code into other areas, but my tabs are all messed up.  |O
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8144
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #445 on: November 23, 2019, 04:47:31 pm »
Now, you need to explain to me why I made these changes and where the pitfalls are:

Code: [Select]
module host_handler (

// input
input wire reset, // GPU reset signal
input wire GPU_CLK, // GPU clock (125 MHz)
//input wire host_CLK, // Microcom clock signal (8 MHz)
input wire host_M1, // Z80 M1 - active LOW
input wire host_MREQ, // Z80 MREQ - active LOW
input wire host_WR, // Z80 WR - active LOW
input wire host_RD, // Z80 RD - active LOW
input wire [21:0]   host_addr, // Microcom 22-bit address bus
input wire [7:0] host_wr_data, // Z80 DATA bus to pass incoming data to GPU RAM
input wire [7:0] gpu_r_data,

// output
//output wire [7:0]  h_rd_data, // Z80 DATA bus to return data from GPU RAM to Z80
//output reg            h_rd_req, //
output reg gpu_wr_ena, // flag HIGH when writing to GPU RAM
output reg data_dir, // control level converter direction for data flow
output reg [19:0]  gpu_addr, // connect to host_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 [7:0]   host_data,
output reg host_data_ena // flag HIGH to write data back to Z80

);

parameter MEMORY_RANGE = 3'b011; // host_addr[21:19] == 3'b011 targets the 512KB 'window' at 0x180000-0x1FFFFF

wire mem_window, Z80_mreq, Z80_write, Z80_read, Write_GPU_RAM, Read_GPU_RAM, GPU_data_oe;

reg last_Z80_WR = 1'b0;  // keep these low on power-up, otherwise a read or write pulse may be triggered
reg last_Z80_RD = 1'b0;

assign mem_window = (host_addr[21:19] == MEMORY_RANGE); // Define an active memory range
assign Z80_mreq = ~host_MREQ && host_M1 ; // Define a bus memory access state
assign Z80_write = ~host_WR && last_Z80_WR ; // Isolate a single write transaction
assign Z80_read = ~host_RD && last_Z80_RD ; // Isolate a single read transaction

assign Write_GPU_RAM = mem_window && Z80_mreq && Z80_write; // Define a GPU Write action
assign Read_GPU_RAM = mem_window && Z80_mreq && Z80_read ; // Define a GPU Read action
assign GPU_data_oe = mem_window && Z80_mreq && ~host_RD ; // Define the time the GPU ouputs data onto the Z80 data bus

always @ (posedge GPU_CLK)
begin

// is this the start of a WR memory cycle being executed within the GPU's memory window?
if (Write_GPU_RAM) // these compares are finalised on the next GPU_CLK
begin
// yes - pass the address (limited to 512 KB) and data through
gpu_wdata <= host_wr_data;
gpu_addr <= host_addr[18:0];
gpu_wr_ena <= 1'b1;

end // memory cycle
else begin

gpu_wr_ena <= 1'b0;

end

if (Read_GPU_RAM)

begin
gpu_addr <= host_addr[18:0] ; // pass address to GPU RAM
gpu_rd_req <= 1'b1 ;               // Send a read request to GPU.

end else if (gpu_rd_rdy)

begin
gpu_rd_req <= 1'b0;                // End the read request once the read is ready
host_data[7:0]  <= gpu_r_data[7:0]; // Latch the GPU ram read into the output register for the Z80
end

data_dir      <= GPU_data_oe ;
host_data_ena <= GPU_data_oe ;

last_Z80_WR <= host_WR;
last_Z80_RD <= host_RD;

end

endmodule
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #446 on: November 23, 2019, 05:16:43 pm »
Now, you need to explain to me why I made these changes and where the pitfalls are:

Code: [Select]
if (Read_GPU_RAM)

begin
gpu_addr <= host_addr[18:0] ; // pass address to GPU RAM
gpu_rd_req <= 1'b1 ;               // Send a read request to GPU.

Okay, so this piece of code is triggered on the falling edge of the Z80 RD signal and is effectively executed once per Z80 read cycle.  It passes the requested address through to the GPU RAM and sets gpu_rd_req HIGH to signal a read.  There's no corresponding input in multiport_gpu_ram.v for this 'gpu_rd_req' signal, so I'm guessing this is for some sort of mux with the RS232 input to the GPU RAM?

Code: [Select]
end else if (gpu_rd_rdy)

begin
gpu_rd_req <= 1'b0;                // End the read request once the read is ready
host_data[7:0]  <= gpu_r_data[7:0]; // Latch the GPU ram read into the output register for the Z80
end

This bit of code above is executed when 'gpu_rd_rdy' is high - again, this isn't defined in the module but must be an input (reg?) from the bus mux which I've yet to create which will gate GPU RAM access between the RS232 module and the Z80 module here.  The gpu_rd_req signal is released (pulled LOW) so the (yet-to-be-created) bus mux can allow the RS232 module access to the GPU RAM again and prevent repeated memory accesses by the Z80 module.  The returned data held in the gpu_r_data registers is passed through to the data output to the Z80.

Code: [Select]
data_dir      <= GPU_data_oe ;
host_data_ena <= GPU_data_oe ;

last_Z80_WR <= host_WR;
last_Z80_RD <= host_RD;

Finally, at the end of every GPU clock cycle, data_dir and host_data_ena are set high depending on whether a valid memory read access is detected, and the edge-detecting last_Z80_WR and last_Z80_RD flags are updated with the current status of the WR and RD lines from the Z80.

As for pitfalls....  :-\  Erm... gpu_rd_rdy isn't reset in the host_handler - so it must be a wire input from the mux module which would go HIGH when it receives data from the multiport_GPU_RAM module, and be reset by mux module when it detects gpu_rd_req going LOW?  Not really a pitfall, more of an observation.

Hmm.. not sure I know enough about the workings of the system to identify pitfalls?  Is there a chance it will still be driving the data outputs a clock or two after the end of the RD cycle?  That's not really a pitfall either though - at 125 MHz, there should be plenty of time for the data_dir signal to update the buffer on the Z80 data bus and get it to reverse direction before the next memory cycle...

Can I phone a friend or ask the audience?  ;)

EDIT:

Shouldn't gpu_rd_req be assigned LOW at the start?

Code: [Select]
assign gpu_rd_req = 1'b0;
« Last Edit: November 23, 2019, 05:23:00 pm by nockieboy »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8144
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #447 on: November 23, 2019, 06:00:41 pm »
You pretty much got it.

The only pitfall to mention is that the new added output reg, gpu_re_req stays high until the incoming confirmation signal gpu_rd_rdy.  Externally, when muxing, or what will be 'priority' selecting access to the gpu's host memory port will need to understand that when the req goes high, it only needs a single read, and it should only make the rdy pulse high after the right address made it through the ram and the ram's output contents are correct, not before.

In the case of having the host ram just tied to this one module alone, you would tie the rdy to 1 (vcc) and ignore the req signal as the ram would always feed the data out correctly within 2 clock cycles of the read address change.

Now, is there any change you can make to ensure no output enable collision between your external bidirectional data buffer and the FPGA tristated IO pins.  There should be a control for outputting data from the IO level translator to the FPGA.  Show me what IC you will be using...


« Last Edit: November 23, 2019, 06:05:40 pm by BrianHG »
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #448 on: November 23, 2019, 06:13:07 pm »
Now, is there any change you can make to ensure no output enable collision between your external bidirectional data buffer and the FPGA tristated IO pins.  There should be a control for outputting data from the IO level translator to the FPGA.  Show me what IC you will be using...

I'm using a 74LVC245 - datasheet here.  It has DIRection and OE pins.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8144
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #449 on: November 23, 2019, 06:16:42 pm »
Remember, all you need is an OE to the FPGA side, or in this case, you just need the 2 control signals setup properly, 'DIR' and 'OE'.  So, I recommend changing your Z80 module's code to just drive those 2 wires.

You will also want to configure the data buss IOs on your FPGA to weak-pullup.  This is for when the buss is in high impedance, your IO's wont float in noise.

BTY, did you like my additions to you gpu core?
« Last Edit: November 23, 2019, 06:20:08 pm by BrianHG »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf