SV之流程控制

    xiaoxiao2022-07-14  187

    目录

     

    loop循环

    'break' and 'continue'

    选择语句

     Blocking & Non-Blocking

    Event

    function

    task

    final block 

    sequence 


    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 = ", cnt); cnt++; end end endmodule

    module tb; initial begin int cnt = 0; do begin $display("cnt = ", 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[ ] = ", 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[ ] = , sum = ", 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[ ] = %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 = ", $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 [ ]", 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 [ ]", 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 ", x); else if (x == 5) $display ("x is ", 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 ", x); else if (x == 5) $display ("x is ", 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 ", x); else if (x == 4) $display ("2. x is ", 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 ", x); else if (x != 5) $display ("x is ", 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) = ", s); // 2. Call function and directly use value returned $display ("sum(5,9) = ", sum(5,9)); $display ("mul(3,1) = ", 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 = ", sum(5,9)); $display ("sum(5,9) = ", sum(5,9)); $display ("mul(3,1) = ", mul(3,1,res)); $display ("res = ", 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= res= ", 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= res= ", 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= res= ", 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= res= ", 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

    最新回复(0)