SystemVerilog - dynamic types in non-procedural context error - system-verilog

I need to bypass programming a bunch of registers in different blocks, the basic infrastructure is something like shown below. This gives me two types of errors:
Dynamic type in non-procedural context
Illegal reference in force/proc assign
Both of these are for line:
force top.design0.register_block.in = in;
Is there any quick solution short of writing an FSM that goes over all register_values?
logic [31:0] register_values[2:0] = {'habcd, 'hbcde, 'hcdef };
class Injector;
task automatic run();
foreach (register_values[i]) force_reg(register_values[i]);
endtask
task automatic force_reg(input logic [31:0] in);
#(negedge top.design0.register_block.clk);
force top.design0.register_block.in = in;
#(negedge top.design0.register_block.clk);
endtask
endclass
module register_block(input logic clk,
input logic[31:0] in);
endmodule
task force_registers();
Injector injector = new();
injector.run();
endtask
module design(input logic clk);
logic[31:0] in;
register_block register_block(clk, in);
endmodule
module top();
logic clk;
design design0(clk);
initial force_registers();
initial begin
clk = 0;
forever #10 clk = ~clk;
end
initial #200 $finish;
endmodule
Tried the tasks without the 'automatic' but that gives same error.

Tasks defined in class are automatic by default. The tool complains about in which is a task argument. Since the task is automatic, the argument variable is considered to be automatic as well.
The way around it is to declare the task static:
task static force_reg(input logic [31:0] in);
Assuming, that there is only a single instance of the task 'run' at any time, it should work.

Related

Unsuccessful assignment in SystemVerilog

I write ldpc_if.sv and ldpc_transaction.sv as follows.
"ldpc_if.sv"
interface ldpc_if#(parameter COLS=9216, parameter ROWS=1024) (input clk, input reset);
logic [COLS-ROWS-1:0] en_enq_data;
logic en_enq_valid;
logic en_enq_ready;
logic [ROWS-1:0] en_deq_data;
logic en_deq_valid;
logic en_deq_ready;
logic [COLS-1:0] de_enq_data;
logic de_enq_valid;
logic de_enq_ready;
logic [COLS-1:0] de_deq_data;
logic de_deq_valid;
logic de_deq_ready;
endinterface
"ldpc_transaction.sv"
class ldpc_transaction#(parameter WIDTH=8192) extends uvm_sequence_item;
rand bit [WIDTH-1:0] data;
bit [8191:0] encode_data_in;
bit [1023:0] encode_data_out;
bit [9215:0] decode_data;
`uvm_object_utils(ldpc_transaction)
function new(string name = "ldpc_transaction");
super.new();
endfunction
endclass
And I write ldpc_monitor.sv to monitor interface.
task ldpc_monitor::collect_one_pkt(ldpc_transaction tr);
while(1) begin
#(posedge vif.clk);
if(vif.en_enq_valid && vif.en_enq_ready) break;
end
tr.encode_data_in <= vif.en_enq_data;
while(1) begin
#(posedge vif.clk)
if(vif.en_deq_valid && vif.en_deq_ready) break;
end
tr.encode_data_out <= vif.en_deq_data;
while(1)begin
#(posedge vif.clk)
if(vif.de_deq_valid && vif.de_deq_ready)begin
break;
end
end
tr.decode_data <= vif.de_deq_data;
$display("tr.decode_data = %0h", tr.decode_data);
$display("vif.de_deq_data = %0h", vif.de_deq_data);
endtask
vcs compiles all files successfully. However, tr.decode_data is always displayed as zero. But, vif.de_deq_data is correct. Why is vif.de_deq_data not assigned to tr.decode_data.
It's because the $display you've used to display your transaction is blocking. Conversely, you've used an non-blocking assignment to set tr.decode_data.
Thus, your $display statement actually gets executed before your assignment. Getting a 0 is just an artefact of your simulator - could be any random stuff in the memory assigned to that variable (though most simulators just reset to 0).
Quick search revealed this useful example which illustrates exactly your problem.
https://verificationguide.com/systemverilog/systemverilog-nonblocking-assignment/

How to pass signal name through $value$plusargs in system verilog

I am writing assertions in system verilog. This assertion check for a signal "lock" which is passed as an test argument through switch $value$plusargs as GPIO_SEL="test_bench.gpio"
So my code is :
module ab
string abc;
$value$plusargs("GPIO_SEL=%s" , abc);
reg lock;
always #*
begin
lock = abc;
end
endmodule
The problem here is that signal lock is not getting value as test_bench.gpio. Is there any way i can pass this signal value from testplaus args
SystemVerilog is a compiled language, not interpreted. You can't access identifiers directly using a string. You can use compiler directives on the command line
module ab;
bit lock;
always_comb
begin
lock = `GPIO_SEL;
end
endmodule
Then when compiling your code use a +define switch
vlog your_file.sv +define+GPIO_SEL=test_bench.gpio
If you think re-compiling your testbench/dut is a significant burden (most tools offer incremental compilation options), and you have a fixed number of paths to access, then you can use a case statement
module ab
string abc;
initial $value$plusargs("GPIO_SEL=%s" , abc);
reg lock;
always_comb
case (abc)
"test_bench.gpio": lock = test_bench.gpio;
"test_bench.gpio1": lock = test_bench.gpio1;
endcase
end
And you could just use a simple number instead of a string to select.
Looks like you need to conver the string to an integer type. Look at the string conversion functions: atoi, atobin, ...
for example
module parg;
string abc;
bit [3:0] sig;
initial begin
$value$plusargs("GPIO_SEL=%s" , abc);
sig = abc.atoi();
$display("sig = %b", sig);
end
endmodule // parg

Changing clocking block clock polarity on the fly

I am creating UVM VIP which is able to switch its clock polarity. Clocking block is used in the interface.
For example, a monitor should sample the data using posedge or negedge of incoming clock depending on UVM configuration - and this polarity change can happen on the fly.
This can be implemented as follows:
// In the interface, two clocking blocks are defined
// one for posedge (passive_cb), one for negedge (passive_cbn).
task wait_clock_event();
if (cfg.pol == 0) #vif.passive_cb;
else #vif.passive_cbn;
endtask
task sample_data();
if (cfg.pol == 0) pkt.data = vif.passive_cb.data;
else pkt.data = vif.passive_cbn.data;
endtask
task run();
wait_clock_event();
sample_data();
endtask
This seems to work but waste code lines and prone to error.
Is there any better solution?
Assuming the monitor has exclusive access to the clocking block, you could consider modifying clocking event in the interface with the iff qualifier.
bit pol;
clocking passive_cb #(posedge clk iff !pol, negedge clk iff pol);
input data;
endclocking
There is a potential race condition if pol changes in the same timestep as the target clock polarity.
Your monitor code would then include a set function and other tasks can be simplified to us only one clocking block.
function void set_vifcb_pol();
vif.pol = cfg.pol;
endfunction
task wait_clock_event();
#vif.passive_cb;
endtask
task sample_data();
pkt.data = vif.passive_cb.data;
endtask
task run();
set_vifcb_pol();
wait_clock_event();
sample_data();
endtask

SystemVerilog Clocking Blocks in Bi-Directional Interface

Let's say I have a bi-directional interface. I want the TB to be able to receive data from the DUT and I want the TB to be able to drive data to the DUT. I need to put in a clocking block in my code because I have race-condition issues. I can fix these issues by putting in a little #1 in the right spot, but I know that a clocking block is the correct solution. The part I'm having difficulty with is the bi-directional part. If it was one direction I might be OK, but the syntax for bi-directional interface is tripping me up. Is the right solution to make 2 clocking blocks, or 2 modports, or something else entirely?
interface MyInterface
(input bit i_Clk);
logic [15:0] r_Data;
logic r_DV = 1'b0;
clocking CB #(posedge i_Clk);
default input #1step output #1step;
endclocking : CB
task t_Clock_Cycles(int N);
repeat (N) #(posedge i_Clk);
endtask : t_Clock_Cycles
modport Driver (clocking CB, output r_Data, r_DV);
modport Receiver (clocking CB, input r_Data, r_DV);
endinterface : MyInterface
package MyPackage;
class MyDriver;
virtual MyInterface.Driver hook;
function new(virtual MyInterface.Driver hook);
this.hook = hook;
endfunction : new
task t_Drive(input [15:0] i_Data);
forever
begin
hook.CB.r_Data = i_Data;
hook.CB.r_DV = 1'b1;
hook.CB.t_Clock_Cycles(1);
end
endtask : t_Drive
endclass : MyDriver
endpackage : MyPackage
module MyModule;
import MyPackage::*;
logic r_Clk = 1'b0;
MyInterface hook(.i_Clk(r_Clk));
always #5 r_Clk = ~r_Clk;
MyDriver d1 = new(hook.Driver);
initial
begin
d1.t_Drive(16'hABCD);
end
endmodule // MyModule
The whole point of using a clocking block is to declare which signals you want to access synchronously. You should add your signals to the clocking block:
clocking CB #(posedge i_Clk);
default input #1step output #1step;
inout r_Data;
inout r_DV;
endclocking : CB
Since you also want to have different access permissions for your driver and receiver, this means you'll need two different clocking blocks:
clocking CB_driver #(posedge i_Clk);
default input #1step output #1step;
output r_Data;
output_DV;
endclocking : CB_driver
// ... direction reversed for CB_receiver
Unfortunately, it's not possible to say that you have a reference to a certain clocking block inside your driver/receiver classes:
class Driver
virtual MyInterface.CB_driver hook; // !!! Not allowed
endclass
If you want to restrict your driver to only be able to drive through CB_driver, you can use a modport:
interface MyInterface;
modport Driver(CB_driver);
endinterface
class Driver;
virtual MyInterface.Driver hook;
endclass
This way you can reference hook.CB_driver when driving your signals. The same goes for your receiver.

system verilog interface with function

How can I add a function in an interface? I am trying to implement half adder using interface having function to calculate sum and carry.Following is my code for the same. When trying without functions it as running by using complemented lines.
module top_ha_interface;
ha_interface nh1();
ha h1(nh1);
ha_tb h2(nh1);
endmodule
interface ha_interface;
logic sum,c_out;
logic a,b;
function summ(a,b,output sum,c_out);
sum=a^b;
c_out=a&b;
endfunction
endinterface
module ha(ha_interface nh1);
// assign nh1.sum=nh1.a^nh1.b;
// assign nh1.c_out=nh1.a&nh1.b;
nh1.summ(nh1.a,nh1.b);
endmodule
module ha_tb(ha_interface nh1);
initial
begin
nh1.a=1'b1;
nh1.b=1'b0;
#10 $display($time,"ns\t",nh1.sum,nh1.c_out);
nh1.a=1'b1;
nh1.b=1'b1;
#20 $display($time,"ns\t",nh1.sum,nh1.c_out);
nh1.a=1'b0;
nh1.b=1'b0;
#30 $display($time,"ns\t",nh1.sum,nh1.c_out);
end
endmodule
Function is synthesizable, but that must be used, within any procedural block of verilog. (Like always or initial)
Tasks and void functions are called as statements within procedural
blocks
So required modifications in your code :
module ha(ha_interface nh1);
// assign nh1.sum=nh1.a^nh1.b;
// assign nh1.c_out=nh1.a&nh1.b;
always # (*)
nh1.summ(nh1.a,nh1.b, nh1.sum, nh1.c_out);
endmodule