mirror of
https://github.com/verilator/verilator.git
synced 2025-01-24 07:14:22 +00:00
35d6da391b
Multiple edge timing controls in class methods would cause compilation errors on the generated C++ code. This is because the `SenExprBuilder` used for these would get recreated per timing control, resulting in duplicate variable names. The fix is to have a single `SenExprBuilder` per scope.
276 lines
6.6 KiB
Systemverilog
276 lines
6.6 KiB
Systemverilog
// DESCRIPTION: Verilator: Verilog Test module
|
|
//
|
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
|
// any use, without warranty, 2022 by Antmicro Ltd.
|
|
// SPDX-License-Identifier: CC0-1.0
|
|
|
|
`ifdef TEST_VERBOSE
|
|
`define WRITE_VERBOSE(args) $write args
|
|
`else
|
|
`define WRITE_VERBOSE(args)
|
|
`endif
|
|
|
|
class BaseClass;
|
|
virtual task sleep;
|
|
endtask
|
|
|
|
virtual task await;
|
|
endtask
|
|
endclass
|
|
|
|
module t;
|
|
// =============================================
|
|
// EVENTS
|
|
class EventClass extends BaseClass;
|
|
event e;
|
|
int trig_count;
|
|
|
|
function new;
|
|
trig_count = 0;
|
|
endfunction
|
|
|
|
task inc_trig_count;
|
|
trig_count++;
|
|
endtask;
|
|
|
|
task sleep;
|
|
@e inc_trig_count;
|
|
`WRITE_VERBOSE(("Event in class triggered at time %0t!\n", $time));
|
|
endtask
|
|
|
|
task wake;
|
|
->e;
|
|
endtask
|
|
endclass
|
|
|
|
class WaitClass extends BaseClass;
|
|
int a;
|
|
int b;
|
|
logic ok;
|
|
|
|
function new;
|
|
a = 0;
|
|
b = 0;
|
|
ok = 0;
|
|
endfunction
|
|
|
|
task await;
|
|
wait(a == 4 && b > 16) if (a != 4 || b <= 16) $stop;
|
|
ok = 1;
|
|
`WRITE_VERBOSE(("Condition in object met at time %0t!\n", $time));
|
|
endtask
|
|
endclass
|
|
|
|
class LocalWaitClass extends BaseClass;
|
|
logic ok;
|
|
|
|
function new;
|
|
ok = 0;
|
|
endfunction
|
|
|
|
task await;
|
|
int a = 0;
|
|
int b = 100;
|
|
fork
|
|
wait(a == 42 || b != 100) if (a != 42 && b == 100) $stop;
|
|
#10 a = 42;
|
|
join
|
|
ok = 1;
|
|
`WRITE_VERBOSE(("Condition with local variables met at time %0t!\n", $time));
|
|
endtask
|
|
endclass
|
|
|
|
class ClkClass;
|
|
logic clk;
|
|
int count;
|
|
|
|
function new;
|
|
clk = 0;
|
|
count = 0;
|
|
endfunction
|
|
|
|
task flip;
|
|
clk = ~clk;
|
|
endtask;
|
|
|
|
task count_5;
|
|
@(posedge clk) count++;
|
|
@(posedge clk) count++;
|
|
@(posedge clk) count++;
|
|
@(posedge clk) count++;
|
|
@(posedge clk) count++;
|
|
endtask
|
|
endclass
|
|
|
|
EventClass ec = new;
|
|
WaitClass wc = new;
|
|
LocalWaitClass lc = new;
|
|
ClkClass cc = new;
|
|
|
|
initial begin
|
|
@ec.e;
|
|
ec.sleep;
|
|
if (wc.ok) $stop;
|
|
wc.await;
|
|
if (lc.ok) $stop;
|
|
lc.await;
|
|
end
|
|
|
|
initial #20 ec.wake;
|
|
initial #40 ->ec.e;
|
|
initial begin
|
|
wc.a = #50 4;
|
|
wc.b = #10 32;
|
|
end
|
|
|
|
always @ec.e begin
|
|
ec.inc_trig_count;
|
|
`WRITE_VERBOSE(("Event in class triggered at time %0t!\n", $time));
|
|
end
|
|
|
|
always #5 cc.flip;
|
|
|
|
initial cc.count_5;
|
|
|
|
initial begin
|
|
#80
|
|
if (cc.count != 5) $stop;
|
|
if (ec.trig_count != 3) $stop;
|
|
if (!wc.ok) $stop;
|
|
if (!lc.ok) $stop;
|
|
end
|
|
|
|
// =============================================
|
|
// DELAYS
|
|
virtual class DelayClass;
|
|
pure virtual task do_delay;
|
|
pure virtual task do_sth_else;
|
|
endclass
|
|
|
|
`ifdef TEST_VERBOSE
|
|
`define DELAY_CLASS(dt) \
|
|
class Delay``dt extends DelayClass; \
|
|
virtual task do_delay; \
|
|
$write("Starting a #%0d delay\n", dt); \
|
|
#dt \
|
|
$write("Ended a #%0d delay\n", dt); \
|
|
endtask \
|
|
virtual task do_sth_else; \
|
|
$write("Task with no delay (in Delay%0d)\n", dt); \
|
|
endtask \
|
|
endclass
|
|
`else
|
|
`define DELAY_CLASS(dt) \
|
|
class Delay``dt extends DelayClass; \
|
|
virtual task do_delay; \
|
|
#dt; \
|
|
endtask \
|
|
virtual task do_sth_else; \
|
|
endtask \
|
|
endclass
|
|
`endif
|
|
|
|
`DELAY_CLASS(10);
|
|
`DELAY_CLASS(20);
|
|
`DELAY_CLASS(40);
|
|
|
|
class NoDelay extends DelayClass;
|
|
virtual task do_delay;
|
|
`WRITE_VERBOSE(("Task with no delay\n"));
|
|
endtask
|
|
virtual task do_sth_else;
|
|
`WRITE_VERBOSE(("Task with no delay (in NoDelay)\n"));
|
|
endtask
|
|
endclass
|
|
|
|
class AssignDelayClass;
|
|
logic x;
|
|
logic y;
|
|
task do_assign;
|
|
y = #10 x;
|
|
`WRITE_VERBOSE(("Did assignment with delay\n"));
|
|
endtask
|
|
endclass
|
|
|
|
initial begin
|
|
DelayClass dc;
|
|
Delay10 d10 = new;
|
|
Delay20 d20 = new;
|
|
Delay40 d40 = new;
|
|
NoDelay dNo = new;
|
|
AssignDelayClass dAsgn = new;
|
|
`WRITE_VERBOSE(("I'm at time %0t\n", $time));
|
|
dc = d10;
|
|
dc.do_delay;
|
|
dc.do_sth_else;
|
|
`WRITE_VERBOSE(("I'm at time %0t\n", $time));
|
|
if ($time != 10) $stop;
|
|
dc = d20;
|
|
dc.do_delay;
|
|
dc.do_sth_else;
|
|
`WRITE_VERBOSE(("I'm at time %0t\n", $time));
|
|
if ($time != 30) $stop;
|
|
dc = d40;
|
|
dc.do_delay;
|
|
dc.do_sth_else;
|
|
`WRITE_VERBOSE(("I'm at time %0t\n", $time));
|
|
if ($time != 70) $stop;
|
|
dc = dNo;
|
|
dc.do_delay;
|
|
dc.do_sth_else;
|
|
`WRITE_VERBOSE(("I'm at time %0t\n", $time));
|
|
dAsgn.x = 1;
|
|
dAsgn.y = 0;
|
|
fork #5 dAsgn.x = 0; join_none
|
|
dAsgn.do_assign;
|
|
if ($time != 80) $stop;
|
|
if (dAsgn.y != 1) $stop;
|
|
// Test if the object is deleted before do_assign finishes:
|
|
fork dAsgn.do_assign; join_none
|
|
#5 dAsgn = null;
|
|
#15 $write("*-* All Finished *-*\n");
|
|
$finish;
|
|
end
|
|
|
|
// =============================================
|
|
// FORKS
|
|
class ForkDelayClass;
|
|
task do_delay; #40; endtask
|
|
endclass
|
|
|
|
class ForkClass;
|
|
int done = 0;
|
|
task do_fork();
|
|
ForkDelayClass d;
|
|
fork
|
|
begin
|
|
#10 done++;
|
|
`WRITE_VERBOSE(("Forked process %0d ending at time %0t\n", done, $time));
|
|
end
|
|
fork
|
|
begin
|
|
#20 done++;
|
|
`WRITE_VERBOSE(("Forked process %0d ending at time %0t\n", done, $time));
|
|
d = new;
|
|
end
|
|
begin
|
|
#30 d.do_delay;
|
|
done++;
|
|
`WRITE_VERBOSE(("Forked process %0d ending at time %0t\n", done, $time));
|
|
end
|
|
join
|
|
join
|
|
done++;
|
|
`WRITE_VERBOSE(("All forked processes ended at time %0t\n", $time));
|
|
endtask
|
|
endclass
|
|
|
|
initial begin
|
|
ForkClass fc = new;
|
|
fc.do_fork;
|
|
if (fc.done != 4 || $time != 70) $stop;
|
|
end
|
|
|
|
initial #101 $stop; // timeout
|
|
endmodule
|