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

0 Members and 7 Guests are viewing this topic.

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #200 on: November 06, 2019, 01:21:25 pm »
Updated diagram:



This is giving me errors, though, when I try to compile:

Code: [Select]
Info (12128): Elaborating entity "vga01" for hierarchy "vga01:inst"
Error (12014): Net "vga01:inst|x[8]", which fans out to "vga01:inst|vga_r", cannot be assigned more than one value
Error (12015): Net is fed by "vga01:inst|sync_generator:display|x[8]"
Error (12015): Net is fed by "vga01:inst|x[8]"
Error (12014): Net "vga01:inst|x[7]", which fans out to "vga01:inst|vga_g", cannot be assigned more than one value
Error (12015): Net is fed by "vga01:inst|sync_generator:display|x[7]"
Error (12015): Net is fed by "vga01:inst|x[7]"
Error (12014): Net "vga01:inst|x[6]", which fans out to "vga01:inst|vga_b", cannot be assigned more than one value
Error (12015): Net is fed by "vga01:inst|sync_generator:display|x[6]"
Error (12015): Net is fed by "vga01:inst|x[6]"
Error (12014): Net "vga01:inst|inDisplay", which fans out to "vga01:inst|vga_r", cannot be assigned more than one value
Error (12015): Net is fed by "vga01:inst|sync_generator:display|inDisplayArea"
Error (12015): Net is fed by "vga01:inst|inDisplay"

Trying to look these up, but it's not obvious (to me at least) what the problem is.  I'm obviously doing something very stupid...  :-//
« Last Edit: November 06, 2019, 01:22:59 pm by nockieboy »
 

Offline Berni

  • Super Contributor
  • ***
  • Posts: 5031
  • Country: si
Re: FPGA VGA Controller for 8-bit computer
« Reply #201 on: November 06, 2019, 01:28:39 pm »
That error to me looks like there are multiple outputs trying to drive 3 bits of net" x" and "inDisplay". You might have an output or input back to front.

Oh and by the way your inDisplay signal is probably the same as a signal that RGB buses tend to call the DE signal (Display Enable). This is a signal that is high whenever pixels inside the viewing area are being sent. Some LCD panels use that instead of Hsync and Vsync
 
The following users thanked this post: nockieboy

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8143
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #202 on: November 06, 2019, 01:41:34 pm »
Your RGB 2 wire buses are driving output pins are single wires each.  Remove the doubled pins and rename the output pins R[0], G[0], B[0] to:

R[1..0]
G[1..0]
B[1..0]

Quartus will understand that those outputs are a collective of pins grouped as a bus with 2 wires going to 2 pins.

This come in handy when you have an output group, like in my design, I have a 256bit ram data bus.  Could you imagine my block diagram with 256 individual BIDIR IOs instead of one single one labeled RDQ[255..0]...

Also, you do not need to label the buss wires as they will take their name from the output pins automatically.
« Last Edit: November 06, 2019, 01:55:28 pm by BrianHG »
 
The following users thanked this post: nockieboy

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #203 on: November 06, 2019, 02:39:23 pm »
That error to me looks like there are multiple outputs trying to drive 3 bits of net" x" and "inDisplay". You might have an output or input back to front.

Okay, it compiles now - I just had to remove this block from vga01.v:

Code: [Select]
// create a 640x480 synch and pixel
   sync_generator #(640,480) display(
      .clk(clk),
      .reset(rst),
      .x(x),
      .y(y),
      .DE(DISP_EN)
   );

I forgot to remove that block, so the HDL was trying to set up two connections to vga01 - one in code, the other in the diagram?

Quartus will understand that those outputs are a collective of pins grouped as a bus with 2 wires going to 2 pins.

This come in handy when you have an output group, like in my design, I have a 256bit ram data bus.  Could you imagine my block diagram with 256 individual BIDIR IOs instead of one single one labeled RDQ[255..0]...

 :o Yikes.

Also, you do not need to label the buss wires as they will take their name from the output pins automatically.

Ah okay, thanks - the info I found online (from Intel, no less) stated in one of the steps to label the bus, so I did. Happy to hear about shortcuts, though. ;)

Talking of which, I'm really liking this diagram method of connecting blocks up - seems much less stressful than the code method!  ;D
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #204 on: November 06, 2019, 03:51:25 pm »
 |O  Okay, so it compiles but I'm getting no image - the screen is going into standby. 

I've attached the files for vga01 and sync_generator with all the latest modifications I've made - they all compile fine into symbols and as part of the main project, but I'm getting nothing out at the monitor.  Below is a pic of the top-level diagram:



I've even tried swapping HSYNC/VSYNC around in case I'd got the pin assignments wrong, but to no avail - I'm not sure what's going wrong to be honest.  Have attached project files in case anyone wants to take a look.

EDIT:

For info, the pin assignments are identical to the project VGA01 - which I've uploaded to the FPGA and tested and that works fine.
« Last Edit: November 06, 2019, 04:00:49 pm by nockieboy »
 

Offline Berni

  • Super Contributor
  • ***
  • Posts: 5031
  • Country: si
Re: FPGA VGA Controller for 8-bit computer
« Reply #205 on: November 06, 2019, 05:17:41 pm »
Instead of looking trough your code for the problem im just going to give you some tips on debugging it.

First choice of action is a oscilloscope. Look at what the pins are doing because this is quick and easy to do, for tracking down the basic mistakes like wonky or missing clocks, swapped signals etc...

