massive overhaul of fpga code
[fleet.git] / src / edu / berkeley / fleet / fpga / mem / wb_vga.v
1 /*
2  * WISHBONE VGA framebuffer
3  * Copyright (C) 2008 Sebastien Bourdeauducq - http://lekernel.net
4  * This file is part of Milkymist.
5  *
6  * Milkymist is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Library General Public License as published
8  * by the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA.
20  */
21
22 module wb_vga(
23         /* Clock and Reset signals are shared between the two buses */
24         input wb_clk_i,
25         input wb_rst_i,
26         
27         /* Wishbone master for reading the framebuffer */
28         output [31:0] fbwb_adr_o,
29         output reg fbwb_stb_o,
30         input fbwb_ack_i,
31         input [31:0] fbwb_dat_i,
32
33         /* Wishbone slave for configuration registers */
34         input [31:0] wb_adr_i,
35         input wb_cyc_i,
36         input wb_stb_i,
37         input wb_we_i,
38         output wb_ack_o,
39         output reg [31:0] wb_dat_o,
40         input [31:0] wb_dat_i,
41
42         /* VGA signals */
43         input vga_clk,
44         output vga_psave,
45         output reg vga_hsync,
46         output reg vga_vsync,
47         output vga_sync,
48         output vga_blank,
49         output reg [7:0] vga_r,
50         output reg [7:0] vga_g,
51         output reg [7:0] vga_b,
52         output vga_clkout
53 );
54
55 /* Internal state */
56
57 /* We need to access at least 640*480 = 307200 words.
58  * 2^19 = 524288
59  */
60 reg [21:0] fbaddr;
61 reg [21:0] fbaddr_next;
62
63 reg hactive;
64 reg hactive_next;
65 reg [9:0] hcounter;
66 reg [9:0] hcounter_next;
67
68 reg vactive;
69 reg vactive_next;
70 reg [9:0] vcounter;
71 reg [9:0] vcounter_next;
72
73 /* Configuration registers */
74
75 reg enable;                     // 00 (0)
76 reg enablesync;                 // 04 (1)
77
78 reg [29:0] fboffset;            // 08 (2)
79 reg [29:0] fboffsetsync;        // 0C (3)
80
81 reg [9:0] hres;                 // 10 (4)
82 reg [8:0] hblank;               // 14 (5)
83 reg [8:0] hsyncmin;             // 18 (6)
84 reg [8:0] hsyncmax;             // 1C (7)
85
86 reg [9:0] vres;                 // 20 (8)
87 reg [8:0] vblank;               // 24 (9)
88 reg [8:0] vsyncmin;             // 28 (a)
89 reg [8:0] vsyncmax;             // 2C (b)
90
91 assign wb_ack_o = wb_cyc_i & wb_stb_i;
92
93 always @(posedge wb_clk_i or posedge wb_rst_i) begin
94         if(wb_rst_i) begin
95                 enable <= 1;
96                 enablesync <= 1;
97                 
98                 fboffset <= 0;
99                 fboffsetsync <= 0;
100                 
101                 hres <= 639;
102                 hblank <= 159;
103                 hsyncmin <= 48;
104                 hsyncmax <= 143;
105                 
106                 vres <= 479;
107                 vblank <= 43;
108                 vsyncmin <= 31;
109                 vsyncmax <= 32;
110
111         end else begin
112                 if(wb_cyc_i & wb_stb_i & wb_we_i) begin
113                         case(wb_adr_i[5:2])
114                                 // enable is READ ONLY
115                                 4'h1: enablesync <= wb_dat_i[0];
116                                 
117                                 // fboffset is READ ONLY
118                                 4'h3: fboffsetsync <= wb_dat_i[31:2];
119                                 
120                                 4'h4: hres <= wb_dat_i[9:0];
121                                 4'h5: hblank <= wb_dat_i[8:0];
122                                 4'h6: hsyncmin <= wb_dat_i[8:0];
123                                 4'h7: hsyncmax <= wb_dat_i[8:0];
124                                 
125                                 4'h8: vres <= wb_dat_i[9:0];
126                                 4'h9: vblank <= wb_dat_i[8:0];
127                                 4'ha: vsyncmin <= wb_dat_i[8:0];
128                                 4'hb: vsyncmax <= wb_dat_i[8:0];
129                         endcase
130                 end
131                 /* Update synchronized registers */
132                 if((~hactive && ~vactive) || ~enable) begin
133                         fboffset <= fboffsetsync;
134                         enable <= enablesync;
135                 end
136         end
137 end
138
139 always @(wb_adr_i
140         or enable or enablesync
141         or fboffset or fboffsetsync 
142         or hres or hblank or hsyncmin or hsyncmax
143         or vres or vblank or vsyncmin or vsyncmax) begin
144         case(wb_adr_i[5:2])
145                 4'h0: wb_dat_o <= {31'b0, enable};
146                 4'h1: wb_dat_o <= {31'b0, enablesync};
147
148                 4'h2: wb_dat_o <= {fboffset, 2'b00};
149                 4'h3: wb_dat_o <= {fboffsetsync, 2'b00};
150                 
151                 4'h4: wb_dat_o <= {22'b0, hres};
152                 4'h5: wb_dat_o <= {23'b0, hblank};
153                 4'h6: wb_dat_o <= {23'b0, hsyncmin};
154                 4'h7: wb_dat_o <= {23'b0, hsyncmax};
155                 
156                 4'h8: wb_dat_o <= {22'b0, vres};
157                 4'h9: wb_dat_o <= {23'b0, vblank};
158                 4'ha: wb_dat_o <= {23'b0, vsyncmin};
159                 4'hb: wb_dat_o <= {23'b0, vsyncmax};
160                 default: wb_dat_o <= 32'hx;
161         endcase
162 end
163
164 /* FIFO */
165
166 wire [25:0] pixelfifo_dout;
167 wire pixelfifo_empty;
168 wire pixelfifo_next;
169
170 reg [25:0] pixelfifo_din;
171 wire pixelfifo_full;
172 reg pixelfifo_wr;
173
174 async_fifo #(
175         .DATA_WIDTH(26),
176         .ADDRESS_WIDTH(4)
177 ) pixelfifo (
178         .Data_out(pixelfifo_dout),
179         .Empty_out(pixelfifo_empty),
180         .ReadEn_in(pixelfifo_next),
181         .RClk(vga_clk),
182
183         .Data_in(pixelfifo_din),
184         .Full_out(pixelfifo_full),
185         .WriteEn_in(pixelfifo_wr),
186         .WClk(wb_clk_i),
187         
188         .Clear_in(wb_rst_i)
189 );
190
191 /* Wishbone master interface and Sequencer */
192
193 assign fbwb_adr_o = {{8'b0, fbaddr_next} + fboffset, 2'b00};
194
195 always @(posedge wb_clk_i or posedge wb_rst_i) begin
196         if(wb_rst_i) begin
197                 hactive <= 0;
198                 hcounter <= 0;
199                 vactive <= 0;
200                 vcounter <= 0;
201                 fbaddr <= 0;
202         end else begin
203                 if(~enable) begin
204                         hactive <= 0;
205                         hcounter <= 0;
206                         vactive <= 0;
207                         vcounter <= 0;
208                         fbaddr <= 0;
209                 end else begin
210                         hactive <= hactive_next;
211                         hcounter <= hcounter_next;
212                         vactive <= vactive_next;
213                         vcounter <= vcounter_next;
214                         fbaddr <= fbaddr_next;
215                 end
216         end
217 end
218
219 always @(hactive or hcounter or vactive or vcounter or hblank or hres or vblank or vres or pixelfifo_full or fbwb_stb_o or fbwb_ack_i) begin
220         if(~pixelfifo_full && (~fbwb_stb_o || fbwb_ack_i)) begin
221                 if(hcounter == 0) begin
222                         if(hactive) begin
223                                 hactive_next <= 0;
224                                 hcounter_next <= hblank; /* horizontal back + front porches */
225                                 vactive_next <= vactive;
226                                 vcounter_next <= vcounter;
227                         end else begin
228                                 hactive_next <= 1;
229                                 hcounter_next <= hres; /* horizontal active video */
230                                 if(vcounter == 0) begin
231                                         if(vactive) begin
232                                                 vactive_next <= 0;
233                                                 vcounter_next <= vblank; /* vertical back + front porches */
234                                         end else begin
235                                                 vactive_next <= 1;
236                                                 vcounter_next <= vres; /* vertical active video */
237                                         end
238                                 end else begin
239                                         vactive_next <= vactive;
240                                         vcounter_next <= vcounter - 1;
241                                 end
242                         end
243                 end else begin
244                         hactive_next <= hactive;
245                         hcounter_next <= hcounter - 1;
246                         vactive_next <= vactive;
247                         vcounter_next <= vcounter;
248                 end
249                 pixelfifo_wr = 1;
250         end else begin
251                 hactive_next <= hactive;
252                 hcounter_next <= hcounter;
253                 vactive_next <= vactive;
254                 vcounter_next <= vcounter;
255                 pixelfifo_wr = 0;
256         end
257 end
258
259 always @(hactive or hcounter or vactive or vcounter or hsyncmin or hsyncmax or vsyncmin or vsyncmax) begin
260         /* HSYNC generator (negative logic) */
261         if(~hactive && (hcounter[8:0] >= hsyncmin) && (hcounter[8:0] <= hsyncmax)) begin
262                 pixelfifo_din[24] <= 0;
263         end else begin
264                 pixelfifo_din[24] <= 1;
265         end
266
267         /* VSYNC generator (negative logic) */
268         if(~vactive && (vcounter[8:0] >= vsyncmin) && (vcounter[8:0] <= vsyncmax)) begin
269                 pixelfifo_din[25] <= 0;
270         end else begin
271                 pixelfifo_din[25] <= 1;
272         end
273 end
274
275 always @(hactive or vactive or pixelfifo_full or fbwb_dat_i or fbwb_ack_i or fbaddr) begin
276         /* Image generator */
277         if(hactive && vactive) begin
278                 pixelfifo_din[23:0] <= fbwb_dat_i[23:0];
279                 if(~pixelfifo_full) begin
280                         fbwb_stb_o <= 1;
281                         if(fbwb_ack_i)
282                                 fbaddr_next <= fbaddr + 1;
283                         else
284                                 fbaddr_next <= fbaddr;
285                 end else begin
286                         fbwb_stb_o <= 0;
287                         fbaddr_next <= fbaddr;
288                 end
289         end else begin
290                 /* Blank (porches and sync) */
291                 pixelfifo_din[23:0] <= 24'h000000;
292                 if(~vactive)
293                         fbaddr_next <= 0;
294                 else
295                         fbaddr_next <= fbaddr;
296                 fbwb_stb_o <= 0;
297         end
298 end
299
300 /* VGA signal generation */
301
302 assign vga_sync = 0;
303 assign vga_psave = 1;
304 assign vga_blank = 1;
305
306 assign vga_clkout = vga_clk;
307
308 assign pixelfifo_next = 1;
309
310 /* We register everything to avoid glitches on the outputs,
311  * especially on sync signals */
312
313 always @(posedge vga_clk) begin
314         vga_vsync <= pixelfifo_dout[25];
315         vga_hsync <= pixelfifo_dout[24];
316         vga_r <= pixelfifo_dout[23:16];
317         vga_g <= pixelfifo_dout[15:8];
318         vga_b <= pixelfifo_dout[7:0];
319 end
320
321 endmodule
322