initial
[fpgammix.git] / rtl / vga.v
blobfe107f68f23c4604b3ca4eb0c0f6ac511754a815
1 /*
2 * TODO: for starters, this could be done in much much less logic with
3 * judicious use of the counter underflow trick, eg. instead of
5 * c <= (c == K-1) ? 0 : c + 1;
7 * allocate one more bit to c and use
9 * c <= (c[MSB] ? K - 2 : c - 1);
11 * TODO: simplify, notably the whole FIFO mess!
13 * TODO: support 800x600. In fact, make all important parameters
14 * (except frequency?) settable at run-time. Adjustable pixel width
15 * may be too expensive though.
17 * TODO: use bursting (once supported)
22 VGA 640x480 core
23 Tommy Thorn
25 This module assumes a 25Mhz clock and implements VESA VGA output
26 in the 640 x 480 resolution, based on the XFree86 modeline
28 "640x480" 25.175 640 664 760 800 480 491 493 525
30 640 x 480 = 307200 pixels
31 At 1 bit pr pixel = 38400/0x9600 bytes, or 9600/0x2580 tetras
35 `timescale 1ns/10ps
37 `define ONE_BPP 1
39 module vga(// Clock
40 input wire clk25MHz // PLL input clock
41 ,input wire reset
43 // Lancelot VGA interface
44 ,output wire [7:0] vga_r
45 ,output wire [7:0] vga_g
46 ,output wire [7:0] vga_b
47 ,output wire vga_m1
48 ,output wire vga_m2
49 ,output wire vga_sync_n
50 ,output wire vga_sync_t
51 ,output wire vga_blank_n
52 ,output reg vga_hs = 0
53 ,output reg vga_vs = 0
55 ,input wire [31:0] fb_address0 // Top of FB
57 // Framebuffer master port
58 ,output reg fb_transfer_request = 0
59 ,output reg [31:0] fb_address
60 ,output wire fb_wren // Don't laugh, it may happen in future
61 ,output wire [31:0] fb_wrdata
62 ,output wire [ 3:0] fb_wrmask
63 ,input wire fb_wait_request
64 ,input wire fb_read_data_valid
65 ,input wire [31:0] fb_read_data
68 parameter debug = 0;
70 parameter M1 = 640;
71 parameter M2 = 664;
72 parameter M3 = 760;
73 parameter M4 = 800;
75 parameter M5 = 480;
76 parameter M6 = 491;
77 parameter M7 = 493;
78 parameter M8 = 525;
80 parameter BPP = 24;
81 parameter FBWL2 = 5;
82 parameter FBW = 1 << FBWL2;
84 parameter BUFL2 = 6; // must be at least 1
86 // VGA interface
87 assign vga_sync_t = 0; // No sync-on-RGB
88 assign vga_sync_n = 1;
89 assign vga_m1 = 0; // Color space configuration: GBR
90 assign vga_m2 = 0;
92 assign fb_wren = 0;
93 assign fb_wrdata = 0;
94 assign fb_wrmask = 0;
97 * The blanking facility didn't work right for me and it seems I
98 * don't really need it.
100 assign vga_blank_n = 1;
101 wire hsync_neg = 1; // Negative hsync
102 wire vsync_neg = 1; // Positive vsync
104 wire [9:0] x_blank = M1; // 640
105 wire [9:0] x_sync = M2; // 664
106 wire [9:0] x_back = M3; // 760
107 wire [9:0] x_max = M4; // 800
109 wire [9:0] y_blank = M5; // 480
110 wire [9:0] y_sync = M6; // 491
111 wire [9:0] y_back = M7; // 493
112 `ifdef SIMULATE_MAIN
113 wire [9:0] y_max = M8; // 525
114 `else
115 wire [9:0] y_max = M8; // 525
116 `endif
118 reg [9:0] x = M4-5; // Diverging from FPGA here to hit issues earlier.
119 reg [9:0] y = M8-4; // Diverging from FPGA here to hit issues earlier.
120 reg [9:0] frame_ctr = 0;
122 /* PIXEL */
123 reg [31:0] pixels32 = 0;
125 reg [23:0] vga_pixel = 0;
126 assign vga_r = vga_pixel[23:16];
127 assign vga_g = vga_pixel[15: 8];
128 assign vga_b = vga_pixel[ 7: 0];
130 /* FIFO */
131 reg [ 31:0] pixel_buffer[0:(1 << BUFL2) - 1]; // Initialised at the end.
132 reg [ 31:0] pixel_buffer_addr = 0;
133 reg [BUFL2-1:0] pixel_buffer_rp = 0, pixel_buffer_wp = 0;
134 wire [BUFL2-1:0] pixel_buffer_rp_plus1 = pixel_buffer_rp + 1;
135 wire [BUFL2-1:0] pixel_buffer_wp_plus1 = pixel_buffer_wp + 1;
136 wire [BUFL2-1:0] pixel_buffer_wp_plus2 = pixel_buffer_wp + 2;
137 wire [BUFL2-1:0] pixel_buffer_wp_plus3 = pixel_buffer_wp + 3;
138 reg [BUFL2-1:0] free = (1 << BUFL2) - 1;
140 parameter FRAMEBUFFER_WORDS = 640 * 480 / 32; // 9,600
141 parameter LEFT_TO_REQUEST_MSB = 14; // 32,768 = 2 * 16,384 > 2 * 9,600
142 reg [LEFT_TO_REQUEST_MSB:0] left_to_request = FRAMEBUFFER_WORDS - 2;
145 FIFO workings. Each clock cycle these events can occur (simultaneously):
147 1. If the last pixel in a word was displayed, one datum was
148 consumed, advancing the pixel_buffer_rp (no check for empty fifo).
149 2. If a read is ready, one datum can be produced advancing the
150 pixel_buffer_wp
151 3. If the fifo isn't full, a read will be scheduled
153 Because of the latencies involved, the controller will generally
154 issue a burst of three reads before the first data tickles in,
155 thus we keep a buffer of three free slots in the fifo.
158 always @(posedge clk25MHz) begin
159 if (reset)
160 fb_transfer_request <= 0;
162 vga_hs <= (x_sync <= x && x < x_back) ^ vsync_neg;
163 vga_vs <= (y_sync <= y && y < y_back) ^ hsync_neg;
165 // $display("%05d VGA: rp %d wp %d free %d",
166 // $time, pixel_buffer_rp, pixel_buffer_wp, free);
168 if (fb_read_data_valid) begin
169 if (debug)$display("%05d VGA: FIFO got %x at pos %d", $time, fb_read_data, pixel_buffer_wp);
170 pixel_buffer[pixel_buffer_wp] <= fb_read_data;
171 pixel_buffer_wp <= pixel_buffer_wp_plus1;
174 if (fb_transfer_request & fb_wait_request)
175 if(debug)$display("%05d VGA: MEMORY BUSY", $time);
177 if (~fb_transfer_request | ~fb_wait_request) begin
179 * Only issue more requests if it won't overflow the FIFO.
182 if (free != 0 && ~left_to_request[LEFT_TO_REQUEST_MSB]) begin
183 if (debug)
184 $display("%05d VGA: SCHEDULE READ from %x (fifo %d free, %d left to fetch)",
185 $time, pixel_buffer_addr, free, left_to_request + 1);
187 left_to_request <= left_to_request - 1;
188 fb_address <= pixel_buffer_addr;
189 fb_transfer_request <= 1;
190 pixel_buffer_addr <= pixel_buffer_addr + 4;
191 free <= free - 1;
192 end else begin
193 // $display("%5d VGA: Ok, FIFO FULL, stop reading", $time);
194 fb_transfer_request <= 0;
198 /* Get the pixel */
199 if (x < x_blank && y < y_blank ||
200 x == x_max-1 && y == y_max-1) // GROSS HACK
201 begin
203 * Grab one bit from the tiny pixel buffer and expand it to
204 * 24 for black or white.
206 /* vga_pixel <= pixels32[31] ? 24'h008000 :
207 (x == 0) ? 24'h0000FF :
208 (y == 0) ? 24'h00FF00 :
209 (x == 639) ? 24'hFF0000 :
210 (y == 479) ? 24'h00FFFF :
211 24'h000000 ; // {24{pixels32[31]}};*/
213 vga_pixel <= pixels32[31] ? 24'h008000 : 24'h000000;
215 if (debug && x == 0 && y == 0)
216 $display("%5d VGA: first word display: %x", $time, pixels32);
218 if (x[4:0] == 31) begin
220 * We just consumed the last pixel in the tiny pixel
221 * buffer, so refill it from the pixel_buffer FIFO.
223 * Notice there's no underflow check as this can't
224 * happen (underflow would be catastrophic and point to
225 * either lack of memory bandwidth or too small a FIFO).
227 pixels32 <= pixel_buffer[pixel_buffer_rp];
228 pixel_buffer_rp <= pixel_buffer_rp_plus1;
229 if (debug)
230 $display("%05d VGA: just read in %x", $time, pixel_buffer[pixel_buffer_rp]);
232 if (fb_transfer_request & ~fb_wait_request & free != 0) begin
233 /* We issued another read, so free remains unchanged. */
234 if (debug)
235 $display("%05d VGA: Simultaneous consuming and requesting!", $time);
236 free <= free;
237 end else begin
238 free <= free+1;
240 end else begin
241 pixels32 <= {pixels32[30:0],1'h1};
243 end else begin
244 vga_pixel <= 24'h0;
246 if (y == y_max-2 && x == 0) begin
247 if (debug) $display("%05d VGA: restart fetching", $time);
250 * We just displayed last visible pixel in this frame.
251 * Resynchronize, clear the fifo, and start fetching from fb_address0.
253 // frame_ctr <= frame_ctr + 1;
255 fb_address <= 0; // XXX Not needed?
256 fb_transfer_request <= 0; // XXX Not needed?
257 pixel_buffer_addr <= fb_address0;
258 pixel_buffer_wp <= 0;
259 pixel_buffer_rp <= 0;
260 free <= ~0;
261 left_to_request <= FRAMEBUFFER_WORDS - 2;
265 /* Advance the (x,y) pointer. */
266 if (x == x_max-1)
267 y <= (y == y_max-1) ? 0 : y+1;
268 x <= (x == x_max-1) ? 0 : x+1;
271 reg [31:0] i;
272 initial for (i = 0; i < (1 << BUFL2) - 1; i = i + 1) pixel_buffer[i] = 0;
273 endmodule
275 `ifdef SIMULATE_VGA
276 module tester();
277 reg clk25MHz, rst;
279 // Lancelot VGA interface
280 wire [7:0] vga_r;
281 wire [7:0] vga_g;
282 wire [7:0] vga_b;
283 wire vga_m1;
284 wire vga_m2;
285 wire vga_sync_n;
286 wire vga_sync_t;
287 wire vga_blank_n;
288 wire vga_hs;
289 wire vga_vs;
291 reg [31:0] fb_address0; // Top of FB
293 // Memory port
294 wire `REQ fb_req;
295 wire `RES fb_res;
297 reg holdit;
299 reg [31:0] addr;
301 assign fb_busy = holdit & (fb_rden | fb_req`W);
302 assign fb_res`RD = addr;
304 always @(posedge clk25MHz) addr <= fb_req`A;
306 vga vga(clk25MHz, rst, vga_r, vga_g, vga_b,
307 vga_m1, vga_m2, vga_sync_n, vga_sync_t, vga_blank_n, vga_hs, vga_vs,
308 'h9000_0000,
309 fb_req, fb_res);
311 always #20 clk25MHz = ~clk25MHz;
312 initial begin
313 #0 clk25MHz = 0; rst = 1; holdit = 0;
314 #40 rst = 0;
316 $monitor(clk25MHz, rst, vga_r,vga_g,vga_b);
317 #4000 holdit = 1; $display("%05d VGA: HOLDIT", $time);
318 #110000 holdit = 0; $display("%05d VGA: ~HOLDIT", $time);
320 endmodule
321 `endif // `ifdef SIMULATE_VGA