I have implemented a BCD converter on which I worked from two different PCs:
As an IDE, I used Vivado 2024.2 for both computers and a Basys3 development board for implementation.
Upon running a behavioral simulation on the BCD (same code for both PCs), on the computer, whatever I do, the output stays at X, but on the laptop the output changes correctly, according to the implemented design. For both PCs, from synthesis onward, the results are the same and the program works correctly after generating the bitstream and loading the program onto the board.
For completeness sake, I will also include the code of the design:
module counter #(parameter SIZE = 4) (
input clk,
input rst,
input en,
output reg [SIZE-1:0] out
);
always @(posedge clk, negedge rst) begin
if(!rst)
out <= 0;
else
if (en) out <= out + 1;
end
endmodule
module loadRegister #(DATA_SIZE = 1, REG_SIZE = 2) (
input clk,
input rst,
input [DATA_SIZE-1:0] Din,
input Sin,
input [REG_SIZE - 1 : 0] cellAdr,
input serialLoad,
input parallelLoad,
output reg [DATA_SIZE * (2 ** REG_SIZE) - 1 : 0] data
);
always @(posedge clk) begin
if(!rst) data <= 0;
else if(parallelLoad) begin
data[DATA_SIZE*cellAdr+:DATA_SIZE] <= Din;
end
else if(serialLoad) begin
data <= {data[DATA_SIZE * (2 ** REG_SIZE) - 2 : 0],Sin};
end
else begin
data <= data;
end
end
endmodule
module Binary2BCDTranscoder #(parameter INPUT_SIZE = 12, parameter OUTPUT_DIGITS = 4, parameter ICNTR_SIZE = 4, parameter JCNTR_SIZE = 2) (
input clk,
input rst,
input convStart,
input [INPUT_SIZE-1:0] data,
output reg convDone,
output reg [4 * OUTPUT_DIGITS - 1:0] convOut
);
localparam WAIT = 2'b00;
localparam CONV_CHK = 2'b01;
localparam FINISHED = 2'b11;
reg iRst, jRst, iEn, jEn;
wire [ICNTR_SIZE-1:0] i;
wire [JCNTR_SIZE-1:0] j;
reg [1:0] state, next_state;
reg [INPUT_SIZE-1:0] inner_in;
reg loadRegRst;
reg [3:0] loadRegDin;
reg loadRegSin;
reg loadRegSerial, loadRegParallel;
reg [1:0] loadRegCellAdr;
wire [4 * OUTPUT_DIGITS - 1:0] inner_out;
always @(posedge clk) begin
if(!rst) state <= WAIT;
else state <= next_state;
end
always @(posedge clk) begin
if(!rst) inner_in <= 0;
else if(state==WAIT) inner_in <= data;
end
always @(posedge clk) begin
if(state==CONV_CHK & convDone) convOut <= inner_out;
end
counter #(.SIZE(ICNTR_SIZE)) iCounter (
.clk(clk),
.rst(iRst),
.en(iEn),
.out(i)
);
counter #(.SIZE(JCNTR_SIZE)) jCounter (
.clk(clk),
.rst(jRst),
.en(jEn),
.out(j)
);
loadRegister #(.DATA_SIZE(4),.REG_SIZE(2)) innerOutReg (
.clk(clk),
.rst(loadRegRst),
.Din(loadRegDin),
.Sin(loadRegSin),
.serialLoad(loadRegSerial),
.parallelLoad(loadRegParallel),
.cellAdr(loadRegCellAdr),
.data(inner_out)
);
always @(*) begin
case(state)
WAIT: begin
convDone = 0;
iRst = 0;
iEn = 0;
jRst = 0;
jEn = 0;
next_state = convStart ? CONV_CHK : WAIT;
end
CONV_CHK: begin
if(i == INPUT_SIZE) begin
convDone = 1;
next_state = FINISHED;
iRst = 0;
jRst = 0;
iEn = 0;
jEn = 0;
end
else if(j == OUTPUT_DIGITS-1) begin
next_state = CONV_CHK;
convDone = 0;
iRst = 1;
jRst = 0;
iEn = 1;
jEn = 0;
end
else begin
next_state = CONV_CHK;
convDone = 0;
iRst = 1;
jRst = 1;
iEn = 0;
jEn = 1;
end
end
FINISHED: begin
next_state = WAIT;
convDone = 1;
iRst = 0;
iEn = 0;
jRst = 0;
jEn = 0;
end
default: begin
next_state = WAIT;
convDone = 0;
iRst = 0;
iEn = 0;
jRst = 0;
jEn = 0;
end
endcase
end
always @(*) begin
case(state)
WAIT: begin
loadRegRst = 0;
loadRegDin = 0;
loadRegSin = 0;
loadRegParallel = 0;
loadRegSerial = 0;
loadRegCellAdr = 0;
end
CONV_CHK: begin
loadRegRst = 1;
if (iEn) begin
loadRegDin = 0;
loadRegSin = inner_in[INPUT_SIZE-1-i];
loadRegParallel = 0;
loadRegSerial = 1;
loadRegCellAdr = 0;
end
else if (jEn) begin
if(inner_out[4*j+:4] >= 5) begin
loadRegDin = inner_out[4*j+:4] + 3;
loadRegSin = 0;
loadRegParallel = 1;
loadRegSerial = 0;
loadRegCellAdr = j;
end
else begin
loadRegDin = 0;
loadRegSin = 0;
loadRegParallel = 0;
loadRegSerial = 0;
loadRegCellAdr = 0;
end
end
else begin
loadRegDin = 0;
loadRegSin = 0;
loadRegParallel = 0;
loadRegSerial = 0;
loadRegCellAdr = 0;
end
end
FINISHED: begin
loadRegRst = 1;
loadRegDin = 0;
loadRegSin = 0;
loadRegParallel = 0;
loadRegSerial = 0;
loadRegCellAdr = 0;
end
default: begin
loadRegRst = 0;
loadRegDin = 0;
loadRegSin = 0;
loadRegParallel = 0;
loadRegSerial = 0;
loadRegCellAdr = 0;
end
endcase
end
endmodule
module Bin2BCDTest();
reg clk, rst, convStart;
reg [11:0] data;
wire convDone;
wire [15:0] convOut;
Binary2BCDTranscoder #(.INPUT_SIZE(12),.OUTPUT_DIGITS(4),.ICNTR_SIZE(4),.JCNTR_SIZE(2)) dut (
.clk(clk),
.rst(rst),
.convStart(convStart),
.data(data),
.convDone(convDone),
.convOut(convOut)
);
initial begin
clk = 0;
forever #5 clk = !clk;
end
initial begin
rst = 0;
convStart = 0;
#10 rst = 1;
#10 data = 1023;
#10 convStart = 1;
#20 convStart = 0;
#1000;
#10 data = 512;
#10 convStart = 1;
#20 convStart = 0;
#1000;
#10 data = 3683;
#10 convStart = 1;
#20 convStart = 0;
#1000;
#10 data = 4091;
#10 convStart = 1;
#20 convStart = 0;
#1000;
#10 data = 4090;
#10 convStart = 1;
#20 convStart = 0;
#1000;
$stop;
end
endmodule
Why does this happen? Is it a problem between the two differing operating systems, but, if so, why does it only affect simulation and not synthesis?
If you also have Verilog code for the testbench, you should show that code as well.
Depending on how you run your simulation, I see how the Binary2BCDTranscoder
convOut
output could remain unknown (X
).
You did not reset this output signal. You declared it as a reg
type, which means it is initialized in simulation as x
. The output will remain at x
until you satisfy the if
condition below:
always @(posedge clk) begin
if(state==CONV_CHK & convDone) convOut <= inner_out;
end
I created a simple testbench to run a simulation, and I always see the output as x
:
module tb;
parameter INPUT_SIZE = 12;
parameter OUTPUT_DIGITS = 4;
bit clk;
bit convStart;
bit [INPUT_SIZE-1:0] data;
bit rst;
wire convDone;
wire [4*OUTPUT_DIGITS-1:0] convOut;
Binary2BCDTranscoder dut (
.clk (clk),
.convStart (convStart),
.data (data),
.rst (rst),
.convDone (convDone),
.convOut (convOut)
);
always #5 clk++;
initial begin
$dumpvars;
repeat (2) @(posedge clk);
rst <= 1;
repeat (2) @(posedge clk);
convStart <= 1;
repeat (1) @(posedge clk);
convStart <= 0;
#500 $finish;
end
endmodule
If I use your reset signal to reset the output, I see the output become known (0):
always @(posedge clk) begin
if (!rst) convOut <= 0;
else if (state==CONV_CHK & convDone) convOut <= inner_out;
end
Now that you have added the testbench code to the question, this confirms my results above. You could reset the convOut
signal as described above to eliminate the x
.