// generates 1024x768 (actually 512x768) @ 60Hz, from a 32.5MHz input clock module videoTimer( input clk, input clk_en, input [1:0] busCycle, input vid_alt, output [21:0] videoAddr, output reg hsync, output reg vsync, output _hblank, output _vblank, output loadPixels ); // timing data from http://tinyvga.com/vga-timing/1024x768@60Hz localparam kVisibleWidth = 128, // (1024/2)/4 kTotalWidth = 168, // (1344/2)/4 kVisibleHeightStart = 42, kVisibleHeightEnd = 725, kTotalHeight = 806, kHsyncStart = 131, // (1048/2)/4 kHsyncEnd = 147, // (1184/2)/4-1 kVsyncStart = 771, kVsyncEnd = 776, kPixelLatency = 1; // number of clk8 cycles from xpos==0 to when pixel data actually exits the video shift register // use screen buffer address for a 4MB RAM layout-- it will wrap // around to the proper address for 1MB, 512K, and 128K layouts localparam kScreenBufferBase = 22'h3FA700; localparam startAddr = kVisibleHeightStart/2 * kVisibleWidth/2; reg [7:0] xpos; reg [9:0] ypos; wire endline = (xpos == kTotalWidth-1); always @(posedge clk) begin if (clk_en) begin if (endline) xpos <= 0; else if (xpos == 0 && busCycle != 0) // hold xpos at 0, until xpos and busCycle are in phase xpos <= 0; else xpos <= xpos + 1'b1; end end always @(posedge clk) begin if (clk_en) begin if (endline) begin if (ypos == kTotalHeight-1) ypos <= 0; else ypos <= ypos + 1'b1; end end end always @(posedge clk) begin if (clk_en) begin hsync <= ~(xpos >= kHsyncStart+kPixelLatency && xpos <= kHsyncEnd+kPixelLatency); vsync <= ~(ypos >= kVsyncStart && ypos <= kVsyncEnd); end end assign _hblank = ~(xpos >= kVisibleWidth+kPixelLatency); assign _vblank = ~(ypos < kVisibleHeightStart || ypos > kVisibleHeightEnd); // The 0,0 address actually starts below kScreenBufferBase, because the Mac screen buffer is // not displayed beginning at 0,0, but at 0,kVisibleHeightStart. // kVisibleHeightStart divided by 2 to account for vertical pixel doubling. // kVisibleWidth divided by 2 because it's the 8MHz visible width times 4 to get actual number of pixels, // then divided by 8 bits per byte assign videoAddr = kScreenBufferBase - (vid_alt ? 16'h0 : 16'h8000) - startAddr[21:0] + { ypos[9:1], xpos[6:2], 1'b0 }; assign loadPixels = _vblank == 1'b1 && _hblank == 1'b1 && busCycle == 2'b00; endmodule