can packed arrays be passed by reference to the task in systemverilog - system-verilog

Can s_clk be passed as argument to xyz task in below code?
module test(input logic m_clk, output [1:0] logic s_clk);
...
xyz (m_clk,s_clk);//assuming m_clks and s_clks are generated from top
...
task automatic xyz (ref logic clk1, ref [1:0] logic clk2);
...
endtask
endmodule

I have read your problem, first of all you have typo mistake
module test(input logic m_clk, output [1:0] logic s_clk);
task automatic xyz (ref logic clk1, ref [1:0] logic clk2);
instead of this you have to write
module test(input logic m_clk, output logic [1:0] s_clk);
task automatic xyz (ref logic clk1, ref logic [1:0] clk2);
For better understanding I have also share one demo code for packed arrays can be passed by reference to the task in systemverilog.
Here is code :
program main();
bit [31:0] a = 25;
initial
begin
#10 a = 7;
#10 a = 20;
#10 a = 3;
#10 $finish;
end
task pass_by_val(int i);
$monitor("===============================================%d",i);
forever
#a $display("pass_by_val: I is %0d",i);
endtask
task pass_by_ref(ref bit [31:0]i);
forever
begin
#a $display("pass_by_ref: I is %0d",i[0]);
$display("This is pass_by value a ====== %d \n a[0] ====== %0d ",a,a[0]);
end
endtask
initial
begin
pass_by_val(a);
end
initial
pass_by_ref(a);
endprogram
By running this example you can observe that packed arrays can be passed by reference to the task in systemverilog and its value is also reflected to it.

pass_by_val task will register the value of the variables
only once at the time when task is called. Subsequently when the variable changes its value, pass_by_val task cannot see the newer values. On the other hand, 'ref' variables in a task are registered whenever its value changes. As a result, when the variable 'a' value changes, the pass_by_ref task can register and display the value correctly.
I simulated Ashutosh Rawal's code and the output display is given below:
=============================================== 25
pass_by_val: I is 25
pass_by_ref: I is 1
This is pass_by value a ====== 7
a[0] ====== 1
pass_by_val: I is 25
pass_by_ref: I is 0
This is pass_by value a ====== 20
a[0] ====== 0
pass_by_val: I is 25
pass_by_ref: I is 1
This is pass_by value a ====== 3
a[0] ====== 1
$finish called from file "testbench.sv", line 13.
$finish at simulation time 40
V C S S i m u l a t i o n R e p o r t

Related

Cross-module reference resolution error from SV wait statement

I wanted to wait on the output variable of a task.
eg: wait(user_defined_task_name(output_variable_type_name) == 1)
In this example shown below, my intention is to make wait statement to be active from 0ns to 3ns (basically from the beginning of timestamp till t2=1)
Here is a working example;
class cl;
task run(output bit t);
$display("time=%0t , t=%0b",$realtime, t);
#1;
$display("time=%0t , t=%0b",$realtime, t);
#2;
t = 1;
$display("time=%0t , t=%0b",$realtime, t);
endtask
endclass
class c2 extends cl;
bit t2;
task run1();
wait(run(t2) == 1); // error from this line, what am i violating here?
$display("t2 working t2=%0b time = %0t", t2, $realtime);
endtask
endclass
module tmp;
c2 c2_h=new;
initial begin
c2_h.run1();
$display("test msg");
end
endmodule
eda output log:
Top Level Modules:
tmp TimeScale is 1 ns / 1 ns
Error-[XMREF] Cross-module reference resolution error testbench.sv, 19
Cross-module reference resolution error is found. Function is
expected, but actual target is not a function. Source info:
run(this.t2)
1 error CPU time: .116 seconds to compile Exit code expected: 0,
received: 1
A couple of problems with your code. A task does not return a value and cannot be used in an expression. You can only call it as a stand alone statement. But if you change run to a function functions cannot consume time.
In your particular example, you do not change the value of the t argument until the end of the task, and output arguments are copied out upon exiting the task, so you might as well just call the task run(t2) as a statement and it will block until returning.
task run1();
run(t2);
$display("t2 working t2=%0b time = %0t", t2, $realtime);
endtask
If on the otherhand run set the t argument somewhere in the middle of the task, and you want to continue the run1 task as soon as that happened, the you would have use a fork/join_none and a ref argument instead.
class cl;
task run(ref bit t);
$display("time=%0t , t=%0b",$realtime, t);
#1;
$display("time=%0t , t=%0b",$realtime, t);
#2;
t = 1;
#2;
$display("time=%0t , t=%0b",$realtime, t);
endtask
endclass
class c2 extends cl;
bit t2;
task run1();
fork
run(t2);
join_none
wait(t2 == 1);
$display("t2 working t2=%0b time = %0t", t2, $realtime);
endtask
endclass
When I run your code on the Cadence simulator, I get a different message:
xmvlog: *E,INVCTX The task 'run' cannot be used in this context.
Using the nchelp utility to get more information on that message:
A task or void function cannot be passed as an actual argument
because they do not return a value that can be used. They also cannot
be used as part of an expression.
In your simple example, there seems to be no need to use wait. You can simply call the task on its own:
task run1();
run(t2);
$display("t2 working t2=%0b time = %0t", t2, $realtime);
endtask