Then if it still does not lead you to any findings have a look at that "RTL View" and see what the compiler has compiled. Since you have basicaly put together the same thing in a diferent way means it should also compile into circuitry that is similar. Try to make sense of the connections in there and if there is something weird that can point you into where exactly the issue might be.

 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #206 on: November 06, 2019, 06:31:59 pm »
 :palm: :palm: :palm:

Fixed it.  I'd linked the reset line to both blocks, but had only inverted the signal in one of them - forgetting to invert the signal to the other, so one was held in permanent reset.  |O

All working again now.  ;D

Previous image of diagram is still current, more or less, except for the inversions on the reset inputs for each block (I've removed the code inverting reset in sync_generator).  :-+
« Last Edit: November 06, 2019, 06:38:19 pm by nockieboy »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8143
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #207 on: November 07, 2019, 09:18:51 am »
Here we go, I cleaned up your sync generator.  You had a few bugs.  Now, I don't have Quartus installed anymore and I just typed it in a text editor, so please bear with possible errors.  Once you understand my changes, you may remove anything you don't like:

Code: [Select]
// (default 640x480) 60Hz VGA Driver
// but can take parameters when initialised
// to output sync signals for any screen
// resolution
//
//                          |- LINE
//        __________________________ ____|
//       |                          |    |
//       |                          |    |
//       |               |    |
//       |               |    |
//       |      DISPLAY AREA        |    |
//       |                          |    |
//       |                          |    |
//       |                          |    |
//       --------------------------------- V_RES
//       |                          |
//       |                          |
//       ---------------------------- SCANLINES

module sync_generator(
// inputs
input wire pclk, // base pixel clock
input wire reset, // reset: restarts frame
// outputs
output reg pc_ena    // ***** New pixel clock enable ...  For now, you will use pc_ena[0] which divides the clock by 2.
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 [9:0] h_count, // current pixel x position
output reg [9:0] v_count // current line y position
);

// default resolution if no parameters are passed
parameter H_RES = 640; // horizontal display resolution
parameter V_RES = 480; // vertical display resolution

// no-draw area definitions
// ***** switched to 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;

// 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 = 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 = 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 = V_RES + V_FRONT_PORCH; // vertical sync ON
localparam VS_END = V_RES + V_FRONT_PORCH + VSYNC_HEIGHT; // vertical sync OFF

/*  moved above
reg [9:0] h_count; // line position
reg [9:0] v_count; // SCANLINES position
reg [9:0] z_count; // Frame counter for CPU animation
*/

// keep x and y bound within the display area
/* obsolete
assign x = (h_count < H_RES) ? h_count : (H_RES - 1'b1); // x = H_RES-1 if current pixel is after display area
assign y = (v_count < V_RES) ? v_count : (V_RES - 1'b1); // y = VS_END-1 if v_count is outside display area
*/
   // generate a 25 MHz pixel strobe
   //reg [15:0] cnt;
   //reg pix_en;

   always @(posedge clk)
pc_ena <= pc_ena + 1'b1;  // This is tempoary

// handle signal generation
always @(posedge clk)                    // I removed ', posedge reset)', since it adds a gate to the CLK input of all the registers in your design slowing down that trigger edge
begin
if (reset) // reset to start of frame
begin
h_count <= 1'b0;
v_count <= 1'b0;
hsync   <= 1'b0;
vsync   <= 1'b0;
vde     <= 1'b0;
hde     <= 1'b0;
end
else
begin
if (pc_ena) // once per pixel
begin

if (h_count == H_RES - 1)
begin
HDE <= 1'b0;
end


// check for generation of HSYNC pulse
if (h_count == HS_STA)
begin
hsync <= 1'b1; // turn on HSYNC pulse
end
else if (h_count == HS_END)
hsync <= 1'b0; // turn off HSYNC pulse

// check for generation of VSYNC pulse
if (v_count == VS_STA)
begin
vsync <= 1'b1; // turn on VSYNC pulse
end
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 <= 1'b0;
HDE     <= 1'b1;  // Turn on horizontal video data enable

if (v_count == SCANLINES - 1) // ****** Now that it's time to increment the H count, this is when you would check if the V-count should be cleared.  End of SCANLINES
begin
v_count <= 1'b0;
VDE     <= 1'b1;   // Turn on vertical video data enable
end
else
begin                                             // ****** If the v_count isn't being cleared, you go ahead and add 1 to the v_count
v_count <= v_count + 1'b1;           // increment v_count to next scanline
if (v_count == V_RES - 1)   VDE <= 1'b0 ; // Turn off  vertical video data enable
end
end
else
h_count <= h_count + 1'b1;           // otherwise, just increment horizontal counter
if (h_count == H_RES - 1)  HDE <= 1'b0 ;  // Turn off  vertical video data enable

/*  ****** BUG *****  Moved into the correct place at the the end of a horizontal line, where you increment the v_count increment, or clear it. see 16 lines above
// reset v_count and blanking at bottom of screen
if (v_count == SCANLINES - 1) // end of SCANLINES
begin
v_count <= 1'b0;
end
*/  ***** End of bug ******

end
end
end

/******* Error ****** this must synchronous with you video clock, however, I recognize this was probably just a last minute patch.
always @(posedge clk)
begin
DE <= (h_count < H_RES) && (v_count < V_RES);
end
/********

endmodule


Now, here is a new module to add to your project.  It mutes the RGB data values when the raster is outside the active display area.  (Once again, I just typed it in notepad.)  It belongs just before your output pins.  Again, read comments inside it.

Code: [Select]
//  This module will force mute mute the RGB video output data outside the active video display area
//  This module will also generate the vid_de_out use by many DVI transmiters
//  This module, as an example, also has all the inputs and outputs used along the pixel pipe
//  it illustrates since there is a pixel delay in the video switch, the syncs and video enables are also delayed
//  making the output picture window perfectly parallel with the vidoe coming in, then being fed out.

module vid_out_stencil(
input wire pclk,
input wire reset,
input wire pc_ena,      // Pixel clock enable
input wire hde_in, // Horizontal Display Enable - high when in display area (valid drawing area)
input wire vde_in, // Vertical Display Enable - high when in display area (valid drawing area)
input wire hs_in, // horizontal sync
input wire vs_in, // vertical sync

input wire [RGB_hbit:0] r_in,
input wire [RGB_hbit:0] g_in,
input wire [RGB_hbit:0] b_in,

output reg hde_out,
output reg vde_out,
output reg hs_out,
output reg vs_out,

output reg [RGB_hbit:0] r_out,
output reg [RGB_hbit:0] g_out,
output reg [RGB_hbit:0] b_out,

output reg vid_de_out      // Actual H&V data enable required by some DVI encoders/serializers
);

parameter RGB_hbit    = 1;  // 1 will make the RGB ports go from 1 to 0, eg [1:0].  I know others prefer a '2' here for 2 bits
parameter HS_invert   = 0;  // use a 1 to invert the HS output, the invert feature is only for this video output module
parameter VS_invert   = 0;  // use a 1 to invert the VS output, the invert feature is only for this video output module


always @(posedge clk)
begin
if (reset) // global reset
begin

// not in use for this module

end
else
begin
if (pc_ena) // once per pixel
begin

hde_out <= hde_in;             // since the this video muting switch algorythm delays the output by 1 pixel clock,
vde_out <= vde_in;             // all the video timing reference signals will also get the 1 pixel delay treatment to keep the output aligned perfectly.
hs_out  <= hs_in ^ HS_invert ; // the invert feature is only for this video output module
vs_out  <= vs_in ^ VS_invert ; // the invert feature is only for this video output module

if ( hde_in && vde_in )
begin
de_out <= 1'b1 ; // turn on video enable for DVI transmitters
r_out  <= r_in ; // copy video input to output
g_out  <= g_in ; // copy video input to output
b_out  <= b_in ; // copy video input to output
end
else
begin
de_out <= 1'b0 ; // turn off video enable for DVI transmitters
r_out  <= 0    ; // Mute video output to black
g_out  <= 0    ; // Mute video output to black
b_out  <= 0    ; // Mute video output to black
end

end
end
end
endmodule


Now, the next thing you need to do is use the IOs from this example, and make a new picture pattern generator which does not use the sync generators H&V counters, but, generates it's own internally using this example IO port setup:

Code: [Select]
module vid_pattern_generator(
input wire pclk,
input wire reset,
input wire pc_ena,      // Pixel clock enable
input wire hde_in, // Horizontal Display Enable - high when in display area (valid drawing area)
input wire vde_in, // Vertical Display Enable - high when in display area (valid drawing area)
input wire hs_in, // horizontal sync
input wire vs_in, // vertical sync


output reg hde_out,
output reg vde_out,
output reg hs_out,
output reg vs_out,

output reg [RGB_hbit:0] r_out,
output reg [RGB_hbit:0] g_out,
output reg [RGB_hbit:0] b_out

);

parameter RGB_hbit    = 1;  // 1 will make the RGB ports go from 1 to 0, eg [1:0].  I know others prefer a '2' here for 2 bits


always @(posedge clk)
begin
if (reset) // global reset
begin



end
else
begin
if (pc_ena) // once per pixel
begin

// ***************  insert generator code here
// ***************  also remember to pass through the hde,vde,hs_out,vs_out
// ***************  in the future, numerous delay sizes may be needed if you are performing functions which take multiple clocks before a true pixel becomes valid

end
end
end // always @clk
endmodule


Let me know when you are done, because, after I've gone over your new pattern generator, your next step is to place either your first text, or, graphics.

Additional: Please place you quartus screenshots on this forum, I'm having trouble with your picture server & since this thread may last long on the forum, it is a good idea to have the photos here.  (If you want a compact picture file size, lossless, use the .png file format...)

***** DON'T forget to update the graphic symbol for the changes I made to you sync generator.  The order of the IO pins have been changes and I changed the available parameters on the block diagram symbol.
« Last Edit: November 07, 2019, 10:31:00 am by BrianHG »
 

Offline Berni

  • Super Contributor
  • ***
  • Posts: 5031
  • Country: si
Re: FPGA VGA Controller for 8-bit computer
« Reply #208 on: November 07, 2019, 09:30:37 am »
Is there a specific reason why not to reuse the X and Y counters from the sync generator?

You will always need to know the pixel X Y coordinates so why not use a counter that already counts this. Only thing i would add is a "pixel_increment" signal that goes high for 1 clock cycle every time it steps onto the next pixel. This can then pace the pixel generation logic at slower speeds than the clock.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8143
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #209 on: November 07, 2019, 10:20:49 am »
Is there a specific reason why not to reuse the X and Y counters from the sync generator?

You will always need to know the pixel X Y coordinates so why not use a counter that already counts this. Only thing i would add is a "pixel_increment" signal that goes high for 1 clock cycle every time it steps onto the next pixel. This can then pace the pixel generation logic at slower speeds than the clock.
Yes.  The counters are good for generating a pattern, especially in a simple design.  However, there are a few advantages to getting used to having a separate what we could more truly call an address counter generator which doe not rely on multiply and add to point to a position in ram specifically.

First advantage.  You notice I separated the display enables into horizontal and vertical, HDE & VDE.  to create a synchronous new H&V counter, one could approach the counters like this:

--------------------------------------------------------------------------------------------------------------
hde_out <= hde_in;
vde_out <= vde_in;

if (~vde) begin  // regardless of the display geometry and timing, the new
   vcount <=0 ;  // vertical count clears immediately after a video frame has finished
  end else begin
                  if (~hde_in && hde_out ) vcount <= vcount +1;  // regardless of the display geometry and timing, the new
                                                                                        // vertical count increments immediately after a video line is finished
                                                                                       // giving time for address generators to do their math and ram access cycles
                  if (~hde) begin
                     hcount <= 0;
                 end else begin
                      hcount <= hcount +1;
                     end
------------------------------------------------------------------------------------------------------------------------------------
Now, the hcount and vcount coming out of the sync generator may be re-aligned with reference to the active picture area to re-center position the picture, the above code with minimal gates will always clear and increment you picture coordinates right after the display activity has ended, giving you maximum time to perform additional actions/access within the same block of system memory to do thing like, copy over memory to fill a sprite, copy memory for audio dac, read a display list for what the video mode of the next line of video should be and where it's display memory is located.

Now, this is a preference as I know that such logic will only eat another 24 logic registers for a full 1024x1024.  This will also increase your FMAX as these counters may now be located right at the inputs of your dual port ram which quartus now only has to route HDE and VDE from your sync generator to wherever the ram address may be in the FPGA.

Remember, the current sync generator only generates a vde, hde, vsync, hsync.  Wait till we add

sprite1.thru.8_hstart
sprite1.thru.8_vstart
sprite1.thru.8_vstop
vertical_interrupt1.thru.2

Stopping here already makes 26 x 10 bit number compares on the hcount and vcount to generate their windowed coordinates.  Adding that to using the same counters to generate additional raster coordinates will kill the FMAX in such a slow old FPGA.

I'm targeting a high speed FMAX so that with a simple dual port ram, on the graphics read side, if the ram is operating at 6x the pixel clock speed, say 150Mhz, for each pixel drawn, I can access 6 different words of system ram and still have the second port 100% dedicated to the CPU interface.

With these 6 access, we will for example, read a top ascii text window layer, loop that output into a resolver for the font memory base address and read that letters correct pixel, another access cycle for reading a font color map.  Also, access graphics memory for a graphics mode.  And since these modes usually are bitplane, they'll only eat 1-2 of my access time slots.  The remainder access slots 3-6 will be used for a hardware rectangle read / modify / write command which will accelerate clear, carriage return scroll text, paste a bitplane picture from one location in memory into another with an optional transparent color 0.  With the 2 megabit Lattice part, an 8Mhz Z80 would be able to basically run a simplified Doom type game with all the sorted character graphic images pre-rendered in the OP's 160x120 256 color mode.


Hmmm, the only ugly thing would be a triangle filling engine.  Thankfully, there is another thread here on this FPGA forum covering that exact topic, with source code...  With a core 150Mhz clock and 8MHZ cpu, the dedicated CPU memory access port if free over 95% of the time for sophisticated triangle, rectangle and circle geometry engine.

« Last Edit: November 07, 2019, 10:45:01 am by BrianHG »
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #210 on: November 07, 2019, 10:39:03 am »
Here we go, I cleaned up your sync generator.  You had a few bugs.

Thanks!  :-+

Well done, by the way, writing all that in a text editor - there were only a couple of typos in variable names, otherwise it all compiled into symbol files just fine.  ;D

Now, the next thing you need to do is use the IOs from this example, and make a new picture pattern generator which does not use the sync generators H&V counters, but, generates it's own internally using this example IO port setup:

Okay, working on it now...  :-/O

Additional: Please place you quartus screenshots on this forum, I'm having trouble with your picture server & since this thread may last long on the forum, it is a good idea to have the photos here.  (If you want a compact picture file size, lossless, use the .png file format...)

Righto - don't know why I got into the habit of externally-hosting the images, but there's no particular reason I still do it.  :-+

***** DON'T forget to update the graphic symbol for the changes I made to you sync generator.  The order of the IO pins have been changes and I changed the available parameters on the block diagram symbol.

No worries. Will get back to you (hopefully) shortly!  :-/O
 

Offline Berni

  • Super Contributor
  • ***
  • Posts: 5031
  • Country: si
Re: FPGA VGA Controller for 8-bit computer
« Reply #211 on: November 07, 2019, 11:04:44 am »
Well yes for counting frame buffer memory addresses you would want to use a separate counter that resets on Vsync. But for other things having X Y tends to be more useful.

For text and tile maps id use grids that are 8 16 32 or something and round the total area to a round power of 2 number so that you can get the index by simply bit shifting and so zero logic is needed to calculate the index from X Y (Yes this wastes memory space but is very useful for smooth scrolling). Also since these are small and often accessed id stick them into separate memory blocks so that you can generate text and color it within a single clock cycle. When deciding about sprites its also common that you need to compare a bunch of registers to the X and Y coordinates. All of it being essentially combinational logic that can work in the background for multiple clock cycles if needed since pixels come out at such a slow rate. You can easily give yourself a 1 pixel head start to it by delaying Hsync output by one pixel so that the pipeline is full and ready when the pixels really start moving out to the monitor.

Anyway many ways to skin a cat, none of them necessarily more correct, just different in there own way.
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #212 on: November 07, 2019, 11:30:12 am »
Okay, vid_pattern_generator.v below for your perusal and criticism.  ;)

I haven't tried it out on the FPGA yet as I'm sure there'll be some feedback and changes I need to make!  I was undecided about how to implement the x, y counters - but the method I've gone with is this one; all the time pc_ena is HIGH (pixels are being drawn), I increment the X and Y counters - so they should range from 0-639 for x and 0-479 for y, or indeed whatever maximum value the current screen resolution goes up to.

As they're counting, the only check that is made is against H_RES to reset x at the end of each scan line.  I don't check y at all as that will stop counting when pc_ena goes LOW and get reset during VSYNC.

As soon as pc_ena goes LOW, the counters no longer increment and when there is a VSYNC pulse, the counters are reset, ready for pc_ena to go HIGH at the start of the next frame and allow them to count again.

Does that sound reasonable, or is there a better way?

Code: [Select]
module vid_pattern_generator(

input wire pclk,
input wire reset,
input wire pc_ena, // Pixel clock enable
input wire hde_in, // Horizontal Display Enable - high when in display area (valid drawing area)
input wire vde_in, // Vertical Display Enable - high when in display area (valid drawing area)
input wire hs_in, // horizontal sync
input wire vs_in, // vertical sync


output reg hde_out,
output reg vde_out,
output reg hs_out,
output reg vs_out,

output reg [RGB_hbit:0] r_out,
output reg [RGB_hbit:0] g_out,
output reg [RGB_hbit:0] b_out

);

parameter RGB_hbit = 1; // 1 will make the RGB ports go from 1 to 0, eg [1:0].  I know others prefer a '2' here for 2 bits

// default resolution if no parameters are passed
parameter H_RES = 640; // horizontal display resolution
parameter V_RES = 480; // vertical display resolution

// set up x & y position counters
reg [9:0] x, y;

always @(posedge pclk)
begin
if (reset) // global reset
begin

// reset position counters
x <= 1'b0;
y <= 1'b0;

end // global reset
else
begin
if (pc_ena) // once per pixel
begin

// Generate some colour bars based on x value
r_out[RGB_hbit] <= x[8];
g_out[RGB_hbit] <= x[7];
b_out[RGB_hbit] <= x[6];

// pass through hde, vde, hs_out, vs_out
hde_out <= hde_in;
vde_out <= vde_in;
hs_out <= hs_in;
vs_out <= vs_in;

// update the position counters
// all the time pc_ena is HIGH, we're in a valid drawing area so
// the counters can be incremented and checked against the
// supplied screen resolution
// reset X & increment Y at end of scanline
if (x == H_RES - 1) // end of line
begin
x <= 1'b0; // reset X
y <= y + 1'b1; // increment Y to next scanline
end
else // not at end of scanline, so just increment X
x <= x + 1'b1;

end // pc_ena
else
begin

// Check for VSYNC and reset X & Y if VSYNC is HIGH
if (vs_in)
begin
x <= 1'b0;
y <= 1'b0;
end
else
begin
// do nothing
end

end // !pc_ena

end // !global reset

end // always @clk

endmodule
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8143
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #213 on: November 07, 2019, 11:54:32 am »
Here's a trick:

----------------------------
                 if (~hde_in) begin
                     x <= 0;
                 end else begin
                      x <= x +1;
                     end
-----------------------------
OR --- your choice ---
----------------------------
                 if (hde_in) begin
                     x <= x + 1;
                 end else begin
                      x <= 0;
                     end
-----------------------------

What's happening here is while the horizontal enable is OFF, the x counter is kept at 0.
When the horizontal enable is high, the x counter counts.  This is elegant since the X position is 0 right after the display is disabled and goes to 1 right at the second active pixel while continuing to count up.

Vertical has 1 additional trick...

-------------------------------------
if (~vde_in) begin
   y <=0 ;  // vertical count=0 after the picture area is finished and stays at 0 while the vertical DE is not enabled
  end else begin
                  if (~hde_in && hde_out ) y <= y +1;  // the trick.... When the horizontal DE line ends by going low, at that 1 pixel
                                                                              // while the horizontal DE output is still high since it's delayed
                                                                              // by 1 pixel clock, the vertical counter will increment by 1.
                                                                              // Using an input  ANDED with a 1 clock delayed copy of that input is
                                                                             // is commonly used to trigger a single shot / one shot action every time
                                                                            // an input transitions from high to low, or low to high depending on your ~...
  end
------------------------------------

What going on here, is that during the first line of the vertical DE, since it was previously cleared, it starts at 0.
It wont increment until the end of the first horizontal line meaning that at the next line, the Y counter will already be at 1.

Operating like this, if you have a variable HDE and VDE window anywhere in your raster at any size, the X and Y counters will be at their next ready valid numerical state immediately after each line has ended and the frame has ended.

« Last Edit: November 07, 2019, 11:59:34 am by BrianHG »
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #214 on: November 07, 2019, 12:14:06 pm »
Operating like this, if you have a variable HDE and VDE window anywhere in your raster at any size, the X and Y counters will be at their next ready valid numerical state immediately after each line has ended and the frame has ended.

Fantastic - works beautifully and is quite small and neat, a great combination.  ;D

867972-0  867976-1

One thing I've noticed - there's more colour bars... Is the x counter incrementing properly?
« Last Edit: November 07, 2019, 12:21:35 pm by nockieboy »
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #215 on: November 07, 2019, 01:05:25 pm »
For text and tile maps id use grids that are 8 16 32 or something and round the total area to a round power of 2 number so that you can get the index by simply bit shifting and so zero logic is needed to calculate the index from X Y (Yes this wastes memory space but is very useful for smooth scrolling).

I'd be hoping that I could just ignore the least significant bits of the x and y counters to get the tile index?  Say my tiles are 8x16 (I was originally planning on 8x8, but it seems it should actually be 8x16? I might just stretch the y-axis by 2 though to keep memory use down initially).  So for example:

Code: [Select]
tile_x = x[9:4];
tile_y = y[9:5];

Surely that'd be quicker than messing around with bit shifts, or am I misunderstanding what you've written and we're talking about the same thing?
 

Offline Berni

  • Super Contributor
  • ***
  • Posts: 5031
  • Country: si
Re: FPGA VGA Controller for 8-bit computer
« Reply #216 on: November 07, 2019, 01:15:21 pm »
For text and tile maps id use grids that are 8 16 32 or something and round the total area to a round power of 2 number so that you can get the index by simply bit shifting and so zero logic is needed to calculate the index from X Y (Yes this wastes memory space but is very useful for smooth scrolling).

I'd be hoping that I could just ignore the least significant bits of the x and y counters to get the tile index?  Say my tiles are 8x16 (I was originally planning on 8x8, but it seems it should actually be 8x16? I might just stretch the y-axis by 2 though to keep memory use down initially).  So for example:

Code: [Select]
tile_x = x[9:4];
tile_y = y[9:5];

Surely that'd be quicker than messing around with bit shifts, or am I misunderstanding what you've written and we're talking about the same thing?

Yes this is bit shifting, just rewireing bits with some offset. On a FPGA such a operation is free as its done inside the signal routeing hence why its so useful.

The reason for also keeping the map size a power of 2 is so that you can build the address for the map memory by simply going
Code: [Select]
assign tilemap_addr = {tile_y,tile_x};
This means that on a 640x480 screen with 8 pixel tiles you would instead of having 80 tiles wide have 128 tiles wide by using a 7bit wide tile_x register. So you waste memory by having more tile memory than is needed to fill the screen, but it also allows the CPU to draw and update tiles outside the visible area. This is how infinite smooth scroling is implemented on most game consoles.
 
The following users thanked this post: nockieboy

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8143
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #217 on: November 07, 2019, 01:24:30 pm »
Operating like this, if you have a variable HDE and VDE window anywhere in your raster at any size, the X and Y counters will be at their next ready valid numerical state immediately after each line has ended and the frame has ended.

Fantastic - works beautifully and is quite small and neat, a great combination.  ;D

(Attachment Link)   (Attachment Link)

One thing I've noticed - there's more colour bars... Is the x counter incrementing properly?
I think you can now invert the hsync and vsync output to get the standard VGA output sync polarity.  Now with the 5v buffered outputs, your monitor should accept the signal.

As for text, you are correct, take a look at my text generator, though it might need a little touch-up to be wired in place of your video generator.  You'll also note in my code that I am ignoring the LSB on the H&V counters in my code.  This is to make each dot in my 8x8 font 2 pixels wide and 2 pixels tall.

I'll take a look at the code tonight and maybe do a little patch for you.
 
The following users thanked this post: nockieboy

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #218 on: November 07, 2019, 02:17:27 pm »
This means that on a 640x480 screen with 8 pixel tiles you would instead of having 80 tiles wide have 128 tiles wide by using a 7bit wide tile_x register. So you waste memory by having more tile memory than is needed to fill the screen, but it also allows the CPU to draw and update tiles outside the visible area. This is how infinite smooth scroling is implemented on most game consoles.

Sounds like an effective way to do it, but I might have to leave that for a while until I have an FPGA with enough RAM.   The Spartan 6 arrived today, but I'm still waiting on the cable programmer for it.  ::)

I think you can now invert the hsync and vsync output to get the standard VGA output sync polarity.  Now with the 5v buffered outputs, your monitor should accept the signal.

Okay, that's strange.  I've inverted the HSYNC and VSYNC by changing the parameters on vid_out_stencil, and I'm now getting fuzzy edges to the colour bars and screen blanking several times a second.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8143
  • Country: ca
    • LinkedIn
Re: FPGA VGA Controller for 8-bit computer
« Reply #219 on: November 07, 2019, 02:31:54 pm »
This means that on a 640x480 screen with 8 pixel tiles you would instead of having 80 tiles wide have 128 tiles wide by using a 7bit wide tile_x register. So you waste memory by having more tile memory than is needed to fill the screen, but it also allows the CPU to draw and update tiles outside the visible area. This is how infinite smooth scroling is implemented on most game consoles.

Sounds like an effective way to do it, but I might have to leave that for a while until I have an FPGA with enough RAM.   The Spartan 6 arrived today, but I'm still waiting on the cable programmer for it.  ::)

I think you can now invert the hsync and vsync output to get the standard VGA output sync polarity.  Now with the 5v buffered outputs, your monitor should accept the signal.

Okay, that's strange.  I've inverted the HSYNC and VSYNC by changing the parameters on vid_out_stencil, and I'm now getting fuzzy edges to the colour bars and screen blanking several times a second.
Your going to have to check the outputs with your scope.  It sounds like the 5v high isn't reaching or maintaining 5v.  With the shorter pulses of a positive sync, the monitor might see the signal better meaning the signal is still operating at the edge of functionality.  For now, just use a positive sync unless you want to scope your 74xxx CMOS buffer's output.

Do you have you CMOS buffer's output tied 4 in parallel like I recommended to give you a higher current drive?  If you monitor have 75ohm load on the sync signals instead of 1kohm, it also may have a high level of around 4v instead of 5v.

Is your vsync also going through the buffer the same way?
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #220 on: November 07, 2019, 03:30:23 pm »
Do you have you CMOS buffer's output tied 4 in parallel like I recommended to give you a higher current drive?  If you monitor have 75ohm load on the sync signals instead of 1kohm, it also may have a high level of around 4v instead of 5v.

Is your vsync also going through the buffer the same way?

VSYNC and HSYNC are both unchanged in that they're both going through the buffer as before.

One thing is that I'm only using one output from the buffer for HSYNC and VSYNC.  Might try the multiple-parallel outputs and see if that improves the image with inverted sync signals then!  :-/O

Oh, I'm also dabbling with trying to build a character generator and am fast realising I know nothing about how to interface a block with a memory block etc...  :o

EDIT:

Yes, that sorted it.  I've only used two outputs per sync signal for the moment, but it's working fine inverted now with no misbehaviour.  :-+
« Last Edit: November 07, 2019, 03:43:39 pm by nockieboy »
 

Offline jhpadjustable

  • Frequent Contributor
  • **
  • Posts: 295
  • Country: us
  • Salt 'n' pepper beard
Re: FPGA VGA Controller for 8-bit computer
« Reply #221 on: November 07, 2019, 03:57:13 pm »
Oh, I'm also dabbling with trying to build a character generator and am fast realising I know nothing about how to interface a block with a memory block etc...  :o
You need Verilog's memory syntax. (ETA: oops, it's not a keyword)
Code: [Select]
reg [7:0] mem [127:0];
always @ (posedge clk) begin
  if (we)
    mem[write_address] <= d;
  q <= mem[read_address]; // q doesn't get d in this clock cycle
end
Note well, that's a non-blocking assignment so adjust your time machine accordingly.

Recommended HDL coding styles handbook, p13 and on, for more examples.
« Last Edit: November 07, 2019, 04:19:07 pm by jhpadjustable »
"There are more things in heaven and earth, Arduino, than are dreamt of in your philosophy."
 
The following users thanked this post: nockieboy

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #222 on: November 07, 2019, 03:58:14 pm »
Okay, so for a character generator, this is what I'm thinking:

Replace the vid_pattern_generator with a vid_char_generator.  It will share all the inputs and outputs of the vid_pattern_generator, but with some extras:

  • An 8-bit data input from video memory.
  • A xx-bit address to video memory.
  • A CE (chip enable) to video memory.
  • An xx-bit address output to character memory.
  • An 1-bit data input from character memory.
  • A CE to character memory.

This will allow the vid_char_generator to compute the current tile address from the internally-generated x & y coordinates, send that address to the video memory and receive a character index back.

Then it will compute a pixel address for that character in the character memory, based on the character index and current x & y coordinates, which will return a single bit of information based on whether the current pixel is background- or foreground-coloured depending on the character in that tile....  Does that make sense?  Am I over-complicating it, because there seems like a lot going here?  :scared:

I would load a font file (OSD_FONT.MIF) and LUT (osd_mem.mif) into the character memory and, initially at least, the video memory will be garbage - but populating that with useful data is a few steps away yet.

I'm a little confused looking at osd_generator.v, but then I don't have a good map in my head of what should be happening here anyway, so understanding it is proving difficult.
 

Offline jhpadjustable

  • Frequent Contributor
  • **
  • Posts: 295
  • Country: us
  • Salt 'n' pepper beard
Re: FPGA VGA Controller for 8-bit computer
« Reply #223 on: November 07, 2019, 04:48:51 pm »
Okay, so for a character generator, this is what I'm thinking:

Replace the vid_pattern_generator with a vid_char_generator.  It will share all the inputs and outputs of the vid_pattern_generator, but with some extras:

  • An 8-bit data input from video memory.
  • A xx-bit address to video memory.
  • A CE (chip enable) to video memory.
  • An xx-bit address output to character memory.
  • An 1-bit data input from character memory.
  • A CE to character memory.
CE is only relevant in discrete implementations where the memory needs to be kept from driving a shared bus. Here, memory is always chip-enabled and you just have write-enables and maybe read-enables. Or, if you prefer to have the tool infer memory from generic HDL, you only have write-enables and are always reading every clock. If you want to hold read data, add your own register.

Quote
This will allow the vid_char_generator to compute the current tile address from the internally-generated x & y coordinates, send that address to the video memory and receive a character index back.

Then it will compute a pixel address for that character in the character memory, based on the character index and current x & y coordinates, which will return a single bit of information based on whether the current pixel is background- or foreground-coloured depending on the character in that tile....  Does that make sense?  Am I over-complicating it, because there seems like a lot going here?  :scared:
No, that's about the size of it. Now you see why I was encouraging you to do a plain old framebuffer to start :)

Block RAMs are commonly used in synchronous mode, which means you'll have a one-cycle delay through each memory. You can get ahead of it such as by reading the next character index or bitmap ahead of time and buffering the data, or you can push other business back such as by applying a two-clock delay to the display enable to the rest of the display system. If you want to give yourself something other than garbage or nothing to stare at, use an initial task to preload a character bitmap or two and to fill the text memory with something. Writing this from sleep-deprived memory:
Code: [Select]
initial begin
  integer i;
  for (i=0; i<255; i++) begin
    charmem[0+i*8] = 8'b00010000 ^ {i[0]{8}}; // invert top half according to bit 0 of tile index
    charmem[1+i*8] = 8'b00101000 ^ {i[0]{8}}; // to give you an easy way to see if you're
    charmem[2+i*8] = 8'b01000100 ^ {i[0]{8}}; // actually reading the tile map
    charmem[3+i*8] = 8'b10000010 ^ {i[0]{8}};
    charmem[4+i*8] = 8'b11111110 ^ {i[1]{8}}; // invert bottom half according to bit 1 of tile index
    charmem[5+i*8] = 8'b10000010 ^ {i[1]{8}};
    charmem[6+i*8] = 8'b10000010 ^ {i[1]{8}};
    charmem[7+i*8] = 8'b00000000 ^ {i[1]{8}};
  end
end

initial begin
  integer i;
  integer j;
  for(i=0;i<80;i=i+1)
    for(j=0;j<25;j=j+1)
      tilemap[i] = i + j;
end

I'm using byte-wide memory here because it's easier to illustrate. You can select just one bit of the read port for output by [x[2:0]] bit-selecting the output wire or reg (but not the memory directly, iirc).
« Last Edit: November 07, 2019, 04:51:09 pm by jhpadjustable »
"There are more things in heaven and earth, Arduino, than are dreamt of in your philosophy."
 

Offline nockieboyTopic starter

  • Super Contributor
  • ***
  • Posts: 1812
  • Country: england
Re: FPGA VGA Controller for 8-bit computer
« Reply #224 on: November 07, 2019, 10:33:17 pm »
CE is only relevant in discrete implementations where the memory needs to be kept from driving a shared bus. Here, memory is always chip-enabled and you just have write-enables and maybe read-enables. Or, if you prefer to have the tool infer memory from generic HDL, you only have write-enables and are always reading every clock. If you want to hold read data, add your own register.

Ah yes, of course - makes sense.

Quote
This will allow the vid_char_generator to compute the current tile address from the internally-generated x & y coordinates, send that address to the video memory and receive a character index back.

Then it will compute a pixel address for that character in the character memory, based on the character index and current x & y coordinates, which will return a single bit of information based on whether the current pixel is background- or foreground-coloured depending on the character in that tile....  Does that make sense?  Am I over-complicating it, because there seems like a lot going here?  :scared:
No, that's about the size of it. Now you see why I was encouraging you to do a plain old framebuffer to start :)

You're not kidding!  ???

I think I understand the concept now though - I just need to get my head around making the right connections in Quartus and setting the memory up properly.  There's a template for new memory objects in the New.. menu, but then on a website I found some code that I've re-purposed into this:

Code: [Select]
module ROM_font (

input [11:0] address,
output [7:0] data,
input wire read_en

);

reg [7:0] mem [0:8192];

assign data = (read_en) ? mem[address] : 8'b0;

initial begin
$readmemb("OSD_FONT.MIF", mem);
end

endmodule

This should read a file in the project (borrowed from BrianHG's altera_osd_20kbit files) which contains the character definitions into memory.  Except I don't think it's loading it into memory - I think it's loading into hardware, so it's creating a 'ROM' in the sense that it can't be written to, but I get the feeling it's an extremely wasteful way of doing it.   Please correct me if I'm wrong.  Isn't there a megafunction or wizard of some kind I need to use to create a RAM/ROM area, or does Quartus do this for me by inferring my intentions from the code?

If you want to give yourself something other than garbage or nothing to stare at, use an initial task to preload a character bitmap or two and to fill the text memory with something. Writing this from sleep-deprived memory:

Yes, I could read a file into memory like with the font file above. I thought the RAM would contain random values, so I wouldn't have to load anything initially to see something come out on the screen... is that not the case then?  Is RAM pre-initialised to zeros or something?

I'm using byte-wide memory here because it's easier to illustrate. You can select just one bit of the read port for output by [x[2:0]] bit-selecting the output wire or reg (but not the memory directly, iirc).

Would it be better to return a byte (an entire row from the selected character) and bit-select from that returned value, or configure the 'ROM' to just return the bit in question pointed to by x & y?  What's the difference in terms of the 'ROM' code?  Is it as simple as:

Code: [Select]
module ROM_font {
    output [7:0] data,
    ...

... to return a byte, or:

Code: [Select]
module ROM_font {
    output data,
    ...

... to return a bit?
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf