'Verilog DUT System Verilog testbench: output to wire assignment 1s replaced with Xs
I have a Modelsim testbench in System Verilog testing a Verilog top-level module (ufm1
) with another Verilog module (wishbone
) used inside it, there's also a System Verilog "stub" (wishbone_sim
) that is connected to the DUT in the testbench.
The DUT and the inner module were originally in System Verilog and worked fine but I had to convert them to Verilog to be able to use Diamond LSE (leaving the testbench in System Verilog)
The inner module inside the DUT has an output which I'm connecting to a wire
(was originally a reg
in the System Verilog version because it gave an error otherwise) inside the DUT and then using the wire to assign to a reg
in a procedural block inside the DUT.
Inside the inner module this output is basically assigned directly from an input.
Now when I'm simulating this the input in the inner module is fine but the output (should be the same because it is a direct assign
) is different with Xs in place of 1s.
The issue only starts appearing when the output of the inner module (rd_data
) is assigned to the wire (wb_rd_data
), which looks strange to be because I don't see how connecting an output port to a wire would affect its value.
The wire inside the DUT is wb_rd_data
which is connected to the rd_data
port of the inner wishbone
module.
How do I fix this?
DUT:
module ufm1(clk, ufm_wr_rq, ufm_rd_rq, ufm_wr_data, ufm_wr_ack, ufm_rd_data, ufm_rd_ack, ufm_done, wb_clk, wb_rst, wb_cyc, wb_stb, wb_we, wb_addr, wb_dat_i, wb_dat_o, wb_ack);
input clk;
input ufm_wr_rq, ufm_rd_rq;
input [7:0] ufm_wr_data;
output reg ufm_wr_ack;
output [7:0] ufm_rd_data;
output ufm_rd_ack;
output reg ufm_done = 0;
output wb_clk;
output wb_rst;
output wb_cyc;
output wb_stb;
output wb_we;
output [7:0] wb_addr;
output [7:0] wb_dat_i;
input [7:0] wb_dat_o;
input wb_ack;
parameter WR_OF = 8'h10;
parameter WR_CF = 8'h11;
parameter WR = 8'h12;
parameter WRE = 8'h13;
parameter RD = 8'h20;
parameter RDI = 8'h21;
parameter JMPI = 8'h30;
parameter END = 8'h40;
parameter Z00 = 8'h00;
parameter FF = 8'hFF;
parameter WR_CMDS = 4'h1;
parameter RD_CMDS = 4'h2;
parameter JMP_CMDS = 4'h3;
parameter END_CMDS = 4'h4;
parameter CMD_EN_CFG_I = 8'h74;
parameter CMD_DIS_CFG_I = 8'h26;
parameter CMD_RD_ST = 8'h3C;
parameter CMD_ZERO_ADDR = 8'h47;
parameter CMD_RD_UFM = 8'hCA;
parameter CMD_WR_UFM = 8'hC9;
parameter CMD_ERASE_UFM = 8'hCB;
parameter CMD_BYPASS = 8'hFF;
parameter ST_IDL = 3'd0;
parameter ST_NEXT_CMD = 3'd1;
parameter ST_WT_WR = 3'd2;
parameter ST_WT_RD = 3'd3;
parameter CMDS_NUM = 9'd196;
parameter WR_PRG_START_INDEX = 9'd103;
parameter [CMDS_NUM*8-1:0] CMDS = {
//**** Erase and read
//Enabled configuration interface
WR_OF, WR, CMD_EN_CFG_I, WR, 8'h08, WR, Z00, WR, Z00, WR_CF,
//Read config status register and repeat till not busy
WR_OF, WR, CMD_RD_ST, WR, Z00, WR, Z00, WR, Z00, RD, RD, RD, WR_CF, JMPI,
//Zero UFM address
WR_OF, WR, CMD_ZERO_ADDR,WR, Z00, WR, Z00, WR, Z00, WR_CF,
//Read UFM page 0 (16 bytes)
WR_OF, WR, CMD_RD_UFM, WR, Z00, WR, Z00, WR, 8'h01,
RDI,RDI,RDI,RDI,RDI,RDI,RDI,RDI,RDI,RDI,RDI,RDI,RDI,RDI,RDI,RDI,
WR_CF,
//Erase UFM
WR_OF, WR, CMD_ERASE_UFM,WR, Z00, WR, Z00, WR, Z00, WR_CF,
//Read config status register and repeat till not busy
WR_OF, WR, CMD_RD_ST, WR, Z00, WR, Z00, WR, Z00, RD, RD, RD, WR_CF, JMPI,
//Disable configuration interface
WR_OF, WR, CMD_DIS_CFG_I, WR, Z00, WR, Z00, WR_CF,
//Bypass (NOP)
WR_OF, WR, CMD_BYPASS, WR, FF, WR, FF, WR, FF, WR_CF,
END,
//**** Write
//Enabled configuration interface
WR_OF, WR, CMD_EN_CFG_I, WR, 8'h08, WR, Z00, WR, Z00, WR_CF,
//Read config status register and repeat till not busy
WR_OF, WR, CMD_RD_ST, WR, Z00, WR, Z00, WR, Z00, RD, RD, RD, WR_CF, JMPI,
//Zero UFM address
WR_OF, WR, CMD_ZERO_ADDR,WR, Z00, WR, Z00, WR, Z00, WR_CF,
//Write UFM page 0 (16 bytes)
WR_OF, WR, CMD_WR_UFM, WR, Z00, WR, Z00, WR, 8'h01,
WRE,WRE,WRE,WRE,WRE,WRE,WRE,WRE,WRE,WRE,WRE,WRE,WRE,WRE,WRE,WRE,
WR_CF,
//Read config status register and repeat till not busy
WR_OF, WR, CMD_RD_ST, WR, Z00, WR, Z00, WR, Z00, RD, RD, RD, WR_CF, JMPI,
//Disable configuration interface
WR_OF, WR, CMD_DIS_CFG_I, WR, Z00, WR, Z00, WR_CF,
//Bypass (NOP)
WR_OF, WR, CMD_BYPASS, WR, FF, WR, FF, WR, FF, WR_CF,
END
};
reg wb_wr_rq = 0, wb_rd_rq = 0;
reg [7:0] wb_wr_data = 0;
wire [7:0] wb_rd_data = 0;
reg [7:0] addr = 0;
wishbone wishbone(.clk(clk), .wr_rq(wb_wr_rq), .rd_rq(wb_rd_rq), .wr_data(wb_wr_data), .rd_data(wb_rd_data),
.addr(addr), .done(wb_done), .wb_clk(wb_clk), .wb_rst(wb_rst), .wb_cyc(wb_cyc), .wb_stb(wb_stb),
.wb_we(wb_we), .wb_addr(wb_addr), .wb_dat_i(wb_dat_i), .wb_dat_o(wb_dat_o), .wb_ack(wb_ack));
reg [2:0] st = 0;
reg [2:0] prev_st = 0;
reg [7:0] prev_cmd = 0;
reg [CMDS_NUM*8-1:0] cmds = CMDS;
reg [8:0] cmd_index = 0;
reg [7:0] lst_rd_data = 0;
wire [7:0] cur_cmd = cmds[((CMDS_NUM-cmd_index-1)*8)+:8];
wire [7:0] next_cmd = cmds[((CMDS_NUM-cmd_index-2)*8)+:8];
assign is_cmd_wre = (cur_cmd == WRE);
assign is_1cmd_wr = (cur_cmd == WRE || cur_cmd == WR_OF || cur_cmd == WR_CF);
assign ufm_rd_ack = (prev_st == ST_WT_RD) && (prev_cmd == RDI) && wb_done;
assign ufm_rd_data = ufm_rd_ack ? wb_rd_data : 0;
always @(posedge clk)
begin
prev_st <= st;
prev_cmd <= cur_cmd;
case(st)
ST_IDL:
begin
ufm_done <= 0;
if(ufm_rd_rq)
begin
st <= ST_NEXT_CMD;
end
else
if(ufm_wr_rq)
begin
st <= ST_NEXT_CMD;
cmd_index <= WR_PRG_START_INDEX;
end
end
ST_NEXT_CMD:
case(cur_cmd[7:4])
WR_CMDS:
begin
wb_wr_rq <= 1;
wb_wr_data <= (cur_cmd == WR_OF) ? 8'h80 :
(cur_cmd == WR_CF ? 8'h00 : (is_cmd_wre ? ufm_wr_data : next_cmd));
addr <= (cur_cmd == WR_OF || cur_cmd == WR_CF) ? 8'h70 : 8'h71;
ufm_wr_ack <= is_cmd_wre;
st <= ST_WT_WR;
end
RD_CMDS:
begin
wb_rd_rq <= 1;
addr <= 8'h73;
st <= ST_WT_RD;
end
JMP_CMDS:
begin
st <= ST_NEXT_CMD;
if(lst_rd_data[4]) //if busy
begin
cmd_index <= cmd_index - 13; //assuming the previous command is reading the status register
end
else
begin
cmd_index <= cmd_index + 1;
end
end
END_CMDS:
begin
st <= ST_IDL;
cmd_index <= 0;
ufm_done <= 1;
end
endcase
ST_WT_WR:
begin
wb_wr_rq <= 0;
ufm_wr_ack <= 0;
if(wb_done)
begin
wb_wr_data <= 0; //todo: not necessary, can be removed if doesn't fit
cmd_index <= cmd_index + (is_1cmd_wr ? 1 : 2);
st <= ST_NEXT_CMD;
end
end
ST_WT_RD:
begin
wb_rd_rq <= 0;
if(wb_done)
begin
lst_rd_data <= wb_rd_data;
cmd_index <= cmd_index + 1;
st <= ST_NEXT_CMD;
end
end
endcase
end
endmodule
Inner Module:
module wishbone(clk, wr_rq, rd_rq, done, addr, wr_data, rd_data, wb_clk, wb_rst, wb_cyc, wb_stb, wb_we, wb_addr, wb_dat_i, wb_dat_o, wb_ack);
input clk;
input wr_rq, rd_rq;
output done;
input [7:0] addr;
input [7:0] wr_data;
output [7:0] rd_data;
output wb_clk;
output wb_rst;
output wb_cyc;
output wb_stb;
output wb_we;
output [7:0] wb_addr;
output [7:0] wb_dat_i;
input [7:0] wb_dat_o;
input wb_ack;
reg wr_in_progress = 0;
reg rd_in_progress = 0;
assign done = wb_ack;
assign wb_clk = clk;
assign wb_addr = (wr_in_progress || rd_in_progress) ? addr : 0;
assign wb_dat_i = wr_in_progress ? wr_data : 0;
assign rd_data = wb_dat_o;
assign wb_rst = 0;
assign wb_cyc = wr_in_progress || rd_in_progress;
assign wb_stb = wb_cyc;
assign wb_we = wr_in_progress;
always @(posedge clk)
begin
if(!wr_in_progress && !rd_in_progress)
begin
if(wr_rq)
begin
wr_in_progress <= 1;
end
else if(rd_rq)
begin
rd_in_progress <= 1;
end
end
else if(wr_in_progress && wb_ack)
begin
wr_in_progress <= 0;
end
else if(rd_in_progress && wb_ack)
begin
rd_in_progress <= 0;
end
end
endmodule
Testbench:
`timescale 100ps / 100ps
module ufm1_tb;
parameter WR_OF = 8'h10;
parameter WR_CF = 8'h11;
parameter WR = 8'h12;
parameter WRE = 8'h13;
parameter RD = 8'h20;
parameter RDI = 8'h21;
parameter JMPI = 8'h30;
parameter END = 8'h40;
parameter Z00 = 8'h00;
parameter FF = 8'hFF;
parameter WR_CMDS = 4'h1;
parameter RD_CMDS = 4'h2;
parameter JMS_CMDS = 4'h3;
parameter CMD_EN_CFG_I = 8'h74;
parameter CMD_DIS_CFG_I = 8'h26;
parameter CMD_RD_ST = 8'h3C;
parameter CMD_ZERO_ADDR = 8'h47;
parameter CMD_RD_UFM = 8'hCA;
parameter CMD_WR_UFM = 8'hC9;
parameter CMD_ERASE_UFM = 8'hCB;
parameter CMD_BYPASS = 8'hFF;
parameter CD = 200; //100ps*200=20nS (50MHz)
parameter HCD = CD/2;
parameter QCD = CD/4;
parameter IGNORE = 8'h00;
parameter BUSY = 8'h10;
parameter FREE = 8'h00;
parameter [7:0] DATA [] = '{
//**** erase/read
//First busy wait
IGNORE, IGNORE, BUSY,
IGNORE, IGNORE, FREE,
//UFM Page 0 read
8'hA0,8'hA1,8'hA2,8'hA3,8'hA4,8'hA5,8'hA6,8'hA7,8'hA8,8'hA9,8'hAA,8'hAB,8'hAC,8'hAD,8'hAE,8'hAF,
//Second busy wait
IGNORE, IGNORE, BUSY,
IGNORE, IGNORE, BUSY,
IGNORE, IGNORE, FREE,
//**** write
//First busy wait
IGNORE, IGNORE, BUSY,
IGNORE, IGNORE, FREE,
//Second busy wait
IGNORE, IGNORE, BUSY,
IGNORE, IGNORE, BUSY,
IGNORE, IGNORE, FREE
};
parameter [7:0] DELAYS [] = '{{dut.CMDS_NUM}{8'h0}};
parameter [7:0] WRDATA [] = '{8'hBF,8'hBE,8'hBD,8'hBC,8'hBB,8'hBA,8'hB9,8'hB8,8'hB7,8'hB6,8'hB5,8'hB4,8'hB3,8'hB2,8'hB1,8'hB0};
parameter ST_IDLE = 0;
parameter ST_READING = 1;
parameter ST_WRITING = 2;
parameter ST_FINISHED = 3;
reg clk = 0;
always #(HCD) clk = ~clk;
wishbone_sim
#(
.CD(CD),
.DATA(DATA),
.NUM_OPERATIONS(dut.CMDS_NUM),
.DELAYS('{{dut.CMDS_NUM}{0'h0}})
)
wb_sim(.wb_clk(dut.wb_clk), .wb_rst(dut.wb_rst), .wb_stb(dut.wb_stb), .wb_cyc(dut.wb_cyc), .wb_we(dut.wb_we), .wb_addr(dut.wb_addr), .wb_dat_i(dut.wb_dat_i), .wb_dat_o(dut.wb_dat_o), .wb_ack(dut.wb_ack));
reg ufm_wr_rq = 0;
reg ufm_rd_rq = 0;
reg [3:0] st = ST_IDLE;
reg [7:0] ufm_wr_data = 8'bZ;
reg [4:0] ufm_wr_data_idx = 0;
wire [7:0] ufm_rd_data;
wire [7:0] wb_addr;
wire [7:0] wb_dat_i;
wire [7:0] wb_dat_o;
ufm1 dut(clk, ufm_wr_rq, ufm_rd_rq, ufm_wr_data, ufm_wr_ack, ufm_rd_data, ufm_rd_ack, ufm_done,
wb_clk, wb_rst, wb_cyc, wb_stb, wb_we, wb_addr, wb_dat_i, wb_dat_o, wb_ack);
always @(posedge clk)
begin
case(st)
ST_IDLE:
begin
ufm_rd_rq <= 1;
st <= ST_READING;
end
ST_READING:
begin
ufm_rd_rq <= 0;
if(ufm_done)
begin
ufm_wr_rq <= 1;
ufm_wr_data <= WRDATA[ufm_wr_data_idx];
ufm_wr_data_idx <= ufm_wr_data_idx + 1;
st <= ST_WRITING;
end
end
ST_WRITING:
begin
ufm_wr_rq <= 0;
if(ufm_wr_ack)
begin
ufm_wr_data <= WRDATA[ufm_wr_data_idx];
ufm_wr_data_idx <= ufm_wr_data_idx + 1;
end
if(ufm_done) st <= ST_FINISHED;
end
endcase
end
endmodule
Solution 1:[1]
The issue was that I had a default value for the wb_rd_data
wire inside the DUT:
wire [7:0] wb_rd_data = 0;
So it should have been just
wire [7:0] wb_rd_data;
So it has multiple assignments to the same wire - from the port and the default value and the bits that conflicted came up as Xs.
I had to try to synthesize this in Diamond to find the issue.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | axk |