2 * WISHBONE VGA framebuffer
3 * Copyright (C) 2008 Sebastien Bourdeauducq - http://lekernel.net
4 * This file is part of Milkymist.
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)
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.
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,
23 /* Clock and Reset signals are shared between the two buses */
27 /* Wishbone master for reading the framebuffer */
28 output [31:0] fbwb_adr_o,
29 output reg fbwb_stb_o,
31 input [31:0] fbwb_dat_i,
33 /* Wishbone slave for configuration registers */
34 input [31:0] wb_adr_i,
39 output reg [31:0] wb_dat_o,
40 input [31:0] wb_dat_i,
49 output reg [7:0] vga_r,
50 output reg [7:0] vga_g,
51 output reg [7:0] vga_b,
57 /* We need to access at least 640*480 = 307200 words.
61 reg [21:0] fbaddr_next;
66 reg [9:0] hcounter_next;
71 reg [9:0] vcounter_next;
73 /* Configuration registers */
76 reg enablesync; // 04 (1)
78 reg [29:0] fboffset; // 08 (2)
79 reg [29:0] fboffsetsync; // 0C (3)
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)
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)
91 assign wb_ack_o = wb_cyc_i & wb_stb_i;
93 always @(posedge wb_clk_i or posedge wb_rst_i) begin
112 if(wb_cyc_i & wb_stb_i & wb_we_i) begin
114 // enable is READ ONLY
115 4'h1: enablesync <= wb_dat_i[0];
117 // fboffset is READ ONLY
118 4'h3: fboffsetsync <= wb_dat_i[31:2];
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];
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];
131 /* Update synchronized registers */
132 if((~hactive && ~vactive) || ~enable) begin
133 fboffset <= fboffsetsync;
134 enable <= enablesync;
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
145 4'h0: wb_dat_o <= {31'b0, enable};
146 4'h1: wb_dat_o <= {31'b0, enablesync};
148 4'h2: wb_dat_o <= {fboffset, 2'b00};
149 4'h3: wb_dat_o <= {fboffsetsync, 2'b00};
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};
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;
166 wire [25:0] pixelfifo_dout;
167 wire pixelfifo_empty;
170 reg [25:0] pixelfifo_din;
178 .Data_out(pixelfifo_dout),
179 .Empty_out(pixelfifo_empty),
180 .ReadEn_in(pixelfifo_next),
183 .Data_in(pixelfifo_din),
184 .Full_out(pixelfifo_full),
185 .WriteEn_in(pixelfifo_wr),
191 /* Wishbone master interface and Sequencer */
193 assign fbwb_adr_o = {{8'b0, fbaddr_next} + fboffset, 2'b00};
195 always @(posedge wb_clk_i or posedge wb_rst_i) begin
210 hactive <= hactive_next;
211 hcounter <= hcounter_next;
212 vactive <= vactive_next;
213 vcounter <= vcounter_next;
214 fbaddr <= fbaddr_next;
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
224 hcounter_next <= hblank; /* horizontal back + front porches */
225 vactive_next <= vactive;
226 vcounter_next <= vcounter;
229 hcounter_next <= hres; /* horizontal active video */
230 if(vcounter == 0) begin
233 vcounter_next <= vblank; /* vertical back + front porches */
236 vcounter_next <= vres; /* vertical active video */
239 vactive_next <= vactive;
240 vcounter_next <= vcounter - 1;
244 hactive_next <= hactive;
245 hcounter_next <= hcounter - 1;
246 vactive_next <= vactive;
247 vcounter_next <= vcounter;
251 hactive_next <= hactive;
252 hcounter_next <= hcounter;
253 vactive_next <= vactive;
254 vcounter_next <= vcounter;
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;
264 pixelfifo_din[24] <= 1;
267 /* VSYNC generator (negative logic) */
268 if(~vactive && (vcounter[8:0] >= vsyncmin) && (vcounter[8:0] <= vsyncmax)) begin
269 pixelfifo_din[25] <= 0;
271 pixelfifo_din[25] <= 1;
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
282 fbaddr_next <= fbaddr + 1;
284 fbaddr_next <= fbaddr;
287 fbaddr_next <= fbaddr;
290 /* Blank (porches and sync) */
291 pixelfifo_din[23:0] <= 24'h000000;
295 fbaddr_next <= fbaddr;
300 /* VGA signal generation */
303 assign vga_psave = 1;
304 assign vga_blank = 1;
306 assign vga_clkout = vga_clk;
308 assign pixelfifo_next = 1;
310 /* We register everything to avoid glitches on the outputs,
311 * especially on sync signals */
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];