fpga - My verilog VGA driver causes the screen to flicker (Basys2) -
i'm trying recreate adventure(1979) in verilog , far have character movement, collision , map generation done. didn't flicker before separated maps modules flickers constantly. when looking issue, found out clock on basys2 board pretty noisy , culprit. however, putting maps modules shouldn't have made worse unless messed up. idea happened?
here's map generator:
module map_generator(clk_vga, reset, currentx, currenty, hblank, vblank, playerposx, playerposy, mapdata ); input clk_vga; input reset; input [9:0]currentx; input [8:0]currenty; input hblank; input vblank; input [9:0]playerposx; input [8:0]playerposy; output [7:0]mapdata; reg [7:0]mcolor; reg [5:0]currentmap = 0; wire [7:0]startcastle; startcastle startcastle( .clk_vga(clk_vga), .currentx(currentx), .currenty(currenty), .mapdata(startcastle) ); @(posedge clk_vga) begin if(reset)begin currentmap <= 0; end end @(posedge clk_vga) begin if(hblank || vblank) begin mcolor <= 0; end else begin if(currentmap == 4'b0000) begin mcolor[7:0] <= startcastle[7:0]; end //add more maps later end end assign mapdata[7:0] = mcolor[7:0]; endmodule
here's startcastle:
module startcastle(clk_vga, currentx, currenty, active, mapdata); input clk_vga; input [9:0]currentx; input [8:0]currenty; input active; output [7:0]mapdata; reg [7:0]mcolor; @(posedge clk_vga) begin if(currenty < 40) begin mcolor[7:0] <= 8'b11100000; end else if(currentx < 40) begin mcolor[7:0] <= 8'b11100000; end else if(~(currentx < 600)) begin mcolor[7:0] <= 8'b11100000; end else if((~(currenty < 440) && (currentx < 260)) || (~(currenty < 440) && ~(currentx < 380))) begin mcolor[7:0] <= 8'b11100000; end else mcolor[7:0] <= 8'b00011100; end assign mapdata = mcolor; endmodule
here's vga driver connected top module:
module vga_driver(clk_50mhz, vs_vga, hs_vga, red, green, blue, hblank, vblank, curx, cury, color, clk_data, reset); input clk_50mhz; output vs_vga; output hs_vga; output [2:0] red; output [2:0] green; output [1:0] blue; output hblank; output vblank; reg vs = 0; reg hs = 0; input reset; //current client data input [7:0] color; output clk_data; output [9:0] curx; output [8:0] cury; //##### module constants (http://tinyvga.com/vga-timing/640x480@60hz) parameter hdisplayarea = 640; // horizontal display area parameter hlimit = 800; // maximum horizontal amount (limit) parameter hfrontporch = 16; // h. front porch parameter hbackporch = 48; // h. porch parameter hsyncwidth = 96; // h. pulse width parameter vdisplayarea = 480; // vertical display area parameter vlimit = 525; // maximum vertical amount (limit) parameter vfrontporch = 10; // v. front porch parameter vbackporch = 33; // v. porch parameter vsyncwidth = 2; // v. pulse width //##### local variables wire clk_25mhz; reg [9:0] curhpos = 0; //maximum of hlimit (2^10 - 1 = 1023) reg [9:0] curvpos = 0; //maximum of vlimit reg hblank_reg, vblank_reg, blank = 0; reg [9:0] currentx = 0; //maximum of hdisplayarea reg [8:0] currenty = 0; //maximum of vdisplayarea (2^9 - 1 = 511) //##### submodule declaration clock_divider clk_div(.clk_in(clk_50mhz), .clk_out(clk_25mhz)); //shifts clock half period (negates it) //see timing diagrams better understanding of reason clock_shift clk_shift(.clk_in(clk_25mhz), .clk_out(clk_data)); //simulate vertical , horizontal positions @(posedge clk_25mhz) begin if(curhpos < hlimit-1) begin curhpos <= curhpos + 1; end else begin curhpos <= 0; if(curvpos < vlimit-1) curvpos <= curvpos + 1; else curvpos <= 0; end if(reset) begin curhpos <= 0; curvpos <= 0; end end //##### vga logic (http://tinyvga.com/vga-timing/640x480@60hz) //hsync logic @(posedge clk_25mhz) if((curhpos < hsyncwidth) && ~reset) hs <= 1; else hs <= 0; //vsync logic @(posedge clk_25mhz) if((curvpos < vsyncwidth) && ~reset) vs <= 1; else vs <= 0; //horizontal logic @(posedge clk_25mhz) if((curhpos >= hsyncwidth + hfrontporch) && (curhpos < hsyncwidth + hfrontporch + hdisplayarea) || reset) hblank_reg <= 0; else hblank_reg <= 1; //vertical logic @(posedge clk_25mhz) if((curvpos >= vsyncwidth + vfrontporch) && (curvpos < vsyncwidth + vfrontporch + vdisplayarea) || reset) vblank_reg <= 0; else vblank_reg <= 1; //do not output color information when in vertical //or horizontal blanking areas. set boolean keep track of this. @(posedge clk_25mhz) if((hblank_reg || vblank_reg) && ~reset) blank <= 1; else blank <= 0; //keep track of current "real" x position. actual current x //pixel location abstracted away timing details @(posedge clk_25mhz) if(hblank_reg && ~reset) currentx <= 0; else currentx <= curhpos - hsyncwidth - hfrontporch; //keep track of current "real" y position. actual current y //pixel location abstracted away timing details @(posedge clk_25mhz) if(vblank_reg && ~reset) currenty <= 0; else currenty <= curvpos - vsyncwidth - vfrontporch; assign curx = currentx; assign cury = currenty; assign vblank = vblank_reg; assign hblank = hblank_reg; assign hs_vga = hs; assign vs_vga = vs; //respects vga blanking areas assign red = (blank) ? 3'b000 : color[7:5]; assign green = (blank) ? 3'b000 : color[4:2]; assign blue = (blank) ? 2'b00 : color[1:0]; endmodule
clk_div:
module clock_divider(clk_in, clk_out); input clk_in; output clk_out; reg clk_out = 0; @(posedge clk_in) clk_out <= ~clk_out; endmodule
clk_shift:
module clock_shift(clk_in, clk_out); input clk_in; output clk_out; assign clk_out = ~clk_in; endmodule
i'm posting answer because cannot put photo in comment.
is design looks like?
my guess atm might have misplaced ports during instantiation of vga_driver
and/or map_generator
(if used old style instantiation). nevertheless, i'm going check vga timmings, can see strange vertical line @ left of screen, if hblank interval visible.
by way: i've changed way generate display. use regs hs, vs, etc, updated next clock cycle. treat display generation fsm, outputs come combinational blocks triggered values (or range of values) counters. besides, start horizontal , vertical counters position (0,0) measured in pixel coordinates in screen maps values (0,0) horizontal , vertical counters, no arithmetic needed.
this version of vga display generation:
module videosyncs ( input wire clk, input wire [2:0] rin, input wire [2:0] gin, input wire [1:0] bin, output reg [2:0] rout, output reg [2:0] gout, output reg [1:0] bout, output reg hs, output reg vs, output wire [10:0] hc, output wire [10:0] vc ); /* http://www.abramovbenjamin.net/calc.html */ // vga 640x480@60hz,25mhz parameter htotal = 800; parameter vtotal = 524; parameter hactive = 640; parameter vactive = 480; parameter hfrontporch = 16; parameter hsyncpulse = 96; parameter vfrontporch = 11; parameter vsyncpulse = 2; parameter hsyncpolarity = 0; parameter vsyncpolarity = 0; reg [10:0] hcont = 0; reg [10:0] vcont = 0; reg active_area; assign hc = hcont; assign vc = vcont; @(posedge clk) begin if (hcont == htotal-1) begin hcont <= 0; if (vcont == vtotal-1) begin vcont <= 0; end else begin vcont <= vcont + 1; end end else begin hcont <= hcont + 1; end end @* begin if (hcont>=0 && hcont<hactive && vcont>=0 && vcont<vactive) active_area = 1'b1; else active_area = 1'b0; if (hcont>=(hactive+hfrontporch) && hcont<(hactive+hfrontporch+hsyncpulse)) hs = hsyncpolarity; else hs = ~hsyncpolarity; if (vcont>=(vactive+vfrontporch) && vcont<(vactive+vfrontporch+vsyncpulse)) vs = vsyncpolarity; else vs = ~vsyncpolarity; end @* begin if (active_area) begin gout = gin; rout = rin; bout = bin; end else begin gout = 3'h00; rout = 3'h00; bout = 2'h00; end end endmodule
which instantiated vga_driver
module, becomes nothing wrapper module:
module vga_driver ( input wire clk_25mhz, output wire vs_vga, output wire hs_vga, output wire [2:0] red, output wire [2:0] green, output wire [1:0] blue, output wire hblank, output wire vblank, output [9:0] curx, output [8:0] cury, input [7:0] color, input wire reset ); assign hblank = 0; assign vblank = 0; videosyncs syncgen ( .clk(clk_25mhz), .rin(color[7:5]), .gin(color[4:2]), .bin(color[1:0]), .rout(red), .gout(green), .bout(blue), .hs(hs_vga), .vs(vs_vga), .hc(curx), .vc(cury) ); endmodule
note in map_generator
, first if
statement in always
block never true. can forget it, vga display module blank rgb outputs when needed.
@(posedge clk_vga) begin if(hblank || vblank) begin // mcolor <= 0; // never reached end // else begin // if(currentmap == 4'b0000) begin mcolor[7:0] <= startcastle[7:0]; end //add more maps later end end
using same approach, i've converted map generator module combinational module. example, map 0 (the castle -without castle, see-) this:
module startcastle( input wire [9:0] currentx, input wire [8:0] currenty, output wire [7:0] mapdata ); reg [7:0] mcolor; assign mapdata = mcolor; @* begin if(currenty < 40) begin mcolor[7:0] <= 8'b11100000; end else if(currentx < 40) begin mcolor[7:0] <= 8'b11100000; end else if(~(currentx < 600)) begin mcolor[7:0] <= 8'b11100000; end else if((~(currenty < 440) && (currentx < 260)) || (~(currenty < 440) && ~(currentx < 380))) begin mcolor[7:0] <= 8'b11100000; end else mcolor[7:0] <= 8'b00011100; end endmodule
just fsm output colour goes in pixel. input being coordinates of current pixel.
so when time display map 0, map_generator switches based upon current value of currentmap
module map_generator ( input wire clk, input wire reset, input wire [9:0]currentx, input wire [8:0]currenty, input wire hblank, input wire vblank, input wire [9:0]playerposx, input wire [8:0]playerposy, output wire [7:0]mapdata ); reg [7:0] mcolor; assign mapdata = mcolor; reg [5:0]currentmap = 0; wire [7:0] castle_map; startcastle startcastle( .currentx(currentx), .currenty(currenty), .mapdata(castle_map) ); @(posedge clk) begin if(reset) begin currentmap <= 0; end end @* begin if(currentmap == 6'b000000) begin mcolor = castle_map; end //add more maps later end endmodule
this may lot of comb logic generated , glitches may happen. it's fast, no noticeable glitches on screen, , can use actual current x , y coordinates choose display on screen. thus, no need inverted clock. final version of design has 1 25mhz clock.
by way, want keep device dependent constructions away design, placing things clock generators in separate modules connected design in top module, should device dependent module.
so, i've written device-agnostic adventure module, contain entire game:
module adventure ( input clk_vga, input reset, output vs_vga, output hs_vga, output [2:0] red, output [2:0] green, output [1:0] blue ); wire hblank, vblank; wire [7:0] color; wire [9:0] curx; wire [8:0] cury; wire [9:0] playerposx = 10'd320; // no used in design yet wire [8:0] playerposy = 9'd240; // no used in design yet vga_driver the_screen (.clk_25mhz(clk_vga), .vs_vga(vs_vga), .hs_vga(hs_vga), .red(red), .green(green), .blue(blue), .hblank(hblank), .vblank(vblank), .curx(curx), .cury(cury), .color(color) ); map_generator the_mapper (.clk(clk_vga), .reset(reset), .currentx(curx), .currenty(cury), .hblank(hblank), .vblank(vblank), .playerposx(playerposx), .playerposy(playerposy), .mapdata(color) ); endmodule
this module not complete: lacks inputs joystick or other input device update player current position. now, player current position fixed.
the top level design (tld) written exclusively fpga trainer have. here need generate proper clocks using device's available resources, such dcm in spartan 3/3e devices.
module tld_basys( input wire clk_50mhz, input wire reset, output wire vs_vga, output wire hs_vga, output wire [2:0] red, output wire [2:0] green, output wire [1:0] blue ); wire clk_25mhz; dcm_clocks gen_vga_clock ( .clkin_in(clk_50mhz), .clkdv_out(clk_25mhz) ); adventure the_game (.clk_vga(clk_25mhz), .reset(reset), .vs_vga(vs_vga), .hs_vga(hs_vga), .red(red), .green(green), .blue(blue) ); endmodule
the dcm generated clocks goes in module (generated xilinx core generator)
module dcm_clocks (clkin_in, clkdv_out ); input clkin_in; output clkdv_out; wire clkfb_in; wire clkfx_buf; wire clkdv_buf; wire clkin_ibufg; wire clk0_buf; wire gnd_bit; assign gnd_bit = 0; bufg clkdv_bufg_inst (.i(clkdv_buf), .o(clkdv_out)); ibufg clkin_ibufg_inst (.i(clkin_in), .o(clkin_ibufg)); bufg clk0_bufg_inst (.i(clk0_buf), .o(clkfb_in)); dcm_sp #(.clkdv_divide(2.0), .clkin_divide_by_2("false"), .clkin_period(20.000), .clkout_phase_shift("none"), .deskew_adjust("system_synchronous"), .dfs_frequency_mode("low"), .dll_frequency_mode("low"), .duty_cycle_correction("true"), .factory_jf(16'hc080), .phase_shift(0), .startup_wait("false") ) dcm_sp_inst (.clkfb(clkfb_in), .clkin(clkin_ibufg), .dssen(gnd_bit), .psclk(gnd_bit), .psen(gnd_bit), .psincdec(gnd_bit), .rst(gnd_bit), .clkdv(clkdv_buf), .clkfx(), .clkfx180(), .clk0(clk0_buf), .clk2x(), .clk2x180(), .clk90(), .clk180(), .clk270(), .locked(), .psdone(), .status()); endmodule
although safe (for xilinx devices) use simple clock divider did. if fear synthesizer won't treat divided clock actual clock, add bufg primitive route output divider global buffer can used clock no problems (see module above example on how this).
as final note, may want add more independency final device, using 24-bit colours graphics. @ tld, use actual number of bits per colour component have, if move basys2 8-bit colour trainer board the, say, nexys4 board, 12-bit colour, automatically enjoy richer output display.
now, looks (no vertical bars @ left, , colours seem more vibrant)
Comments
Post a Comment