2
module dut_top;
    wire [31:0] ctrl_32bit;
    wire        ctrl_1bit;
    assign ctrl_32bit = 0;
    assign ctrl_1bit=0;
    
    initial begin #1000ns; end
endmodule

program automatic test;
    initial begin
        repeat(5) begin
            #100ns;
            force dut_top.ctrl_32bit[0] =~ dut_top.ctrl_32bit[0]; //LINE 1
            force dut_top.ctrl_1bit     =~ dut_top.ctrl_1bit;     //LINE 2
            force dut_top.ctrl_32bit[0] =  dut_top.ctrl_1bit;     //LINE 3
        end
    end
endprogram

My code is shown above. LINE 1 gets stuck. But after commenting out LINE 1, LINE 2 and LINE 3 work fine.

  1. What is the reason? I think it is related to timeslot, but I can't explain it myself.
  2. How should I solve this requirement?

I want to force a single bit in an array of bits every once in a while.

toolic
  • 57,801
  • 17
  • 75
  • 117
Jack
  • 46
  • 6

2 Answers2

1

The force statement does not deposit the value, it forcefully applies the expression to the target. It behaves similar to the concurrent assignment of an assign statement. Therefore force dut_top.ctrl_32bit[0] = ~dut_top.ctrl_32bit[0]; has zero-time feedback to itself.

To understand how force works when the right-hand-side is a variable, consider the following:

module force_example;
  reg [3:0] a,b;
  initial begin
    $monitor("a:%b b:%b @ %2t", $time);
    force b = a;
    for(a=0; a<5; a=a+1) #1;
    #1 $finish();
  end
endmodule

b will equal a even as a changes. Regardless of the simulator, it will will display the following:

a:0000 b:0000 @  0
a:0001 b:0001 @  1
a:0010 b:0010 @  2
a:0011 b:0011 @  3
a:0100 b:0100 @  4
a:0101 b:0101 @  5

You need to break the feedback loop to resolve your force issue. There are two ways to do this:

  1. Conditional logic and force to a constant:

    program automatic test;
        initial begin
            repeat(5) begin
                #100ns;
                if (dut_top.ctrl_32bit[0])
                    force dut_top.ctrl_32bit[0] = 1'b0;
                else
                    force dut_top.ctrl_32bit[0] = 1'b1;
                if (dut_top.ctrl_1bit)
                    force dut_top.ctrl_1bit     = 1'b0;
                else
                    force dut_top.ctrl_1bit     = 1'b1;
                force dut_top.ctrl_32bit[0] =  dut_top.ctrl_1bit; // This is okay
            end
        end
    endprogram
    
  2. Intermediate variables:

    program automatic test;
        logic [31:0] tb_ctrl_32bit;
        logic        tb_ctrl_1bit;
        initial begin
            tb_ctrl_32bit = dut_top.ctrl_32bit;
            tb_ctrl_1bit  = dut_top.ctrl_1bit;
            force dut_top.ctrl_32bit[0] =~ tb_ctrl_32bit[0]; //LINE 1
            force dut_top.ctrl_1bit     =~ tb_ctrl_1bit;     //LINE 2
            force dut_top.ctrl_32bit[0] =  tb_ctrl_1bit;     //LINE 3
            repeat(5) begin
                #100ns;
                tb_ctrl_32bit[0] =~ dut_top.ctrl_32bit[0];
                tb_ctrl_1bit     =~ dut_top.ctrl_1bit;
                tb_ctrl_32bit[0] =  dut_top.ctrl_1bit;
            end
        end
    endprogram
    
Greg
  • 18,111
  • 5
  • 46
  • 68
  • Got it. The concept that ``force`` acts like ``assign`` is very critical. Thanks a lot! – Jack Feb 27 '23 at 07:53
0

Your code ran fine for me on one simulator (Cadence), but it behaved the way you described on another simulator (Synopsys VCS).

VCS showed me this message:

No TimeScale specified

Warning-[DRTZ] Detect delay value roundoff to 0
  Delay from design or SDF file roundoff to 0 based on timescale
  Please use switch -diag timescale to dump detailed information.

That made me wonder what timescale was being used, so I added the $printtimescale task to each module. Cadence uses 1ns for both the time unit and precision by default, and VCS uses 1s. For VCS, since your delays (1000ns and 100ns) are smaller than the default precision (1s), the delays are set to 0.

Since the IEEE Std 1800-2017 does not specify the default timescale, you must set it explicitly. One way is to use the `timescale compiler directive as shown below (refer to IEEE Std 1800-2017, section 22.7 `timescale):

`timescale 1ns/1ns

module dut_top;
    wire [31:0] ctrl_32bit;
    wire        ctrl_1bit;
    assign ctrl_32bit = 0;
    assign ctrl_1bit  = 0;
    
    initial $printtimescale;
    initial begin #1000ns; end
endmodule

program automatic test;
    initial begin
        $printtimescale;
        $monitor($time,, dut_top.ctrl_1bit,, dut_top.ctrl_32bit[0]);
        repeat(5) begin
            #100ns;
            force dut_top.ctrl_32bit[0] = ~dut_top.ctrl_32bit[0]; //LINE 1
            force dut_top.ctrl_1bit     = ~dut_top.ctrl_1bit;     //LINE 2
            force dut_top.ctrl_32bit[0] =  dut_top.ctrl_1bit;     //LINE 3
        end
    end
endprogram

Here is the VCS output for me (runnable on EDA playground):

TimeScale of dut_top is 1 ns / 1 ns 
TimeScale of test is 1 ns / 1 ns 
                   0 0 0
                 100 1 1
                 200 0 0
                 300 1 1
                 400 0 0
                 500 1 1
$finish at simulation time                  500

I added the $monitor task to display the output. As you can see, both bits are toggling, as expected. There is no problem with your force statements.

toolic
  • 57,801
  • 17
  • 75
  • 117