system verilog slicing arrays - verilog

I am still not sure how the array slicing works in System Verilog?
For example, let's say that I have a packed 2D array.
localparam [0:2][4:0] TEMP = {5'd4,5'd9,5'd20};
So my array has three rows and each row is a 5-bit number.
So, when I am trying to do something like this, it doesn't quite work !!!
logic [1:0] arr;
assign arr = TEMP[0][1:0]
How come this doesn't work?
The compiler doesn't complain, but the simulation shows all 'X !!
Here I am including the module that has the issue:
module slice_issue ();
// clock and reset
reg board_resetl;
reg tb_clkh;
parameter CLK_PER = 4;
always #(CLK_PER/2) tb_clkh = ~ tb_clkh;
initial begin: main_process
board_resetl = 0;
tb_clkh = 0;
#100
#(posedge tb_clkh);
board_resetl = 1;
end
localparam logic [4:0] PARAM_1 = 14;
localparam logic [4:0] PARAM_2 = 18;
localparam logic [4:0] PARAM_3 = 26;
localparam [0:2] [4:0] CAND_MODE_LIST = {PARAM_1, PARAM_2, PARAM_3};
logic [1:0] temp;
logic [4:0] temp2;
logic [1:0] in_pred_mode;
logic [4:0] cnt_reg;
always # (posedge tb_clkh or negedge board_resetl)
begin
if (~board_resetl) begin
in_pred_mode <= 0;
cnt_reg <= 0;
end else
cnt_reg <= cnt_reg + 1;
if (cnt_reg == 31) begin
in_pred_mode <= $urandom_range(0, 1);
end
end
// bad
assign temp = CAND_MODE_LIST[in_pred_mode][1:0];
// good
assign temp2 = CAND_MODE_LIST[in_pred_mode];
endmodule

A self contained example could be :
module tb;
localparam [0:2][4:0] TEMP = {5'd4,5'd9,5'd20};
logic [1:0] arr;
assign arr = TEMP[0][1:0];
initial begin
$display("arr : %b", arr);
#1ps;
$display(TEMP[0]);
$display(TEMP[1]);
$display(TEMP[2]);
$display("arr : %b", arr);
end
endmodule
For me this (correctly) outputs:
# KERNEL: arr : 00
# KERNEL: 4
# KERNEL: 9
# KERNEL: 20
# KERNEL: arr : 00
This does not show the error condition from the question, unless the question adds more information, the exact reason for the error can not be determined.
example on EDA Playground

instead of negative points, I should've gotten a positive one. I contacted the vendor (Aldec), and it turned out it is Aldec's simulator issue, and they are going to fixed it in their next revision.

Related

verilog testbench(with for loop) for 3-8 decoder signal value not updating

I'm new to verilog. I write a 3-8 decoder and a testbench for it. This is 38_decoder_tb.v:
module decoder_38(input [2:0] in, output reg [7:0] out);
always #* begin
case (in) //Switch based on concatenation of control signals
3'b000 : out = 8'b00000001;
3'b001 : out = 8'b00000010;
3'b010 : out = 8'b00000100;
3'b011 : out = 8'b00001000;
3'b100 : out = 8'b00010000;
3'b101 : out = 8'b00100000;
3'b110 : out = 8'b01000000;
3'b111 : out = 8'b10000000;
endcase
end
endmodule
This is 38_decoder_tb.v:
`timescale 1ns / 1ns
module tb_decoder_38;
// decoder_38 Parameters
parameter PERIOD = 20;
// decoder_38 Inputs
reg [2:0] in ;
// decoder_38 Outputs
wire [7:0] out ;
reg clk;
integer i;
initial
begin
clk = 0;
forever #(PERIOD/2) clk=~clk;
end
decoder_38 u_decoder_38 (
.in ( in [2:0] ),
.out ( out [7:0] )
);
initial
begin
in = 0;
#(PERIOD*2);
for (i = 3'd0; i < 3'd8; i=i+1) begin
in = i;
#PERIOD;
end
end
endmodule
I stimulate the testbench module with ModelSim 10.5. As you can see, the signal just doesn't update. What's the problem? Does it have something to do with the for loop?
Yes, the issue is with the loop.
Your problem is in definition of 3'd8. 8 is the same as 1000 in binary presentation and requires 4 bits. Therefore, 3-bits of it (as you requested) yield 000. As a result your loop does not run at all, looking like the following: for (i = 3'd0; i < 0; i++).
Variable i is defined as integer. An integer type is a 4-state data type, 32-bit signed integer. So, rewriting loop as for(i = 0; i < 8; i++) will solve your issues. There is absolutely no need to define sizes for the constants in this loop.
And, to avoid infinite loop, you need to use $finish when appropriate, as suggested in the other answer.
The posted testbench has an infinite loop because of the forever loop with nothing to stop it.
I added a #30 delay to see the last clock of data, and $finish to stop it.
After that the code behaves as expected.
All 4 simulators on EDA Playground show the same.
initial
begin
in = 0;
#(PERIOD*2);
for (i = 3'd0; i <= 3'd7; i=i+1) begin
in = i;
#PERIOD;
end
//
#30;
$finish;
end
Here is a link playground I put together https://www.edaplayground.com/x/aZYL

Systemverilog recursion update value for next stage

I am trying to create a recursive logic in Systemverilog but I seem to be missing the right logic to carry the output of one iteration to the next.
Here is an example of the problem:
parameter WIDTH=4;
module test_ckt #(parameter WIDTH = 4)(CK, K, Z);
input CK;
input [WIDTH-1:0] K;
output reg Z;
wire [WIDTH/2-1:0] tt;
wire [WIDTH-1:0] tempin;
assign tempin = K;
genvar i,j;
generate
for (j=$clog2(WIDTH); j>0; j=j-1)
begin: outer
wire [(2**(j-1))-1:0] tt;
for (i=(2**j)-1; i>0; i=i-2)
begin
glitchy_ckt #(.WIDTH(1)) gckt (tempin[i:i], tempin[(i-1):i-1], tt[((i+1)/2)-1]);
end
// How do I save the value for the next iteration?
wire [(2**(j-1))-1:0] tempin;
assign outer[j].tempin = outer[j].tt;
end
endgenerate
always #(posedge CK)
begin
// How do I use the final output here?
Z <= tt[0];
end
endmodule
module glitchy_ckt #(parameter WIDTH = 1)(A1, B1, Z1);
input [WIDTH-1:0] A1,B1;
output Z1;
assign Z1 = ~A1[0] ^ B1[0];
endmodule
Expected topology:
S1 S2
K3--<inv>--|==
|XOR]---<inv>----|
K2---------|== |
|==
<--gckt---> |XOR]
|==
K1--<inv>--|== |
|XOR]------------|
K0---------|== <-----gckt---->
Example input and expected outputs:
Expected output:
A - 1010
----
S1 0 0 <- j=2 and i=3,1.
S2 1 <- j=1 and i=1.
Actual output:
A - 1010
----
S1 0 0 <- j=2 and i=3,1.
S2 0 <- j=1 and i=1. Here, because tempin is not updated, inputs are same as (j=2 & i=1).
Test-bench:
`timescale 1 ps / 1 ps
`include "test_ckt.v"
module mytb;
reg CK;
reg [WIDTH-1:0] A;
wire Z;
test_ckt #(.WIDTH(WIDTH)) dut(.CK(CK), .K(A), .Z(Z));
always #200 CK = ~CK;
integer i;
initial begin
$display($time, "Starting simulation");
#0 CK = 0;
A = 4'b1010;
#500 $finish;
end
initial begin
//dump waveform
$dumpfile("test_ckt.vcd");
$dumpvars(0,dut);
end
endmodule
How do I make sure that tempin and tt get updated as I go from one stage to the next.
Your code does not have any recursion in it. You were trying to solve it using loops, but generate blocks are very limited constructs and, for example, you cannot access parameters defined in other generate iterations (but you can access variables or module instances).
So, the idea is to use a real recursive instantiation of the module. In the following implementation the module rec is the one which is instantiated recursively. It actually builds the hierarchy from your example (I hope correctly).
Since you tagged it as system verilog, I used the system verilog syntax.
module rec#(WIDTH=1) (input logic [WIDTH-1:0]source, output logic result);
if (WIDTH <= 2) begin
always_comb
result = source; // << generating the result and exiting recursion.
end
else begin:blk
localparam REC_WDT = WIDTH / 2;
logic [REC_WDT-1:0] newSource;
always_comb // << calculation of your expression
for (int i = 0; i < REC_WDT; i++)
newSource[i] = source[i*2] ^ ~source[(i*2)+1];
rec #(REC_WDT) rec(newSource, result); // << recursive instantiation with WIDTH/2
end // else: !if(WIDTH <= 2)
initial $display("%m: W=%0d", WIDTH); // just my testing leftover
endmodule
The module is instantiated first time from the test_ckt:
module test_ckt #(parameter WIDTH = 4)(input logic CK, input logic [WIDTH-1:0] K, output logic Z);
logic result;
rec#(WIDTH) rec(K, result); // instantiate first time )(top)
always_ff #(posedge CK)
Z <= result; // assign the results
endmodule // test_ckt
And your testbench, a bit changed:
module mytb;
reg CK;
reg [WIDTH-1:0] A;
wire Z;
test_ckt #(.WIDTH(WIDTH)) dut(.CK(CK), .K(A), .Z(Z));
always #200 CK = ~CK;
integer i;
initial begin
$display($time, "Starting simulation");
CK = 0;
A = 4'b1010;
#500
A = 4'b1000;
#500 $finish;
end
initial begin
$monitor("Z=%b", Z);
end
endmodule // mytb
Use of $display/$monitor is more convenient than dumping traces for such small examples.
I did not do much testing of what I created, so there could be issues, but you can get basic ideas from it in any case. I assume it should work with any WIDTH which is power of 2.

problem with flattening an array in verilog

I tried to flatten an array with numbers into a variable in order to pass it as an input to a module in verilog. But, I get the error:
Port 1 (DATA_IN) of process_data expects 64 bits, got 4096. Pruning
4032 high bits of the expression.
I know that my module process_data in not ready yet and hence it does not work properly, but my problem for now is that the input is a lot more bits than it should.
Do you know how could I fix it?
module process_data(input wire [63:0] DATA_IN , input wire [6:0]AdrR , input wire [6:0]AdrW, input R_W , input Cen, input clk, input reset, output reg [63:0]Reg_Data_Out);
integer i;
reg [63:0]Memory[63:0]; //64 * 64 bit array
initial
begin
i=0;
//++for
repeat (64)
begin
Memory[i]=64'd1; //64 = number of the thesis that the vector has
i=i+1;
end
end
always #(negedge(clk))
//initial AdrR ,AdrW = 0; // 7'b0000_000;
begin
if(Cen == 1'b1) begin // cen = chip enabled
case (R_W)
1'b1:
//++check if not empty
Reg_Data_Out = Memory[AdrR]; // (read) out put memory context
1'b0:
//++check if not full
Memory[AdrW] = DATA_IN; // write input to memory
default:
Reg_Data_Out = 64'bxxxxxxxx;
endcase
end
end
endmodule
module TOP();
reg [63:0] inputdata1 [0:127]; //array
reg [64*64-1:0] flattened_inputdata1;
reg [6:0] AddressR,AddressW;
reg cen,clk, R_W, reset;
wire [63:0] Data_Out;
//pass the numbers
integer count;
initial
begin
count = 0;
while (count < 128) // Execute loop till count is 127. exit at count 128
begin
// every timh that the integer variable count takes must be also passed into reg inputdata1
inputdata1[count] = count;
count = count + 1;
end
end
//flattening
initial
begin
count = 0;
while (count < 128) // Execute loop till count is 127. exit at count 128
begin
flattened_inputdata1[64*count +: 64] = inputdata1[count];
//flattened_inputdata1[(64*count) +63) : (64*count)] = inputdata1[count]; //declare a number is dekadikos
count = count + 1;
end
end
//call module for data I/O
process_data process_data( flattened_inputdata1, AddressR, AddressW, R_W , cen, clk, reset, Data_Out); //reset does not do anything yet
always #10 clk=~clk;
initial
begin
$display("flattenedinputdata1=%d", flattened_inputdata1);
cen=1'b1; //chip enabled
#50
R_W=1'b1; //read
AddressR=7'b0000_000;
#50
//R_W=1'b1; //read
//AddressR=7'b0000_001;
$finish; //#50 $finish;
end
endmodule
edaplayground link
You can see from the declarations that the sizes are different:
input wire [63:0] DATA_IN
and the thing you're passing in to it:
reg [64*64-1:0] flattened_inputdata1;
DATA_IN is 64 bits and flattened_inputdata1 is 4096 bits. So you'll need to change one of them so that the two sizes match.

verilog code for ram

I am trying to simulate the following code for an asynchronous ram in verilog. But dout remains xxxx all the time.
The first time I tried the code dout was equal to din for the time when write signal was 1.After that it was all xxxx.Can anyone tell me the problem?It'd be great if you could suggest a better code.
module ram(cs,wr,addr,din,dout);
parameter adds = 10, wsize =16, memsize =1024;
input cs,wr;
input [adds-1:0] addr;
input [wsize-1 : 0]din;
output [wsize-1:0]dout;
reg [wsize-1:0] mem [memsize-1:0];
assign dout = mem[addr];
always #(cs or wr)
begin
if(wr) mem[addr]= din;
end
endmodule
The test bench for the above code is :
module ramtest;
// Inputs
reg cs;
reg wr;
reg [9:0] addr;
reg [15:0] din;
// Outputs
wire [15:0] dout;
integer k,myseed;
// Instantiate the Unit Under Test (UUT)
ram uut (
.cs(cs),
.wr(wr),
.addr(addr),
.din(din),
.dout(dout)
);
initial begin
for(k = 0; k<=1023; k = k+1)
begin
din = k % 256; wr = 1; cs= 1;addr= k ;
end
repeat(20)
begin
#2 addr = $random(myseed) % 1024 ;
wr = 0; cs =1;
$display("Address = %5d, data = %4d",addr,dout);
end
end
initial myseed = 35 ;
endmodule
Several errors:
Your wr and cs do not change so the always #( cs or wr) is only
entered once for write and once for read.
Your 'write' code in
the testbench for(k = 0; k<=1023; k = k+1) does not have a delay.
So there is no time for the write to actually happen.
However the biggest danger is that you 'just' add the address to the sensitivity list and it all 'works':
always #(cs or wr or addr)
It would probably have helped you if you had first looked up a datasheet of an async RAM. They do not work the way you model it. There data is stored when the CS or Write goes away (which ever first). The data and address have to be stable a certain time before and after that.
In your model you change the address whilst keeping the WR and CS active. In real life the address does not change from one value to another instantaneous. It will go from for example 0000 to 000F but the bits will change one at a time: 0000 => 0004 => 0005 => 000D ==> 000F. Thus you could have messed up the contents of 5 different addresses.
Make a write signal from CS and WR: do_write = cs & wr; Do the actual write when that signal goes away: always #(negedge do_write) .....`.
It appears that you are trying to write an asynchronous RAM. In that case, you need to also add addr and din to your sensitivity list. Also, dout should get mem[addr] when cs is high and wr is low. In your testbench, you need to add a delay in your for loop before supplying the next input. One possible implementation is as follows:
module ram(oe, cs, wr, addr, din, dout);
parameter adds = 6,
wsize = 16,
memsize = 1 << adds;
input cs, wr;
input oe; // output enable
input [adds-1:0] addr;
input [wsize-1:0] din;
output [wsize-1:0] dout;
reg [wsize-1:0] dout;
reg [wsize-1:0] mem [memsize-1:0];
always #(cs, wr, oe, addr, din)
begin
if (cs) begin
if (wr) mem[addr] <= din;
else if (oe) dout <= mem[addr];
end
end
endmodule
And corresponding testbench could be:
module ram_tb;
parameter adds = 6,
wsize = 16,
memsize = 1 << adds;
reg cs, wr;
reg oe; // output enable
reg [adds-1:0] addr;
reg [wsize-1:0] din;
wire [wsize-1:0] dout;
integer k, myseed;
// Instantiate the Unit Under Test (UUT)
ram uut (
.cs(cs),
.wr(wr),
.oe(oe),
.addr(addr),
.din(din),
.dout(dout)
);
initial myseed = 35;
initial begin
for(k = 0; k < memsize; k = k+1) begin
#2
din <= k % 256;
wr <= 1;
cs <= 1;
addr <= k;
oe <= 0;
end
repeat(20) begin
#2
addr <= ($random(myseed)) % memsize ;
wr <= 0;
cs <= 1;
oe <= 1;
$display("Address = %5d, data = %4d", addr, dout);
end
#10 $finish;
end
endmodule

My verilog VGA driver causes the screen to flicker (Basys2)

I'm trying to recreate Adventure(1979) in Verilog and so far I have character movement, collision and map generation done. It didn't flicker that much before I separated the maps into modules now it flickers constantly. When I was looking up this issue, I found out that the clock on the Basys2 board is pretty noisy and could be the culprit. However, putting the maps into modules shouldn't have made it worse unless I messed something up. Any idea what happened?
Here's my 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)
);
always #(posedge clk_vga) begin
if(reset)begin
currentMap <= 0;
end
end
always #(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 the 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;
always #(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 the VGA driver which is connected to my 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. back 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. back 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 the clock by half a period (negates it)
//see timing diagrams for a better understanding of the reason for this
clock_shift clk_shift(.clk_in(clk_25MHz), .clk_out(CLK_DATA));
//simulate the vertical and horizontal positions
always #(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
always #(posedge clk_25MHz)
if((CurHPos < HSyncWidth) && ~RESET)
HS <= 1;
else
HS <= 0;
//VSync logic
always #(posedge clk_25MHz)
if((CurVPos < VSyncWidth) && ~RESET)
VS <= 1;
else
VS <= 0;
//Horizontal logic
always #(posedge clk_25MHz)
if((CurHPos >= HSyncWidth + HFrontPorch) && (CurHPos < HSyncWidth + HFrontPorch + HDisplayArea) || RESET)
HBlank_reg <= 0;
else
HBlank_reg <= 1;
//Vertical logic
always #(posedge clk_25MHz)
if((CurVPos >= VSyncWidth + VFrontPorch) && (CurVPos < VSyncWidth + VFrontPorch + VDisplayArea) || RESET)
VBlank_reg <= 0;
else
VBlank_reg <= 1;
//Do not output any color information when we are in the vertical
//or horizontal blanking areas. Set a boolean to keep track of this.
always #(posedge clk_25MHz)
if((HBlank_reg || VBlank_reg) && ~RESET)
Blank <= 1;
else
Blank <= 0;
//Keep track of the current "real" X position. This is the actual current X
//pixel location abstracted away from all the timing details
always #(posedge clk_25MHz)
if(HBlank_reg && ~RESET)
CurrentX <= 0;
else
CurrentX <= CurHPos - HSyncWidth - HFrontPorch;
//Keep track of the current "real" Y position. This is the actual current Y
//pixel location abstracted away from all the timing details
always #(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;
always #(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 this as an answer because I cannot put a photo in a comment.
Is this what your design looks like?
My only guess ATM is that you might have misplaced some ports during instantiation of vga_driver and/or map_generator (if you used the old style instantiation). Nevertheless, I'm going to check VGA timmings, as I can see a strange vertical line at the left of the screen, as if the hblank interval was visible.
By the way: I've changed the way you generate the display. You use regs for HS, VS, etc, which get updated the next clock cycle. I treat display generation as a FSM, so outputs come from combinational blocks triggered by certain values (or range of values) from the counters. Besides, I start horizontal and vertical counters so position (0,0) measured in pixel coordinates in the screen actually maps to values (0,0) from horizontal and vertical counters, so no arithmetic needed.
This is my 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;
always #(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
always #* 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
always #* 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 is instantiated by your vga_driver module, which becomes nothing but a wrapper for this 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 that in map_generator, the first if statement in this always block will never be true. We can forget about it, as the VGA display module will blank RGB outputs when needed.
always #(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 the same approach, I've converted the map generator module to be a combinational module. For example, for map 0 (the castle -without the castle, I see-) it is like 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;
always #* 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 a FSM whose output is the colour that goes in a pixel. The input being the coordinates of the current pixel.
So when it is time to display map 0, map_generator simply switches to it based upon the 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)
);
always #(posedge clk) begin
if(reset) begin
currentMap <= 0;
end
end
always #* begin
if(currentMap == 6'b000000) begin
mColor = castle_map;
end
//Add more maps later
end
endmodule
This may look like a lot of comb logic is generated and so glitches may happen. It's actually very fast, no noticeable glitches on screen, and you can use the actual current x and y coordinates to choose what to display on screen. Thus, no need for an inverted clock. My final version of your design has only one 25MHz clock.
By the way, you want to keep device dependent constructions away from your design, placing things like clock generators in separate modules that will be connected to your design in the top module, which should be the only device dependent module.
So, I've written a device-agnostic adventure module, which will contain the 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 actually used in the design yet
wire [8:0] playerPosY = 9'd240; // no actually used in the 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 is not complete: it lacks inputs from joystick or any other input device to update player current position. For now, player current position is fixed.
The top level design (TLD) is written exclusively for the FPGA trainer you have. It is here where you need to generate proper clocks using your device's available resources, such as the 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 this module (generated by the 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 it is safe (for Xilinx devices) to use a simple clock divider as you did. If you fear that the synthesizer won't treat your divided clock as an actual clock, add a BUFG primitive to route the output from the divider to a global buffer so it can be used as a clock with no problems (see the module above for an example on how to do this).
As a final note, you may want to add more independency from the final device, by using 24-bit colours for your graphics. At the TLD, you will use the actual number of bits per colour component you really have, but if you move from the Basys2 with 8-bit colour trainer board to the, say, Nexys4 board, with 12-bit colour, you will automatically enjoy a richer output display.
Now, it looks like this (no vertical bars at the left, and colours seem to be more vibrant)

Resources