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

0 Members and 2 Guests are viewing this topic.

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #275 on: November 09, 2019, 01:55:02 pm »
You got it.  Now, remember the specs.

2 different clocks.
1 port read only.
other port read & write.
8 bits
13 addressees, or, 8192 words.

and clock the input controls as well as the output data.  As you change your options, the illustration will update showing you what you are creating.  send me the final image + the verilog.v example text...

Okay, using dual-clock (separate clocks for A and B ports as opposed to input and output clocks).  Will disable write on port 1 in the code when it produces it (there's no option for that in the wizard), 8-bit data is configured and I've gone for 14 addresses (16384 words), as I'm now using the EP4CE6 (still with Quartus II 13.0sp1) and have ~30KB of RAM to play with.

EDIT:

Do I need rd_en signals for either port?
« Last Edit: November 09, 2019, 01:57:30 pm by nockieboy »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7737
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #276 on: November 09, 2019, 01:58:04 pm »
You got it.  Now, remember the specs.

2 different clocks.
1 port read only.
other port read & write.
8 bits
13 addressees, or, 8192 words.

and clock the input controls as well as the output data.  As you change your options, the illustration will update showing you what you are creating.  send me the final image + the verilog.v example text...

Okay, using dual-clock (separate clocks for A and B ports as opposed to input and output clocks).  Will disable write on port 1 in the code when it produces it (there's no option for that in the wizard), 8-bit data is configured and I've gone for 14 addresses (16384 words), as I'm now using the EP4CE6 (still with Quartus II 13.0sp1) and have ~30KB of RAM to play with.
Unless your EP4CE6 board is ready to run now, we can still get a lot done on the CycloneII board.
Show me the final illustration from the megawizard.
Memory size wont make a difference anyways as your MAX_MEM_ADDR will overwrite what the wizard has filled into the example.v file we will just be using as a guide.
« Last Edit: November 09, 2019, 02:01:04 pm by BrianHG »
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #277 on: November 09, 2019, 02:03:08 pm »
Unless your EP4CE6 board is ready to run now, we can still get a lot done on the CycloneII board.

The EP4CE6 is set up and outputting the video RAM contents as good as anything.  The only downside is that it has 1-bit colour output on the VGA connector.

Show me the final illustration from the megawizard.
Memory size wont make a difference anyways as your MAX_MEM_ADDR will overwrite what the wizard has filled into the example.v file we will just be using as a guide.

 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7737
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #278 on: November 09, 2019, 02:06:00 pm »
Perfect, paste the example into your  ''gpu_dual_port_ram_INTEL.v'' module and wire the ports to the modules IO declarations.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7737
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #279 on: November 09, 2019, 02:10:00 pm »
The EP4CE6 is set up and outputting the video RAM contents as good as anything.  The only downside is that it has 1-bit colour output on the VGA connector.

Now when you are saying 'outputting the video RAM contents', you mean the OSD generator we have written here, correct?

As for the color, you should eventually be able to do something about that.  Remember to choose output pins which are in the same IO bank as the current 1 bit RGB output pins.
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #280 on: November 09, 2019, 02:20:07 pm »
The EP4CE6 is set up and outputting the video RAM contents as good as anything.  The only downside is that it has 1-bit colour output on the VGA connector.

Now when you are saying 'outputting the video RAM contents', you mean the OSD generator we have written here, correct?

Yes.  Without all these new modules we're working on at the moment.

As for the color, you should eventually be able to do something about that.  Remember to choose output pins which are in the same IO bank as the current 1 bit RGB output pins.

Yes, just means I'll have to use my breadboard resistor ladder and VGA breakout connector again.  ::)
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #281 on: November 09, 2019, 02:25:37 pm »
Perfect, paste the example into your  ''gpu_dual_port_ram_INTEL.v'' module and wire the ports to the modules IO declarations.

Okay, I'm a little confused right now.  I've got a megafunction-generated file, gpu_ram.v, which isn't normally included in the project files as Quartus wants me to leave the source code alone and just use the diagram component.

I've included the gpu_ram.v file so I can get to the code, but do I need to change anything there?  Can't I just wire up the gpu_ram component in the diagram to the multiport_gpu_ram component?
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7737
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #282 on: November 09, 2019, 02:30:19 pm »
The EP4CE6 is set up and outputting the video RAM contents as good as anything.  The only downside is that it has 1-bit colour output on the VGA connector.

Now when you are saying 'outputting the video RAM contents', you mean the OSD generator we have written here, correct?

Yes.  Without all these new modules we're working on at the moment.

As for the color, you should eventually be able to do something about that.  Remember to choose output pins which are in the same IO bank as the current 1 bit RGB output pins.

Yes, just means I'll have to use my breadboard resistor ladder and VGA breakout connector again.  ::)
Not, get the board's schematic as they have a series resistor feeding the analog RGB video, and add a 2x value in series of each resistor to a new IO pin each.  This will give you 2 bit color.  Add a 4x series resistor to the analog VGA on a third IO pin to get 3 bit color...

example:
currently:
Current Red IO --------220ohm-----VGAred

Cheap DAC without the R2R ladder.
Current Red IO --------220ohm--------|--- VGAred
New Red IO2 ----------440ohm--------|
New Red IO3 ----------880ohm--------|
New Red IO4 ---------1760ohm-------|

It should be simple enough to hand wire this addition...
 
The following users thanked this post: nockieboy

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7737
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #283 on: November 09, 2019, 02:38:44 pm »
I've included the gpu_ram.v file so I can get to the code, but do I need to change anything there?  Can't I just wire up the gpu_ram component in the diagram to the multiport_gpu_ram component?

Nope, just copy and paste it into your  ''gpu_dual_port_ram_INTEL.v'' module.  We will make some changes and pass some parameters.  Don't bother with the Quartus copyright text.  Take a look at how I did it with my altsyncram in my current OSD generator.  It was copied and pasted from the same megawizard as well.

Though you could have read intel's manual on the dual port memory types and all their controls, the wizard just helps give you the basic feature setup visually.  You can always add or remove files from your project at any time.

The ''gpu_dual_port_ram_INTEL.v'' has a few more things than just the ram and you don't want a wizard file which can edit the structure if you place the file or click on the block diagram file.


OH, BTW, obviously delete the memory symbol from your block diagram file.  Your obviously wont be using that one.
« Last Edit: November 09, 2019, 02:42:17 pm by BrianHG »
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #284 on: November 09, 2019, 02:42:35 pm »
I've included the gpu_ram.v file so I can get to the code, but do I need to change anything there?  Can't I just wire up the gpu_ram component in the diagram to the multiport_gpu_ram component?

Nope, just copy and paste it into your  ''gpu_dual_port_ram_INTEL.v'' module.  We will make some changes and pass some parameters.  Don't bother with the Quartus copyright text.  Take a look at how I did it with my altsyncram in my current OSD generator.  It was copied and pasted from the same megawizard as well.

Though you could have read intel's manual on the dual port memory types and all their controls, the wizard just helps give you the basic feature setup visually.  You can always add or remove files from your project at any time.

The ''gpu_dual_port_ram_INTEL.v'' has a few more things than just the ram and you don't want a wizard file which can edit the structure if you place the file or click on the block diagram file.

So, this is my gpu_dual_port_ram_INTEL.v file now:

Code: [Select]
module gpu_dual_port_ram_INTEL (
input clk,
input [2:0] pc_ena_in,
input wr_en_a,
input clk_host,
input [19:0] addr_a,
input [19:0] addr_b,
input [7:0] data_a,
input [7:0] data_b
);

// ****************************************************************************************************************************
// Multiport GPU RAM
// ****************************************************************************************************************************
altsyncram altsyncram_component (
.clock0 (clock_a),
.wren_a (wren_a),
.address_b (address_b),
.clock1 (clock_b),
.data_b (data_b),
.wren_b (wren_b),
.address_a (address_a),
.data_a (data_a),
.q_a (sub_wire0),
.q_b (sub_wire1),
.aclr0 (1'b0),
.aclr1 (1'b0),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clocken0 (1'b1),
.clocken1 (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.eccstatus (),
.rden_a (1'b1),
.rden_b (1'b1));
defparam
altsyncram_component.address_reg_b = "CLOCK1",
altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_input_b = "BYPASS",
altsyncram_component.clock_enable_output_a = "BYPASS",
altsyncram_component.clock_enable_output_b = "BYPASS",
altsyncram_component.indata_reg_b = "CLOCK1",
altsyncram_component.init_file = "../osd_mem.mif",
altsyncram_component.intended_device_family = "Cyclone IV E",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 16384,
altsyncram_component.numwords_b = 16384,
altsyncram_component.operation_mode = "BIDIR_DUAL_PORT",
altsyncram_component.outdata_aclr_a = "NONE",
altsyncram_component.outdata_aclr_b = "NONE",
altsyncram_component.outdata_reg_a = "CLOCK0",
altsyncram_component.outdata_reg_b = "CLOCK1",
altsyncram_component.power_up_uninitialized = "FALSE",
altsyncram_component.read_during_write_mode_port_a = "OLD_DATA",
altsyncram_component.read_during_write_mode_port_b = "OLD_DATA",
altsyncram_component.widthad_a = 14,
altsyncram_component.widthad_b = 14,
altsyncram_component.width_a = 8,
altsyncram_component.width_b = 8,
altsyncram_component.width_byteena_a = 1,
altsyncram_component.width_byteena_b = 1,
altsyncram_component.wrcontrol_wraddress_reg_b = "CLOCK1";

endmodule

I've just cut 'n' pasted like you said.  Seems to be a lot of clock enables for some reason?  Obviously I'm going to have to prune/sort out the IO assignments.

869066-0
« Last Edit: November 09, 2019, 02:54:46 pm by nockieboy »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7737
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #285 on: November 09, 2019, 02:55:19 pm »
Fill and wire the IO ports.
Add the max address parameter, pass the parameters to the altsyncram's params.
When wiring the ram's address ports, limit the max ram adr inside the wiring there.

And, add a @(posedge clock), for the graphics, delay and pass through the read address, aux data and  pc_ena[3:0] with the same number of clock cycles as the memory's read pipe delay.

Try to think why I want you to do this.  What can these be used for.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7737
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #286 on: November 09, 2019, 02:59:39 pm »

Okay, this is where I am so far with multiport_gpu_ram.v:

Code: [Select]
module multiport_gpu_ram (

input clk, // Primary clk input (125 MHz)
input [3:0] pc_ena, // Pixel clock enable
input clk_host, // Host (Z80) clock input
input hc_ena, // Host (Z80) clock enable

// address buses (input)
input [19:0] address_0,
input [19:0] address_1,
input [19:0] address_2,
input [19:0] address_3,
input [19:0] address_4,
input [19:0] addr_host,

// auxilliary read command buses (input)
input [7:0] aux_read_0,
input [7:0] aux_read_1,
input [7:0] aux_read_2,
input [7:0] aux_read_3,
input [7:0] aux_read_4,

// address buses (pass-thru outputs)
output reg [19:0] addrPT_0,
output reg [19:0] addrPT_1,
output reg [19:0] addrPT_2,
output reg [19:0] addrPT_3,
output reg [19:0] addrPT_4,

// auxilliary read command buses (pass-thru output)
output reg [7:0] auxRdPT_0,
output reg [7:0] auxRdPT_1,
output reg [7:0] auxRdPT_2,
output reg [7:0] auxRdPT_3,
output reg [7:0] auxRdPT_4,

// data buses (output)
output reg [7:0] dataOUT_0,
output reg [7:0] dataOUT_1,
output reg [7:0] dataOUT_2,
output reg [7:0] dataOUT_3,
output reg [7:0] dataOUT_4,
output [7:0] data_host

);

// dual-port GPU RAM handler

// define the maximum address bit - effectively the RAM size
parameter MAX_ADDR_BIT = 20;

// create a GPU RAM instance
gpu_dual_port_ram_INTEL gpu_RAM(
// TBC
);

always @(posedge clk) begin

if (pc_ena[2:0] == 0) begin



end // pc_ena

end // always @clk

endmodule

addr_host and data_host are host Z80 buses.

Am I going along the right lines?
Change the 'hc_ena' to 'hc_write_ena'.  This is for writing data.  The host can read or write just like normal ram.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7737
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #287 on: November 09, 2019, 03:03:21 pm »

I've just cut 'n' pasted like you said.  Seems to be a lot of clock enables for some reason?  Obviously I'm going to have to prune/sort out the IO assignments.

(Attachment Link)

Those have been hardwired to stay always enabled.  Leave them like that.

As for your photo of the 'multiport_gpu_ram', LOL  :-DD  You didn't have to generate a symbol of it....
That's just absurd...
You will obviously just call an instance of it in the OSG graphics generator...
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #288 on: November 09, 2019, 03:19:37 pm »
Time's up for me today - for a while at least.  Here's the latest where I've gotten to with gpu_dual_port_ram_INTEL.v:

Code: [Select]
module gpu_dual_port_ram_INTEL (
// inputs
input clk,
input [2:0] pc_ena_in,
input wr_en_a,
input clk_host,
input wr_en_host,
input [19:0] addr_a,
input [19:0] addr_b,
input [7:0] data_in_a,
input [7:0] data_in_b,
// outputs
output [7:0] data_out_a,
output [7:0] data_out_b
);

// define delay pipe registers
reg [MAX_ADDR_BIT:0] rd_addr_pipe;
reg [7:0] aux_dat_pipe;
reg [3:0] pc_ena_pipe;

// define the maximum address bit - effectively the RAM size
parameter MAX_ADDR_BIT = 14;

// ****************************************************************************************************************************
// Multiport GPU RAM
// ****************************************************************************************************************************
altsyncram altsyncram_component (
.clock0 (clk),
.wren_a (wr_en_a),
.address_b (addr_b[MAX_ADDR_BIT:0]),
.clock1 (clk_host),
.data_b (data_b),
.wren_b (wr_en_host),
.address_a (addr_a[MAX_ADDR_BIT:0]),
.data_a (data_a),
.q_a (data_out_a),
.q_b (data_out_b),
.aclr0 (1'b0),
.aclr1 (1'b0),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clocken0 (1'b1),
.clocken1 (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.eccstatus (),
.rden_a (1'b1),
.rden_b (1'b1));

defparam
altsyncram_component.address_reg_b = "CLOCK1",
altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_input_b = "BYPASS",
altsyncram_component.clock_enable_output_a = "BYPASS",
altsyncram_component.clock_enable_output_b = "BYPASS",
altsyncram_component.indata_reg_b = "CLOCK1",
altsyncram_component.init_file = "../osd_mem.mif",
altsyncram_component.intended_device_family = "Cyclone IV E",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 16384,
altsyncram_component.numwords_b = 16384,
altsyncram_component.operation_mode = "BIDIR_DUAL_PORT",
altsyncram_component.outdata_aclr_a = "NONE",
altsyncram_component.outdata_aclr_b = "NONE",
altsyncram_component.outdata_reg_a = "CLOCK0",
altsyncram_component.outdata_reg_b = "CLOCK1",
altsyncram_component.power_up_uninitialized = "FALSE",
altsyncram_component.read_during_write_mode_port_a = "OLD_DATA",
altsyncram_component.read_during_write_mode_port_b = "OLD_DATA",
altsyncram_component.widthad_a = MAX_ADDR_BIT,
altsyncram_component.widthad_b = MAX_ADDR_BIT,
altsyncram_component.width_a = 8,
altsyncram_component.width_b = 8,
altsyncram_component.width_byteena_a = 1,
altsyncram_component.width_byteena_b = 1,
altsyncram_component.wrcontrol_wraddress_reg_b = "CLOCK1";

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

always @(posedge clk) begin

// **************************************************************************************************************************
// *** Create a serial pipe where the PIPE_DELAY parameter selects the pixel count delay for the xxx_in to the xxx_out ports
// **************************************************************************************************************************

rd_addr_pipe  <= addr_a;

hde_pipe[7:1] <= hde_pipe[6:0];
hde_out       <= hde_pipe[PIPE_DELAY-2];

end

endmodule

Thanks for your help and patience so far!!  ;D
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7737
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #289 on: November 09, 2019, 03:33:54 pm »
Time's up for me today - for a while at least.  Here's the latest where I've gotten to with gpu_dual_port_ram_INTEL.v:

Code: [Select]
module gpu_dual_port_ram_INTEL (
// inputs
input clk,
input [2:0] pc_ena_in,
input wr_en_a,          *************** Unused
input clk_host,
input wr_en_host,
input [19:0] addr_a,
input [19:0] addr_b,
input [7:0] data_in_a,   **************** Unused
input [7:0] data_in_b,
// outputs
output [7:0] data_out_a,
output [7:0] data_out_b
);

// define delay pipe registers  ********** dont forget that these will be output ports as well
reg [19:0] rd_addr_pipe;  ************************* even though the ram may be smaller, we should still pipe through all the 20 address bits.
reg [7:0] aux_dat_pipe;
reg [3:0] pc_ena_pipe;

// define the maximum address bit - effectively the RAM size
parameter MAX_ADDR_BITS = 14;   ********************** this is 32k, however, for less confusion, I've changed it to BITS

// ****************************************************************************************************************************
// Multiport GPU RAM
// ****************************************************************************************************************************
altsyncram altsyncram_component (
.clock0 (clk),
.wren_a (wr_en_a),
.address_b (addr_b[MAX_ADDR_BITS-1:0]),           *****************   bits-1
.clock1 (clk_host),
.data_b (data_b),
.wren_b (wr_en_host),
.address_a (addr_a[MAX_ADDR_BITS-1:0]),               *********************
.data_a (data_a),
.q_a (data_out_a),
.q_b (data_out_b),
.aclr0 (1'b0),
.aclr1 (1'b0),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clocken0 (1'b1),
.clocken1 (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.eccstatus (),
.rden_a (1'b1),
.rden_b (1'b1));

defparam
altsyncram_component.address_reg_b = "CLOCK1",
altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_input_b = "BYPASS",
altsyncram_component.clock_enable_output_a = "BYPASS",
altsyncram_component.clock_enable_output_b = "BYPASS",
altsyncram_component.indata_reg_b = "CLOCK1",
altsyncram_component.init_file = "../osd_mem.mif",
altsyncram_component.intended_device_family = "Cyclone IV E",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 16384,    ************** fix, needs calculation based on MAX_ADDR_BITS
altsyncram_component.numwords_b = 16384,  ***********    fix, needs calculation based on MAX_ADDR_BITS
altsyncram_component.operation_mode = "BIDIR_DUAL_PORT",
altsyncram_component.outdata_aclr_a = "NONE",
altsyncram_component.outdata_aclr_b = "NONE",
altsyncram_component.outdata_reg_a = "CLOCK0",
altsyncram_component.outdata_reg_b = "CLOCK1",
altsyncram_component.power_up_uninitialized = "FALSE",
altsyncram_component.read_during_write_mode_port_a = "OLD_DATA",
altsyncram_component.read_during_write_mode_port_b = "OLD_DATA",
altsyncram_component.widthad_a = MAX_ADDR_BITS,                           *************** bits-0
altsyncram_component.widthad_b = MAX_ADDR_BITS,
altsyncram_component.width_a = 8,
altsyncram_component.width_b = 8,
altsyncram_component.width_byteena_a = 1,
altsyncram_component.width_byteena_b = 1,
altsyncram_component.wrcontrol_wraddress_reg_b = "CLOCK1";

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

always @(posedge clk) begin

// **************************************************************************************************************************
// *** Create a serial pipe where the PIPE_DELAY parameter selects the pixel count delay for the xxx_in to the xxx_out ports
// **************************************************************************************************************************

rd_addr_pipe  <= addr_a;   *********** remember, there are 2 pipe steps before you should output the rd_addr out...

hde_pipe[7:1] <= hde_pipe[6:0];                  ******** unused
hde_out       <= hde_pipe[PIPE_DELAY-2];  ******* unused

end

endmodule


Thanks for your help and patience so far!!  ;D

Comments in code with the asterisk ****...
I'll check in later tonight...
« Last Edit: November 09, 2019, 03:58:37 pm by BrianHG »
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #290 on: November 09, 2019, 04:47:53 pm »
And, add a @(posedge clock), for the graphics, delay and pass through the read address, aux data and  pc_ena[3:0] with the same number of clock cycles as the memory's read pipe delay.

Okay, latest code here:

Code: [Select]
module gpu_dual_port_ram_INTEL (
// inputs
input clk,
input [2:0] pc_ena_in,
input clk_host,
input wr_en_host,
input [19:0] addr_a,
input [19:0] addr_b,
input [7:0] data_in_b,
// outputs
output [19:0] addr_out_a,
output [7:0] data_out_a,
output [7:0] data_out_b,
output [2:0] pc_ena_out
);

// define the maximum address bit - effectively the RAM size
parameter MAX_ADDR_BITS = 14;

// define delay pipe registers  ********** dont forget that these will be output ports as well
reg [MAX_ADDR_BITS - 1:0] rd_addr_pipe;
reg [7:0] dat_out_a_pipe;
reg [3:0] pc_ena_pipe;

// ****************************************************************************************************************************
// Dual-port GPU RAM
//
// Port A - read only by GPU
// Port B - read/writeable by host system
// Data buses - 8 bits / 1 byte wide
// Address buses - MAX_ADDR_BITS wide (14 bits default)
// Memory word size - 2^MAX_ADDR_BITS (16384 bytes default)
// ****************************************************************************************************************************
altsyncram altsyncram_component (
.clock0 (clk),
.wren_a (1'b1),
.address_b (addr_b[MAX_ADDR_BITS - 1:0]),
.clock1 (clk_host),
.data_b (data_in_b),
.wren_b (wr_en_host),
.address_a (addr_a[MAX_ADDR_BITS - 1:0]),
.data_a (8'b00000000),
.q_a (data_out_a_pipe),
.q_b (data_out_b),
.aclr0 (1'b0),
.aclr1 (1'b0),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clocken0 (1'b1),
.clocken1 (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.eccstatus (),
.rden_a (1'b1),
.rden_b (1'b1));

defparam
altsyncram_component.address_reg_b = "CLOCK1",
altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_input_b = "BYPASS",
altsyncram_component.clock_enable_output_a = "BYPASS",
altsyncram_component.clock_enable_output_b = "BYPASS",
altsyncram_component.indata_reg_b = "CLOCK1",
altsyncram_component.init_file = "../osd_mem.mif",
altsyncram_component.intended_device_family = "Cyclone IV E",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 2 ** MAX_ADDR_BITS,
altsyncram_component.numwords_b = 2 ** MAX_ADDR_BITS,
altsyncram_component.operation_mode = "BIDIR_DUAL_PORT",
altsyncram_component.outdata_aclr_a = "NONE",
altsyncram_component.outdata_aclr_b = "NONE",
altsyncram_component.outdata_reg_a = "CLOCK0",
altsyncram_component.outdata_reg_b = "CLOCK1",
altsyncram_component.power_up_uninitialized = "FALSE",
altsyncram_component.read_during_write_mode_port_a = "OLD_DATA",
altsyncram_component.read_during_write_mode_port_b = "OLD_DATA",
altsyncram_component.widthad_a = MAX_ADDR_BITS - 1,
altsyncram_component.widthad_b = MAX_ADDR_BITS - 1,
altsyncram_component.width_a = 8,
altsyncram_component.width_b = 8,
altsyncram_component.width_byteena_a = 1,
altsyncram_component.width_byteena_b = 1,
altsyncram_component.wrcontrol_wraddress_reg_b = "CLOCK1";

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

always @(posedge clk) begin

// **************************************************************************************************************************
// *** Create a serial pipe where the PIPE_DELAY parameter selects the pixel count delay for the xxx_in to the xxx_out ports
// **************************************************************************************************************************
rd_addr_pipe  <= addr_a;
addr_out_a <= rd_addr_pipe;

//dat_out_a_pipe <= data_out_a;
data_out_a <= dat_out_a_pipe;

pc_ena_pipe <= pc_ena_in;
pc_ena_out <= pc_ena_pipe;
// **************************************************************************************************************************

end

endmodule

Not sure I've got the delay pipes set up correctly.  data_out_a is coming straight out of the memory module, so only needs 1 (or actually probably 0?) delays.

Try to think why I want you to do this.  What can these be used for.

Well, the address pass-through and pc_ena signal will arrive at the next module at the same time as the data, so should all be valid at the same time and can thus be used to process the data?
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #291 on: November 09, 2019, 07:37:53 pm »
Code: [Select]
// define delay pipe registers  ********** dont forget that these will be output ports as well
reg [MAX_ADDR_BITS - 1:0] rd_addr_pipe;
reg [7:0] aux_dat_pipe;
reg [3:0] pc_ena_pipe;

Why do they need to be output ports?  I've got them outputting their contents into outputs already...?

Code: [Select]
// **************************************************************************************************************************
// *** Create a serial pipe where the PIPE_DELAY parameter selects the pixel count delay for the xxx_in to the xxx_out ports
// **************************************************************************************************************************
rd_addr_pipe  <= addr_a;
addr_out_a <= rd_addr_pipe;

//dat_out_a_pipe <= data_out_a;
data_out_a <= dat_out_a_pipe;

pc_ena_pipe <= pc_ena_in;
pc_ena_out <= pc_ena_pipe;
// **************************************************************************************************************************

addr_out_a, data_out_a and pc_ena_out are all outputs?
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7737
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #292 on: November 10, 2019, 01:54:09 am »
Ok, when defining a module IO, IE any signal/wires going in and out, all must be labeled as an input, or output, or bidir. (There are a few others, but not all compilers support it.)

Now, I know you have:
reg [MAX_ADDR_BITS - 1:0] rd_addr_pipe;

However, if you want that reg to be exposed to you function's declared IO port, yes, it will also need to be declared as an output as well.  The one small exception is if when you declare 'INPUT', with nothing else written, it is also automatically considered a 'WIRE' even though you did not specifically write 'input wire'...

To make things easier on you, you should declare you function's IO pins like the way you have it in your first verilog program 'sync_generator.v'.  Writing out each passed wire/bus as an 'input wire', 'output wire', 'output reg', would really clean thing up for coding.  I know my 15 year old OSD code stuffed everything like a 'C' code declaration.

From now on, declare your verilog functions and update your current one like this:
Code: [Select]
module sync_generator(
// inputs
input wire clk, // base clock
input wire pix_enable, // pixel clock
input wire reset, // reset: restarts frame
// outputs
output reg hsync, // horizontal sync
output reg vsync, // vertical sync
output wire [9:0] x, // current pixel x position
output wire [9:0] y, // current pixel y position
output reg inDisplayArea // high when in display area (valid drawing area)
);


Now, for the memory data out.  That clocking and registering of that bus is something happening inside quartus' 'altsyncram' function.  From your point of view, that data signal is an 'OUTPUT' of the 'altsyncram' function which you are receiving, so, in your function, it is like you are receiving an input.   You do not want to pass the data out through another clocked register in you code.  It is already delayed 2 clocks behind the address input and putting it through a reg will now make that 3 clocks before the data bus exist your verilog function module.  In your function's declaration, just make a wire:

---------------------------------------------
output wire [7:0] data_out_a,
---------------------------------------------



However, if Quartus does not like this, you might need to declare an internal subwire. This means just above you 'altsyncram' function add this:
--------------------------------------------------
wire [7:0] sub_data_out_a;
wire [7:0] data_out_a = sub_data_out_a[7:0];
----------------------------------------------

And in the 'altsyncram' change:
         .q_a (data_out_a),
to:
         .q_a (sub_data_out_a),


In the body of your @(posedge clk), you do not need to touch the read data, it's just a wire right through from ram block to your outer function.
« Last Edit: November 10, 2019, 01:57:22 am by BrianHG »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7737
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #293 on: November 10, 2019, 04:20:22 am »
OT: Thread read rankings:
In FPGA, we are #35 out of 285 since the inception of EEVblog forum.
In FPGA, in threads reads for a thread which existed for only the last 3 weeks, we are #1!

Ok, I realize that FPGA is a low read count on this forum...

Cant wait for the OP to get past this synchronous parallel access memory, the programmable memory pointing address generators is the last fancy thing, everything else is nothing more than 2 lookup palette table rams a few conditional A/B stencil switches, and the project will be finished.

Adding internal DVI serializer would be version 1.5 as it needs a routed PCB.
Adding a sophisticated pixel/line/box copy/drawing blitter would be considered next level version #2, though it is just added logic.  I ensured everything else which exists, how it is written in a way which will support the plugin.

 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #294 on: November 10, 2019, 12:49:36 pm »
Ok, when defining a module IO, IE any signal/wires going in and out, all must be labeled as an input, or output, or bidir. (There are a few others, but not all compilers support it.)

Now, I know you have:
reg [MAX_ADDR_BITS - 1:0] rd_addr_pipe;

However, if you want that reg to be exposed to you function's declared IO port, yes, it will also need to be declared as an output as well. 

That's my point though, it's just an internal register? The output from the altsyncram function goes into the pipe, which then goes into the output (addr_out_a, or pc_ena_out).  Do I still need to declare the pipe as an out as well then?

Now, for the memory data out.  That clocking and registering of that bus is something happening inside quartus' 'altsyncram' function.  From your point of view, that data signal is an 'OUTPUT' of the 'altsyncram' function which you are receiving, so, in your function, it is like you are receiving an input.   You do not want to pass the data out through another clocked register in you code.  It is already delayed 2 clocks behind the address input and putting it through a reg will now make that 3 clocks before the data bus exist your verilog function module.

Seems to compile with no errors as is - see latest gpu_dual_port_ram_INTEL.v code below.

Code: [Select]
module gpu_dual_port_ram_INTEL (

// inputs
input clk,
input [2:0] pc_ena_in,
input clk_host,
input wr_en_host,
input [19:0] addr_a,
input [19:0] addr_b,
input [7:0] data_in_b,

// registered outputs
output reg [19:0] addr_out_a,
output reg [2:0] pc_ena_out,

// direct outputs
output wire [7:0] data_out_a,
output wire [7:0] data_out_b

);

// define the maximum address bit - effectively the RAM size
parameter MAX_ADDR_BITS = 14;

// define delay pipe registers
reg [MAX_ADDR_BITS - 1:0] rd_addr_pipe;
reg [3:0] pc_ena_pipe;

// ****************************************************************************************************************************
// Dual-port GPU RAM
//
// Port A - read only by GPU
// Port B - read/writeable by host system
// Data buses - 8 bits / 1 byte wide
// Address buses - MAX_ADDR_BITS wide (14 bits default)
// Memory word size - 2^MAX_ADDR_BITS (16384 bytes default)
// ****************************************************************************************************************************
altsyncram altsyncram_component (
.clock0 (clk),
.wren_a (1'b1),
.address_b (addr_b[MAX_ADDR_BITS - 1:0]),
.clock1 (clk_host),
.data_b (data_in_b),
.wren_b (wr_en_host),
.address_a (addr_a[MAX_ADDR_BITS - 1:0]),
.data_a (8'b00000000),
.q_a (data_out_a),
.q_b (data_out_b),
.aclr0 (1'b0),
.aclr1 (1'b0),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clocken0 (1'b1),
.clocken1 (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.eccstatus (),
.rden_a (1'b1),
.rden_b (1'b1));

defparam
altsyncram_component.address_reg_b = "CLOCK1",
altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_input_b = "BYPASS",
altsyncram_component.clock_enable_output_a = "BYPASS",
altsyncram_component.clock_enable_output_b = "BYPASS",
altsyncram_component.indata_reg_b = "CLOCK1",
altsyncram_component.init_file = "../osd_mem.mif",
altsyncram_component.intended_device_family = "Cyclone IV E",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 2 ** MAX_ADDR_BITS,
altsyncram_component.numwords_b = 2 ** MAX_ADDR_BITS,
altsyncram_component.operation_mode = "BIDIR_DUAL_PORT",
altsyncram_component.outdata_aclr_a = "NONE",
altsyncram_component.outdata_aclr_b = "NONE",
altsyncram_component.outdata_reg_a = "CLOCK0",
altsyncram_component.outdata_reg_b = "CLOCK1",
altsyncram_component.power_up_uninitialized = "FALSE",
altsyncram_component.read_during_write_mode_port_a = "OLD_DATA",
altsyncram_component.read_during_write_mode_port_b = "OLD_DATA",
altsyncram_component.widthad_a = MAX_ADDR_BITS - 1,
altsyncram_component.widthad_b = MAX_ADDR_BITS - 1,
altsyncram_component.width_a = 8,
altsyncram_component.width_b = 8,
altsyncram_component.width_byteena_a = 1,
altsyncram_component.width_byteena_b = 1,
altsyncram_component.wrcontrol_wraddress_reg_b = "CLOCK1";

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

always @(posedge clk) begin

// **************************************************************************************************************************
// *** Create a serial pipe where the PIPE_DELAY parameter selects the pixel count delay for the xxx_in to the xxx_out ports
// **************************************************************************************************************************
rd_addr_pipe  <= addr_a;
addr_out_a <= rd_addr_pipe;

pc_ena_pipe <= pc_ena_in;
pc_ena_out <= pc_ena_pipe;
// **************************************************************************************************************************

end

endmodule


In the body of your @(posedge clk), you do not need to touch the read data, it's just a wire right through from ram block to your outer function.

Okay, the code above should be updated with the changes you've pointed out.  The only area I'm not sure about are the delay pipes - they're internal to the function and don't directly output, hence my reluctance to declare them as outputs.  Let me know if this is wrong and I've misunderstood something!   :popcorn:
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7737
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #295 on: November 10, 2019, 01:19:27 pm »
Ok, when defining a module IO, IE any signal/wires going in and out, all must be labeled as an input, or output, or bidir. (There are a few others, but not all compilers support it.)

Now, I know you have:
reg [MAX_ADDR_BITS - 1:0] rd_addr_pipe;

However, if you want that reg to be exposed to you function's declared IO port, yes, it will also need to be declared as an output as well. 


That's my point though, it's just an internal register? The output from the altsyncram function goes into the pipe, which then goes into the output (addr_out_a, or pc_ena_out).  Do I still need to declare the pipe as an out as well then?

Nope, we just want the final addr_out[] reg exposed.
Note that no matter the configured ram size, we still want to pass through all 20 address bits to the output, even if the ram is configured to only 16 or 17.
Quote

Now, for the memory data out.  That clocking and registering of that bus is something happening inside quartus' 'altsyncram' function.  From your point of view, that data signal is an 'OUTPUT' of the 'altsyncram' function which you are receiving, so, in your function, it is like you are receiving an input.   You do not want to pass the data out through another clocked register in you code.  It is already delayed 2 clocks behind the address input and putting it through a reg will now make that 3 clocks before the data bus exist your verilog function module.

Seems to compile with no errors as is - see latest gpu_dual_port_ram_INTEL.v code below.

Code: [Select]
module gpu_dual_port_ram_INTEL (

// inputs
input clk,
input [2:0] pc_ena_in,
input clk_host,
input wr_en_host,
input [19:0] addr_a,
input [19:0] addr_b,
input [7:0] data_in_b,

// registered outputs
output reg [19:0] addr_out_a,
output reg [2:0] pc_ena_out,

// direct outputs
output wire [7:0] data_out_a,
output wire [7:0] data_out_b

);

// define the maximum address bit - effectively the RAM size
parameter MAX_ADDR_BITS = 14;

// define delay pipe registers
reg [MAX_ADDR_BITS - 1:0] rd_addr_pipe;
reg [3:0] pc_ena_pipe;

// ****************************************************************************************************************************
// Dual-port GPU RAM
//
// Port A - read only by GPU
// Port B - read/writeable by host system
// Data buses - 8 bits / 1 byte wide
// Address buses - MAX_ADDR_BITS wide (14 bits default)
// Memory word size - 2^MAX_ADDR_BITS (16384 bytes default)
// ****************************************************************************************************************************
altsyncram altsyncram_component (
.clock0 (clk),
.wren_a (1'b1),
.address_b (addr_b[MAX_ADDR_BITS - 1:0]),
.clock1 (clk_host),
.data_b (data_in_b),
.wren_b (wr_en_host),
.address_a (addr_a[MAX_ADDR_BITS - 1:0]),
.data_a (8'b00000000),
.q_a (data_out_a),
.q_b (data_out_b),
.aclr0 (1'b0),
.aclr1 (1'b0),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clocken0 (1'b1),
.clocken1 (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.eccstatus (),
.rden_a (1'b1),
.rden_b (1'b1));

defparam
altsyncram_component.address_reg_b = "CLOCK1",
altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_input_b = "BYPASS",
altsyncram_component.clock_enable_output_a = "BYPASS",
altsyncram_component.clock_enable_output_b = "BYPASS",
altsyncram_component.indata_reg_b = "CLOCK1",
altsyncram_component.init_file = "../osd_mem.mif",
altsyncram_component.intended_device_family = "Cyclone IV E",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 2 ** MAX_ADDR_BITS,
altsyncram_component.numwords_b = 2 ** MAX_ADDR_BITS,
altsyncram_component.operation_mode = "BIDIR_DUAL_PORT",
altsyncram_component.outdata_aclr_a = "NONE",
altsyncram_component.outdata_aclr_b = "NONE",
altsyncram_component.outdata_reg_a = "CLOCK0",
altsyncram_component.outdata_reg_b = "CLOCK1",
altsyncram_component.power_up_uninitialized = "FALSE",
altsyncram_component.read_during_write_mode_port_a = "OLD_DATA",
altsyncram_component.read_during_write_mode_port_b = "OLD_DATA",
altsyncram_component.widthad_a = MAX_ADDR_BITS - 1,
altsyncram_component.widthad_b = MAX_ADDR_BITS - 1,
altsyncram_component.width_a = 8,
altsyncram_component.width_b = 8,
altsyncram_component.width_byteena_a = 1,
altsyncram_component.width_byteena_b = 1,
altsyncram_component.wrcontrol_wraddress_reg_b = "CLOCK1";

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

always @(posedge clk) begin

// **************************************************************************************************************************
// *** Create a serial pipe where the PIPE_DELAY parameter selects the pixel count delay for the xxx_in to the xxx_out ports
// **************************************************************************************************************************
rd_addr_pipe  <= addr_a;
addr_out_a <= rd_addr_pipe;

pc_ena_pipe <= pc_ena_in;
pc_ena_out <= pc_ena_pipe;
// **************************************************************************************************************************

end

endmodule

Ok, first little error:
altsyncram_component.widthad_a = MAX_ADDR_BITS - 1,
altsyncram_component.widthad_b = MAX_ADDR_BITS - 1,
Get rid of the '-1' here, Intel is asking for the 'Width' of the port, not what the maximum address wire number is.

Next,
---------------
   rd_addr_pipe  <= addr_a;
   addr_out_a <= rd_addr_pipe;
   
   pc_ena_pipe <= pc_ena_in;
   pc_ena_out <= pc_ena_pipe;
------------------
Correct.  You forgot, I asked for an additional auxiliary command to be piped through just like the addr_a in&out pipe.  Call it  'cmd_in' and 'cmd_out' and make it 16 bits ie. [15:0]...

The command is useful for example to direct the destination read to a specific register, or describe a specific pixel type, like bitplane, or 256 color, or, set how many pixels that read pixel should clock for, like a horizontal size/scale.  It can also be used to further expand one or more of our read ports into another 2 through 16 parallel read channels operating below the pixel 25MHz pixel clock speed.  (for example, maybe multichannel audio playback engine...)

Do not worry about all the extra wires, registers, and ports.  The compiler automatically simplifies out any un-wired logic.

Quote


In the body of your @(posedge clk), you do not need to touch the read data, it's just a wire right through from ram block to your outer function.

Okay, the code above should be updated with the changes you've pointed out.  The only area I'm not sure about are the delay pipes - they're internal to the function and don't directly output, hence my reluctance to declare them as outputs.  Let me know if this is wrong and I've misunderstood something!   :popcorn:

Wit the last addition of the cmd in&out pipe, it's time to wire this module into the multiport gpu ram.
« Last Edit: November 10, 2019, 01:54:02 pm by BrianHG »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7737
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #296 on: November 10, 2019, 01:25:47 pm »
Also, you can optionally change:
--------------------------
input clk_host,
input wr_en_host,
-------------------------
to:
input clk_b
input wr_en_b

If you like....

Note that it is the job of the ''multiport_gpu_ram'' to wire the 2 port memory and wire  port b into the names clk_host, wr_en_host, data_in_host, data_out_host...


remember, you labeled port 2 addr_b, data in B, data out B....

And dont forget to change the:
reg [MAX_ADDR_BITS - 1:0] rd_addr_pipe;
to:
reg [19:0] rd_addr_pipe;
« Last Edit: November 10, 2019, 01:36:00 pm by BrianHG »
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #297 on: November 10, 2019, 02:20:42 pm »
Nope, we just want the final addr_out[] reg exposed.
Not that no matter the configured ram size, we still want to pass through all 20 address bits to the output, even if the ram is configured to only 16 or 17.

Marvellous, was thinking along the right lines then. 

Correct.  You forgot, I asked for an additional auxiliary command to be piped through just like the addr_a in&out pipe.  Call it  'cmd_in' and 'cmd_out' and make it 16 bits ie. [15:0]...

Yes, my memory isn't my strong point.  ::)  Sorted now.

Wit the last addition of the cmd in&out pipe, it's time to wire this module into the multiport gpu ram.

I've made a start on the multiport_gpu_ram already - lots of things going off here though, so can't focus on this at the moment - will be back to it later tonight.

Current multiport_gpu_ram.v:

Code: [Select]
module multiport_gpu_ram (

input clk, // Primary clk input (125 MHz)
input [3:0] pc_ena, // Pixel clock enable
input clk_b, // Host (Z80) clock input
input write_ena_b, // Host (Z80) clock enable

// address buses (input)
input [19:0] address_0,
input [19:0] address_1,
input [19:0] address_2,
input [19:0] address_3,
input [19:0] address_4,
input [19:0] addr_host,

// auxilliary read command buses (input)
input [7:0] aux_read_0,
input [7:0] aux_read_1,
input [7:0] aux_read_2,
input [7:0] aux_read_3,
input [7:0] aux_read_4,

// address pass-thru bus (output)
output reg [19:0] addr_passthru,

// auxilliary read command buses (pass-thru output)
output reg [7:0] auxRdPT_0,
output reg [7:0] auxRdPT_1,
output reg [7:0] auxRdPT_2,
output reg [7:0] auxRdPT_3,
output reg [7:0] auxRdPT_4,

// data buses (output)
output reg [7:0] dataOUT_0,
output reg [7:0] dataOUT_1,
output reg [7:0] dataOUT_2,
output reg [7:0] dataOUT_3,
output reg [7:0] dataOUT_4,
output [7:0] data_host

);

// dual-port GPU RAM handler

// define the maximum address bits - effectively the RAM size
parameter MAX_ADDR_BITS = 20;

reg [MAX_ADDR_BITS - 1:0] address_mux;
reg [7:0] aux_read_mux;

// create a GPU RAM instance
gpu_dual_port_ram_INTEL gpu_RAM(
.clk(clk),
.pc_ena_in(pc_ena),
.clk_b(clk_b),
.wr_en_b(wr_en_b),
.addr_a(address_mux),
.addr_b(),
.data_in_b(),
.addr_out_a(addr_passthru),
.pc_ena_out(),
.data_out_a(aux_read_mux),
.data_out_b()
);

always @(posedge clk) begin

// perform 5:1 mux for all inputs to the dual-port RAM
case (pc_ena[2:0])
3'b000 : begin
address_mux <= address_0;
aux_read_mux <= aux_read_0;
addr_passthru <= address_0;
end
3'b001 : begin
address_mux <= address_1;
aux_read_mux <= aux_read_1;
addr_passthru <= address_1;
end
3'b011 : begin
address_mux <= address_2;
aux_read_mux <= aux_read_2;
addr_passthru <= address_2;
end
3'b100 : begin
address_mux <= address_3;
aux_read_mux <= aux_read_3;
addr_passthru <= address_3;
end
3'b101 : begin
address_mux <= address_4;
aux_read_mux <= aux_read_4;
addr_passthru <= address_4;
end
endcase

end // always @clk

endmodule

Have merged all the pass-through addresses into one bus - otherwise can't see the point of having them in the mux at all?  addr_passthru is the passed-through address, muxed from one of the five input addresses depending on pc_ena value.  Hopefully that's right.   ???

Also, you can optionally change:
--------------------------
input clk_host,
input wr_en_host,
-------------------------
to:
input clk_b
input wr_en_b

If you like....

Note that it is the job of the ''multiport_gpu_ram'' to wire the 2 port memory and wire  port b into the names clk_host, wr_en_host, data_in_host, data_out_host...


remember, you labeled port 2 addr_b, data in B, data out B....

And dont forget to change the:
reg [MAX_ADDR_BITS - 1:0] rd_addr_pipe;
to:
reg [19:0] rd_addr_pipe;

Think I've done all that - things have gotten busy here though and I've had to cut this short, so attaching files for perusal, but there may be incomplete bits errors.

gpu_dual_port_ram_INTEL:

Code: [Select]
module gpu_dual_port_ram_INTEL (

// inputs
input clk,
input [3:0] pc_ena_in,
input clk_b,
input wr_en_b,
input [19:0] addr_a,
input [19:0] addr_b,
input [7:0] data_in_b,
input [15:0] cmd_in,

// registered outputs
output reg [19:0] addr_out_a,
output reg [2:0] pc_ena_out,
output reg [15:0] cmd_out,

// direct outputs
output wire [7:0] data_out_a,
output wire [7:0] data_out_b

);

// define the maximum address bit - effectively the RAM size
parameter MAX_ADDR_BITS = 14;

// define delay pipe registers
reg [19:0] rd_addr_pipe_a;
reg [15:0] cmd_pipe;
reg [3:0] pc_ena_pipe;

// ****************************************************************************************************************************
// Dual-port GPU RAM
//
// Port A - read only by GPU
// Port B - read/writeable by host system
// Data buses - 8 bits / 1 byte wide
// Address buses - MAX_ADDR_BITS wide (14 bits default)
// Memory word size - 2^MAX_ADDR_BITS (16384 bytes default)
// ****************************************************************************************************************************
altsyncram altsyncram_component (
.clock0 (clk),
.wren_a (1'b1),
.address_b (addr_b[MAX_ADDR_BITS:0]),
.clock1 (clk_b),
.data_b (data_in_b),
.wren_b (wr_en_b),
.address_a (addr_a[MAX_ADDR_BITS:0]),
.data_a (8'b00000000),
.q_a (data_out_a),
.q_b (data_out_b),
.aclr0 (1'b0),
.aclr1 (1'b0),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clocken0 (1'b1),
.clocken1 (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.eccstatus (),
.rden_a (1'b1),
.rden_b (1'b1));

defparam
altsyncram_component.address_reg_b = "CLOCK1",
altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_input_b = "BYPASS",
altsyncram_component.clock_enable_output_a = "BYPASS",
altsyncram_component.clock_enable_output_b = "BYPASS",
altsyncram_component.indata_reg_b = "CLOCK1",
altsyncram_component.init_file = "../osd_mem.mif",
altsyncram_component.intended_device_family = "Cyclone IV E",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 2 ** MAX_ADDR_BITS,
altsyncram_component.numwords_b = 2 ** MAX_ADDR_BITS,
altsyncram_component.operation_mode = "BIDIR_DUAL_PORT",
altsyncram_component.outdata_aclr_a = "NONE",
altsyncram_component.outdata_aclr_b = "NONE",
altsyncram_component.outdata_reg_a = "CLOCK0",
altsyncram_component.outdata_reg_b = "CLOCK1",
altsyncram_component.power_up_uninitialized = "FALSE",
altsyncram_component.read_during_write_mode_port_a = "OLD_DATA",they're
altsyncram_component.read_during_write_mode_port_b = "OLD_DATA",
altsyncram_component.widthad_a = MAX_ADDR_BITS - 1,
altsyncram_component.widthad_b = MAX_ADDR_BITS - 1,
altsyncram_component.width_a = 8,
altsyncram_component.width_b = 8,
altsyncram_component.width_byteena_a = 1,
altsyncram_component.width_byteena_b = 1,
altsyncram_component.wrcontrol_wraddress_reg_b = "CLOCK1";

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

always @(posedge clk) begin

// **************************************************************************************************************************
// *** Create a serial pipe where the PIPE_DELAY parameter selects the pixel count delay for the xxx_in to the xxx_out ports
// **************************************************************************************************************************
rd_addr_pipe <= addr_a;
addr_out_a <= rd_addr_pipe;

cmd_pipe <= cmd_in;
cmd_out <= cmd_pipe;

pc_ena_pipe <= pc_ena_in;
pc_ena_out <= pc_ena_pipe;
// **************************************************************************************************************************

end

endmodule
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7737
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #298 on: November 10, 2019, 03:00:03 pm »

I've made a start on the multiport_gpu_ram already - lots of things going off here though, so can't focus on this at the moment - will be back to it later tonight.

Current multiport_gpu_ram.v:

Code: [Select]
module multiport_gpu_ram (

input clk, // Primary clk input (125 MHz)
input [3:0] pc_ena, // Pixel clock enable
input clk_b, // Host (Z80) clock input
input write_ena_b, // Host (Z80) clock enable

// address buses (input)
input [19:0] address_0,
input [19:0] address_1,
input [19:0] address_2,
input [19:0] address_3,
input [19:0] address_4,
input [19:0] addr_host,

// auxilliary read command buses (input)
input [7:0] aux_read_0,
input [7:0] aux_read_1,
input [7:0] aux_read_2,
input [7:0] aux_read_3,
input [7:0] aux_read_4,

// address pass-thru bus (output)
output reg [19:0] addr_passthru,

// auxilliary read command buses (pass-thru output)
output reg [7:0] auxRdPT_0,
output reg [7:0] auxRdPT_1,
output reg [7:0] auxRdPT_2,
output reg [7:0] auxRdPT_3,
output reg [7:0] auxRdPT_4,

// data buses (output)
output reg [7:0] dataOUT_0,
output reg [7:0] dataOUT_1,
output reg [7:0] dataOUT_2,
output reg [7:0] dataOUT_3,
output reg [7:0] dataOUT_4,
output [7:0] data_host

);

// dual-port GPU RAM handler

// define the maximum address bits - effectively the RAM size
parameter MAX_ADDR_BITS = 20;

reg [MAX_ADDR_BITS - 1:0] address_mux;
reg [7:0] aux_read_mux;

// create a GPU RAM instance
gpu_dual_port_ram_INTEL gpu_RAM(
.clk(clk),
.pc_ena_in(pc_ena),
.clk_b(clk_b),
.wr_en_b(wr_en_b),
.addr_a(address_mux),
.addr_b(),
.data_in_b(),
.addr_out_a(addr_passthru),
.pc_ena_out(),
.data_out_a(aux_read_mux),
.data_out_b()
);

always @(posedge clk) begin

// perform 5:1 mux for all inputs to the dual-port RAM
case (pc_ena[2:0])
3'b000 : begin
address_mux <= address_0;
aux_read_mux <= aux_read_0;
addr_passthru <= address_0;
end
3'b001 : begin
address_mux <= address_1;
aux_read_mux <= aux_read_1;
addr_passthru <= address_1;
end
3'b011 : begin
address_mux <= address_2;
aux_read_mux <= aux_read_2;
addr_passthru <= address_2;
end
3'b100 : begin
address_mux <= address_3;
aux_read_mux <= aux_read_3;
addr_passthru <= address_3;
end
3'b101 : begin
address_mux <= address_4;
aux_read_mux <= aux_read_4;
addr_passthru <= address_4;
end
endcase

end // always @clk

endmodule

Have merged all the pass-through addresses into one bus - otherwise can't see the point of having them in the mux at all?  addr_passthru is the passed-through address, muxed from one of the five input addresses depending on pc_ena value.  Hopefully that's right.   ???

Ok, from what I can see so far:
After initiating the "gpu_dual_port_ram_INTEL gpu_RAM(.....);", you need the:
---------------------------
          defparam
                           gpu_RAM.MAX_ADDR_BITS = MAX_ADDR_BITS ;
---------------------------
     This will pass the module multiport_gpu_ram's MAX_ADDR_BITS parameter into the gpu_dual_port_ram_INTEL's MAX_ADDR_BITS parameter.  It may be useful to pass the 'altsyncram_component.numwords_a&b' since it may be possible to allocate 24kb in the FPGA since it has that much memory, yet not 32kb.

   .addr_out_a(addr_passthru), should change to (addr_passthru_mux) and don't forget to declare it as a wire.
   .data_out_a(aux_read_mux) is also a wire.
   .pc_ena_out(pc_ena_out), is also a wire and an output

   // address pass-thru bus (output)
   output reg [19:0] addr_out,   There are 5 of these to match the read address ins 0 through 5 in.

   // auxilliary read command buses (input)
   input [7:0] aux_read_0,
   input [7:0] aux_read_1,
   input [7:0] aux_read_2,
   input [7:0] aux_read_3,
   input [7:0] aux_read_4,
 change all these to cmd_in[15:0].  (global search and replace)

   // auxilliary read command buses (pass-thru output)
   output reg [7:0] auxRdPT_0,
   output reg [7:0] auxRdPT_1,
   output reg [7:0] auxRdPT_2,
   output reg [7:0] auxRdPT_3,
   output reg [7:0] auxRdPT_4,
change these to cmd_out[15:0]

reg [MAX_ADDR_BITS - 1:0] address_mux; 
change to reg [19:0] address_mux; 

reg [7:0] aux_read_mux;
change to reg [15:0] cmd_read_mux  (global search and replace)

Your missing a few of the new ports for 'gpu_dual_port_ram_INTEL gpu_RAM(...);'

Almost done, next you will resort the read ram contents, the piped through address & cmds into their output registers and sync those to your new delayed 'pc_ena_out[3:0]' coming out of the Intel ram module.

Note that we forgot to wire through the 'pc_ena_out[3:0]' coming out of the Intel ram module thought to the multiport_gpu_ram ( ...) ports, so that the rest of our graphics pipe heading to the output pins will incorporate the delay shift generated by the memory.  (Though we can work around this through sophisticated re-syncing all the ram outputs back to the next pc_ena_in==0 cycle, this ena signal in the FPGA is beginning to drive so much logic limiting our FMAX, this is an opportune point to D-clock pipe the signals for the second half of our graphics pipe.)

« Last Edit: November 10, 2019, 03:02:07 pm by BrianHG »
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #299 on: November 11, 2019, 10:12:38 am »
After initiating the "gpu_dual_port_ram_INTEL gpu_RAM(.....);", you need the:
---------------------------
          defparam
                           gpu_RAM.MAX_ADDR_BITS = MAX_ADDR_BITS ;
---------------------------
     This will pass the module multiport_gpu_ram's MAX_ADDR_BITS parameter into the gpu_dual_port_ram_INTEL's MAX_ADDR_BITS parameter.  It may be useful to pass the 'altsyncram_component.numwords_a&b' since it may be possible to allocate 24kb in the FPGA since it has that much memory, yet not 32kb.

Okay, stupid question - altsyncram specifies altsyncram_component.numwords_a (and b) - I had 2 ** MAXSIZE in there, but if they're the number of words, I'll need to divide that by word size (8), otherwise the RAM will (try to be) 8 times larger than what I think I'm specifying?

So, for example, this:

Code: [Select]
// define the memory size (number of words) - this allows RAM sizes other than multiples of 2
// but defaults to power-of-two sizing based on MAX_ADDR_BITS if not otherwise specified
parameter WORDS = 2 ** MAX_ADDR_BITS;


..needs to be this:

Code: [Select]
// define the memory size (number of words) - this allows RAM sizes other than multiples of 2
// but defaults to power-of-two sizing based on MAX_ADDR_BITS if not otherwise specified
parameter WORDS = (2 ** MAX_ADDR_BITS) / 8;


??

   // address pass-thru bus (output)
   output reg [19:0] addr_out,   There are 5 of these to match the read address ins 0 through 5 in.

   // auxilliary read command buses (input)
   input [7:0] aux_read_0,
   input [7:0] aux_read_1,
   input [7:0] aux_read_2,
   input [7:0] aux_read_3,
   input [7:0] aux_read_4,
 change all these to cmd_in[15:0].  (global search and replace)

   // auxilliary read command buses (pass-thru output)
   output reg [7:0] auxRdPT_0,
   output reg [7:0] auxRdPT_1,
   output reg [7:0] auxRdPT_2,
   output reg [7:0] auxRdPT_3,
   output reg [7:0] auxRdPT_4,
change these to cmd_out[15:0]

reg [MAX_ADDR_BITS - 1:0] address_mux; 
change to reg [19:0] address_mux; 

reg [7:0] aux_read_mux;
change to reg [15:0] cmd_read_mux  (global search and replace)

These should all be present and correct now... I think.  Got a little confused earlier with all the changes, so I'll be double-checking it all, but I think it would benefit from a close look.

Your missing a few of the new ports for 'gpu_dual_port_ram_INTEL gpu_RAM(...);'

They should all be present and correct now.  :D

Almost done, next you will resort the read ram contents, the piped through address & cmds into their output registers and sync those to your new delayed 'pc_ena_out[3:0]' coming out of the Intel ram module.

Have made a bit of a start on this - the 5:1 mux code is modified according to my present understanding.  The read address is passed through to the ram module, the pass-through address is passed out to the appropriate address bus according to the current mux step, as is the data read from memory.

I'm a little unsure about the command bus, though.  It's piped into the memory via cmd_read_mux, but that seems like an unnecessary step as I only have one cmd_in bus (and one cmd_out bus) - should these be increased to 5 as well?  It's possible I've misunderstood your instruction to 'change all these to cmd_in[15:0]'...  ???

Note that we forgot to wire through the 'pc_ena_out[3:0]' coming out of the Intel ram module thought to the multiport_gpu_ram ( ...) ports, so that the rest of our graphics pipe heading to the output pins will incorporate the delay shift generated by the memory.  (Though we can work around this through sophisticated re-syncing all the ram outputs back to the next pc_ena_in==0 cycle, this ena signal in the FPGA is beginning to drive so much logic limiting our FMAX, this is an opportune point to D-clock pipe the signals for the second half of our graphics pipe.)

Okay, I think I understand - but pc_ena passes through the gpu_dual_port_ram_INTEL module via a register pipe, which will fulfil the need to D-clock the signal, right?

gpu_dual_port_ram_INTEL.v:

Code: [Select]
module gpu_dual_port_ram_INTEL (

// inputs
input clk,
input [3:0] pc_ena_in,
input clk_b,
input wr_en_b,
input [19:0] addr_a,
input [19:0] addr_b,
input [7:0] data_in_b,
input [15:0] cmd_in,

// registered outputs
output reg [19:0] addr_out_a,
output reg [3:0] pc_ena_out,
output reg [15:0] cmd_out,

// direct outputs
output wire [7:0] data_out_a,
output wire [7:0] data_out_b

);

// define the maximum address bit
parameter MAX_ADDR_BITS = 14;

// define the memory size (number of words) - this allows RAM sizes other than multiples of 2
// but defaults to power-of-two sizing based on MAX_ADDR_BITS if not otherwise specified
parameter WORDS = 2 ** MAX_ADDR_BITS;

// define delay pipe registers
reg [19:0] rd_addr_pipe_a;
reg [15:0] cmd_pipe;
reg [3:0] pc_ena_pipe;

// ****************************************************************************************************************************
// Dual-port GPU RAM
//
// Port A - read only by GPU
// Port B - read/writeable by host system
// Data buses - 8 bits / 1 byte wide
// Address buses - MAX_ADDR_BITS wide (14 bits default)
// Memory word size - 2^MAX_ADDR_BITS (16384 bytes default)
// ****************************************************************************************************************************
altsyncram altsyncram_component (
.clock0 (clk),
.wren_a (1'b1),
.address_b (addr_b[MAX_ADDR_BITS:0]),
.clock1 (clk_b),
.data_b (data_in_b),
.wren_b (wr_en_b),
.address_a (addr_a[MAX_ADDR_BITS:0]),
.data_a (8'b00000000),
.q_a (data_out_a),
.q_b (data_out_b),
.aclr0 (1'b0),
.aclr1 (1'b0),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clocken0 (1'b1),
.clocken1 (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.eccstatus (),
.rden_a (1'b1),
.rden_b (1'b1));

defparam
altsyncram_component.address_reg_b = "CLOCK1",
altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_input_b = "BYPASS",
altsyncram_component.clock_enable_output_a = "BYPASS",
altsyncram_component.clock_enable_output_b = "BYPASS",
altsyncram_component.indata_reg_b = "CLOCK1",
altsyncram_component.init_file = "../osd_mem.mif",
altsyncram_component.intended_device_family = "Cyclone IV E",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = WORDS,
altsyncram_component.numwords_b = WORDS,
altsyncram_component.operation_mode = "BIDIR_DUAL_PORT",
altsyncram_component.outdata_aclr_a = "NONE",
altsyncram_component.outdata_aclr_b = "NONE",
altsyncram_component.outdata_reg_a = "CLOCK0",
altsyncram_component.outdata_reg_b = "CLOCK1",
altsyncram_component.power_up_uninitialized = "FALSE",
altsyncram_component.read_during_write_mode_port_a = "OLD_DATA",they're
altsyncram_component.read_during_write_mode_port_b = "OLD_DATA",
altsyncram_component.widthad_a = MAX_ADDR_BITS - 1,
altsyncram_component.widthad_b = MAX_ADDR_BITS - 1,
altsyncram_component.width_a = 8,
altsyncram_component.width_b = 8,
altsyncram_component.width_byteena_a = 1,
altsyncram_component.width_byteena_b = 1,
altsyncram_component.wrcontrol_wraddress_reg_b = "CLOCK1";

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

always @(posedge clk) begin

// **************************************************************************************************************************
// *** Create a serial pipe where the PIPE_DELAY parameter selects the pixel count delay for the xxx_in to the xxx_out ports
// **************************************************************************************************************************
rd_addr_pipe <= addr_a;
addr_out_a <= rd_addr_pipe;

cmd_pipe <= cmd_in;
cmd_out <= cmd_pipe;

pc_ena_pipe <= pc_ena_in;
pc_ena_out <= pc_ena_pipe;
// **************************************************************************************************************************

end

endmodule


multiport_gpu_ram.v:

Code: [Select]
module multiport_gpu_ram (

input clk, // Primary clk input (125 MHz)
input [3:0] pc_ena_in, // Pixel clock enable
input clk_b, // Host (Z80) clock input
input write_ena_b, // Host (Z80) clock enable

// address buses (input)
input [19:0] address_0,
input [19:0] address_1,
input [19:0] address_2,
input [19:0] address_3,
input [19:0] address_4,
input [19:0] addr_host,

// auxilliary read command buses (input)
input [15:0] cmd_in,

// outputs
output wire [3:0] pc_ena_out,

// address pass-thru bus (output)
output reg [19:0] addr_passthru_0,
output reg [19:0] addr_passthru_1,
output reg [19:0] addr_passthru_2,
output reg [19:0] addr_passthru_3,
output reg [19:0] addr_passthru_4,
output reg [19:0] addr_host_passthru,

// auxilliary read command bus (pass-thru output)
output reg [15:0] cmd_out,

// data buses (output)
output reg [7:0] dataOUT_0,
output reg [7:0] dataOUT_1,
output reg [7:0] dataOUT_2,
output reg [7:0] dataOUT_3,
output reg [7:0] dataOUT_4,
output [7:0] data_host

);

// dual-port GPU RAM handler

// define the maximum address bits - effectively the RAM size
parameter MAX_ADDR_BITS = 20;

reg [19:0] address_mux;
reg [15:0] cmd_read_mux;
wire [19:0] addr_passthru_mux;
wire [7:0] data_mux;

// create a GPU RAM instance
gpu_dual_port_ram_INTEL gpu_RAM(
.clk(clk),
.pc_ena_in(pc_ena_in),
.clk_b(clk_b),
.wr_en_b(wr_en_b),
.addr_a(address_mux),
.addr_b(),
.data_in_b(),
.cmd_in(cmd_read_mux),
.addr_out_a(addr_passthru_mux),
.pc_ena_out(pc_ena_out),
.cmd_out(cmd_out),
.data_out_a(data_mux),
.data_out_b()
);

// pass MAX_ADDR_BITS into the gpu_RAM instance
defparam gpu_RAM.MAX_ADDR_BITS = MAX_ADDR_BITS;

// set none-default word size for the RAM (24 KB)
defparam gpu_RAM.WORDS = 24576;  // ************** should this be divided by 8?

always @(posedge clk) begin

// route non-muxed pass-throughs
cmd_read_mux <= cmd_in;

// perform 5:1 mux for all inputs to the dual-port RAM
case (pc_ena[2:0])
3'b000 : begin
address_mux <= address_0;
addr_passthru_0 <= addr_passthru_mux;
dataOUT_0 <= data_mux;
end
3'b001 : begin
address_mux <= address_1;
addr_passthru_1 <= addr_passthru_mux;
dataOUT_1 <= data_mux;
end
3'b011 : begin
address_mux <= address_2;
addr_passthru_2 <= addr_passthru_mux;
dataOUT_2 <= data_mux;
end
3'b100 : begin
address_mux <= address_3;
addr_passthru_3 <= addr_passthru_mux;
dataOUT_3 <= data_mux;
end
3'b101 : begin
address_mux <= address_4;
addr_passthru_4 <= addr_passthru_mux;
dataOUT_4 <= data_mux;
end
endcase

end // always @clk

endmodule

 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf