Systemverilog: referencing to a register/variable with a string and changing its value - system-verilog

I have a sample of code of what I would like to do below
logic [3:0] c0_var, c1_var, c2_var, c3_var;
string pref = "c1"
always_ff #(posedge clk) begin
//Change the value of c1_var to 11
pref + "_var" = 4'd11;
end
Is there a way to use strings to reference to a variable/logic/register so that I could change them? I'm not sure how well I am phrasing my question, but please do let me know if you need more clarification. Thank you!

Preprocessor can handle this case.
logic [3:0] c0_var, c1_var, c2_var, c3_var;
`define PREF c1
`define ADD_VAR(VAR) VAR``_var
always_ff #(posedge clk) begin
//Change the value of c1_var to 11
//pref + "_var" = 4'd11;
`ADD_VAR(`PREF) = 4'd11;
end

Related

SystemVerilog 2-bit register decoding problem

I have two 2-bit inputs and an output of 1-bit. So what I'm trying to do is code a next state value with taking AND of the two inputs, then use non-blocking <= to assign that value into register xy_r, which is now two D-flipflops. So I am seeking to get always the XOR-value of the xy_r previous clock edge as my output xor_out. I suppose that the way below is not the right one?
It worked in the simulation but then again in RTL Synthesis I didn't end up with an XOR gate but a third flip-flop so it seems like xor_out is treated as a register.
I suppose I can't use assign outside of the if-else-statements because in that case the output wouldn't follow the xy_r previous state but the present.
Can you please assist me how to solve this issue, if there is a somewhat simple way.
module 2ffsxor
(input logic clk,
input logic rst_n,
input logic [1:0] x_in,
input logic [1:0] y_in,
output logic xor_out
);
logic [1:0] xy;
logic [1:0] xy_r;
always # (posedge clk or negedge rst_n)
begin
if (rst_n == '0)
xy_r <= '0;
else
begin
xy = x_in & y_in;
xy_r <= xy;
xor_out = xy_r[0] ^ xy_r[1];
end
end
endmodule
And here's a schematic what it should be:
Schematic 2ffsxor
Synthesis treats this code as if you had wrote
begin
xy_r <= x_in & y_in;
xor_out <= xy_r[0] ^ xy_r[1];
end
That's two stages of registers. But since you assigned xor_out with a blocking assignment, you have a simulation race condition for any process that tries to read xor_out. The race would be from the result from the previous values of xy_r and the result before that!
If you use an continuous assign statement outside this always block, any process reading xor_out on the clock edge will see the result from the previous state of xy_r.
But why not write this as on register:
always # (posedge clk or negedge rst_n)
begin
if (rst_n == '0)
xor_out <= '0;
else
begin
xy = x_in & y_in;
xor_out <= xy[0] ^ xy[1];
end
end

system verilog expect behavior

I tried using expect with the following property
module tb;
logic a;
logic clk=0;
default clocking #(posedge clk); endclocking
always
#5ns clk = ~clk;
initial begin
$dumpfile("dump.vcd"); $dumpvars;
$display("START");
a = 0;
#100ns;
a = 1;
#100ns;
$finish;
end
initial begin
#10ns;
expect(#(posedge clk) 1 ##1 $changed(a) |-> 1) $display("SUCCESS"); else
$display("FAIL");
end
endmodule
Is the expect going to block until a change from 0 to 1 at 100ns ?
No, it will block until the second (posedge clk), regardless of the value of a, and will always pass.
The expect statement does not start evaluating the property until the first clk edge. The antecedent takes two cycle to either match or not match. Since the consequent is always true, the property passes on match. If there is no match, the property also passes, vacuously.

What is the difference between #(posedge clk) begin end.... and #(posedge clk);?

What is the difference between:
1
forever begin
**#posedge(clk) begin**
if(vif.sof == 1) begin
//some code here
end
end
end
2
forever begin
**#posedge(clk);**
if(vif.sof == 1) begin
//some code here
end
end
Does the begin..end that goes with #(posedge clk) make a difference ?
event_controls like # and # in procedural code are not statement by themselves; they are prefixes to the statements that follow. And a statement can be a simple statement, like an assignment, or a block like begin/end or fork/join. And a block is allowed wherever a single statement is allowed.
When you write #(posedge clk); it is really #(posedge clk) null_statement;
I should have given you enough information to answer your question, but here is another variation:
forever
#posedge(clk)
if(vif.sof == 1) begin
//some code here
end
Now there is a big difference if a semicolon follows #(posedge clk) or not.

How to make an empty datatype or conditional field in SystemVerilog

I'd like to write code in which some inputs don't exist. I'd like to create a structure which contains those params (among other things). How can I do it? I tried to use generate, define an empty struct (typedef struct {} empty_t) and 0-size array (logic foo[0]) but all my attempts failed as syntax errors.
EDIT: I'm trying to do something like (simplified):
module foo(clk, data_in, opt_data_in);
parameter USE_OPT_IN = 1;
input logic clk;
input logic data_in;
input logic opt_data_in;
typedef struct packed {
logic data_in;
// Since it's stored in on-chip RAM I'd like it
// to be as small as possible
if (USE_OPT_IN != 0)
logic opt_data_in;
} ram_entry_t;
ram_entry_t my_ram[4096];
always_ff #(posedge clk) begin
ram_entry_t new_entry;
new_entry.data_in = data_in;
if (USE_OPT_IN != 0)
new_entry.opt_data_in = opt_data_in;
my_ram[$random() % 4096] = new_entry;
end
endmodule
This of course does not compile.
EDIT2: Above is simplified example. There is over 7 optional fields and coding 128 structs seems unreasonable - compared with just using unstructured bit field.
There is no such thing as an optional port or empty datatype. The code inside the module that references the port has to make sense for any situation.
There are a few things you can do
You can assign a default value to an input port that will be used if left unconnected.
You can parameterize a port type to use different structures with different number of fields. Again, the code inside the module must work for any datatype. This usually means casting the input to a known type inside the module.
You can also use an variable sized array if all the input types are the same.
I guess the only suggestion is to use a TYPE as a parameter to the module where the latter could be a struct with or without the field. You will also need a genarate block unless you can work with interfaces or classes. Here is a skeleton way of doing it. It would probably require yet another OPT param though for generate block. Sorry, i have not try to compile it.
module foo#(parameter type TYPE = int, parameter bit OPT = 1)
(clk, data_in, opt_data_in);
input logic clk;
input logic data_in;
input logic out_data_in;
TYPE my_ram[4096];
if (OPT) begin
always_ff #(posedge clk) begin
TYPE new_entry;
new_entry.data_in <= data_in;
new_entry.opt_data_in <= opt_data_in;
my_ram[$random() % 4096] <= new_entry;
end
else begin
always_ff #(posedge clk) begin
TYPE new_entry;
new_entry.data_in <= data_in;
my_ram[$random() % 4096] <= new_entry;
end
end
endmodule
now you can instantiate you module with different classes:
typedef struct packed {
logic data_in;
logic opt_data_in;
} ram_entry_full_t;
typedef struct packed {
logic data_in;
} ram_entry_short_t;
foo#(ram_entry_full_t, 1) foo_full(..);
foo#(ram_entry_short_t, 0) foo_short(..);
I'll build on #Serge answer. If you want to make your RAM entry types external to the module and use them as parameters, you can do away with the USE_OPT_IN parameter altogether, as this is redundant:
typedef struct packed {
logic data_in;
logic opt_data_in;
} ram_entry_full_t;
typedef struct packed {
logic data_in;
} ram_entry_short_t;
module foo #(parameter type TYPE = int) (clk, data_in, opt_data_in);
input logic clk;
input logic data_in;
input logic out_data_in;
TYPE my_ram[4096];
if (TYPE == type(ram_entry_full_t)) begin
always_ff #(posedge clk) begin
TYPE new_entry;
new_entry.data_in <= data_in;
new_entry.opt_data_in <= opt_data_in;
my_ram[$random() % 4096] <= new_entry;
end
end
else if (TYPE == type(ram_entry_short_t)) begin
always_ff #(posedge clk) begin
TYPE new_entry;
new_entry.data_in <= data_in;
my_ram[$random() % 4096] <= new_entry;
end
end
else begin
$error("Unsuported type ...");
end
endmodule
You could replace the if/else cascade with a case to make the code slightly more readable. You could also refactor the initialization code a bit by extracting functions to avoid duplication.
You could also use TYPE as a port for your top module, instead of data_in and opt_data_in.
For option 2, you can leave your module signature as in your question and do all the magic inside:
module foo(clk, data_in, opt_data_in);
parameter bit USE_OPT_IN = 1;
input logic clk;
input logic data_in;
input logic opt_data_in;
typedef struct packed {
logic data_in;
logic opt_data_in;
} ram_entry_full_t;
typedef struct packed {
logic data_in;
} ram_entry_short_t;
if (USE_OPT_IN) begin
always_ff #(posedge clk) begin
ram_entry_full_t new_entry;
new_entry.data_in <= data_in;
new_entry.opt_data_in <= opt_data_in;
my_ram[$random() % 4096] <= new_entry;
end
end
else begin
always_ff #(posedge clk) begin
ram_entry_short_t new_entry;
new_entry.data_in <= data_in;
my_ram[$random() % 4096] <= new_entry;
end
end
endmodule
This way, it's impossible to instantiate your module with the wrong parameter and if you don't break interface compatibility if your module is already in use somewhere.

How to pass information from inside sequence/property to the outside world

I have a property that is waiting for two different sequence to happen, as follows (the sequence and property are already simplified here):
sequence seq1;
logic [3:0] a;
#(posedge clk) (some_signal1 == 1, a = other_signal1);
endsequence
sequence seq2;
logic [3:0] b;
#(posedge clk) (some_signal2 == 1, b = other_signal2);
endsequence
property abc;
seq1 ##[1:$] seq2;
endproperty
some_signal1,some_signal2, other_signal1, other_signal2 are all RTL signals.
My question:
When the property abc is passed or covered, I want to know the corresponding other_signal1 and other_signal2 sampled in the sequences. How can I access those signals values?
I am thinking to hierarchically access the sequence local variables, such as seq1.a or seq2.b. But these are not allowed, as they are locals.
Declaring those variables as formal argument also does not help, because formal argument can not be assigned in the LHS.
My intention is that I am going to use those signals for other purpose, i.e.:
cover property (abc) begin
// grab seq1.a and seq2.b, and do some other tasks
end
You can do one of 3 things on the right of the comma inside the ( , ):
an assignment (to a local variable)
increment or decrement
call a tasks, task method, void function, void function method or system task.
(see IEEE1800-2012, 16.11 Calling subroutines on match of a sequence)
A task call is the key to this. You can assign to variables outside the task scope within a task called from within the sequence:
sequence seq1;
#(posedge clk) (some_signal1 == 1, assign_a(other_signal1));
endsequence
sequence seq2;
#(posedge clk) (some_signal2 == 1, assign_b(other_signal2));
endsequence
property abc;
seq1 ##[1:$] seq2;
endproperty
cover property (abc)
$display("a= %d, b= %d", a , b);
task assign_a(input int i);
a = i;
endtask
task assign_b(input int i);
b = i;
endtask
http://www.edaplayground.com/x/akw