head 1.4; access; symbols; locks; strict; comment @# @; 1.4 date 2008.05.20.08.30.20; author lekernel; state Exp; branches; next 1.3; commitid 77c648328c124567; 1.3 date 2008.02.11.18.04.39; author lekernel; state Exp; branches; next 1.2; commitid 48af47b08e2b4567; 1.2 date 2008.02.03.18.26.41; author lekernel; state Exp; branches; next 1.1; commitid 2b3a47a6074d4567; 1.1 date 2008.02.03.17.45.12; author lekernel; state Exp; branches; next ; commitid 244a47a5fd9b4567; desc @@ 1.4 log @Fixed sensitivity lists @ text @/* * WISHBONE VGA framebuffer * Copyright (C) 2008 Sebastien Bourdeauducq - http://lekernel.net * This file is part of Milkymist. * * Milkymist is free software; you can redistribute it and/or modify it * under the terms of the GNU Library General Public License as published * by the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ module wb_vga( /* Clock and Reset signals are shared between the two buses */ input wb_clk_i, input wb_rst_i, /* Wishbone master for reading the framebuffer */ output [31:0] fbwb_adr_o, output reg fbwb_stb_o, input fbwb_ack_i, input [31:0] fbwb_dat_i, /* Wishbone slave for configuration registers */ input [31:0] wb_adr_i, input wb_cyc_i, input wb_stb_i, input wb_we_i, output wb_ack_o, output reg [31:0] wb_dat_o, input [31:0] wb_dat_i, /* VGA signals */ input vga_clk, output vga_psave, output reg vga_hsync, output reg vga_vsync, output vga_sync, output vga_blank, output reg [7:0] vga_r, output reg [7:0] vga_g, output reg [7:0] vga_b, output vga_clkout ); /* Internal state */ /* We need to access at least 640*480 = 307200 words. * 2^19 = 524288 */ reg [18:0] fbaddr; reg [18:0] fbaddr_next; reg hactive; reg hactive_next; reg [9:0] hcounter; reg [9:0] hcounter_next; reg vactive; reg vactive_next; reg [9:0] vcounter; reg [9:0] vcounter_next; /* Configuration registers */ reg enable; // 00 (0) reg enablesync; // 04 (1) reg [29:0] fboffset; // 08 (2) reg [29:0] fboffsetsync; // 0C (3) reg [9:0] hres; // 10 (4) reg [8:0] hblank; // 14 (5) reg [8:0] hsyncmin; // 18 (6) reg [8:0] hsyncmax; // 1C (7) reg [9:0] vres; // 20 (8) reg [8:0] vblank; // 24 (9) reg [8:0] vsyncmin; // 28 (a) reg [8:0] vsyncmax; // 2C (b) assign wb_ack_o = wb_cyc_i & wb_stb_i; always @@(posedge wb_clk_i or posedge wb_rst_i) begin if(wb_rst_i) begin enable <= 0; enablesync <= 0; fboffset <= 0; fboffsetsync <= 0; hres <= 639; hblank <= 159; hsyncmin <= 48; hsyncmax <= 143; vres <= 479; vblank <= 43; vsyncmin <= 31; vsyncmax <= 32; end else begin if(wb_cyc_i & wb_stb_i & wb_we_i) begin case(wb_adr_i[5:2]) // enable is READ ONLY 4'h1: enablesync <= wb_dat_i[0]; // fboffset is READ ONLY 4'h3: fboffsetsync <= wb_dat_i[31:2]; 4'h4: hres <= wb_dat_i[9:0]; 4'h5: hblank <= wb_dat_i[8:0]; 4'h6: hsyncmin <= wb_dat_i[8:0]; 4'h7: hsyncmax <= wb_dat_i[8:0]; 4'h8: vres <= wb_dat_i[9:0]; 4'h9: vblank <= wb_dat_i[8:0]; 4'ha: vsyncmin <= wb_dat_i[8:0]; 4'hb: vsyncmax <= wb_dat_i[8:0]; endcase end /* Update synchronized registers */ if((~hactive && ~vactive) || ~enable) begin fboffset <= fboffsetsync; enable <= enablesync; end end end always @@(wb_adr_i or enable or enablesync or fboffset or fboffsetsync or hres or hblank or hsyncmin or hsyncmax or vres or vblank or vsyncmin or vsyncmax) begin case(wb_adr_i[5:2]) 4'h0: wb_dat_o <= {31'b0, enable}; 4'h1: wb_dat_o <= {31'b0, enablesync}; 4'h2: wb_dat_o <= {fboffset, 2'b00}; 4'h3: wb_dat_o <= {fboffsetsync, 2'b00}; 4'h4: wb_dat_o <= {22'b0, hres}; 4'h5: wb_dat_o <= {23'b0, hblank}; 4'h6: wb_dat_o <= {23'b0, hsyncmin}; 4'h7: wb_dat_o <= {23'b0, hsyncmax}; 4'h8: wb_dat_o <= {22'b0, vres}; 4'h9: wb_dat_o <= {23'b0, vblank}; 4'ha: wb_dat_o <= {23'b0, vsyncmin}; 4'hb: wb_dat_o <= {23'b0, vsyncmax}; default: wb_dat_o <= 32'hx; endcase end /* FIFO */ wire [25:0] pixelfifo_dout; wire pixelfifo_empty; wire pixelfifo_next; reg [25:0] pixelfifo_din; wire pixelfifo_full; reg pixelfifo_wr; async_fifo #( .DATA_WIDTH(26), .ADDRESS_WIDTH(4) ) pixelfifo ( .Data_out(pixelfifo_dout), .Empty_out(pixelfifo_empty), .ReadEn_in(pixelfifo_next), .RClk(vga_clk), .Data_in(pixelfifo_din), .Full_out(pixelfifo_full), .WriteEn_in(pixelfifo_wr), .WClk(wb_clk_i), .Clear_in(wb_rst_i) ); /* Wishbone master interface and Sequencer */ assign fbwb_adr_o = {{11'b0, fbaddr} + fboffset, 2'b00}; always @@(posedge wb_clk_i or posedge wb_rst_i) begin if(wb_rst_i) begin hactive <= 0; hcounter <= 0; vactive <= 0; vcounter <= 0; fbaddr <= 0; end else begin if(~enable) begin hactive <= 0; hcounter <= 0; vactive <= 0; vcounter <= 0; fbaddr <= 0; end else begin hactive <= hactive_next; hcounter <= hcounter_next; vactive <= vactive_next; vcounter <= vcounter_next; fbaddr <= fbaddr_next; end end end 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 if(~pixelfifo_full && (~fbwb_stb_o || fbwb_ack_i)) begin if(hcounter == 0) begin if(hactive) begin hactive_next <= 0; hcounter_next <= hblank; /* horizontal back + front porches */ vactive_next <= vactive; vcounter_next <= vcounter; end else begin hactive_next <= 1; hcounter_next <= hres; /* horizontal active video */ if(vcounter == 0) begin if(vactive) begin vactive_next <= 0; vcounter_next <= vblank; /* vertical back + front porches */ end else begin vactive_next <= 1; vcounter_next <= vres; /* vertical active video */ end end else begin vactive_next <= vactive; vcounter_next <= vcounter - 1; end end end else begin hactive_next <= hactive; hcounter_next <= hcounter - 1; vactive_next <= vactive; vcounter_next <= vcounter; end pixelfifo_wr = 1; end else begin hactive_next <= hactive; hcounter_next <= hcounter; vactive_next <= vactive; vcounter_next <= vcounter; pixelfifo_wr = 0; end end always @@(hactive or hcounter or vactive or vcounter or hsyncmin or hsyncmax or vsyncmin or vsyncmax) begin /* HSYNC generator (negative logic) */ if(~hactive && (hcounter[8:0] >= hsyncmin) && (hcounter[8:0] <= hsyncmax)) begin pixelfifo_din[24] <= 0; end else begin pixelfifo_din[24] <= 1; end /* VSYNC generator (negative logic) */ if(~vactive && (vcounter[8:0] >= vsyncmin) && (vcounter[8:0] <= vsyncmax)) begin pixelfifo_din[25] <= 0; end else begin pixelfifo_din[25] <= 1; end end always @@(hactive or vactive or pixelfifo_full or fbwb_dat_i or fbwb_ack_i or fbaddr) begin /* Image generator */ if(hactive && vactive) begin pixelfifo_din[23:0] <= fbwb_dat_i[23:0]; if(~pixelfifo_full) begin fbwb_stb_o <= 1; if(fbwb_ack_i) fbaddr_next <= fbaddr + 1; else fbaddr_next <= fbaddr; end else begin fbwb_stb_o <= 0; fbaddr_next <= fbaddr; end end else begin /* Blank (porches and sync) */ pixelfifo_din[23:0] <= 24'h000000; if(~vactive) fbaddr_next <= 0; else fbaddr_next <= fbaddr; fbwb_stb_o <= 0; end end /* VGA signal generation */ assign vga_sync = 0; assign vga_psave = 1; assign vga_blank = 1; assign vga_clkout = vga_clk; assign pixelfifo_next = 1; /* We register everything to avoid glitches on the outputs, * especially on sync signals */ always @@(posedge vga_clk) begin vga_vsync <= pixelfifo_dout[25]; vga_hsync <= pixelfifo_dout[24]; vga_r <= pixelfifo_dout[23:16]; vga_g <= pixelfifo_dout[15:8]; vga_b <= pixelfifo_dout[7:0]; end endmodule @ 1.3 log @Expanded sync registers @ text @d139 1 a139 1 or enable d218 1 a218 1 always @@(hactive or hcounter or vactive or vcounter or pixelfifo_full or fbwb_stb_o or fbwb_ack_i) begin d258 1 a258 1 always @@(hactive or hcounter or vactive or vcounter) begin @ 1.2 log @Load fboffset when enabled @ text @d82 3 a84 3 reg [7:0] hblank; // 14 (5) reg [7:0] hsyncmin; // 18 (6) reg [7:0] hsyncmax; // 1C (7) d87 3 a89 3 reg [7:0] vblank; // 24 (9) reg [7:0] vsyncmin; // 28 (a) reg [7:0] vsyncmax; // 2C (b) d120 3 a122 3 4'h5: hblank <= wb_dat_i[7:0]; 4'h6: hsyncmin <= wb_dat_i[7:0]; 4'h7: hsyncmax <= wb_dat_i[7:0]; d125 3 a127 3 4'h9: vblank <= wb_dat_i[7:0]; 4'ha: vsyncmin <= wb_dat_i[7:0]; 4'hb: vsyncmax <= wb_dat_i[7:0]; d151 3 a153 3 4'h5: wb_dat_o <= {24'b0, hblank}; 4'h6: wb_dat_o <= {24'b0, hsyncmin}; 4'h7: wb_dat_o <= {24'b0, hsyncmax}; d156 4 a159 4 4'h9: wb_dat_o <= {24'b0, vblank}; 4'ha: wb_dat_o <= {24'b0, vsyncmin}; 4'hb: wb_dat_o <= {24'b0, vsyncmax}; default: wb_dat_o <= 32'habadface; d260 1 a260 1 if(~hactive && (hcounter[7:0] >= hsyncmin) && (hcounter[7:0] <= hsyncmax)) begin d267 1 a267 1 if(~vactive && (vcounter[7:0] >= vsyncmin) && (vcounter[7:0] <= vsyncmax)) begin @ 1.1 log @Initial import @ text @d131 4 a134 2 if(~hactive && ~vactive) fboffset <= fboffsetsync; if((~hactive && ~vactive) || ~enable) enable <= enablesync; @