'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