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

0 Members and 1 Guest are viewing this topic.

Offline jhpadjustable

  • Frequent Contributor
  • **
  • Posts: 295
  • Country: us
  • Salt 'n' pepper beard
Re: FPGA VGA Controller for 8-bit computer
« Reply #600 on: December 07, 2019, 07:44:08 pm »
Now, forgive me if I'm completely wrong, jhpadjustable, it's been a few years since I've used my Amigas... but HAM mode - didn't that work by using two bytes for the colour, but reserved the top two bits to create a 4-value command that modified the remaining colour bits for the next pixel, or from the previous pixel (or something)?

That seems like it would require some serious horsepower from the Z80 CPU to get that to work - but that's coming from me with little understanding of how it works in the first place.
Close. The top two bits (of six) held a command and the bottom four held a parameter. At each pixel you could load all three primaries from a palette entry, or modify one primary directly while holding the other two. HAM was really best suited to displaying pre-drawn static bitmap images, like a background playfield, and (with care) could be used for compositing in special cases, as long as you remember to always put a fixed palette load at each leftmost pixel.

As BrianHG observed, maybe it's not all that useful anymore, in favor of visually superior and simpler luminance:chrominance encodings like YCbCr. Never mind  :)
"There are more things in heaven and earth, Arduino, than are dreamt of in your philosophy."
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7747
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #601 on: December 07, 2019, 08:56:46 pm »
When usure about your altsyncram function, use the block diagram megafunction wizard to generate a reference .v file for you. You will see a picture of the memory module as you create it.
Then copy-paste the numbers and names generated in the example .v file.
Use Quartus 9.1 for this on a blank new block diagram on a blank new dummy project.

It should look like this:


making this code:
Code: [Select]
module palette256 (
address_a,
address_b,
clock_a,
clock_b,
data_a,
data_b,
enable_a,
enable_b,
wren_a,
wren_b,
q_a,
q_b);

input [7:0]  address_a;
input [8:0]  address_b;
input   clock_a;
input   clock_b;
input [15:0]  data_a;
input [7:0]  data_b;
input   enable_a;
input   enable_b;
input   wren_a;
input   wren_b;
output [15:0]  q_a;
output [7:0]  q_b;



wire [15:0] sub_wire0;
wire [7:0] sub_wire1;
wire [15:0] q_a = sub_wire0[15:0];
wire [7:0] q_b = sub_wire1[7:0];

altsyncram altsyncram_component (
.clocken0 (enable_a),
.clocken1 (enable_b),
.wren_a (wren_a),
.clock0 (clock_a),
.wren_b (wren_b),
.clock1 (clock_b),
.address_a (address_a),
.address_b (address_b),
.data_a (data_a),
.data_b (data_b),
.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),
.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 = "NORMAL",
altsyncram_component.clock_enable_input_b = "NORMAL",
altsyncram_component.clock_enable_output_a = "NORMAL",
altsyncram_component.clock_enable_output_b = "NORMAL",
altsyncram_component.indata_reg_b = "CLOCK1",
altsyncram_component.init_file = "palette.mif",
altsyncram_component.init_file_layout = "PORT_A",
altsyncram_component.intended_device_family = "Cyclone III",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 256,
altsyncram_component.numwords_b = 512,
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 = 8,
altsyncram_component.widthad_b = 9,
altsyncram_component.width_a = 16,
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

Remember, for the A port, we may use the clock enable if you want the palette to look like a 2 pixel pipe delay.
Also, please do not hard-wire the write-enable on that port to a 1 this time around.
Another thing, make sure you have a page address parameter which protects the writing on the host port.
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #602 on: December 07, 2019, 11:41:10 pm »
Okay, not had much time to work on this tonight, but this is where I am so far.  Would appreciate some tips (like for the wr_ena and page address parameter etc) and corrections where there's bound to be errors.  ;)
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7747
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #603 on: December 07, 2019, 11:53:58 pm »
Okay, not had much time to work on this tonight, but this is where I am so far.  Would appreciate some tips (like for the wr_ena and page address parameter etc) and corrections where there's bound to be errors.  ;)
No piping or reging needed on this memory.  Just feed the address in and data out.  Optionally use the clock enable, however, you may accelerate things to 1 pixel by re-latchting the data out of the ram on the pc_ena[3:0]==0 since the memory takes less than 4 clock to return a valid result.

This means just wiring the ram's .clocken# to 1.  Also, the data output would be reged once for now as also will be the HDE,VDE,HS,VS.  I force you to have the .clocken# so you may wire them in the future if needed.

There is no command pipe in or out here anymore, it is straight memory to a final pixel.  Reg that pixel at the output of the palette module with the HDE,VDE,HS,VS.

Once done, you should be able to produce VGA colored text with the current bg_color reg with 1 modification, change this line in the 'bitplane_to_raster.sv'.:

change this line 87 to this:
                  pixel_out[4]   <= 1'b0;   // 1'b1;
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #604 on: December 08, 2019, 12:45:21 pm »
Okay, feel like I've lost my way a little and need some more steerage to get back on course.

Am I piping HDE, VDE, HS and VS through the dual_port_palette_ram_text module, or just through the palette_mixer module?  I guess I'd need to delay them by 1 clock if I don't pipe them through the ram module as well?

What exactly is required of the palette_mixer module?  I've included both files below, but am somewhat lost with what else needs to be added to palette_mixer (other than the pass-thru sync signals above)....?  :-//
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7747
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #605 on: December 08, 2019, 01:15:24 pm »
The palette mixer module just carries the 2 ram palettes, 1 with the 4444 text palette and the 565 graphics palette.

You do realize the next iteration of the vid_osd_generator will output 2 pictures at the same time, right?

The mixer module will mix/superimpose 1 picture on top of the other, right?

You know, like an over head projector with 2 transparent stencils, 1 with a text engine output using a 4444 palette and another beneath showing conventional pixel graphics using a second palette holding the 565 colors.

We need 2 palettes working in parallel.  The palettes also have a read delay which we need to line up to the pixel clock.  The HS,VS,HDE,VDE also needs to match that delay.  You also need to take into consideration the fact that a new pixel will happen every 5 (or other number) clock cycles set by the pc_ena[3:0]==0.

You also want the palette mixer module to be standard verilog calling the 'memory' modules which are exclusive to Intel Quartus altsyncram function which would be swapped out in a different FPGA vendor's dual port memory scheme.  I guess you should rename the the 'dual_port_palette_ram_text.v' to 'dual_port_palette_ram_INTEL.v'.  Remember, the text and graphics palette ram are identical, so 1 ram module would be used twice.  It's just the way the 16 output bits are wired to the RGB on each ram module inside the 'palette_mixer.v' module.  And each ram just has a different .mif file.  So, I am sure you know how to pass parameters and call 2 different instances of 1 memory module, with different memory address and different wiring to the inputs and outputs.

The output of the 2 palettes will just go through 2xRGB multiplies each, then the sum added together at then end.  The 'Alpha' output on the text palette controls the multiplication factor of each palette's RGB output, mixing the 2 opposite proportions of each image together.
« Last Edit: December 08, 2019, 01:23:03 pm by BrianHG »
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #606 on: December 08, 2019, 01:59:44 pm »
Quick question:  Can I specify a RAM parameter in the instantiating code?

