Ok, @nockieboy, now that I know you can simulate code in Quartus, let's take a look at point #3 here:
https://www.eevblog.com/forum/fpga/fpga-vga-controller-for-8-bit-computer/msg2788698/#msg2788698
...
Now, you will need to re-work it to the specs and controls I laid out for your GPU....
module GPU_HW_Control_Regs (
input reg rst,
input reg clk,
input reg we,
input reg [19:0] addr_in,
input reg [7:0] data_in,
output reg [7:0] GPU_HW_Control_regs[HW_REGS_SIZE]
);
parameter HW_REGS_SIZE = HW_REGS;
parameter [7:0] RST_VALUES [32] = {
8'h01,
8'h02,
8'h03,
8'h04,
8'h05,
8'h06,
8'h07,
8'h08,
8'h09,
8'h0A,
8'h0B,
8'h0C,
8'h0D,
8'h0E,
8'h0F,
8'h10,
8'h11,
8'h12,
8'h13,
8'h14,
8'h15,
8'h16,
8'h17,
8'h18,
8'h19,
8'h1A,
8'h1B,
8'h1C,
8'h1D,
8'h1E,
8'h1F,
8'h20
};
wire valid_wr;
assign valid_wr = we && ( addr_in[19:8] == data_in ); // upper 8-bits of addr_in should equal data_in for a successful write
integer i;
always @ (posedge clk) begin
if (rst) begin
// reset key registers to initial values
for (i = 0; i < 32; i = i + 1) begin
GPU_HW_Control_regs[i] <= RST_VALUES[i];
end
// reset remaining registers to zero
for (i = 33; i < HW_REGS_SIZE; i = i + 1) begin
GPU_HW_Control_regs[i] <= 8'h0;
end
end
else
begin
if (valid_wr) begin
// apply written value to addressed register
GPU_HW_Control_regs[addr_in[7:0]] <= data_in;
end
end
end
endmodule
c) Base write address for the write input. The module will always take in all 20 address bits, but when writing data to the 8 bit data input port, the upper address wires [19:8] should equal this number for the write to be successful. If parameter (b) is >256, then the bottom of the upper base write address [8] will be ignored as you will be opening a 512 byte window, and so on. (Set this default to the base of the last 256 bytes in your system memory. Yes, it will occupy the same last 256 bytes as your system GPU ram.)
You can see how Altera/Intel labels the ' register memory bank ' differently outside the Verilog module where I fed some of the individual register bytes to a few output pins.
This bank of registers is what will hold and feed all you graphics control settings. As you can see, you write to them just like ram, but, you have individual access to all their contents at all times.
Ok, @nockieboy, now that I know you can simulate code in Quartus, let's take a look at point #3 here:
https://www.eevblog.com/forum/fpga/fpga-vga-controller-for-8-bit-computer/msg2788698/#msg2788698
...
Now, you will need to re-work it to the specs and controls I laid out for your GPU....
Okay, have done that (thanks for the example - makes my life much easier and assists my understanding no end!) - code below:Code: [Select]module GPU_HW_Control_Regs (
input reg rst,
input reg clk,
input reg we,
input reg [19:0] addr_in,
input reg [7:0] data_in,
output reg [7:0] GPU_HW_Control_regs[HW_REGS_SIZE]
);
parameter HW_REGS_SIZE = HW_REGS;
parameter [7:0] RST_VALUES [32] = {
8'h01,
8'h02,
8'h03,
8'h04,
8'h05,
8'h06,
8'h07,
8'h08,
8'h09,
8'h0A,
8'h0B,
8'h0C,
8'h0D,
8'h0E,
8'h0F,
8'h10,
8'h11,
8'h12,
8'h13,
8'h14,
8'h15,
8'h16,
8'h17,
8'h18,
8'h19,
8'h1A,
8'h1B,
8'h1C,
8'h1D,
8'h1E,
8'h1F,
8'h20
};
wire valid_wr;
assign valid_wr = we && ( addr_in[19:8] == data_in ); // upper 8-bits of addr_in should equal data_in for a successful write
integer i;
always @ (posedge clk) begin
if (rst) begin
// reset key registers to initial values
for (i = 0; i < 32; i = i + 1) begin
GPU_HW_Control_regs[ i ] <= RST_VALUES[ i ];
end
// reset remaining registers to zero
for (i = 4; i < HW_REGS_SIZE; i = i + 1) begin
GPU_HW_Control_regs[ i ] <= 8'h0;
end
end
else
begin
if (valid_wr) begin
// apply written value to addressed register
GPU_HW_Control_regs[addr_in[7:0]] <= data_in;
end
end
end
endmodule
So I've been wading through the specifications you provided in the earlier post for the GPU_HW_Control_Regs, but I'm struggling with this bit:Quotec) Base write address for the write input. The module will always take in all 20 address bits, but when writing data to the 8 bit data input port, the upper address wires [19:8] should equal this number for the write to be successful. If parameter (b) is >256, then the bottom of the upper base write address [8] will be ignored as you will be opening a 512 byte window, and so on. (Set this default to the base of the last 256 bytes in your system memory. Yes, it will occupy the same last 256 bytes as your system GPU ram.)
I've got the check to make sure the data value is the same as the upper 8-bits of the address value, but the bit in bold is making my brain ache. Can you rephrase those three sentences, or reduce them to single-syllable words with crayon sketches?You can see how Altera/Intel labels the ' register memory bank ' differently outside the Verilog module where I fed some of the individual register bytes to a few output pins.
This bank of registers is what will hold and feed all you graphics control settings. As you can see, you write to them just like ram, but, you have individual access to all their contents at all times.
So I'll need 32 output pins - one for each of the 'main' registers?
I've got the check to make sure the data value is the same as the upper 8-bits of the address value, but the bit in bold is making my brain ache. Can you rephrase those three sentences, or reduce them to single-syllable words with crayon sketches?
So I'll need 32 output pins - one for each of the 'main' registers?
You just about got it... #1, see error in 'RED'
// reset key registers to initial values
for (i = 0; i < 32; i = i + 1) begin
GPU_HW_Control_regs[ i ] <= RST_VALUES[ i ];
end
// reset remaining registers to zero
for (i = 4; i < HW_REGS_SIZE; i = i + 1) begin
GPU_HW_Control_regs[ i ] <= 8'h0;
end
Ok, you need another Parameter. Call it 'BASE_WRITE_ADDRESS'. Now, change to your current valid write enable condition:
assign valid_wr = we && ( addr_in[19:HW_REGS_SIZE] == BASE_WRITE_ADDRESS[19:HW_REGS_SIZE] ); // upper addr_in should equal to the same specified upper base address. This will allow us to map the register bank anywhere inside the 20bit address range.
assign valid_wr = we && ( addr_in[19:HW_REGS_SIZE] == BASE_WRITE_ADDRESS[19:HW_REGS_SIZE] );
BASE_ADDRESS = 19'b0000011111100000000;
module GPU_HW_Control_Regs (
input reg rst,
input reg clk,
input reg we,
input reg [19:0] addr_in,
input reg [7:0] data_in,
output reg [7:0] GPU_HW_Control_regs[2**HW_REGS_SIZE]
);
parameter HW_REGS_SIZE = HW_REGS;
parameter BASE_WRITE_ADDRESS = 0;
parameter [7:0] RST_VALUES [32] = {
8'h01,
8'h02,
8'h03,
8'h04,
8'h05,
8'h06,
8'h07,
8'h08,
8'h09,
8'h0A,
8'h0B,
8'h0C,
8'h0D,
8'h0E,
8'h0F,
8'h10,
8'h11,
8'h12,
8'h13,
8'h14,
8'h15,
8'h16,
8'h17,
8'h18,
8'h19,
8'h1A,
8'h1B,
8'h1C,
8'h1D,
8'h1E,
8'h1F,
8'h20
};
wire valid_wr;
assign valid_wr = we && ( addr_in[19:HW_REGS_SIZE] == BASE_WRITE_ADDRESS[19:HW_REGS_SIZE] ); // upper 8-bits of addr_in should equal data_in for a successful write
integer i;
always @ (posedge clk) begin
if (rst) begin
// reset key registers to initial values
for (i = 0; i < 32; i = i + 1) begin
GPU_HW_Control_regs[i] <= RST_VALUES[i];
end
// reset remaining registers to zero
for (i = 4; i < 2**HW_REGS_SIZE; i = i + 1) begin
GPU_HW_Control_regs[i] <= 8'h0;
end
end
else
begin
if (valid_wr) begin
// apply written value to addressed register
GPU_HW_Control_regs[addr_in[HW_REG_SIZE-1:0]] <= data_in;
end
end
end
endmodule
module GPU_HW_Control_Regs (
input rst,
input clk,
input we,
input [19:0] addr_in,
input [7:0] data_in,
output reg [7:0] GPU_HW_Control_regs[2**HW_REGS_SIZE]
);
parameter HW_REGS_SIZE = 5;
parameter BASE_WRITE_ADDRESS = 0;
parameter bit [7:0] RST_VALUES [32] = '{
8'h01,
8'h02,
8'h03,
8'h04,
8'h05,
8'h06,
8'h07,
8'h08,
8'h09,
8'h0A,
8'h0B,
8'h0C,
8'h0D,
8'h0E,
8'h0F,
8'h10,
8'h11,
8'h12,
8'h13,
8'h14,
8'h15,
8'h16,
8'h17,
8'h18,
8'h19,
8'h1A,
8'h1B,
8'h1C,
8'h1D,
8'h1E,
8'h1F,
8'h20
};
wire valid_wr;
assign valid_wr = we && ( addr_in[19:HW_REGS_SIZE] == BASE_WRITE_ADDRESS[19:HW_REGS_SIZE] ); // upper 8-bits of addr_in should equal data_in for a successful write
integer i;
always @ (posedge clk) begin
if (rst) begin
// reset key registers to initial values
for (i = 0; i < 32; i = i + 1) begin
GPU_HW_Control_regs[i] <= RST_VALUES[i];
end
// reset remaining registers to zero
for (i = 4; i < 2**HW_REGS_SIZE; i = i + 1) begin
GPU_HW_Control_regs[i] <= 8'h0;
end
end
else
begin
if (valid_wr) begin
// apply written value to addressed register
GPU_HW_Control_regs[addr_in[HW_REGS_SIZE-1:0]] <= data_in;
end
end
end
endmodule
BASE_WRITE_ADDRESS: Can I just declare it as a hex or binary value?Code: [Select]BASE_ADDRESS = 19'b0000011111100000000;
That should be the address of the top 256 bytes of the GPU's 16K RAM?
Now I'm hitting error after error trying to create symbol files for GPU_HW_Control_Regs. I've saved it as a .sv file to try to enforce the System Verilog language, but it's really not happy with the RST_VALUES parameter array.
This is where I am currently - this error message:
Error (10016): Can't create symbol/include/instantiation/component file for module "GPU_HW_Control_Regs" because port "GPU_HW_Control_regs" has an unsupported typeCode: [Select]module GPU_HW_Control_Regs (
input rst,
input clk,
input we,
input [19:0] addr_in,
input [7:0] data_in,
output reg [7:0] GPU_HW_Control_regs[2**HW_REGS_SIZE]
);
parameter HW_REGS_SIZE = 5;
parameter BASE_WRITE_ADDRESS = 0;
parameter bit [7:0] RST_VALUES [32] = '{
8'h01,
8'h02,
8'h03,
8'h04,
8'h05,
8'h06,
8'h07,
8'h08,
8'h09,
8'h0A,
8'h0B,
8'h0C,
8'h0D,
8'h0E,
8'h0F,
8'h10,
8'h11,
8'h12,
8'h13,
8'h14,
8'h15,
8'h16,
8'h17,
8'h18,
8'h19,
8'h1A,
8'h1B,
8'h1C,
8'h1D,
8'h1E,
8'h1F,
8'h20
};
wire valid_wr;
assign valid_wr = we && ( addr_in[19:HW_REGS_SIZE] == BASE_WRITE_ADDRESS[19:HW_REGS_SIZE] ); // upper 8-bits of addr_in should equal data_in for a successful write
integer i;
always @ (posedge clk) begin
if (rst) begin
// reset key registers to initial values
for (i = 0; i < 32; i = i + 1) begin
GPU_HW_Control_regs[i] <= RST_VALUES[i];
end
// reset remaining registers to zero
for (i = 4; i < 2**HW_REGS_SIZE; i = i + 1) begin
GPU_HW_Control_regs[i] <= 8'h0;
end
end
else
begin
if (valid_wr) begin
// apply written value to addressed register
GPU_HW_Control_regs[addr_in[HW_REGS_SIZE-1:0]] <= data_in;
end
end
end
endmodule
I've made changes to how RST_VALUES is declared, including specifying a type which I think is causing this error, but I get other errors if I don't specify a type.
It's in part a limitation of QuartusIIv9's 'generate sheet symbol' function, you just need to generate the sheet symbol in Quartus Prime, then it will still load in QuartusIIv9 once generated. Once you have the symbol, everything else is still functional in both versions of Quartus.
If you cant solve the reset parameter array, just make 8 16 bit parameter inputs and assign the 8 to the first 16 regs. We will most likely only use 4 of these, filled with 16 or even 24 bit quantities as they will point to the power up display address pointer settings to generate a valid picture.
module GPU_HW_Control_Regs (
input rst,
input clk,
input we,
input [19:0] addr_in,
input [7:0] data_in,
output reg [7:0] GPU_HW_Control_regs[2**HW_REGS_SIZE]
);
parameter HW_REGS_SIZE = 5;
parameter BASE_WRITE_ADDRESS = 0;
parameter RST_VALUE_0 = 16'b0000000000000000;
parameter RST_VALUE_1 = 16'b0000000000000001;
parameter RST_VALUE_2 = 16'b0000000000000010;
parameter RST_VALUE_3 = 16'b0000000000000011;
parameter RST_VALUE_4 = 16'b0000000000000100;
parameter RST_VALUE_5 = 16'b0000000000000101;
parameter RST_VALUE_6 = 16'b0000000000000110;
parameter RST_VALUE_7 = 16'b0000000000000111;
wire valid_wr;
assign valid_wr = we && ( addr_in[19:HW_REGS_SIZE] == BASE_WRITE_ADDRESS[19:HW_REGS_SIZE] ); // upper 8-bits of addr_in should equal data_in for a successful write
integer i;
always @ (posedge clk) begin
if (rst) begin
// reset key registers to initial values
GPU_HW_Control_regs[0] <= RST_VALUE_0[7:0];
GPU_HW_Control_regs[1] <= RST_VALUE_0[15:8];
GPU_HW_Control_regs[2] <= RST_VALUE_1[7:0];
GPU_HW_Control_regs[3] <= RST_VALUE_1[15:8];
GPU_HW_Control_regs[4] <= RST_VALUE_2[7:0];
GPU_HW_Control_regs[5] <= RST_VALUE_2[15:8];
GPU_HW_Control_regs[6] <= RST_VALUE_3[7:0];
GPU_HW_Control_regs[7] <= RST_VALUE_3[15:8];
GPU_HW_Control_regs[8] <= RST_VALUE_4[7:0];
GPU_HW_Control_regs[9] <= RST_VALUE_4[15:8];
GPU_HW_Control_regs[10] <= RST_VALUE_5[7:0];
GPU_HW_Control_regs[11] <= RST_VALUE_5[15:8];
GPU_HW_Control_regs[12] <= RST_VALUE_6[7:0];
GPU_HW_Control_regs[13] <= RST_VALUE_6[15:8];
GPU_HW_Control_regs[14] <= RST_VALUE_7[7:0];
GPU_HW_Control_regs[15] <= RST_VALUE_7[15:8];
// reset remaining registers to zero
for (i = 16; i < 2**HW_REGS_SIZE; i = i + 1) begin
GPU_HW_Control_regs[i] <= 8'h0;
end
end
else
begin
if (valid_wr) begin
// apply written value to addressed register
GPU_HW_Control_regs[addr_in[HW_REGS_SIZE-1:0]] <= data_in;
end
end
end
endmodule
It's in part a limitation of QuartusIIv9's 'generate sheet symbol' function, you just need to generate the sheet symbol in Quartus Prime, then it will still load in QuartusIIv9 once generated. Once you have the symbol, everything else is still functional in both versions of Quartus.
It's Quartus Prime I'm getting the errors in.If you cant solve the reset parameter array, just make 8 16 bit parameter inputs and assign the 8 to the first 16 regs. We will most likely only use 4 of these, filled with 16 or even 24 bit quantities as they will point to the power up display address pointer settings to generate a valid picture.
I'll go for this solution, I think. Here's the updated code:Code: [Select]module GPU_HW_Control_Regs (
input rst,
input clk,
input we,
input [19:0] addr_in,
input [7:0] data_in,
output reg [7:0] GPU_HW_Control_regs[2**HW_REGS_SIZE]
);
parameter HW_REGS_SIZE = 8;
parameter BASE_WRITE_ADDRESS = 20'h003F00;
parameter RST_VALUE_0 = 16'h0000;
parameter RST_VALUE_1 = 16'h0011;
parameter RST_VALUE_2 = 16'h0022;
parameter RST_VALUE_3 = 16'h0033;
parameter RST_VALUE_4 = 16'h0044;
parameter RST_VALUE_5 = 16'h0055;
parameter RST_VALUE_6 = 16'h0066;
parameter RST_VALUE_7 = 16'h0077;
This should be more functional, closer to my spec, and easier to see on the simulation.
output reg [7:0] GPU_HW_Control_regs[2**HW_REGS_SIZE]
module GPU_HW_Control_Regs (
input rst,
input clk,
input we,
input [19:0] addr_in,
input [7:0] data_in,
output reg [7:0] GPU_HW_Control_regs[0:(2**HW_REGS_SIZE-1)]
);
parameter HW_REGS_SIZE = 8;
parameter int BASE_WRITE_ADDRESS = 20'h0;
parameter int RST_VALUES [32] = '{
8'h01, 8'h02, 8'h03, 8'h04, 8'h05, 8'h06, 8'h07, 8'h08,
8'h09, 8'h0A, 8'h0B, 8'h0C, 8'h0D, 8'h0E, 8'h0F, 8'h10,
8'h11, 8'h12, 8'h13, 8'h14, 8'h15, 8'h16, 8'h17, 8'h18,
8'h19, 8'h1A, 8'h1B, 8'h1C, 8'h1D, 8'h1E, 8'h1F, 8'h20 };
wire valid_wr;
assign valid_wr = we && ( addr_in[19:HW_REGS_SIZE] == BASE_WRITE_ADDRESS[19:HW_REGS_SIZE] ); // upper 8-bits of addr_in should equal data_in for a successful write
integer i;
always @ (posedge clk) begin
if (rst) begin
// reset key registers to initial values
for (i = 0; i < 32; i = i + 1) begin
GPU_HW_Control_regs[i] <= RST_VALUES[i][7:0];
end
// reset remaining registers to zero
for (i = 32; i < 2**HW_REGS_SIZE; i = i + 1) begin
GPU_HW_Control_regs[i] <= 8'h0;
end
end
else
begin
if (valid_wr) begin
// apply written value to addressed register
GPU_HW_Control_regs[addr_in[HW_REGS_SIZE-1:0]] <= data_in;
end
end
end
endmodule
I've attached a simulation test bench Quartus project of the above code testing a few writes, then a sudden reset. As you can see, the simulation proves the module is doing exactly what we want it to do...
Ok, it's time to get onto points #4 and #5 here: https://www.eevblog.com/forum/fpga/fpga-vga-controller-for-8-bit-computer/msg2788698/#msg2788698
Note: place the register bank in the last 256 bytes of the 16kb addressable memory and tie it's data/address/we inputs to the RS232 debugger outputs in parallel with system 'host_' memory.
I'm assuming you will be updating points #2. And, we will wait on point #1 so you will see what happens and why point #1 needs to be taken care of after you finish point #5.
using the 'for' loop trick, point #5 is easily doable for all those cursors. Mix the outputs in the new temporary 'color_sel' color mixing block. Mix the lines with simple 'OR' gates right at the 'out[11:0]', prior to the modules output pins. Show us your results...
Damn, we would be nowhere if I didn't re-install Quartus myself...
Ok, it's time to get onto points #4 and #5 here: https://www.eevblog.com/forum/fpga/fpga-vga-controller-for-8-bit-computer/msg2788698/#msg2788698
Note: place the register bank in the last 256 bytes of the 16kb addressable memory and tie it's data/address/we inputs to the RS232 debugger outputs in parallel with system 'host_' memory.
Ok, it's time to get onto points #4 and #5 here: https://www.eevblog.com/forum/fpga/fpga-vga-controller-for-8-bit-computer/msg2788698/#msg2788698
Note: place the register bank in the last 256 bytes of the 16kb addressable memory and tie it's data/address/we inputs to the RS232 debugger outputs in parallel with system 'host_' memory.
Okay, point #4 is nearly done. I've attached the project below - that's the LATEST version, with all the changes so far including point #4 changes and some tweaks I made to the format of the vid_osd_generator code at the very start to make the I/O declarations easier to read. I've also made the connections on the design sheet to the outputs of the RS232 module, so should all be present and correct.
Here's a screenshot of the design sheet now - it's getting hard to fit into one screen:
(Attachment Link)
(I'm aware I've set a hex value when it should be INT for the BASE_WRITE_ADDRESS in the design sheet - that's corrected). However, it won't compile due to bus width errors - it seems Quartus isn't happy with me connecting a 14-lane bus from host_addr in the RS232 debugger module to a 20-lane input bus for addr_in in the GPU_HW_Control_Regs module. It's also complaining about bus width errors elsewhere where the GPU_HW_REGS_BUS connects to other modules.
I just clicked on 1 error at a time and fixed them. It compiled fine... See here...
Though, you will eventually need to up, or decide what to do with the RS232_Debugger's 14 bit address.
The desktop param does feed everything else properly. (Warning, I changer the extension of the GPU_HW_Control_Regs.sv to GPU_HW_Control_Regs.v)
(Attachment Link)
Is there an easy way to interface buses of different sizes without changing parameters in the modules themselves?
In danger of diluting the focus of our current efforts (#4 & #5 in the current task list), here's my latest on #1. I've updated the sync_generator to include the 16 pixel X and Y offset. Not 100% convinced you'll be happy with how I've done it, but it seems to work according to how I'm interpreting the simulation results (below).
(Attachment Link)
It's not visible in the screenshot above, but hde doesn't go high until the h_count = 16, and vde doesn't go high until v_count = 16. Here's some close-ups:
(Attachment Link)
Above shows hde going high after 16 pixels.
(Attachment Link)
Above shows vde going high on the 16th line.
Your going to have to get rid of the '-1' and accept that the outputs are delayed by 1-Hcount and 1-Vcount. Unfortunately, with the way it is setup, if you choose an offset of 0x0, the beginning positions would never happen.
Your going to need to verify that you actually have 640 'ON' pixels, not 641... Same with lines. Also, when does the sync begin and end. (Remember I said there would be a pitfall...)
5. Add a new 48 register output to the 'sync_gen.v' called 'raster_HV_trigger[47:0]'.
Make the even 'raster_HV_trigger[47:0]' wires pulse at a horizontal pixel position and the odd wires pulse on a vertical line according to the new 'GPU_HW_Control_regs[HW_REGS_SIZE*8-1:0]' onput wire, organized as 16 bit words, using the first 10 of 16 bits of each 2x2 bytes coming from the new input "GPU_HW_Control_regs[HW_REGS_SIZE*8-1:0]".
You should add a new parameter input to the sync_gen.v which will allow you to shift the beginning byte base (beginning) address of GPU_HW_Controls where the 'raster_HV_trigger' get their 48 settings from. IE 48x2 bytes = 96 bytes total.
Once done, out on your top block diagram, 'or' these 48 HV triggers outputs together and 'or' tie them to to the upper ro[],go[],bo[] bits of the OSD_img output. Test with the GPUtalk app. You should be able to move 24 vertical lines and 24 horizontal lines around the screen, with the first 0-15 coordinates being non-visible as those coordinates are too far to the left and above the top of the picture since we moved the picture window by 16 pixels to the right and down in step (1). (Once working, mix and combine / change around the 'or' driven ro[]&go[]&bo[] colors VS raster_HV_trigger[47:0] even and odd pairs so each set of coordinate generated lines may generate a different color on the display.)
for (..i..) begin
if ( h_count[9:8] == HW_REGS[i*4+base_offset+0][1:0] && h_count[7:0] == HW_REGS[i*4+base_offset+1][7:0] ) raster_HV_triggers[i*2+0] <= 1;
else raster_HV_triggers[i*2+0] <= 0;
end
then inside the 'v_count increment 'IF()begin' Place the vertical version. // reset h_count & increment v_count at end of scanline
if (h_count == LINE - 1) // end of line
begin
h_count <= 9'b0; // reset h_count
// Now h_count has been zeroed, check if the V-count should be cleared at end of SCANLINES
if (v_count == SCANLINES - 1)
begin
v_count <= 9'b0;
frame_ctr <= frame_ctr + 1'b1; // Increment the frame counter
end
else
begin // If v_count isn't being cleared, increment v_count
v_count <= v_count + 1'b1; // increment v_count to next scanline
if (v_count == IMAGE_OFFSET_Y)
vde <= 1'b1; // Turn on vertical video data enable - start of display area
else if (v_count == IMAGE_OFFSET_Y + V_RES - 1)
vde <= 1'b0 ; // Turn off vertical video data enable - reached bottom of display area
end
end
Your going to miss the vcount position '==0' if you keep your 'VDE' in it's current location. Move it outside the vcounter increment but leave it inside when the vcoun increment happens...
This is what you have now, it needs correcting...Code: [Select]// reset h_count & increment v_count at end of scanline
if (h_count == LINE - 1) // end of line
begin
h_count <= 9'b0; // reset h_count
// Now h_count has been zeroed, check if the V-count should be cleared at end of SCANLINES
if (v_count == SCANLINES - 1)
begin
v_count <= 9'b0;
frame_ctr <= frame_ctr + 1'b1; // Increment the frame counter
end
else
begin // If v_count isn't being cleared, increment v_count
v_count <= v_count + 1'b1; // increment v_count to next scanline
if (v_count == IMAGE_OFFSET_Y)
vde <= 1'b1; // Turn on vertical video data enable - start of display area
else if (v_count == IMAGE_OFFSET_Y + V_RES - 1)
vde <= 1'b0 ; // Turn off vertical video data enable - reached bottom of display area
end
end
Actually, this would still work unless you vde is set to SCANLINES - 1....
if (v_count == IMAGE_OFFSET_Y)
vde <= 1'b1; // Turn on vertical video data enable - start of display area
else if (v_count == IMAGE_OFFSET_Y + V_RES - 1)
vde <= 1'b0 ; // Turn off vertical video data enable - reached bottom of display area
// Now h_count has been zeroed, check if the V-count should be cleared at end of SCANLINES
if (v_count == SCANLINES - 1)
begin
v_count <= 9'b0;
frame_ctr <= frame_ctr + 1'b1; // Increment the frame counter
end
else
begin // If v_count isn't being cleared, increment v_count
v_count <= v_count + 1'b1; // increment v_count to next scanline
end
example:Code: [Select]for (..i..) begin
if ( h_count[9:8] == HW_REGS[i*4+base_offset+0][1:0] && h_count[7:0] == HW_REGS[i*4+base_offset+1][7:0] ) raster_HV_triggers[i*2+0] <= 1;
else raster_HV_triggers[i*2+0] <= 0;
end
then inside the 'v_count increment 'IF()begin' Place the vertical version.
Your Syncgen should have a new output register wire called 'raster_HV_triggers[47:0]' each even one should pulse on and off for a pixel. Make sure the odd wires in there pulse on, then off for an entire line.
Take that bundle and tie it into the current 'color_sel' module. Make a bunch of the 'raster_HV_triggers[47:0]' force on your choice of color for each wire, over the text box and static noise, or, whatever may be feeding the color_sel modules output so you may see if what you made works...
h_count[9:8] == GPU_HW_Control_regs[i*4+BASE_OFFSET][1:0] && h_count[7:0] == GPU_HW_Control_regs[i*4+BASE_OFFSET+1][7:0]
Now, you better figure out what the 'base_offset' is, otherwise I'll go nuts.
// VGA Sync Generator
//
// Default: 640x480x60
//
// Can take parameters when initialised
// to output sync signals for any screen
// resolution
module sync_generator(
// inputs
input wire pclk, // base pixel clock (125 MHz)
input wire reset, // reset: restarts frame
input wire [7:0] GPU_HW_Control_regs[0:(2**HW_REGS_SIZE-1)],
// outputs
output reg [3:0] pc_ena, // Pixel clock enable (4-bit to allow clock division in video sub-modules)
output reg hde, // Horizontal Display Enable - high when in display area (valid drawing area)
output reg vde, // Vertical Display Enable - high when in display area (valid drawing area)
output reg hsync, // horizontal sync
output reg vsync, // vertical sync
output reg [15:0] frame_ctr,
output reg [47:0] raster_HV_triggers
);
// default resolution if no parameters are passed
parameter H_RES = 640; // horizontal display resolution
parameter V_RES = 480; // vertical display resolution
// image offset parameters
parameter IMAGE_OFFSET_X = 16; // offset the display to allow room at the start
parameter IMAGE_OFFSET_Y = 16; // for things to go off edge of screen
// no-draw area definitions
// defined as parameters so you can edit these on Quartus' block diagram editor
parameter H_FRONT_PORCH = 16;
parameter HSYNC_WIDTH = 96;
parameter H_BACK_PORCH = 48;
parameter V_FRONT_PORCH = 10;
parameter VSYNC_HEIGHT = 2;
parameter V_BACK_PORCH = 33;
parameter PIX_CLK_DIVIDER = 4;
parameter HW_REGS_SIZE = 8; // hardware register size set by HW_REGS parameter in design sheet
parameter BASE_OFFSET = 32; // hardware register base offset for raster triggers
// total screen resolution
localparam LINE = H_RES + H_FRONT_PORCH + HSYNC_WIDTH + H_BACK_PORCH; // complete line (inc. horizontal blanking area)
localparam SCANLINES = V_RES + V_FRONT_PORCH + VSYNC_HEIGHT + V_BACK_PORCH; // total scan lines (inc. vertical blanking area)
// useful trigger points
localparam HS_STA = IMAGE_OFFSET_X + H_RES + H_FRONT_PORCH - 1; // horizontal sync ON (the minus 1 is because hsync is a REG, and thus one clock behind)
localparam HS_END = IMAGE_OFFSET_X + H_RES + H_FRONT_PORCH + HSYNC_WIDTH - 1; // horizontal sync OFF (the minus 1 is because hsync is a REG, and thus one clock behind)
localparam VS_STA = IMAGE_OFFSET_Y + V_RES + V_FRONT_PORCH; // vertical sync ON
localparam VS_END = IMAGE_OFFSET_Y + V_RES + V_FRONT_PORCH + VSYNC_HEIGHT; // vertical sync OFF
reg [9:0] h_count; // current pixel x position
reg [9:0] v_count; // current line y position
always @(posedge pclk)
if (pc_ena == PIX_CLK_DIVIDER) pc_ena <= 0;
else pc_ena <= pc_ena +1;
integer i;
// handle signal generation
always @(posedge pclk)
begin
if (reset) // reset to start of frame
begin
h_count <= (IMAGE_OFFSET_X + H_RES - 2);
v_count <= (SCANLINES - 2);
//z_count <= 1'b0;
hsync <= 1'b0;
vsync <= 1'b0;
vde <= 1'b0;
hde <= 1'b0;
frame_ctr <= 16'h0000;
end
else
begin
if (pc_ena[3:0] == 0) // once per pixel
begin
// horizontal raster trigger generation
for (i = 0; i < 24; i = i + 2) begin
if ( h_count[9:8] == GPU_HW_Control_regs[i*4+BASE_OFFSET][1:0] && h_count[7:0] == GPU_HW_Control_regs[i*4+BASE_OFFSET+1][7:0] )
raster_HV_triggers[i*2+0] <= 1'b1;
else
raster_HV_triggers[i*2+0] <= 1'b0;
end
// horizontal blanking area - set HDE LOW
if (h_count == IMAGE_OFFSET_X + H_RES - 1)
hde <= 1'b0; // Turn off horizontal video data enable
else if (h_count == IMAGE_OFFSET_X)
hde <= 1'b1; // Turn on horizontal video data enable
// check for generation of HSYNC pulse
if (h_count == HS_STA)
hsync <= 1'b1; // turn on HSYNC pulse
else if (h_count == HS_END)
hsync <= 1'b0; // turn off HSYNC pulse
// check for generation of VSYNC pulse
if (v_count == VS_STA)
vsync <= 1'b1; // turn on VSYNC pulse
else if (v_count == VS_END)
vsync <= 1'b0; // turn off VSYNC pulse
// reset h_count & increment v_count at end of scanline
if (h_count == LINE - 1) // end of line
begin
h_count <= 9'b0; // reset h_count
// vertical raster trigger generation
for (i = 1; i < 24; i = i + 2) begin
if ( v_count[9:8] == GPU_HW_Control_regs[i*4+BASE_OFFSET][1:0] && v_count[7:0] == GPU_HW_Control_regs[i*4+BASE_OFFSET+1][7:0] )
raster_HV_triggers[i*2+0] <= 1'b1;
else
raster_HV_triggers[i*2+0] <= 1'b0;
end
if (v_count == IMAGE_OFFSET_Y)
vde <= 1'b1; // Turn on vertical video data enable - start of display area
else if (v_count == IMAGE_OFFSET_Y + V_RES - 1)
vde <= 1'b0 ; // Turn off vertical video data enable - reached bottom of display area
// Now h_count has been zeroed, check if the V-count should be cleared at end of SCANLINES
if (v_count == SCANLINES - 1)
begin
v_count <= 9'b0;
frame_ctr <= frame_ctr + 1'b1; // Increment the frame counter
end
else
begin // If v_count isn't being cleared, increment v_count
v_count <= v_count + 1'b1; // increment v_count to next scanline
end
end
else // not at end of scanline, so just increment horizontal counter
begin
h_count <= h_count + 1'b1;
if (h_count == IMAGE_OFFSET_X + H_RES - 1)
hde <= 1'b0 ; // Turn off horizontal video data enable
end // if (h_count == LINE - 1)
end // if (pc_ena)
end // else !reset
end // always @clk
endmodule