How to make 4 bit ring counter with 4 flip flops?

I have this 4 bit ring counter that I'm trying to make, and I feel like I'm so close, but I can't figure out how to make one input depend on the previous state's output. Here's what I have:
`default_nettype none
// Empty top module
module top (
// I/O ports
input logic hz100, reset,
input logic [20:0] pb,
output logic [7:0] left, right
);
// Your code goes here...
q[3:0];
assign q[3:0] = right[3:0];
hc74_set setFF(.c(pb[0]), .d(pb[1]), .q(right[0]), .sn(pb[16]));
hc74_reset resetFF1(.c(pb[0]), .d(pb[1]), .q0(right[1]), .rn(pb[16]));
hc74_reset resetFF2(.c(pb[0]), .d(pb[1]), .q1(right[2]), .rn(pb[16]));
hc74_reset resetFF3(.c(pb[0]), .d(pb[1]), .q2(right[3]), .rn(pb[16]));
endmodule
// Add more modules down here...
// This is a single D flip-flop with an active-low asynchronous set (preset).
// It has no asynchronous reset because the simulator does not allow it.
// Other than the lack of a reset, it is half of a 74HC74 chip.
module hc74_set(input logic d, c, sn,
output logic q, qn);
assign qn = ~q;
always_ff #(posedge c, negedge sn)
if (sn == 1'b0)
q <= 1'b1;
else
q <= d;
endmodule
// This is a single D flip-flop with an active-low asynchronous reset (clear).
// It has no asynchronous set because the simulator does not allow it.
// Other than the lack of a set, it is half of a 74HC74 chip.
module hc74_reset(input logic d, c, rn,
output logic q, qn);
assign qn = ~q;
always_ff #(posedge c, negedge rn)
if (rn == 1'b0)
q <= 1'b0;
else
q <= d;
endmodule
This is on an FPGA simulator, which is why there are a few things like pb (these are push buttons) and left, right outputs which are sets of 8 LEDs each.
Let's first make sure we are on the same page
Based on wikipedia description of a ring counter
This could be implemented as follows:
module top (
// I/O ports
input logic reset_n,
input logic clk,
output logic [3:0] ring
);
// Your code goes here...
always #(posedge clk or negedge reset_n) begin
if(~reset_n) begin
ring = 4'b0001;
end
else begin
ring[0] <= ring[3];
ring[1] <= ring[0];
ring[2] <= ring[1];
ring[3] <= ring[2];
end
end
endmodule
The output ring is a 4-bit one hot vector, reset_n = 0 makes ring = 0001 every clock with reset_n = 1 rolls the ring to the right, [0001, 0010, 0100, 1000, 0001, ...].
But you want to use instances of the flops you defined. Notice that in an assignment a <= b, a is the output of the flop (q port), and b is the input of the flop (d port).
module top (
// I/O ports
input logic reset_n,
input logic clk,
output logic [3:0] ring
);
// Your code goes here...
hc74_set setFF(.c(clk), .d(ring[3]), .q(ring[0]), .sn(reset_n));
hc74_reset resetFF1(.c(clk), .d(ring[0]), .q0(ring[1]), .rn(reset_n));
hc74_reset resetFF2(.c(clk), .d(ring[1]), .q1(ring[2]), .rn(reset_n));
hc74_reset resetFF3(.c(clk), .d(ring[2]), .q2(ring[3]), .rn(reset_n));
endmodule
You have to connect the ports accordingly, I just used clk for the clock and reset_n for the negated reset signal.

How do I design Serial to Parallel Buffer in Verilog only using clocks?

I am looking to design a serial to parallel converter in Verilog which converts a fast clock serial input to a slower clock parallel input. I tried the following code which works in RTL but does not verify on Cadence Conformal. nclk is 16 times faster than clk. The serial data comes in at nclk and the parallel data is intended to come out at clk rate.
sEEG - Serial Input
eegOut - Parallel output
I can only have clk and nclk as my operation references due to tape-out bond pad limitations.
Following is the code that I have come up with which works well in functional simulation but Formal Verification fails.
module deserializer(sEEG, nclk, clk, eegOut);
input sEEG;
input nclk,clk;
reg [15:0] temp;
output reg [15:0] eegOut;
reg [4:0] i;
always #(negedge nclk) begin
temp[i] = sEEG;
i = i + 1;
end
always#(posedge clk) begin
i<=0;
eegOut <= temp;
end
endmodule
I feel you should use four bits in order to index 16 elements. If you parameterize the module, this could be done with:
# (parameter WIDTH = 16)
then later use it as:
localparam BIT_SEL = $clog2(WIDTH); //..this should give you "4" if WIDTH = 16
reg [BIT_SEL-1:0] i;
Also, you might want to include a clock synchronizer, you don't want metastability problems, an easy way to do this is to include "double-triggers", practically is to buffer the data and replicate it to the next slow clock cycle (adds 1 slow clock cycle latency). So, maybe this works:
module deserializer
# (parameter WIDTH = 16)
(sEEG, nclk, clk, eegOut);
input sEEG;
input nclk,clk;
reg [WIDTH-1:0] temp;
reg [WIDTH-1:0] temp_reg; //..synchronizer
output reg [WIDTH-1:0] eegOut;
always #(negedge nclk) begin
temp[WIDTH-2:0] <= temp[WIDTH-1:1];
temp[WIDTH-1] <= sEEG;
end
always#(posedge clk) begin
temp_reg <= temp;
eegOut <= temp_reg;
end
endmodule

Delays within tasks in system verilog

I am trying to call 4 tasks within another task as follows:
task execute();
logic [0:3] req1, port_select;
logic [0:3] req2;
logic [0:3] req3;
logic [0:3] req4;
logic [0:31] data11, data21;
logic [0:31] data12, data22;
logic [0:31] data13, data23;
logic [0:31] data14, data24;
bfm.reset_task();
//drive multiple ports
//repeat(1)
//begin: random_stimulus
port_select = generate_combination();
repeat(1)
begin: per_combination_iteration
//port1
req1 = port_select[0]? generate_command() : 0;
data11 = generate_data();
data21 = generate_data();
//bfm.drive_ip_port1(req,data1,data2);
//port2
req2 = port_select[1]? generate_command() : 0;
data12 = generate_data();
data22 = generate_data();
//bfm.drive_ip_port2(req,data1,data2);
//port3
req3 = port_select[2]? generate_command() : 0;
data13 = generate_data();
data23 = generate_data();
//bfm.drive_ip_port3(req,data1,data2);
//port4
req4 = port_select[3]? generate_command() : 0;
data14 = generate_data();
data24 = generate_data();
//bfm.drive_ip_port4(req,data1,data2);
fork
bfm.drive_ip_port1(req1,data11,data21);
bfm.drive_ip_port2(req2,data12,data22);
bfm.drive_ip_port3(req3,data13,data23);
bfm.drive_ip_port4(req4,data14,data24);
join
end: per_combination_iteration
//end: random_stimulus
$stop;
endtask: execute
And one of my drive_ip_port function is as follows:
//driving port2
task drive_ip_port2(input logic [0:3] req2, input logic [0:31] data1_port2, data2_port2);
req2_cmd_in = req2; //req2 command
req2_data_in = data1_port2; //req2 first operand
#200;
req2_cmd_in = 0;
req2_data_in = data2_port2; //req2 second operand
#1000;
endtask: drive_ip_port2
This is what I am trying to achieve:
I want the execute task to drive 4 ports randomly. On the first clock, I want them to send a command and data. And then on the next clock, the command should be 0 and only data need to be sent.
This is what I have tried:
As shown in my code, I have written the above code. The thought behind this code was that since tasks can handle time delays, I can call the task once and pass the data and the command and let task handle all the work.
The problem I have:
After the first clock period, I have a delay of #200(equal to my clock). Thereafter, the wire should become 0 and should remain 0 for #1000. However, I am never getting the value 0 on command. It looks like the command gets driven by this task again. I have tried using Local variables, using the watch feature, using breakpoint but still couldn't debug it. Can anyone suggest what's wrong?
I don't know why req2_cmd_in does not get set to zero. Maybe there is somewhere else an overriding assignment like a typo in another task. (Try call only one task and see what that does.)
I do know that if you want something to happen at or after a clock, wait for that clock, do not use a delay. Safest is also to make sure you start at a determined point from a clock edge. Therefore I prefer to use in my test-benches code like this:
task drive_ip_port2(input logic [0:3] req2,
input logic [0:31] data1_port2, data2_port2);
// Use this or make sure you call the task at a
// determined point from the clock
# (posedge clk) ;
// Signals here change as if they come from a clocked register
req2_cmd_in <= req2; //req2 command
req2_data_in <= data1_port2; //req2 first operand
# (posedge clk) ;
req2_cmd_in <= 0; // No command
req2_data_in <= data2_port2; // only req2 second operand
repeat (4) // 4 or 5 depends on if you wait for clock at top
# (posedge clk) ;
endtask: drive_ip_port2

How to save SystemC variables during vcs simulation and restore back based on stimulus change

My tests are in SystemC env and for all my tests i run a common routine(basically its my init sequence which doesn't vary much based on seed). This common routine is followed by my actual test.
It looks something like this:
Test_MAIN
init_seq();
my_test();;
Here init_seq() is that common routine and my_test() is my actual test that have actual test sequence which initiate multiple SC threads, for initiating various kind of traffic.
Now, my problem is that i want a way to avoid init_seq() for every run. The entire test should run for one time and the next time i should have a mechanism to directly run from my_test()(which is basically skipping/preloading init_seq() part to the simulator).
VCS save and restore can't be used directly as in this case we also will have to restore the SystemC variables.
Could you please direct me on this..!!
Thanks
ByreddyNaresh
I tried with a SystemC test with init() and run() routine, and use VCS save/restart to restore the test. It seems OK to me that a member variable 'cnt' in test is restored successfully.
This is my test:
// test.h
#ifndef TEST_H
#define TEST_H
#include <systemc.h>
class test: public sc_module{
public:
sc_in<bool> init_start;
sc_in<bool> run_start;
void init();
void run();
int cnt;
SC_CTOR(test){
SC_THREAD(init);
sensitive_pos << init_start;
dont_initialize();
SC_THREAD(run);
sensitive_pos << run_start;
dont_initialize();
}
};
#endif
and
// test.cpp
#include "test.h"
void test::init(){
printf("test init:\n");
while(1){
cnt = 10;
printf("init cnt to %d\n", cnt);
wait();
}
}
void test::run(){
printf("test run:\n");
while(1){
cnt++;
printf("cnt = %d\n", cnt);
wait();
}
}
and the top:
// tb.v
module tb;
reg init_start;
reg run_start;
initial begin
init_start = 0;
run_start = 0;
#100
init_start = 1;
#100
$save("save.chk");
#100
run_start = 1;
#100
$finish;
end
test u_test(
.init_start(init_start),
.run_start(run_start)
);
endmodule
After 'save.chk' is generated, I run it directly and the output is:
$ save.chk
Chronologic VCS simulator copyright 1991-2012
Contains Synopsys proprietary information.
Compiler version G-2012.09; Runtime version G-2012.09; Mar 5 18:01 2014
test run:
cnt = 11
$finish called from file "tb.v", line 17.
$finish at simulation time 1300
V C S S i m u l a t i o n R e p o r t
Time: 1300 ps
CPU Time: 0.260 seconds; Data structure size: 0.0Mb
Wed Mar 5 18:01:08 2014
It seems variable 'cnt' is restored from '10', as is done in init() routine.
Don't know if this is the case you met?