Logic array not updated on continuous assignment

Dear all, I have an extremely frustrating situation with a logic array not being updated when a single bit of the array is forced to a constant through the force directive. I’ll illustrate what I mean with an excerpt of the code.

We have a memory array in the DUT that is declared as a reg, in the TB we instantiate a module that we call ‘interface_module’ that uses an SV interface for integration with the environment, so here they are:


interface int_obs_if();

logic [15:0] ram_matrix[0:511];
//... many other signals

modport ram_mapping(input clk, input wr, input ce, input ram_matrix);
//... many other modports

endinterface // int_obs_if


module int_obs_ifm(int_obs_if i_int_obs_if);

wire [15:0] ram_matrix[0:511];
//... many other signals

assign ram_matrix[0:511] = DUT.RAM.ram_matrix[0:511];
//... many more assignments

i_int_obs_if.ram_matrix[0:511] = ram_matrix[0:511];
//... many more assignments
endmodule // int_obs_ifm

Now the int_obs_ifm module is connected through its interface to another component which consumes the data in the ram_matrix in the following way:


module ram_manager(int_obs_if i_int_obs_if);

always @(posedge i_int_obs_if.clk) begin
  if (i_int_obs_if.ce && i_int_obs_if.wr) begin
    somelocalvar = i_int_obs_if.ram_matrix[i_int_obs_if.addr];
  end
end

// do something with somelocalvar

endmodule // ram_manager


Until now everything seems ok and indeed everything seems to work. But the real issue come when another module is trying to force a value directly in the DUT ram_matrix in the following way:


module fault_injection_ifm();

  case (injection_type)
    SOME_FAULT: begin
      if (injection_active) begin
        // Forcing one bit of the array to flip
        tmp = DUT.RAM.ram_matrix[someaddr][somebit];
        force DUT.RAM.ram_matrix[someaddr][somebit] = ~ tmp;
      end else begin
        // Releasing the fault
        force DUT.RAM.ram_matrix[someaddr][somebit] = tmp;
        release DUT.RAM.ram_matrix[someaddr][somebit];
      end // if (injection_active)
    end  // SOME_FAULT
  endcase // case (injection_type)
  
endmodule // fault_injection_ifm


So in this case we are forcing the ram_matrix RTL directly while our int_obs_ifm should simply ‘observe’ that something is changing. Apparently the i_int_obs_if.ram_matrix is never updated when we are forcing the signal and this behavior is definitely unexpected and the most bizarre thing is that the whole ram_matrix is not updated, as if it was ‘frozen’. I’ve been thinking whether it was the type definition of the ram_matrix in my interface that was wrong (logic vs wire), but changing it didn’t solve the issue.

Any idea on why this could happen?

On a similar note we’ve also been trying to inject the fault through the interface but it doesn’t seem to work, as if the assign statement doesn’t really propagate in ‘both’ directions (from and to the DUT). Any pointer is appreciated.

Thanks a lot,

Al

In reply to abs:

There are a couple of problems with what you are trying to do.

You are correct about an assign statement being “one-way” when it comes to a force. The LHS cannot affect the RHS. The same is true for a port connection when one or both sides of the port is a variable. That creates an implicit assign statement. (The only exception is for a ref port, and then both sides of the port must be variables).

It is illegal to have a non-constant select on the LHS of a force. You did not define someaddr, somebit, but I was assuming it is a variable. If your simulator is not flagging that as an error, the force is undefined if the select changes while the force is still active.

It is illegal to force a bit select of a packed array variable. This LRM restriction was left over from Verilog, but I think most simulators now support it in SystemVerilog.

A better approach for error injection in a memory is to force the output of the memory, especially when the memory is modeled in a separate block.

assign memory_output = memory_array[select1][select2]...;


force DUT.RAM.memory_output = (memory_array[select1][select2]...) ~^ error_mask;

In reply to dave_59:

In reply to abs:
There are a couple of problems with what you are trying to do.
You are correct about an assign statement being “one-way” when it comes to a force. The LHS cannot affect the RHS. The same is true for a port connection when one or both sides of the port is a variable. That creates an implicit assign statement. (The only exception is for a ref port, and then both sides of the port must be variables).

meaning that in my int_obs_ifm I should have a variable instead of a net and then have a procedural assignment rather than a continuous assignment, right?

It is illegal to have a non-constant select on the LHS of a force. You did not define someaddr, somebit, but I was assuming it is a variable. If your simulator is not flagging that as an error, the force is undefined if the select changes while the force is still active.

I haven’t defined the someaddress, but indeed it’s a constant (is actually set through a parameter), therefore there shouldn’t be any change in it while forcing it. That might not be the case should we want to inject the fault randomly and/or dynamically.

It is illegal to force a bit select of a packed array variable. This LRM restriction was left over from Verilog, but I think most simulators now support it in SystemVerilog.

We are using Incisive 14.2. Should I then file a bug report for not being compliant to the LRM restriction you are referring to?

A better approach for error injection in a memory is to force the output of the memory, especially when the memory is modeled in a separate block.

assign memory_output = memory_array[select1][select2]...;
force DUT.RAM.memory_output = (memory_array[select1][select2]...) ~^ error_mask;

Uhm, since we are not allowed to change the memory model (which is provided by the foundry), you are essentially suggesting to write a wrapper around it and have some sort of interface where I could inject my fault, is my understanding correct? At this point I would need to have some sort of tristate modeling since I cannot change the memory module I/O signature and I need the rest of the DUT to take into account the fault I’m injecting.

Isn’t the ‘UVM register model’ more appropriate for these kind of scenarios? On top of it the RAM module is sitting in a larger DUT, through several hierarchical layers, how do I connect my TB to the inner parts and yet have a reusable environment?

In reply to abs:

If by injecting errors you want to change memory values, you don’t need a force to do that. And yes the UVM register model is good for that as it uses the VPI for backdoor access getting around many SystemVerilog language limitations.

You could use the bind construct to inject errors without modifying the memory model. But without knowing the details of your design or foundry, it is difficult to suggest the best way of injecting errors.