Search code examples
verilogsystem-verilogverificationtest-bench

Testbench of a simple compare-two-values design output is always x


Here is the design that is supposed to compare two floating point numbers:

// IEEE 764: FP[31] = sign, FP[30:23] = exp, FP[22:0] = mantissa
module compare_fp(input [31:0] floatA,
                  input [31:0] floatB,
                  output reg [1:0] compareRes);
  
  // equal: 2, A>B: 1, A<B: 0
  
  assign compareRes[1] = (floatA == floatB); // if they are equal, set the MSB of output to 1
  // if A > B set the LSB of output to 1
  // 1. A>0, B<0 
  // 2. sign same, compare exp
  // 3. sign and exp same, compare matissa
  assign compareRes[0] = ((!floatA[31]) && floatB[31]) || 
    (((floatA[31] & floatB[31]) == 1) && (floatA[30:23] < floatB[30:23])) ||
    (((floatA[31] | floatB[31]) == 0) && (floatA[30:23] > floatB[30:23])) ||
    (((floatA[31] & floatB[31]) == 1) && (floatA[30:23] == floatB[30:23]) && (floatA[22:0] < floatB[22:0])) ||
    (((floatA[31] | floatB[31]) == 0) && (floatA[30:23] == floatB[30:23]) && (floatA[22:0] > floatB[22:0]));
    
endmodule

I wrote a simple testbench following https://verificationguide.com/systemverilog-examples/systemverilog-testbench-example-adder-2/, and I attached it here:

// Code your testbench here
// or browse Examples
class transaction;
 
  randc bit [31:0] floatA;
  randc bit [31:0] floatB;
  logic [1:0] compareRes;
  
  // constrain the input
  //constraint constraint_float {floatA==floatB;}
  
  // actual float
  shortreal float_A;
  shortreal float_B;  
  
  function void display(string name);
    $display("---------");
    $display("%s",name);
    $display("a = %0f, b = %0f, result = %0d", float_A, float_B, compareRes);
    $display("a = %0b, b = %0b, result = %0d", floatA, floatB, compareRes);
    $display("---------");
    
  endfunction
  
endclass

class generator;
  rand transaction trans;
  mailbox gen2drv;
  event ended;
  int generateCount; // number of cases
  
  function new(mailbox gen2drv);
    this.gen2drv = gen2drv;
  endfunction
  
  task main();
    
    repeat(generateCount) begin
        // create and randomize
        trans = new();
        if( !trans.randomize()) $fatal("gen: trans randomization failed");
        // calculating actual number
        trans.float_A = $bitstoshortreal(trans.floatA);
        trans.float_B = $bitstoshortreal(trans.floatB);
        gen2drv.put(trans);  
    end
    -> ended; //end of generation
  endtask
  
endclass

interface intf;
  bit [31:0] floatA;
  bit [31:0] floatB;
  logic [1:0] compareRes;
endinterface


class driver;
  int transaction_count;
  mailbox gen2drv;
  virtual intf vif;
  
  // constructor
  function new(virtual intf vif, mailbox gen2drv);
    this.vif = vif;
    this.gen2drv = gen2drv;
  endfunction
  
  task main;
    forever begin
      transaction trans;
      gen2drv.get(trans);
      vif.floatA = trans.floatA;
      vif.floatB = trans.floatB;
      trans.display("[ Driver ]");
      transaction_count++;
    end
    
  endtask
  
endclass


class environment;
  generator gen;
  driver drv;
  mailbox gen2drv;
  virtual intf vif;
  
  //constructor
  function new(virtual intf vif);
    this.vif = vif;
    gen2drv = new();
    gen = new(gen2drv);
    drv = new(vif, gen2drv);
  endfunction
  
  task test();
    fork
      gen.main();
      drv.main();
    join_any
  endtask
  
  task post_test();
    wait(gen.ended.triggered);
    wait(gen.generateCount == drv.transaction_count);
  endtask
  
  task run;
    test();
    post_test();
    $finish;
  endtask
  
endclass

program test(intf intf);
  
  environment env;
  
  initial begin
    env = new(intf);
    env.gen.generateCount = 3;
    env.run();
  end

endprogram

module tb;
  
  intf intf1();
  
  test t1(intf1);
  
  compare_fp dut (.floatA(intf1.floatA), .floatB(intf1.floatB), .compareRes(intf1.compareRes));
  
endmodule

But the output (compareRes) is always x.

I then wrote a simpler testbench where I manually choose input, and the design works just fine. Below is the simple testbench:

// Code your testbench here
// or browse Examples
interface test;

  logic [31:0] floatA;
  logic [31:0] floatB;
  logic [1:0] compareRes;

endinterface


module tb;
    test st();
    shortreal float_A;
        shortreal float_B;

  
  compare_fp dut(.floatA(st.floatA), .floatB(st.floatB), .compareRes(st.compareRes));
  
    
    initial begin
      #10 st.floatA = 32'b11000000010101001100000001011001;
      #10 st.floatB = 32'b01000000010101000111101011100001;
      #10 float_A = $bitstoshortreal(st.floatA);
      #10 float_B = $bitstoshortreal(st.floatB);

      #10 $display("%f %f is %0d", float_A, float_B, st.compareRes);
    end
  

endmodule

So maybe there is something wrong with the value delivery in my testbench? Can anyone give me some suggestion as how to debug the testbench?


Solution

  • In your transaction display function, compareRes is always x because you never assigned a value to it. You declared compareRes as a logic type, which defaults to x, but then you never changed its value.

    You correctly connected the dut compareRes signal to the intf1 interface in the testbench, but you never made an assignment from the interface compareRes to the transaction compareRes. This is usually done in a testbench monitor. You should add a monitor to your testbench where you make the assignment. If you are going through all the trouble of creating a class-based testbench, consider adopting the UVM, which is now standard in the industry.

    In the driver, I added the assignment with a delay to try to avoid race conditions:

      task main;
        forever begin
          transaction trans;
          gen2drv.get(trans);
          vif.floatA = trans.floatA;
          vif.floatB = trans.floatB;
          #1 trans.compareRes = vif.compareRes;
          trans.display("[ Driver ]");
          transaction_count++;
        end
      endtask
    

    When I run the simulation, I see result = 1 and 0, instead of x. Although this might work for you, this is not the recommended approach.

    Also, you should not rely only on $display output. You should also dump a waveform database and look at signals in a waveform viewer. I did so and was able to confirm the the signals in the dut had the expected values.

    I tried reading the link that you provided to understand why they created a testbench without a monitor, but I found the pop-up ads too distracting.