Excellent reading there. I think it might be desirable to do a power-up test from the lowest resolution to the highest resolution to test a monitor's functionality, then pop back to 640x480x60Hz and ask the user if all resolutions were visible, each resolution having a number in the upper left. An interactive self-test.
This is the code I have for the 2 main modules that display 1280x1024. The first is a modified version of your code, running on one clock:
Code:
/*
* vga.v
*
* VGA timing generator
*
* This module only generates the VGA sync signals and control signals
* for the actual video generator. Add a suitable video generator to
* create real outputs.
*
* +-------+ +-----------------+
* | vga.v | ----> | video generator | ----> RGB outputs
* +---+---+ +-----------------+
* |
* `-----------------------------------> Hsync/Vsync ouputs
*
* The hstart/vstart and blank signals are short enable pulses with a
* width equal to the pixel clock (pclk). They are intended to drive
* a state machine in the video generator. All outputs are fully
* registered. The 'hstart' pulse signals that next pclk will shift out
* the leftmost pixel of each line. The 'hblank' pulse signals that the
* next pclk must be blanked. Likewise, the 'vstart' and 'vblank' enables
* do the same thing for vertical blanking, but with less precise timing.
*
* So, use the vstart/vblank to update state machine, and use hstart/hblank
* for pixel timing.
*
* The hsync/vsync signals are delayed by 1 pclk period with
* respect to the vstart/hstart/blank signals. It is intended that
* the video generator will introduce a 1 pclk pipeline delay so that
* the signals will align again. If the video generator requires a
* larger delay, the hsync and vsync signals need to be delayed as well.
*
* The design has been optimised for speed (>200 MHz on Spartan-3
* with ISE 9.2 and one-hot FSM encoding selected).
*
* (C) Arlet Ottens, <arlet@c-scape.nl>
*/
module vga( input clk108,
output reg hsync, // horizontal sync
output reg vsync, // vertical sync
output reg hstart, // line start on next clock
output reg vstart, // field starts on next clock
output reg hblank = 1, // hblank on next clock
output reg vblank = 0 // vblank on next clock
);
// timing parameters
parameter
HFRONT = 53, // front porch (+right border)
HVIDEO = 1280, // active video area
HSYNC = 112, // hsync
HBACK = 243; // back porch (+left border)
parameter
VFRONT = 10, // front porch (+bottom border)
VVIDEO = 1024, // active vide area
VSYNC = 3, // vsync
VBACK = 29; // back porch (+top border)
// states (used for both H and V state machines)
parameter
VIDEO = 2'd0,
FRONT = 2'd1,
SYNC = 2'd2,
BACK = 2'd3;
// horizontal counters and state
reg [11:0] hcount = 0; // down counter for horizontal state
reg [10:0] hload; // next load value
reg [1:0] hstate = FRONT; // horizontal state
wire hcount_done = hcount[11]; // done when count < 0
// vertical counters and state
reg [10:0] vcount = 0; // down counter for vertical state
reg [9:0] vload; // next load value
reg [1:0] vstate = FRONT; // horizontal state
wire vcount_done = vcount[10]; // done when count < 0
// enable signal for V state machine
wire venable = (hstate == FRONT) & hcount_done;
// preload next timer value depending on state
// subtract 2 from timing value to compensate for pipeline delays
// This should be synthesized as a small LUT ROM, so it's quite
// efficient.
always @(posedge clk108)
hload <= (hstate == BACK) ? HVIDEO - 2:
(hstate == VIDEO) ? HFRONT - 2:
(hstate == FRONT) ? HSYNC - 2:
HBACK - 2;
// do the same for vertical
always @(posedge clk108)
vload <= (vstate == BACK) ? VVIDEO - 2:
(vstate == VIDEO) ? VFRONT - 2:
(vstate == FRONT) ? VSYNC - 2:
VBACK - 2;
// adjust down counter. Reload when it's done.
always @(posedge clk108)
if( hcount_done )
hcount <= { 1'b0, hload };
else
hcount <= hcount - 1;
// when down counter is done, go to next state
always @(posedge clk108)
if( hcount_done )
case( hstate )
VIDEO : hstate <= FRONT;
FRONT : hstate <= SYNC;
SYNC : hstate <= BACK;
BACK : hstate <= VIDEO;
endcase
// start of active line
always @(posedge clk108)
hstart <= (hstate == BACK) & hcount_done & ~vblank;
// vstart pulse to signal end of vertical blanking
always @(posedge clk108)
vstart <= (vstate == BACK) & venable & vcount_done;
// adjust down counter. Reload when it's done.
always @(posedge clk108)
if( venable ) begin
if( vcount_done )
vcount <= { 1'b0, vload };
else
vcount <= vcount - 1;
end
// when down counter is done, go to next state
always @(posedge clk108)
if( venable & vcount_done )
case( vstate )
VIDEO : vstate <= FRONT;
FRONT : vstate <= SYNC;
SYNC : vstate <= BACK;
BACK : vstate <= VIDEO;
endcase
// update registered outputs
always @(posedge clk108) begin
hsync <= hstate == SYNC;
vsync <= vstate == SYNC;
hblank <= (vstate == VIDEO) & (hstate == VIDEO) & hcount_done;
vblank <= vstate != VIDEO;
end
endmodule
Code:
module SRAMif( output reg [20:0] SRAddr = 0,
output reg WEn = 1,
output reg SRCS = 1,
input [15:0] SRD,
output reg [4:0] Red_data,
output reg [5:0] Green_data,
output reg [4:0] Blue_data,
output reg DACBLANKn = 1,
input clk108,
input hstart,
input vstart,
input hblank
);
reg [15:0] SRDtemp;
reg countflag;
always @(posedge clk108)
if ( hstart )
countflag <= 1; //countflag active in display area
else if ( hblank )
countflag <= 0;
always @(posedge clk108)
if ( vstart ) //reset address counter at top left of screen
SRAddr <= 0;
else if ( countflag )
SRAddr <= SRAddr + 1; //increment RAM address counter
always @(posedge clk108)
if ( countflag )
begin
SRDtemp <= SRD; //latch incoming data
Red_data <= hblank ? 0 : SRDtemp [15:11]; //outgoing data to videoDAC
Green_data <= hblank ? 0 : SRDtemp [10:5];
Blue_data <= hblank ? 0 : SRDtemp [4:0];
end
endmodule
EDIT: I'm thinking my code could be further compacted (untested):
Code:
module SRAMif( output reg [20:0] SRAddr = 0,
output reg WEn = 1,
output reg SRCS = 1,
input [15:0] SRD,
output reg [4:0] Red_data,
output reg [5:0] Green_data,
output reg [4:0] Blue_data,
output reg DACBLANKn = 1,
input clk108,
input hstart,
input vstart,
input hblank
);
reg [15:0] SRDtemp;
reg countflag;
always @(posedge clk108)
if ( hstart )
countflag <= 1; //countflag active in display area
else if ( hblank )
countflag <= 0;
always @(posedge clk108)
if ( vstart ) //reset address counter at top left of screen
SRAddr <= 0;
else if ( countflag )
begin
SRAddr <= SRAddr + 1; //increment RAM address counter
SRDtemp <= SRD; //latch incoming data
Red_data <= hblank ? 0 : SRDtemp [15:11]; //outgoing data to videoDAC
Green_data <= hblank ? 0 : SRDtemp [10:5];
Blue_data <= hblank ? 0 : SRDtemp [4:0];
end
endmodule
EDIT (10/5/12): Tonight, I tested this last bit of code. I also compared the resource usage numbers in the ISE summary report (short of looking at the RTL schematic) after synthesis and it is the exact same as the previous bit of code... My goal when learning Verilog is to use the least amount of characters/typing as possible. I already skim across my 65Org16.b code and see alot I will change and update/test.