Code: [Select]
// create a text/sprite palette RAM instance
dual_port_palette_ram_INTEL text_palette_RAM(

// inputs
.clock_a(clk),
.clock_b(clk),
.pc_ena_in(pc_ena_in),
.pixel_in(pixel_in),
.address_b(4'h0000),
.data_a(2'h00),
.data_b(2'h00),
.enable_a(pc_enable),
.enable_b(pc_enable),
.wren_a(1'b0),
.wren_b(1'b0),

// outputs
.pc_ena_out(pc_ena_out),
.pixel_out(pixel_out),
.data_out_b(data_out_b),

// registered outputs
.addr_out_a()

);

defparam text_palette_RAM.init_file = "palette_4444.mif";
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7747
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #607 on: December 08, 2019, 02:04:13 pm »
Yes, just look at our existing examples in the multiport ram and intel ram where we specify memory size.
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #608 on: December 08, 2019, 04:21:37 pm »
The palette mixer module just carries the 2 ram palettes, 1 with the 4444 text palette and the 565 graphics palette.

Got that in palette_mixer.v right now - both palettes are instantiated as text_palette_RAM and graphics_palette_RAM.

You do realize the next iteration of the vid_osd_generator will output 2 pictures at the same time, right?

The mixer module will mix/superimpose 1 picture on top of the other, right?

You know, like an over head projector with 2 transparent stencils, 1 with a text engine output using a 4444 palette and another beneath showing conventional pixel graphics using a second palette holding the 565 colors.

Ah okay, that makes a little more sense.  So it should have two 'image' inputs from the vid_osd_generator to look up the relevant palette entry for each one and mix them together into one 16-bit output?

We need 2 palettes working in parallel.  The palettes also have a read delay which we need to line up to the pixel clock.  The HS,VS,HDE,VDE also needs to match that delay.

You also need to take into consideration the fact that a new pixel will happen every 5 (or other number) clock cycles set by the pc_ena[3:0]==0.

Okay, I've got HS, VS, HDE and VDE passed through the palette_mixer module, with a user-definable delay.  It defaults to 6, but I'm sure you mentioned a delay of 1 or 2 earlier..? Might have imagined that, though.  :-//

You also want the palette mixer module to be standard verilog calling the 'memory' modules which are exclusive to Intel Quartus altsyncram function which would be swapped out in a different FPGA vendor's dual port memory scheme.

I guess you should rename the the 'dual_port_palette_ram_text.v' to 'dual_port_palette_ram_INTEL.v'.

Done.

Remember, the text and graphics palette ram are identical, so 1 ram module would be used twice.  It's just the way the 16 output bits are wired to the RGB on each ram module inside the 'palette_mixer.v' module.  And each ram just has a different .mif file.  So, I am sure you know how to pass parameters and call 2 different instances of 1 memory module, with different memory address and different wiring to the inputs and outputs.

Yes, that's all done.  I'll include dual_port_palette_ram_INTEL.v at the bottom for checking, but that's pretty much done with now.  It's instantiated twice in palette_mixer.v, specifying a different .mif file for each one.  My focus is now on palette_mixer.v and getting it set up properly.

The output of the 2 palettes will just go through 2xRGB multiplies each, then the sum added together at then end.

Can you give me an example please?  Are we splitting the 16-bit data from the dual_port_palette_ram into its RGB components and performing these multiplies, as the two palettes are in different RGB formats?

palette_mixer.v
Code: [Select]
module palette_mixer (

// inputs
input clk,
input vs_in,
input hs_in,
input hde_in,
input vde_in,
input [3:0] pc_ena_in,
input [7:0] txt_pixel_in,
input [7:0] gfx_pixel_in,

// outputs
output reg [15:0] pixel_out,
output reg [3:0]  pc_ena_out,
output reg [7:0]  data_out_b,
output reg vs_out,
output reg hs_out,
output reg hde_out,
output reg vde_out

);

parameter PIPE_DELAY = 6; // This parameter selects the number of pixel clocks to delay the VDE and sync outputs.  Only use 2 through 9.

wire pc_enable = (pc_ena[3:0]==0);
wire txt_pixel_out;
wire gfx_pixel_out;

reg [9:0] hde_pipe, vde_pipe, hs_pipe, vs_pipe; // passthru delay pipes

// *********************************************************************
// *
// * create a text/sprite palette RAM instance (4444)
// *
// *********************************************************************
dual_port_palette_ram_INTEL text_palette_RAM(

// inputs
.clock_a(clk),
.clock_b(clk),
.pc_ena_in(pc_ena_in),
.pixel_in(txt_pixel_in),
.address_b(4'h0000),
.data_a(2'h00),
.data_b(2'h00),
.enable_a(pc_enable),
.enable_b(pc_enable),
.wren_a(1'b0),
.wren_b(1'b0),

// outputs
.pc_ena_out(pc_ena_out),
.pixel_out(txt_pixel_out),
.data_out_b(data_out_b),

// registered outputs
.addr_out_a()

);

defparam text_palette_RAM.init_file = "palette_4444.mif";

// *********************************************************************
// *
// * create a graphics palette RAM instance (565)
// *
// *********************************************************************
dual_port_palette_ram_INTEL graphics_palette_RAM(

// inputs
.clock_a(clk),
.clock_b(clk),
.pc_ena_in(pc_ena_in),
.pixel_in(gfx_pixel_in),
.address_b(4'h0000),
.data_a(2'h00),
.data_b(2'h00),
.enable_a(pc_enable),
.enable_b(pc_enable),
.wren_a(1'b0),
.wren_b(1'b0),

// outputs
.pc_ena_out(pc_ena_out),
.pixel_out(gfx_pixel_out),
.data_out_b(data_out_b),

// registered outputs
.addr_out_a()

);

defparam graphics_palette_RAM.init_file = "palette_565.mif";

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

always @(posedge clk) begin

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

hde_pipe[0] <= hde_in;
hde_pipe[9:1] <= hde_pipe[8:0];
hde_out <= hde_pipe[PIPE_DELAY-1];

vde_pipe[0] <= vde_in;
vde_pipe[9:1] <= vde_pipe[8:0];
vde_out <= vde_pipe[PIPE_DELAY-1];

hs_pipe[0] <= hs_in;
hs_pipe[9:1] <= hs_pipe[8:0];
hs_out <= hs_pipe[PIPE_DELAY-1];

vs_pipe[0] <= vs_in;
vs_pipe[9:1] <= vs_pipe[8:0];
vs_out <= vs_pipe[PIPE_DELAY-1];

pixel_out <= txt_pixel_out & gfx_pixel_out; // *** placeholder *** for palette mixing function

end

end

endmodule

dual_port_palette_ram_INTEL.v
Code: [Select]
module dual_port_palette_ram_text (

// inputs
input clock_a,
input clock_b,
input [3:0]  pc_ena_in,
input [7:0]  pixel_addr_in, // address_a
input [8:0]  address_b,
input [15:0] data_a,
input [7:0]  data_b,
input enable_a,
input enable_b,
input wren_a,
input wren_b,

// outputs
output [3:0]  pc_ena_out,

// registered outputs
output reg [15:0] pixel_out, // data_out_a
output reg [7:0]  data_out_b,
output reg [19:0] addr_out_a

);

// ****************************************************************************************************************************
// Dual-port palette RAM
// ****************************************************************************************************************************
altsyncram altsyncram_component (
.clocken0 (1'b1),
.clocken1 (enable_b),
.wren_a (wren_a),
.clock0 (clock_a),
.wren_b (wren_b),
.clock1 (clock_b),
.address_a (pixel_addr_in),
.address_b (address_b),
.data_a (data_a),
.data_b (data_b),
.q_a (pixel_out),
.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),
.clocken2 (1'b1),
.clocken3 (1'b1),
.eccstatus (1'b0),
.rden_a (1'b1),
.rden_b (1'b1)
);

defparam
altsyncram_component.address_reg_b = "CLOCK1",
altsyncram_component.clock_enable_input_a = "NORMAL",
altsyncram_component.clock_enable_input_b = "NORMAL",
altsyncram_component.clock_enable_output_a = "NORMAL",
altsyncram_component.clock_enable_output_b = "NORMAL",
altsyncram_component.indata_reg_b = "CLOCK1",
altsyncram_component.init_file_layout = "PORT_A",
altsyncram_component.intended_device_family = "Cyclone IV",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 256,
altsyncram_component.numwords_b = 512,
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 = 8,
altsyncram_component.widthad_b = 9,
altsyncram_component.width_a = 16,
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
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7747
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #609 on: December 08, 2019, 04:51:59 pm »
Ah okay, that makes a little more sense.  So it should have two 'image' inputs from the vid_osd_generator to look up the relevant palette entry for each one and mix them together into one 16-bit output?
Yes.

Quote
We need 2 palettes working in parallel.  The palettes also have a read delay which we need to line up to the pixel clock.  The HS,VS,HDE,VDE also needs to match that delay.

You also need to take into consideration the fact that a new pixel will happen every 5 (or other number) clock cycles set by the pc_ena[3:0]==0.

Okay, I've got HS, VS, HDE and VDE passed through the palette_mixer module, with a user-definable delay.  It defaults to 6, but I'm sure you mentioned a delay of 1 or 2 earlier..? Might have imagined that, though.  :-//
I will let you figure out what the delay should be.
Quote
The output of the 2 palettes will just go through 2xRGB multiplies each, then the sum added together at then end.

Can you give me an example please?  Are we splitting the 16-bit data from the dual_port_palette_ram into its RGB components and performing these multiplies, as the two palettes are in different RGB formats?

First, make from the palette outputs wires
text_r[7:0],text_g[7:0],text_b[7:0], graphics_r[7:0], graphics_g[7:0], graphics_b[7:0], alpha_blend[3:0].

Wire them to the palette out bits like here: https://www.eevblog.com/forum/fpga/fpga-vga-controller-for-8-bit-computer/msg2813672/#msg2813672

Output regs:
pixel_out_r[7:0], pixel_out_g[7:0], pixel_out_b[7:0]

This is a 1 clock step version (which will probably be too slow for a CycloneIV):
pixel_out_r <=  ( (text_r[7:0] * ~alpha_blend[3:0]) + (graphics_r[7:0] * alpha_blend[3:0]) ) >> 4 ;

Now, you should be able to guess the _g, and _b.
Since you asked, I just wanted the text palette out for now, but let's do the full formula.

     If your F-Max drops below 140Mhz, you will need to separate the 2 multiplies into 2 separate regs shifted down to 8 bits each, then the final pixel_out_r/g/b will need to be the sum of those 2 inbetween 8 bit regs making everything take 2 clocks to compute a pixel instead of 1 clock.  With the 2 stages, you will have 0 trouble ever achieving the FMAX.

YES, the output of the palette mixer is 24 bit color.  Just feed the top 12 bits into the vid_out_stencil
YES, since we are mixing colors, if you really want to see 24bits from your 12 bit dac, you can mix into the final 24bit RGB with my white nose generator before sending that into the 4 bit dac, but, do that in a separate module before feeding the vid_out_stencil.  Lets not complicate things right now.
« Last Edit: December 08, 2019, 05:20:09 pm by BrianHG »
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #610 on: December 08, 2019, 06:22:27 pm »
I've not had much time to work on this today, been busy here and going to have to take another break now.  Here's the latest project zip - everything compiles, I've got a design for the palette_mixer, I just need to wire it up.  Would appreciate a check of the palette_mixer code, though, to make sure there's no glaring mistakes, if you have a spare 5 mins!
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7747
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #611 on: December 08, 2019, 08:19:57 pm »
I've not had much time to work on this today, been busy here and going to have to take another break now.  Here's the latest project zip - everything compiles, I've got a design for the palette_mixer, I just need to wire it up.  Would appreciate a check of the palette_mixer code, though, to make sure there's no glaring mistakes, if you have a spare 5 mins!
|O  pixel_out_r <=  ( (text_r[7:0] * ~alpha_blend[3:0]) + (graphics_r[7:0] * alpha_blend[3:0]) ) >> 4 ;   |O

You were supposed to figure that one out, I slipped.....
Ok, I'll I'm doing a quick check now...

 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7747
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #612 on: December 08, 2019, 08:41:49 pm »
I've not had much time to work on this today, been busy here and going to have to take another break now.  Here's the latest project zip - everything compiles, I've got a design for the palette_mixer, I just need to wire it up.  Would appreciate a check of the palette_mixer code, though, to make sure there's no glaring mistakes, if you have a spare 5 mins!
Ok, on the intel ram: (see stars)
Code: [Select]
module dual_port_palette_ram_text (

// inputs
input clock_a,
input clock_b,
input [3:0]  pc_ena_in,  ***************************** GET RID
input [7:0]  pixel_addr_in, // address_a
input [8:0]  address_b,
input [15:0] data_a,
input [7:0]  data_b,
input enable_a,
input enable_b,
input wren_a,
input wren_b,

// outputs
output [3:0]  pc_ena_out,  *********************************** GET RID

// registered outputs
output reg [15:0] pixel_out, // data_out_a  ************* WIRE not reg
output reg [7:0]  data_out_b,                                   ************* WIRE not reg
output reg [19:0] addr_out_a  *********************************** GET RID

);

// ****************************************************************************************************************************
// Dual-port palette RAM
// ****************************************************************************************************************************
altsyncram altsyncram_component (
.clocken0 (1'b1),  ******************************** (enable_a)
.clocken1 (enable_b),
.wren_a (wren_a),
.clock0 (clock_a),
.wren_b (wren_b),
.clock1 (clock_b),
.address_a (pixel_addr_in),
.address_b (address_b),
.data_a (data_a),
.data_b (data_b),
.q_a (pixel_out),
.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),
.clocken2 (1'b1),
.clocken3 (1'b1),
.eccstatus (1'b0),
.rden_a (1'b1),
.rden_b (1'b1)
);

defparam
altsyncram_component.address_reg_b = "CLOCK1",
altsyncram_component.clock_enable_input_a = "NORMAL",
altsyncram_component.clock_enable_input_b = "NORMAL",
altsyncram_component.clock_enable_output_a = "NORMAL",
altsyncram_component.clock_enable_output_b = "NORMAL",
altsyncram_component.indata_reg_b = "CLOCK1",
***************************************************************************
altsyncram_component.init_file = INIT_PALETTE,  ************** You still need to call this something and call it a parameter at the beginning of this .v file, even if the parameter name is 'init_file'...  To be clean use my name

**********************************************************************
altsyncram_component.init_file_layout = "PORT_A",
altsyncram_component.intended_device_family = "Cyclone IV",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 256,
altsyncram_component.numwords_b = 512,
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 = 8,
altsyncram_component.widthad_b = 9,
altsyncram_component.width_a = 16,
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

You can rename the address & data B to host_  so they match what you see in the vid_osd_generator as port B serves the same function.

Next:
Code: [Select]
module palette_mixer (

// inputs
input clk,
input vs_in,
input hs_in,
input hde_in,
input vde_in,
input [3:0] pc_ena_in,
input [7:0] txt_pixel_in,
input [7:0] gfx_pixel_in,

// outputs
output reg [3:0] pc_ena_out,
//
output reg [7:0] raster_out_r,   *****************  Lets change raster_ for pixel_
output reg [7:0] raster_out_g,
output reg [7:0] raster_out_b,
//
output reg [7:0] data_out_b,   *********************** make a proper complete host_ port ans place it at the bottom of all the graphics port IOs
output reg vs_out,
output reg hs_out,
output reg hde_out,
output reg vde_out

);

parameter PIPE_DELAY = 6; // This parameter selects the number of pixel clocks to delay the VDE and sync outputs.  Only use 2 through 9.

wire pc_enable = (pc_ena_in[3:0]==0);

wire [15:0] txt_pixel_out;
wire [3:0] alpha_blend;
wire [7:0] text_r;
wire [7:0] text_g;
wire [7:0] text_b;

wire [15:0] gfx_pixel_out;
wire [7:0] graphics_r;
wire [7:0] graphics_g;
wire [7:0] graphics_b;

reg [9:0] hde_pipe, vde_pipe, hs_pipe, vs_pipe; // passthru delay pipes

assign alpha_blend = txt_pixel_out[15:12];
assign text_r[7:4] = txt_pixel_out[11:8];
assign text_r[3:0] = txt_pixel_out[11:8];
assign text_g[7:4] = txt_pixel_out[7:4];
assign text_g[3:0] = txt_pixel_out[7:4];
assign text_b[7:4] = txt_pixel_out[3:0];
assign text_b[3:0] = txt_pixel_out[3:0];

assign graphics_r[7:3] = gfx_pixel_out[15:11];
assign graphics_r[2:0] = gfx_pixel_out[15:13];
assign graphics_g[7:2] = gfx_pixel_out[10:5];
assign graphics_g[1:0] = gfx_pixel_out[10:9];
assign graphics_b[7:3] = gfx_pixel_out[4:0];
assign graphics_b[2:0] = gfx_pixel_out[4:2];

// *********************************************************************
// *
// * create a text/sprite palette RAM instance (4444)
// *
// *********************************************************************
dual_port_palette_ram_INTEL text_palette_RAM(

// inputs
.clock_a(clk),
.clock_b(clk),
.pc_ena_in(pc_ena_in),
.pixel_in(txt_pixel_in),
.address_b(4'h0000),
.data_a(2'h00),
.data_b(2'h00),
.enable_a(pc_enable),
.enable_b(pc_enable),  ********************** Wire this to 1'b1 as the host_ port we don't use the clock enable.
.wren_a(1'b0),
.wren_b(1'b0),

// outputs
.pc_ena_out(pc_ena_out),
.pixel_out(txt_pixel_out),
.data_out_b(data_out_b),   *********************** host_ port...

// registered outputs
.addr_out_a()              ******************** get rid

);

defparam text_palette_RAM.init_file = "palette_4444.mif";

// *********************************************************************
// *
// * create a graphics palette RAM instance (565)
// *
// *********************************************************************
dual_port_palette_ram_INTEL graphics_palette_RAM(

// inputs   **************************** perform the same corrections as above...
.clock_a(clk),
.clock_b(clk),
.pc_ena_in(pc_ena_in),
.pixel_in(gfx_pixel_in),
.address_b(4'h0000),
.data_a(2'h00),
.data_b(2'h00),
.enable_a(pc_enable),
.enable_b(pc_enable),
.wren_a(1'b0),
.wren_b(1'b0),

// outputs
.pc_ena_out(pc_ena_out),
.pixel_out(gfx_pixel_out),
.data_out_b(data_out_b),

// registered outputs
.addr_out_a()

);

defparam graphics_palette_RAM.init_file = "palette_565.mif";

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

always @(posedge clk) begin

if (pc_ena_in[3:0] == 0) begin

hde_pipe[0] <= hde_in;
hde_pipe[9:1] <= hde_pipe[8:0];
hde_out <= hde_pipe[PIPE_DELAY-1];

vde_pipe[0] <= vde_in;
vde_pipe[9:1] <= vde_pipe[8:0];
vde_out <= vde_pipe[PIPE_DELAY-1];

hs_pipe[0] <= hs_in;
hs_pipe[9:1] <= hs_pipe[8:0];
hs_out <= hs_pipe[PIPE_DELAY-1];

vs_pipe[0] <= vs_in;
vs_pipe[9:1] <= vs_pipe[8:0];
vs_out <= vs_pipe[PIPE_DELAY-1];

// mix output rgb
raster_out_r <= ((text_r[7:0] * ~alpha_blend[3:0]) + (graphics_r[7:0] * alpha_blend[3:0])) >> 4;
raster_out_g <= ((text_g[7:0] * ~alpha_blend[3:0]) + (graphics_g[7:0] * alpha_blend[3:0])) >> 4;
raster_out_b <= ((text_b[7:0] * ~alpha_blend[3:0]) + (graphics_b[7:0] * alpha_blend[3:0])) >> 4;

end

end

endmodule


Change the 'raster_out_rgb' ahem, 'pixel_out_rgb' equation into a 2 stage pipe.  Like I wrote in the previous message, stage 1 will be the 2 multiplies/contrasts on their own >>4 to get 2 8 bit results.  Stage 2 will sum those 2 together to give you a final pixel value.
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #613 on: December 08, 2019, 10:47:10 pm »
Latest pixel_mixer.v:

Code: [Select]
module palette_mixer (

// inputs
input clk,
input vs_in,
input hs_in,
input hde_in,
input vde_in,
input [3:0] pc_ena_in,
input [7:0] txt_pixel_in,
input [7:0] gfx_pixel_in,

// outputs
output reg [3:0] pc_ena_out,
//
output reg [7:0] pixel_out_r,
output reg [7:0] pixel_out_g,
output reg [7:0] pixel_out_b,
//
output reg vs_out,
output reg hs_out,
output reg hde_out,
output reg vde_out,

// host port
input [9:0] host_addr_in,
input [7:0] host_data_in,
output reg [7:0] host_data_out

);

parameter PIPE_DELAY = 6; // This parameter selects the number of pixel clocks to delay the VDE and sync outputs.  Only use 2 through 9.

wire pc_enable = (pc_ena_in[3:0]==0);

// **** TO DO:
wire host_wren_txt; // need to complete this so that they go high when
wire host_wren_gfx; // the palette is correctly addressed by the host
// ****

wire [15:0] txt_pixel_out;
wire [3:0] alpha_blend;
wire [7:0] text_r;
wire [7:0] text_g;
wire [7:0] text_b;

wire [15:0] gfx_pixel_out;
wire [7:0] graphics_r;
wire [7:0] graphics_g;
wire [7:0] graphics_b;

reg [9:0] hde_pipe, vde_pipe, hs_pipe, vs_pipe; // passthru delay pipes
reg [7:0] pixel_r1, pixel_r2, pixel_g1, pixel_g2, pixel_b1, pixel_b2;

assign alpha_blend = txt_pixel_out[15:12];
assign text_r[7:4] = txt_pixel_out[11:8];
assign text_r[3:0] = txt_pixel_out[11:8];
assign text_g[7:4] = txt_pixel_out[7:4];
assign text_g[3:0] = txt_pixel_out[7:4];
assign text_b[7:4] = txt_pixel_out[3:0];
assign text_b[3:0] = txt_pixel_out[3:0];

assign graphics_r[7:3] = gfx_pixel_out[15:11];
assign graphics_r[2:0] = gfx_pixel_out[15:13];
assign graphics_g[7:2] = gfx_pixel_out[10:5];
assign graphics_g[1:0] = gfx_pixel_out[10:9];
assign graphics_b[7:3] = gfx_pixel_out[4:0];
assign graphics_b[2:0] = gfx_pixel_out[4:2];

// *********************************************************************
// *
// * create a text/sprite palette RAM instance (4444)
// *
// *********************************************************************
dual_port_palette_ram_INTEL text_palette_RAM(

// inputs
.clock_a(clk),
.clock_b(clk),
.pixel_in(txt_pixel_in),
.host_address(host_addr_in),
.data_a(2'h00),
.host_data_in(host_data_in),
.enable_a(pc_enable),
.host_enable(1'b1),
.wren_a(1'b0),
.host_wren(host_wren_txt),

// outputs
.pixel_out(txt_pixel_out),
.host_data_out(host_data_out)

);

defparam text_palette_RAM.INIT_PALETTE = "palette_4444.mif";

// *********************************************************************
// *
// * create a graphics palette RAM instance (565)
// *
// *********************************************************************
dual_port_palette_ram_INTEL graphics_palette_RAM(

// inputs
.clock_a(clk),
.clock_b(clk),
.pixel_in(gfx_pixel_in),
.host_address(host_addr_in),
.data_a(2'h00),
.host_data_in(host_data_in),
.enable_a(pc_enable),
.host_enable(1'b1),
.wren_a(1'b0),
.host_wren(host_wren_gfx),

// outputs
.pixel_out(gfx_pixel_out),
.host_data_out(data_out_b)

);

defparam graphics_palette_RAM.INIT_PALETTE = "palette_565.mif";

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

always @(posedge clk) begin

if (pc_ena_in[3:0] == 0) begin

hde_pipe[0] <= hde_in;
hde_pipe[9:1] <= hde_pipe[8:0];
hde_out <= hde_pipe[PIPE_DELAY-1];

vde_pipe[0] <= vde_in;
vde_pipe[9:1] <= vde_pipe[8:0];
vde_out <= vde_pipe[PIPE_DELAY-1];

hs_pipe[0] <= hs_in;
hs_pipe[9:1] <= hs_pipe[8:0];
hs_out <= hs_pipe[PIPE_DELAY-1];

vs_pipe[0] <= vs_in;
vs_pipe[9:1] <= vs_pipe[8:0];
vs_out <= vs_pipe[PIPE_DELAY-1];

// mix output rgb
pixel_r1 <= (text_r[7:0] * ~alpha_blend[3:0]) >> 4;
pixel_r2 <= (graphics_r[7:0] * alpha_blend[3:0]) >> 4;
pixel_out_r <= pixel_r1 + pixel_r2;

pixel_g1 <= (text_g[7:0] * ~alpha_blend[3:0]) >> 4;
pixel_g2 <= (graphics_g[7:0] * alpha_blend[3:0]) >> 4;
pixel_out_g <= pixel_g1 + pixel_g2;

pixel_b1 <= (text_b[7:0] * ~alpha_blend[3:0]) >> 4;
pixel_b2 <= (graphics_b[7:0] * alpha_blend[3:0]) >> 4;
pixel_out_b <= pixel_b1 + pixel_b2;

end

end

endmodule


Latest dual_port_palette_ram_INTEL.v:

Code: [Select]
module dual_port_palette_ram_text (

// inputs
input clock_a,
input clock_b,
input [7:0]  pixel_addr_in, // address_a
input [8:0]  host_address,
input [15:0] data_a,
input [7:0]  host_data_in,
input enable_a,
input host_enable,
input wren_a,
input host_wren,

// outputs
output [15:0] pixel_out, // data_out_a
output [7:0]  host_data_out

);

parameter INIT_PALETTE = "palette_565.mif";

// ****************************************************************************************************************************
// Dual-port palette RAM
// ****************************************************************************************************************************
altsyncram altsyncram_component (
.clocken0 (enable_a),
.clocken1 (host_enable),
.wren_a (wren_a),
.clock0 (clock_a),
.wren_b (host_wren),
.clock1 (clock_b),
.address_a (pixel_addr_in),
.address_b (host_address),
.data_a (data_a),
.data_b (host_data_in),
.q_a (pixel_out),
.q_b (host_data_out),
.aclr0 (1'b0),
.aclr1 (1'b0),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.eccstatus (1'b0),
.rden_a (1'b1),
.rden_b (1'b1)
);

defparam
altsyncram_component.address_reg_b = "CLOCK1",
altsyncram_component.clock_enable_input_a = "NORMAL",
altsyncram_component.clock_enable_input_b = "NORMAL",
altsyncram_component.clock_enable_output_a = "NORMAL",
altsyncram_component.clock_enable_output_b = "NORMAL",
altsyncram_component.indata_reg_b = "CLOCK1",
altsyncram_component.init_file = INIT_PALETTE,
altsyncram_component.init_file_layout = "PORT_A",
altsyncram_component.intended_device_family = "Cyclone IV",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 256,
altsyncram_component.numwords_b = 512,
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 = 8,
altsyncram_component.widthad_b = 9,
altsyncram_component.width_a = 16,
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


Need to sort the wr_en's for the graphics and text palette memory though.
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7747
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #614 on: December 08, 2019, 10:56:13 pm »
Latest pixel_mixer.v:

Code: [Select]
module palette_mixer (

// inputs
input clk,
input vs_in,
input hs_in,
input hde_in,
input vde_in,
input [3:0] pc_ena_in,
input [7:0] txt_pixel_in,
input [7:0] gfx_pixel_in,

// outputs
output reg [3:0] pc_ena_out,
//
output reg [7:0] pixel_out_r,
output reg [7:0] pixel_out_g,
output reg [7:0] pixel_out_b,
//
output reg vs_out,
output reg hs_out,
output reg hde_out,
output reg vde_out,

// host port
input [9:0] host_addr_in,
input [7:0] host_data_in,
output reg [7:0] host_data_out

);

parameter PIPE_DELAY = 6; // This parameter selects the number of pixel clocks to delay the VDE and sync outputs.  Only use 2 through 9.

wire pc_enable = (pc_ena_in[3:0]==0);

// **** TO DO:
wire host_wren_txt; // need to complete this so that they go high when
wire host_wren_gfx; // the palette is correctly addressed by the host
// ****

wire [15:0] txt_pixel_out;
wire [3:0] alpha_blend;
wire [7:0] text_r;
wire [7:0] text_g;
wire [7:0] text_b;

wire [15:0] gfx_pixel_out;
wire [7:0] graphics_r;
wire [7:0] graphics_g;
wire [7:0] graphics_b;

reg [9:0] hde_pipe, vde_pipe, hs_pipe, vs_pipe; // passthru delay pipes
reg [7:0] pixel_r1, pixel_r2, pixel_g1, pixel_g2, pixel_b1, pixel_b2;

assign alpha_blend = txt_pixel_out[15:12];
assign text_r[7:4] = txt_pixel_out[11:8];
assign text_r[3:0] = txt_pixel_out[11:8];
assign text_g[7:4] = txt_pixel_out[7:4];
assign text_g[3:0] = txt_pixel_out[7:4];
assign text_b[7:4] = txt_pixel_out[3:0];
assign text_b[3:0] = txt_pixel_out[3:0];

assign graphics_r[7:3] = gfx_pixel_out[15:11];
assign graphics_r[2:0] = gfx_pixel_out[15:13];
assign graphics_g[7:2] = gfx_pixel_out[10:5];
assign graphics_g[1:0] = gfx_pixel_out[10:9];
assign graphics_b[7:3] = gfx_pixel_out[4:0];
assign graphics_b[2:0] = gfx_pixel_out[4:2];

// *********************************************************************
// *
// * create a text/sprite palette RAM instance (4444)
// *
// *********************************************************************
dual_port_palette_ram_INTEL text_palette_RAM(

// inputs
.clock_a(clk),
.clock_b(clk),
.pixel_in(txt_pixel_in),
.host_address(host_addr_in),
.data_a(2'h00),
.host_data_in(host_data_in),
.enable_a(pc_enable),
.host_enable(1'b1),
.wren_a(1'b0),
.host_wren(host_wren_txt),

// outputs
.pixel_out(txt_pixel_out),
.host_data_out(host_data_out)

);

defparam text_palette_RAM.INIT_PALETTE = "palette_4444.mif";

// *********************************************************************
// *
// * create a graphics palette RAM instance (565)
// *
// *********************************************************************
dual_port_palette_ram_INTEL graphics_palette_RAM(

// inputs
.clock_a(clk),
.clock_b(clk),
.pixel_in(gfx_pixel_in),
.host_address(host_addr_in),
.data_a(2'h00),
.host_data_in(host_data_in),
.enable_a(pc_enable),
.host_enable(1'b1),
.wren_a(1'b0),
.host_wren(host_wren_gfx),

// outputs
.pixel_out(gfx_pixel_out),
.host_data_out(data_out_b)

);

defparam graphics_palette_RAM.INIT_PALETTE = "palette_565.mif";

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

always @(posedge clk) begin

if (pc_ena_in[3:0] == 0) begin

hde_pipe[0] <= hde_in;
hde_pipe[9:1] <= hde_pipe[8:0];
hde_out <= hde_pipe[PIPE_DELAY-1];

vde_pipe[0] <= vde_in;
vde_pipe[9:1] <= vde_pipe[8:0];
vde_out <= vde_pipe[PIPE_DELAY-1];

hs_pipe[0] <= hs_in;
hs_pipe[9:1] <= hs_pipe[8:0];
hs_out <= hs_pipe[PIPE_DELAY-1];

vs_pipe[0] <= vs_in;
vs_pipe[9:1] <= vs_pipe[8:0];
vs_out <= vs_pipe[PIPE_DELAY-1];

// mix output rgb
pixel_r1 <= (text_r[7:0] * ~alpha_blend[3:0]) >> 4;
pixel_r2 <= (graphics_r[7:0] * alpha_blend[3:0]) >> 4;
pixel_out_r <= pixel_r1 + pixel_r2;

pixel_g1 <= (text_g[7:0] * ~alpha_blend[3:0]) >> 4;
pixel_g2 <= (graphics_g[7:0] * alpha_blend[3:0]) >> 4;
pixel_out_g <= pixel_g1 + pixel_g2;

pixel_b1 <= (text_b[7:0] * ~alpha_blend[3:0]) >> 4;
pixel_b2 <= (graphics_b[7:0] * alpha_blend[3:0]) >> 4;
pixel_out_b <= pixel_b1 + pixel_b2;

end

end

endmodule


Latest dual_port_palette_ram_INTEL.v:

Code: [Select]
module dual_port_palette_ram_text (

// inputs
input clock_a,
input clock_b,
input [7:0]  pixel_addr_in, // address_a
input [8:0]  host_address,
input [15:0] data_a,
input [7:0]  host_data_in,
input enable_a,
input host_enable,
input wren_a,
input host_wren,

// outputs
output [15:0] pixel_out, // data_out_a
output [7:0]  host_data_out

);

parameter INIT_PALETTE = "palette_565.mif";

// ****************************************************************************************************************************
// Dual-port palette RAM
// ****************************************************************************************************************************
altsyncram altsyncram_component (
.clocken0 (enable_a),
.clocken1 (host_enable),
.wren_a (wren_a),
.clock0 (clock_a),
.wren_b (host_wren),
.clock1 (clock_b),
.address_a (pixel_addr_in),
.address_b (host_address),
.data_a (data_a),
.data_b (host_data_in),
.q_a (pixel_out),
.q_b (host_data_out),
.aclr0 (1'b0),
.aclr1 (1'b0),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.eccstatus (1'b0),
.rden_a (1'b1),
.rden_b (1'b1)
);

defparam
altsyncram_component.address_reg_b = "CLOCK1",
altsyncram_component.clock_enable_input_a = "NORMAL",
altsyncram_component.clock_enable_input_b = "NORMAL",
altsyncram_component.clock_enable_output_a = "NORMAL",
altsyncram_component.clock_enable_output_b = "NORMAL",
altsyncram_component.indata_reg_b = "CLOCK1",
altsyncram_component.init_file = INIT_PALETTE,
altsyncram_component.init_file_layout = "PORT_A",
altsyncram_component.intended_device_family = "Cyclone IV",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 256,
altsyncram_component.numwords_b = 512,
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 = 8,
altsyncram_component.widthad_b = 9,
altsyncram_component.width_a = 16,
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


Need to sort the wr_en's for the graphics and text palette memory though.
For the host ADDR in, you will need all 20 addresses & you will want 2 parameters to assign where each 512 bytes of ram will be written to and read from.
You should use a single host_write_ena, it's the base address parameters which will decide if the write should be executed or not.
Wire the thing up and test.
Compute & set the proper default pipeline size for this module.

 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7747
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #615 on: December 08, 2019, 11:30:17 pm »
Or, you can just put 9 addresses in and wire the msb & ~msb on the address into and && with the host_write_enable input.
Stacking the 2 palettes on top of each other and externally handling the paging of these memory blocks.
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #616 on: December 09, 2019, 10:07:15 am »
Or, you can just put 9 addresses in and wire the msb & ~msb on the address into and && with the host_write_enable input.
Stacking the 2 palettes on top of each other and externally handling the paging of these memory blocks.

Well I'm not sure about the host_write_enable - we can't have both palette memories being written to at the same time, so I've done my own thing to interpret which palette should be written to and which palette's data should be output to the host...  it's probably wrong and I've likely completely misunderstood why you want a common host_write_enable, but here it is in a nutshell:

Code: [Select]
parameter [19:0] TXT_PALETTE_ADDR = 19'h4000; // Position the text palette after the top of the GPU RAM
parameter [19:0] GFX_PALETTE_ADDR = 19'h4200; // Position the graphics palette after the text palette

wire host_gfx_data_out; // host data outputs from the
wire host_txt_data_out; // palette RAMs

wire host_wrena_txt; // write enables to each
wire host_wrena_gfx; // palette RAM
wire txt_addressed;
wire gfx_addressed;

// write enables to either palette RAM only go high if addressed correctly during a write
assign txt_addressed = (host_addr_in[19:10] == TXT_PALETTE_ADDR[19:10]);
assign gfx_addressed = (host_addr_in[19:10] == GFX_PALETTE_ADDR[19:10]);
assign host_wrena_txt = host_wrena & txt_addressed;
assign host_wrena_gfx = host_wrena & gfx_addressed;

always @(posedge clk) begin

// route the correct palette data out to the host
if (txt_addressed)
host_data_out <= host_txt_data_out;
else if (gfx_addressed)
host_data_out <= host_gfx_data_out;

end

Above is pseudo-code effectively as I've just cut 'n' pasted the appropriate bits of code from palette_mixer.v to illustrate the point.  The full code is in the attached project.  host_wrena is tied to ground in the design anyway, so it shouldn't be affecting the issue below...

I've wired up the palette_mixer and tested in the dev board - just getting a black screen apart from the trigger bars; no text or background, despite messing with the bitplane_to_raster settings.  :-\  I've either made a silly wiring mistake that I can't spot or there's a problem with the code.  I haven't worked out the PIPE_DELAY yet as I figured I should get something on the screen, even if the delay is wildly out, but there's nothing.  :-/O
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7747
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #617 on: December 09, 2019, 12:09:19 pm »
Ok, I cannot fault you on this one, change the following lines:

Code: [Select]
// mix output rgb
pixel_r1 <= (text_r[7:0] * (15-alpha_blend[3:0]) ) >> 4;
pixel_r2 <= (graphics_r[7:0] * alpha_blend[3:0]) >> 4;
pixel_out_r <= pixel_r1 + pixel_r2;

pixel_g1 <= (text_g[7:0] * (15-alpha_blend[3:0]) ) >> 4;
pixel_g2 <= (graphics_g[7:0] * alpha_blend[3:0]) >> 4;
pixel_out_g <= pixel_g1 + pixel_g2;

pixel_b1 <= (text_b[7:0] * (15-alpha_blend[3:0]) ) >> 4;
pixel_b2 <= (graphics_b[7:0] * alpha_blend[3:0]) >> 4;
pixel_out_b <= pixel_b1 + pixel_b2;

Is there someone else out there who can tell me why this works?
pixel_r1      <= (text_r[7:0] * (15-alpha_blend[3:0]) ) >> 4;

But, this fails and always returns '0'?
pixel_r1      <= (text_r[7:0] * (~alpha_blend[3:0]) ) >> 4;
Even this fails:
pixel_r1      <= (text_r[7:0] * (4'hF ^ alpha_blend[3:0]) ) >> 4;
And this fails too:
pixel_r1      <= (text_r[7:0] * (4'hF - alpha_blend[3:0]) ) >> 4;

EVEN THIS FAILS!!!  :scared:
pixel_r1      <= (text_r[7:0] * (4'd15 - alpha_blend[3:0]) ) >> 4;  :scared:

BUT WHY DO THESE WORK???
pixel_r1      <= (text_r[7:0] * (32'd15-alpha_blend[3:0]) ) >> 4;
pixel_r1      <= (text_r[7:0] * (16'd15-alpha_blend[3:0]) ) >> 4;


@nockieboy, your text is too far to the left, however, since this is the first time you will see the results, you will need to adjust a few things.

As for selecting the 'Host_data_out'.  Some other adjustments will mat be needed as well.
I'm thinking of using the memories' :

            .rden_a (rden_a),
            .rden_b (rden_b),

Now, feed each rd_en_b(), (read enable) the txt_addressed and gfx_addressed wires.  .rden_a (rden_a), should obviously be (1'b1).

When rd_en_b(), is low, the read contents will be 0.  So, you just 'OR' the output data buses.
Same will go for the GPU main memory.  Assign a base address based on the gpu_RAM.ADDR_SIZE (which you have yet to expose to the top block diagram) and create for that memory an appropriate '.rden_b (rden_b),'.  On the block diagram, just 'OR' that host_data buss with the palette's host_data bus and all the memorie's data output will have a single 7 bit result, no additional register clock delays...
« Last Edit: December 09, 2019, 12:49:18 pm by BrianHG »
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #618 on: December 09, 2019, 12:49:47 pm »
@nockieboy, your text is too far to the left, however, since this is the first time you will see the results, you will need to adjust a few things.

I tweaked palette_mixer's PIPE_DELAY down to 3 and it appears to be aligned okay now.  :-/O

886896-0

I'm not sure that's the right way to fix it, though - the HV triggers seem to be out at 19, 16, 658, 495.  The horizontal triggers seem to be out by 3 pixels?

EDIT: Ignore me, being a dunce.  It's because the HV triggers aren't delayed through the palette_mixer as well, isn't it?
« Last Edit: December 09, 2019, 01:00:25 pm by nockieboy »
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #619 on: December 09, 2019, 12:51:17 pm »
As for selecting the 'Host_data_out'.  Some other adjustments will mat be needed as well.
I'm thinking of using the memories' :

            .rden_a (rden_a),
            .rden_b (rden_b),

Now, feed each rd_en_b(), (read enable) the txt_addressed and gfx_addressed wires.  .rden_a (rden_a), should obviously be (1'b1).

When rd_en_b(), is low, the read contents will be 0.  So, you just 'OR' the output data buses.

Is this okay?

palette_mixer.v:
Code: [Select]
module palette_mixer (

// inputs
input clk,
input [3:0] pc_ena_in,
input [7:0] txt_pixel_in,
input [7:0] gfx_pixel_in,
input hde_in,
input vde_in,
input hs_in,
input vs_in,

// outputs
output reg [3:0] pc_ena_out,
//
output reg [7:0] pixel_out_r,
output reg [7:0] pixel_out_g,
output reg [7:0] pixel_out_b,
//
output reg hde_out,
output reg vde_out,
output reg hs_out,
output reg vs_out,

// host port
input host_wrena,
input [19:0] host_addr_in,
input [7:0] host_data_in,
output reg [7:0] host_data_out

);

parameter PIPE_DELAY = 6; // This parameter selects the number of pixel clocks to delay the VDE and sync outputs.  Only use 2 through 9.

parameter [19:0] TXT_PALETTE_ADDR = 19'h4000; // Position the text palette after the top of the GPU RAM
parameter [19:0] GFX_PALETTE_ADDR = 19'h4200; // Position the graphics palette after the text palette

wire pc_enable = (pc_ena_in[3:0]==0);

wire host_gfx_data_out; // host data outputs from the
wire host_txt_data_out; // palette RAMs

wire host_wrena_txt; // write enables to each
wire host_wrena_gfx; // palette RAM
wire txt_addressed;
wire gfx_addressed;

wire [15:0] txt_pixel_out;
wire [3:0]  alpha_blend;
wire [7:0]  text_r;
wire [7:0]  text_g;
wire [7:0]  text_b;

wire [15:0] gfx_pixel_out;
wire [7:0]  graphics_r;
wire [7:0]  graphics_g;
wire [7:0]  graphics_b;

reg [9:0] hde_pipe, vde_pipe, hs_pipe, vs_pipe; // passthru delay pipes
reg [7:0] pixel_r1, pixel_r2, pixel_g1, pixel_g2, pixel_b1, pixel_b2;

assign alpha_blend = txt_pixel_out[15:12];
assign text_r[7:4] = txt_pixel_out[11:8];
assign text_r[3:0] = txt_pixel_out[11:8];
assign text_g[7:4] = txt_pixel_out[7:4];
assign text_g[3:0] = txt_pixel_out[7:4];
assign text_b[7:4] = txt_pixel_out[3:0];
assign text_b[3:0] = txt_pixel_out[3:0];

assign graphics_r[7:3] = gfx_pixel_out[15:11];
assign graphics_r[2:0] = gfx_pixel_out[15:13];
assign graphics_g[7:2] = gfx_pixel_out[10:5];
assign graphics_g[1:0] = gfx_pixel_out[10:9];
assign graphics_b[7:3] = gfx_pixel_out[4:0];
assign graphics_b[2:0] = gfx_pixel_out[4:2];

// write enables to either palette RAM only go high if addressed correctly during a write
assign txt_addressed = (host_addr_in[19:10] == TXT_PALETTE_ADDR[19:10]);
assign gfx_addressed = (host_addr_in[19:10] == GFX_PALETTE_ADDR[19:10]);
assign host_wrena_txt = host_wrena & txt_addressed;
assign host_wrena_gfx = host_wrena & gfx_addressed;

// *********************************************************************
// *
// * create a text/sprite palette RAM instance (4444)
// *
// *********************************************************************
dual_port_palette_ram_INTEL text_palette_RAM(

// inputs
.clock_a(clk),
.clock_b(clk),
.pixel_addr_in(txt_pixel_in),
.host_address(host_addr_in[8:0]),
.data_a(2'h00),
.host_data_in(host_data_in),
.enable_a(pc_enable),
.host_enable(1'b1),
.rden_a(1'b1),
.rden_b(txt_addressed),
.wren_a(1'b0),
.host_wren(host_wrena_txt),

// outputs
.pixel_out(txt_pixel_out),
.host_data_out(host_txt_data_out)

);

defparam text_palette_RAM.INIT_PALETTE = "palette_4444.mif";

// *********************************************************************
// *
// * create a graphics palette RAM instance (565)
// *
// *********************************************************************
dual_port_palette_ram_INTEL graphics_palette_RAM(

// inputs
.clock_a(clk),
.clock_b(clk),
.pixel_addr_in(gfx_pixel_in),
.host_address(host_addr_in[8:0]),
.data_a(2'h00),
.host_data_in(host_data_in),
.enable_a(pc_enable),
.host_enable(1'b1),
.rden_a(1'b1),
.rden_b(gfx_addressed),
.wren_a(1'b0),
.host_wren(host_wrena_gfx),

// outputs
.pixel_out(gfx_pixel_out),
.host_data_out(host_gfx_data_out)

);

defparam graphics_palette_RAM.INIT_PALETTE = "palette_565.mif";

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

always @(posedge clk) begin

if (pc_ena_in[3:0] == 0) begin

hde_pipe[0] <= hde_in;
hde_pipe[9:1] <= hde_pipe[8:0];
hde_out <= hde_pipe[PIPE_DELAY-1];

vde_pipe[0] <= vde_in;
vde_pipe[9:1] <= vde_pipe[8:0];
vde_out <= vde_pipe[PIPE_DELAY-1];

hs_pipe[0] <= hs_in;
hs_pipe[9:1] <= hs_pipe[8:0];
hs_out <= hs_pipe[PIPE_DELAY-1];

vs_pipe[0] <= vs_in;
vs_pipe[9:1] <= vs_pipe[8:0];
vs_out <= vs_pipe[PIPE_DELAY-1];

// mix output rgb
pixel_r1 <= (text_r[7:0] * (15-alpha_blend[3:0])) >> 4;
pixel_r2 <= (graphics_r[7:0] * alpha_blend[3:0]) >> 4;
pixel_out_r <= pixel_r1 + pixel_r2;

pixel_g1 <= (text_g[7:0] * (15-alpha_blend[3:0])) >> 4;
pixel_g2 <= (graphics_g[7:0] * alpha_blend[3:0]) >> 4;
pixel_out_g <= pixel_g1 + pixel_g2;

pixel_b1 <= (text_b[7:0] * (15-alpha_blend[3:0])) >> 4;
pixel_b2 <= (graphics_b[7:0] * alpha_blend[3:0]) >> 4;
pixel_out_b <= pixel_b1 + pixel_b2;

end

// route the palette data out to the host
host_data_out <= host_txt_data_out | host_gfx_data_out;

end

endmodule


Same will go for the GPU main memory.  Assign a base address based on the gpu_RAM.ADDR_SIZE (which you have yet to expose to the top block diagram) and create for that memory an appropriate '.rden_b (rden_b),'.  On the block diagram, just 'OR' that host_data buss with the palette's host_data bus and all the memorie's data output will have a single 7 bit result, no additional register clock delays...

Okay, will get on that later this evening.  :-+
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7747
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #620 on: December 09, 2019, 01:02:30 pm »
As for selecting the 'Host_data_out'.  Some other adjustments will mat be needed as well.
I'm thinking of using the memories' :

            .rden_a (rden_a),
            .rden_b (rden_b),

Now, feed each rd_en_b(), (read enable) the txt_addressed and gfx_addressed wires.  .rden_a (rden_a), should obviously be (1'b1).

When rd_en_b(), is low, the read contents will be 0.  So, you just 'OR' the output data buses.

Is this okay?

palette_mixer.v:
Code: [Select]

// route the palette data out to the host
host_data_out <= host_txt_data_out | host_gfx_data_out;

end

endmodule
Switch the 'host_data_out'  into a wire and assign it to '= host_txt_data_out | host_gfx_data_out'.  Now there will be 0 clock cycles for clocking a register.
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #621 on: December 09, 2019, 01:16:27 pm »
Switch the 'host_data_out'  into a wire and assign it to '= host_txt_data_out | host_gfx_data_out'.  Now there will be 0 clock cycles for clocking a register.

Done.  Also sorted the HV trigger problem - now delayed through palette_mixer, although I imagine that's a temporary debugging thing and I'll be removing them from the final display before too long.

EDIT: Forgot to mention, Fmax is down to 146.63 MHz now.
« Last Edit: December 09, 2019, 01:18:54 pm by nockieboy »
 
The following users thanked this post: BrianHG

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7747
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #622 on: December 09, 2019, 01:39:18 pm »
Found a bug in the 'bitplane_to_raster' module.

Line 146: if (x_out[3])...
I'll let you figure out the bug...

Also, make the HW_regs font default color to 240, that cyan background is ugly...
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #623 on: December 09, 2019, 02:05:34 pm »
Found a bug in the 'bitplane_to_raster' module.

Line 146: if (x_out[3])...
I'll let you figure out the bug...

Ooh.. shouldn't that be x_out[2]?

Also, make the HW_regs font default color to 240, that cyan background is ugly...

Yeah, that was my reaction when I saw it too.  :-DD  Didn't think to change the HW_regs default settings - I was setting it to E1 in the RS232_debugger.  ;D
 

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 7747
  • Country: ca
Re: FPGA VGA Controller for 8-bit computer
« Reply #624 on: December 09, 2019, 02:06:12 pm »
Your pixels are still off by 1 or 2 according to the image I see.

The original code has 0 delay on the bit selection.  This new code has 1, so long as you remove all the internal delays.  You need the other modules to take that into effect.

Right, fixed that by changing the x_in input to the bitplane_to_raster instance from dly6_disp_x to dly5_disp_x.  :-+

Remember my comment after this fix post of yours:
https://www.eevblog.com/forum/fpga/fpga-vga-controller-for-8-bit-computer/msg2816848/#msg2816848

Doing it the wrong way will come back to haunt you later....  Like soon...
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf