Pong game

FPGA projects on this site, or abroad

Pong game

Postby kdkiser » Fri Feb 26, 2010 2:52 am

This is my first post!

I'm fairly new to FPGAs and I wanted to try conquering VGA output. There wasn't a whole lot of easy to understand information out on the web but the Pong tutorial here was great! I played around with the code on a Avnet Spartan 3A demo board, a cheap $50 board but it woks. Soldered together a VGA port with three 220 Ohm and two 10 Ohm resistors and got the video working. In hardware at least. Set up a DCM so my 16 MHz clock generated 25 MHz vga pixle clock and set up all the ports.

I learned to program long before I picked up my demo board and am used to working with code a lot differently then in HDL, as such I tried to make the code from the Pong game give me my X-pos and Y-pos coordinates. I failed, lots. I did a lot of reading and finally found a decent waveform diagram[2] to go with the VGA timing specs I found. I tried writing code from scratch and failed miserably, and went to modifying the code from the Pong tutorial. Through my modification I found that it didn't stick to the VGA specs as much as it could and I made the following modifications...

Our input clock is at 25MHz, which gives us 40nS clock period. According to the VGA timing specs[1] I found, the timing would be:
Horz. Frame - 31.77uS -> 794.25 clock cycles per frame
Vert. Frame - 16.68mS -> 525.19 cycles per frame
The CounterX is a ten bit counter with 1024 values, which is perfect because we need 794, but CounterY is a nine bit counter with only 512 values. 512 values does not allow us to reach all 525 cycles per frame. To correct this I made the following alterations:
Code: Select all
reg [9:0] CounterY;
wire CounterXmaxed = (CounterX==794);
wire CounterYmaxed = (CounterY==525);

   always @(posedge clk) begin
      if(CounterXmaxed)
         if( CounterYmaxed )
            CounterY <= 0;
         else
            CounterY <= CounterY + 1;
      
      end // always @(posedge clk)

Next, I made the V. Sync and H. Sync signals exact as I possibly could:
Code: Select all
   always @(posedge clk) begin
      //vga_HS <= (CounterX[9:4]==6'h00); // change this value to move the display horizontally
      //vga_VS <= (CounterY==500); // change this value to move the display vertically
      
      if( CounterY >= 9'd0 & CounterY < 9'd2 ) begin
         vga_VS <= 1;
         end // if( CounterY ... )
      else begin
         vga_VS <= 0;
         end // if( CounterY ... ) else
      
      if( CounterX >= 10'd24 & CounterX < 10'd117 ) begin
         vga_HS <= 1;
         end // if( CounterX ... )
      else begin
         vga_HS <= 0;
         end // if( CounterX ... ) else
         
      end // always @(posedge clk)


When drawing the border around the screen, you would use the following code:
Code: Select all
   wire videoActive = ( ( CounterX > 10'd163 ) & ( CounterY > 33 & CounterY < 514 ) );
   wire borderX = ( CounterX >= 165 & CounterX < 172 ) || ( CounterX > 786 & CounterX <= 793 );
   wire borderY = ( CounterY >= 34 & CounterY < 41 ) || ( CounterY > 505 & CounterY <= 512 );

   assign vga_r = videoActive & (borderX || borderY);
   assign vga_g = videoActive & (borderX || borderY);
   assign vga_b = videoActive & (borderX || borderY);


I drew out the waveforms and set my timing to:
    Vertical:

      0: Sync active low
      2: Sync is inactive
      2-33: Front porch
      34-513: Video active
      514-524: Back porch
    Horizontal:
      0-23: Back porch
      24-116: Sync active low
      117-163: Front porch
      164-793: Video active

Tell me what you think! Did I manage to get closer to the actual VGA timing specs?

[1] http://www.epanorama.net/documents/pc/vga_timing.html
[2] http://www.mil.ufl.edu/4712/docs/vga_figures.pdf

Complete code:
Code: Select all
module core(
    input clock,        /* C10 */ /* 16 MHz */
    input reset_button, /* K3 */
    input enable,       /* H5 */
    output vga_r,       /* G1 */
    output vga_g,       /* K1 */
    output vga_b,       /* M1 */
    output vga_h_sync,  /* N1 */
    output vga_v_sync,  /* N2 */
    output [3:0] leds   /* B15, C15, C16, D14 */
    );

   wire clk;
   wire vga_reset = reset_button;
   wire vga_locked;

   vga_clock_gen vga_dcm( .CLKIN_IN( clock ),
      .RST_IN( vga_reset ),
      .CLKFX_OUT( clk ),
      .CLKIN_IBUFG_OUT(  ),
      .CLK0_OUT( ),
      .LOCKED_OUT( vga_locked ));
   
   
   reg [9:0] CounterX;
   reg [9:0] CounterY;
   
   wire CounterXmaxed = (CounterX==794);
   wire CounterYmaxed = (CounterY==525);
   
   wire videoActive = ( ( CounterX > 10'd163 ) & ( CounterY > 33 & CounterY < 514 ) );
   
   reg vga_HS, vga_VS;
   
   initial begin
      CounterX = 0;
      CounterY = 0;
      vga_HS = 0;
      vga_VS = 0;
      
      end
   
   always @(posedge clk)
      if(CounterXmaxed)
         CounterX <= 0;
      else
         if( ~enable ) CounterX <= CounterX + 1;

   always @(posedge clk) begin
      if(CounterXmaxed)
         if( CounterYmaxed )
            CounterY <= 0;
         else
            CounterY <= CounterY + 1;
   
      
      end // always @(posedge clk)
      
   always @(posedge clk) begin
      //vga_HS <= (CounterX[9:4]==6'h00); // change this value to move the display horizontally
      //vga_VS <= (CounterY==500); // change this value to move the display vertically
      
      if( CounterY >= 9'd0 & CounterY < 9'd2 ) begin
         vga_VS <= 1;
         end // if( CounterY ... )
      else begin
         vga_VS <= 0;
         end // if( CounterY ... ) else
      
      if( CounterX >= 10'd24 & CounterX < 10'd117 ) begin
         vga_HS <= 1;
         end // if( CounterX ... )
      else begin
         vga_HS <= 0;
         end // if( CounterX ... ) else
         
      end // always @(posedge clk)

   //wire border = (CounterX[9:3]==0) || (CounterX[9:3]==79) || (CounterY[8:3]==0) || (CounterY[8:3]==59);
   wire borderX = ( CounterX >= 163 & CounterX < 170 ) || ( CounterX > 788 & CounterX <= 795 );
   wire borderY = ( CounterY >= 34 & CounterY < 41 ) || ( CounterY > 506 & CounterY <= 513 );

   assign vga_h_sync = ~vga_HS;
   assign vga_v_sync = ~vga_VS;
   
   assign vga_r = videoActive & (borderX || borderY);
   assign vga_g = videoActive & (borderX || borderY);
   assign vga_b = videoActive & (borderX || borderY);
   
   assign leds[3:0] = { vga_locked, CounterX[9:7] };

endmodule


edit: I corrected by +-2 pixles in the complete code.
Ken
kdkiser
 
Posts: 1
Joined: Fri Feb 26, 2010 1:17 am
Location: Cibolo, Tx

Return to General projects