SV之流程控制

目录

 

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 = %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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值