目录
loop循环
(1)while and do-while
do...while与while的区别就是前者至少会执行一次.格式如下:
while (<condition>) begin
// Multiple statements
end
do begin
// Multiple statements
end while (<condition>);
例子:
module tb;
initial begin
int cnt = 0;
while (cnt < 5) begin
$display("cnt = %0d", cnt);
cnt++;
end
end
endmodule
module tb;
initial begin
int cnt = 0;
do begin
$display("cnt = %0d", cnt);
cnt++;
end while (cnt == 0);
end
endmodule
(2)foreach
一般用来遍历数组元素,格式如下:
foreach(<variable>[<iterator>]) begin//数组的索引从0开始,foreach不需要定义索引变量
// Multiple statements
end
等同于:
for (int i = 0; i < $size(array); i++) begin
// Statements inside the for loop
end
例子:
module tb;
int array[5] = '{1, 2, 3, 4, 5};
int sum;
initial begin
// Here, "i" is the iterator and can be named as anything you like
// Iterate through each element from index 0 to end using a foreach
// loop.
foreach (array[i])
$display ("array[%0d] = %0d", i, array[i]);
// Multiple statements in foreach loop requires begin end
// Here, we are calculating the sum of all numbers in the array
// And because there are 2 statements within foreach there should
// be a begin-end
foreach (array[l_index]) begin
sum += array[l_index];
$display ("array[%0d] = %0d, sum = %0d", l_index, array[l_index], sum);
end
end
endmodule
结果:
(3)for
格式:
for ( [initialization]; <condition>; [modifier]) begin
// Multiple statements
end
例子:
module tb;
string array [5] = '{"apple", "orange", "pear", "blueberry", "lemon"};
initial begin
for (int i = 0; i < $size(array); i++)
$display ("array[%0d] = %s", i, array[i]);
end
endmodule
结果:
(4)forever
在SV中,always块不能存在于类和其他过程块中,所以用forever代替。格式如下:
always begin
// Multiple statements
end
例子:
class Monitor;
virtual task run();
forever begin
@(posedge vif.clk);
if (vif.write & vif.sel)
// Capture write data
if (!vif.write & vif.sel)
// Capture read data
end
endtask
endclass
module tb;
Monitor mon;
// Start the monitor task and allow it to continue as
// long as there is activity on the bus
initial begin
fork
mon.run();
join_none
end
endmodule
(5)repeat
格式:
repeat (<number>) begin//执行number次
// Multiple Statements
end
例子:
module tb;
bit clk;
always #10 clk = ~clk;
initial begin
bit [2:0] num = $random;
$display ("[%0t] Repeat loop is going to start with num = %0d", $time, num);
repeat (num) @(posedge clk);
$display ("[%0t] Repeat loop has finished", $time);
$finish;
end
endmodule
结果:
'break' and 'continue'
两个关键字的用法与其他语言一致.
module tb;
initial begin
// This for loop increments i from 0 to 9 and exit
for (int i = 0 ; i < 10; i++) begin
$display ("Iteration [%0d]", i);
// Let's create a condition such that the
// for loop exits when i becomes 7
if (i == 7)
break;
end
end
endmodule
module tb;
initial begin
// This for loop increments i from 0 to 9 and exit
for (int i = 0 ; i < 10; i++) begin
// Let's create a condition such that the
// for loop
if (i == 7)
continue;
$display ("Iteration [%0d]", i);
end
end
endmodule
选择语句
(1)if...else
与verilog中的用法相同,不在叙述。下面介绍几种SV独特的if语句:
unique-if, unique0-if
对于unique-if ,如果condition没有一个匹配且没有加else语句,则会报告一个错误;如果超过1个condition匹配,也会报告错误;
unique0-if与unique-if的不同之处在于,如果没有一个condition匹配也不会报错;
例子:
module tb;
int x = 4;
initial begin
// This if else if construct is declared to be "unique"
// Error is not reported here because there is a "else"
// clause in the end which will be triggered when none of
// the conditions match
unique if (x == 3)
$display ("x is %0d", x);
else if (x == 5)
$display ("x is %0d", x);
else
$display ("x is neither 3 nor 5");
// When none of the conditions become true and there
// is no "else" clause, then an error is reported
unique if (x == 3)
$display ("x is %0d", x);
else if (x == 5)
$display ("x is %0d", x);
end
endmodule
module tb;
int x = 4;
initial begin
// This if else if construct is declared to be "unique"
// When multiple if blocks match, then error is reported
unique if (x == 4)
$display ("1. x is %0d", x);
else if (x == 4)
$display ("2. x is %0d", x);
else
$display ("x is not 4");
end
endmodule
priority-if
如果condition没有一个匹配且没有加else语句,则会报告一个错误;如果有多个condition匹配,排在前面的优先级最高,且执行完最高的优先级后退出选择;
module tb;
int x = 4;
initial begin
// Exits if-else block once the first match is found
priority if (x == 4)
$display ("x is %0d", x);
else if (x != 5)
$display ("x is %0d", x);
end
endmodule
(2)case
与verilog中的case相同,下面介绍几种SV独特的case语句:
unique,unique0 case
对于unique case ,如果case没有一个匹配且,则会报告一个错误;如果超过1个condition匹配,也会报告错误,同时执行第一个匹配到的case;
unique0-case与unique-case的不同之处在于,如果没有一个case匹配也不会报错;
module tb;
bit [1:0] abc;
initial begin
abc = 1;
// None of the case items match the value in "abc"
// A violation is reported here
unique case (abc)
0 : $display ("Found to be 0");
2 : $display ("Found to be 2");
endcase
end
endmodule
没有一个case匹配,所以会报错,结果如下:
module tb;
bit [1:0] abc;
initial begin
abc = 0;
// Multiple case items match the value in "abc"
// A violation is reported here
unique case (abc)
0 : $display ("Found to be 0");
0 : $display ("Again found to be 0");
2 : $display ("Found to be 2");
endcase
end
endmodule
多个匹配,会报错,且执行第一个匹配case,结果如下:
priority case
出现多个匹配的case情况不会报错,且执行第一个case.
Blocking & Non-Blocking
(1) Blocking
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a = 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
#10 b = 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c = 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
#5 d = 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
#5 e = 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
(2)Non-blocking
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a <= 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
#10 b <= 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c <= 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
#5 d <= 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
#5 e <= 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
Event
一个事件是一个静态对象句柄用来同步多个并发线程,一个线程触发事件,另一个线程等待事件.
事件的默认值是null,可以将事件作为参数传递给队列、任务和函数.
event over; // a new event is created called over
event over_again = over; // over_again becomes an alias to over
event empty = null; // event variable with no synchronization object
事件的触发可以用操作符"->"或"->>",线程等待事件触发可以用"@(事件名)"或"wait(事件名..triggered)"。注意, triggered 后不能加括号!!
module tb;
// Create an event variable that processes can use to trigger and wait
event event_a;
// Thread1: Triggers the event using "->" operator
initial begin
#20 ->event_a;
$display ("[%0t] Thread1: triggered event_a", $time);
end
// Thread2: Waits for the event using "@" operator
initial begin
$display ("[%0t] Thread2: waiting for trigger ", $time);
@(event_a);
$display ("[%0t] Thread2: received event_a trigger ", $time);
end
// Thread3: Waits for the event using ".triggered"
initial begin
$display ("[%0t] Thread3: waiting for trigger ", $time);
wait(event_a.triggered);
$display ("[%0t] Thread3: received event_a trigger", $time);
end
endmodule
下面来看一下两条等待触发语句的区别:
module tb;
// Create an event variable that processes can use to trigger and wait
event event_a;
// Thread1: Triggers the event using "->" operator at 20ns
initial begin
#20 ->event_a;
$display ("[%0t] Thread1: triggered event_a", $time);
end
// Thread2: Starts waiting for the event using "@" operator at 20ns
initial begin
$display ("[%0t] Thread2: waiting for trigger ", $time);
#20 @(event_a);
$display ("[%0t] Thread2: received event_a trigger ", $time);
end
// Thread3: Starts waiting for the event using ".triggered" at 20ns
initial begin
$display ("[%0t] Thread3: waiting for trigger ", $time);
#20 wait(event_a.triggered);
$display ("[%0t] Thread3: received event_a trigger", $time);
end
endmodule
上面的例子,事件触发和等待事件触发的语句都在20ns的时刻发生,对于"@"操作符由于是边沿敏感的,所以它会错过事件的触发;而"wait"则是电平敏感的,它会捕捉到事件的触发。所以输出结果如下:
wait_order
等待多个事件以给定的顺序触发,如果事件的触发不是给定的顺序则会报错.
module tb;
// Declare three events that can be triggered separately
event a, b, c;
// This block triggers each event one by one
initial begin
#10 -> a;
#10 -> b;
#10 -> c;
end
// This block waits until each event is triggered in the given order
initial begin
wait_order (a,b,c)
$display ("Events were executed in the correct order");
else //还可以添加else语句
$display ("Events were NOT executed in the correct order !");
end
endmodule
Merging Events 合并事件
如果将一个事件赋给另一个事件,则所有等待事件1触发的线程同时也在等待事件2触发,也就是说两个事件是关联的.
module tb;
// Create event variables
event event_a, event_b;
initial begin
fork
// Thread1: waits for event_a to be triggered
begin
wait(event_a.triggered);
$display ("[%0t] Thread1: Wait for event_a is over", $time);
end
// Thread2: waits for event_b to be triggered
begin
wait(event_b.triggered);
$display ("[%0t] Thread2: Wait for event_b is over", $time);
end
// Thread3: triggers event_a at 20ns
#20 ->event_a;
// Thread4: triggers event_b at 30ns
#30 ->event_b;
// Thread5: Assigns event_b to event_a at 10ns
begin
// Comment code below and try again to see Thread2 finish later
#10 event_b = event_a;//将事件a赋给事件b
end
join
end
endmodule
event在触发后会一直保持触发状态直到仿真结束,所以上面的例子在20ns 的时候触发了a和b后,再在30ns触发b的语句也就没有作用了。
function
函数只能返回一个值,且不能包含任何延时语句
- 一个函数不能带时间控制语句,如@、#、fork...join、always、wait等;
- 函数不能调用任务,因为任务允许时延;
module tb;
// There are two ways to call the function:
initial begin
// 1. Call function and assign value to a variable, and then use variable
int s = sum(3, 4);
$display ("sum(3,4) = %0d", s);
// 2. Call function and directly use value returned
$display ("sum(5,9) = %0d", sum(5,9));
$display ("mul(3,1) = %0d", mul(3,1));
end
// This function returns value of type "byte", and accepts two
// arguments "x" and "y". A return variable of the same name as
// function is implicitly declared and hence "sum" can be directly
// assigned without having to declare a separate return variable
function byte sum (int x, int y);
sum = x + y;
endfunction
// Instead of assigning to "mul", the computed value can be returned
// using "return" keyword
function byte mul (int x, y);
return x * y;
endfunction
endmodule
上面的函数定义格式为C语言格式,也可以采用原始的verilog格式,即声明input和ouput
module tb;
initial begin
int res, s;
s = sum(5,9);
$display ("s = %0d", sum(5,9));
$display ("sum(5,9) = %0d", sum(5,9));
$display ("mul(3,1) = %0d", mul(3,1,res));
$display ("res = %0d", res);
end
// Function has an 8-bit return value and accepts two inputs
// and provides the result through its output port and return val
function bit [7:0] sum;
input int x, y;
output sum;
sum = x + y;
endfunction
// Same as above but ports are given inline
function byte mul (input int x, y, output int res);
res = x*y + 1;
return x * y;
endfunction
endmodule
通过值传递参数,函数内部任何对参数的改变操作对于函数外部都不可见.看下例:
module tb;
initial begin
int a, res;
// 1. Lets pick a random value from 1 to 10 and assign to "a"
a = $urandom_range(1, 10);
$display ("Before calling fn: a=%0d res=%0d", a, res);
// Function is called with "pass by value" which is the default mode
res = fn(a);
// Even if value of a is changed inside the function, it is not reflected here
$display ("After calling fn: a=%0d res=%0d", a, res);
end
// This function accepts arguments in "pass by value" mode
// and hence copies whatever arguments it gets into this local
// variable called "a".
function int fn(int a);
// Any change to this local variable is not
// reflected in the main variable declared above within the
// initial block
a = a + 5;//对于函数局部变量a的任何操作,在函数外部都是不可见的
// Return some computed value
return a * 10;
endfunction
endmodule
由于参数a对于函数外部不可见,所以调用函数后a依然是原来的随机化值.结果如下:
通过引用"ref"传递参数,函数内部任何对参数的改变对于函数外部都是透明的,即函数外部可以看见函数内部对于参数的改变.看下例:
module tb;
initial begin
int a, res;
// 1. Lets pick a random value from 1 to 10 and assign to "a"
a = $urandom_range(1, 10);
$display ("Before calling fn: a=%0d res=%0d", a, res);
// Function is called with "pass by value" which is the default mode
res = fn(a);
// Even if value of a is changed inside the function, it is not reflected here
$display ("After calling fn: a=%0d res=%0d", a, res);
end
// This function accepts arguments in "pass by reference" mode
// A reference to the original argument is passed to the subroutine
// Also make the function automatic
function automatic int fn(ref int a);//引用传递方式要加关键字ref
// Any change to this local variable WILL be
// reflected in the main variable declared above within the
// initial block
a = a + 5;
// Return some computed value
return a * 10;
endfunction
endmodule
采用引用传递参数的函数要带"automatic"修饰符,这表明子程序的内部都是自动存储的.由于a的操作在函数外部可见,所以调用函数后a的值为7,结果如下:
task
相对于函数,task更加通用,可以带多个返回值,也可以使用延时语句。下面是task的两种定义格式:
// Style 1
task [name];
input [port_list];
inout [port_list];
output [port_list];
begin
[statements]
end
endtask
// Style 2
task [name] (input [port_list], inout [port_list], output [port_list]);
begin
[statements]
end
endtask
默认下,task和function都是static的,为了使用动态存储,必须添加关键字"automiatic"。如果不添加,则对于每个调用子程序的对象,这些子程序都是共有的,也就是在任何一个对象中对子程序的改变都会影响其他对象的子程序.
task sum (input [7:0] a, b, output [7:0] c);
begin
c = a + b;
end
endtask
// or
task sum;
input [7:0] a, b;
output [7:0] c;
begin
c = a + b;
end
endtask
initial begin
reg [7:0] x, y , z;
sum (x, y, z);
end
下面对比function和task:
任务可以调用其他的任务和方法,函数不能调用任务和方法.
final block
final块与initial块一样都是定义语句的过程块,只不过final块一般用于module的最后部分执行,并且其中只能调用系统函数,而且不能消耗时间.例子如下:
module final_block ();
initial begin
for (int i = 0 ; i < 10; i ++) begin
if ( (i >= 5) && (i < 8)) begin
$display ("@%g Continue with next interation", $time);
continue;
end
#1 $display ("@%g Current value of i = %g", $time, i);
end
#1 $finish;
end
final begin
$display ("Final block called at time %g", $time);
$display ("---- We can not have delays in it ----");
end
endmodule
sequence
此处的sequence就是SVA中的sequence,它不仅可以用于断言,也可以在其他情况,如下:
module sequence_event ();
reg a, b, c;
reg clk = 0;
sequence abc; //定义sequence
@(posedge clk) a ##1 b ##1 c;
endsequence
always @ (posedge clk)
begin
@ (abc) $display ("@%g ABC all are asserted", $time);//调用sequence的方法和event类似
end
// Testbench code
initial begin
$monitor("@%g clk %b a %b b %b c %b", $time, clk, a, b, c);
repeat (2) begin
#2 a = 1;
#2 b = 1;
#2 c = 1;
#2 a = 0;
b = 0;
c = 0;
end
#2 $finish;
end
always #1 clk = ~clk;
endmodule
module sequence_wait ();
reg a, b, c, d, e;
reg clk = 0;
sequence abc;
@(posedge clk) a ##1 b ##1 c;
endsequence
sequence de;
@(negedge clk) d ##[2:5] e;
endsequence
initial begin
forever begin
wait (abc.triggered || de.triggered); //wait()语句用于等待事件触发
if (abc.triggered) begin
$display( "@%g abc succeeded", $time );
end
if (de.triggered) begin
$display( "@%g de succeeded", $time );
end
#2;
end
end
// Testbench code
initial begin
$monitor("@%g clk %b a %b b %b c %b d %b e %b", $time, clk, a, b, c, d, e);
repeat (2) begin
#2 a = 1;
d = 1;
#2 b = 1;
e = 1;
#2 c = 1;
#2 a = 0;
b = 0;
c = 0;
e = 0;
end
#2 $finish;
end
always #1 clk = ~clk;
endmodule