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)
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
40 input wire clk25MHz
// PLL input clock
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
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
82 parameter FBW
= 1 << FBWL2
;
84 parameter BUFL2
= 6; // must be at least 1
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
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
113 wire [9:0] y_max
= M8
; // 525
115 wire [9:0] y_max
= M8
; // 525
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;
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];
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
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
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
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;
193 // $display("%5d VGA: Ok, FIFO FULL, stop reading", $time);
194 fb_transfer_request
<= 0;
199 if (x
< x_blank
&& y
< y_blank ||
200 x
== x_max
-1 && y
== y_max
-1) // GROSS HACK
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
;
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. */
235 $display("%05d VGA: Simultaneous consuming and requesting!", $time);
241 pixels32
<= {pixels32
[30:0],1'h1
};
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;
261 left_to_request
<= FRAMEBUFFER_WORDS
- 2;
265 /* Advance the (x,y) pointer. */
267 y
<= (y
== y_max
-1) ?
0 : y
+1;
268 x
<= (x
== x_max
-1) ?
0 : x
+1;
272 initial for (i
= 0; i
< (1 << BUFL2
) - 1; i
= i
+ 1) pixel_buffer
[i
] = 0;
279 // Lancelot VGA interface
291 reg [31:0] fb_address0
; // Top of FB
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
,
311 always #20 clk25MHz
= ~clk25MHz
;
313 #0 clk25MHz
= 0; rst
= 1; holdit
= 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);
321 `endif // `ifdef SIMULATE_VGA