Delays within tasks in system verilog - 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

Related

Connections between sub modules wrong

My codes for Alu and Mux:
module alu(input logic [31:0]srca, srcb,
input logic [2:0] alucontrol,
output logic zero,
output logic [31:0]aluout);
logic [31:0] addr, subr, sltr, Andr, Orr;
assign addr = srca + srcb;
assign subr = srca - srcb;
assign sltr = (srca < srcb) ? 1 : 0;
assign Andr = srca & srcb;
assign Orr = srca | srcb;
always_comb
begin
case(alucontrol)
3'b010: aluout = addr;
3'b110: aluout = subr;
3'b111: aluout = sltr;
3'b000: aluout = Andr;
3'b001: aluout = Orr;
default: aluout = 32'bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
endcase
zero = (subr == 0) ? 1 : 0;
end
endmodule
module mux2#(parameter WIDTH = 8)
(input logic [WIDTH-1:0] d0, d1,
input logic s,
output logic [WIDTH-1:0] y);
always_comb
begin
y = s?d1 : d0;
end
endmodule
and their instantiation under the same top module:
alu alu(srca, srcb, alucontrol, aluout, zero);
mux2 #(32) resmux(aluout, readdata, memtoreg, result);
When I try to connect my 2-1Mux resmux with my alu, the aluout doesn't get connected to resmux
aluout gets suspended
I could solve this by exchanging the order of aluout and zero, but could anybody explain why this happens and how to avoid? Thanks a lot!
There are a number of mechanisms for connecting ports in SystemVerilog. The mechanism you are using is positional. That means each signal connection has to go in the prescribed order that they appear in the module declaration of alu. The way your code is written, the signal zero in module top is connected to last port aluout declared in module alu.
SystemVerilog also has a by_name syntax .portname(signal). You can list all your port connections in any order that way.
alu alu(.srca(srca), .srcb(srcb), .alucontrol(alucontrol), .alu(aluout), .zero(zero));
When the signal name you are connecting matches the declared port name, you can just use .portname.
alu alu(.alucontrol, .aluout, .zero, .srca, .srcb);
And finally, if all the port names match the signal names, you can use a wildcard, .* as well as list exceptions explicitly
alu alu(.*, .zero(my_zero));

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.

System Verilog- How to ensure that specific code will be executed before another?

I have the following code in my monitor:
virtual task run_phase(uvm_phase phase);
fork
begin : clock_c
forever begin
wait(vif.fact_log2_samp != fact_log2_samp_init);
for(int counter = 0; counter < 46; counter++) begin
check = 1'b0;
#(posedge vif.clk);
end
**check =1'b1;**
end// forever
end// clock_c
begin : main_0
forever begin
mon_trx = tx_lin_int_transaction::type_id::create("mon_trx");
mon_trx.fact_log_2 = fact_log2_samp_init;
**wait (vif.xn_valid == 1'b1);**
#1;
mon_trx.rand_data_xi = vif.xi;
mon_trx.rand_data_xq = vif.xq;
if (check == 1'b0)
mon_trx.check = FALSE;
else
fact_log2_samp_init = vif.fact_log2_samp;
$cast(t, mon_trx.clone());
//send transaction to scoreboard via TLM write()
ap.write(t);
wait (vif.xn_valid == 1'b0);
end// forever
end// main_0
join
endtask: run_phase
The problem is that
wait(vif.xn_valid == 1'b1);
and the code after it execute just before
check =1'b1;
(same time).
How can I ensure that the
check =1'b1;
will execute before?
I would follow the named events method, as AndresM suggested, but if you need a quick sync in the very same block with the very same trigger condition, a simple #0 might solve the issue, thou it is unreliable due to the simulation time handling reasons. Might worth a try:
begin : clock_c
...
**check =1'b1;**
...
end
begin : main_0
...
#0;
**wait (vif.xn_valid == 1'b1);**
...
end
also you can use labels for the begin-end blocks to look and read better, eg.:
begin: main_0, end: main_0 instead of end // main_o
You might want to take a look at Chapter 15 of the IEEE Std 1800-2012, where they cover in great detail every aspect related to the different interprocess synchronization and communication mechanisms that the SystemVerilog language offers. Those options are listed below (follow the hyperlinks to see a few examples and how to use each one of them):
Semaphores
Mailboxes
Named Events

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

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