0
点赞
收藏
分享

微信扫一扫

Verilog 时序控制



关键词:时延控制,事件触发,边沿触发,电平触发

Verilog 提供了 2 大类时序控制方法:时延控制和事件控制。事件控制主要分为边沿触发事件控制与电平敏感事件控制。

时延控制

基于时延的时序控制出现在表达式中,它指定了语句从开始执行到执行完毕之间的时间间隔。

时延可以是数字、标识符或者表达式。

根据在表达式中的位置差异,时延控制又可以分为常规时延与内嵌时延。

常规时延

遇到常规延时时,该语句需要等待一定时间,然后将计算结果赋值给目标信号。

格式为:#delay procedural_statement,例如:

reg  value_test ;
reg  value_general ;
#10  value_general    = value_test ;

该时延方式的另一种写法是直接将井号 #

#10 ;
value_ single         = value_test ;

内嵌时延

遇到内嵌延时时,该语句先将计算结果保存,然后等待一定的时间后赋值给目标信号。

内嵌时延控制加在赋值号之后。例如:

reg  value_test ;
reg  value_embed ;
value_embed        = #10 value_test ;

需要说明的是,这 2 种时延控制方式的效果是有所不同的。

当延时语句的赋值符号右端是常量时,2 种时延控制都能达到相同的延时赋值效果。

当延时语句的赋值符号右端是变量时,2 种时延控制可能会产生不同的延时赋值效果。

例如下面仿真代码:


实例



`timescale 1ns/1ns
 
module test ;
    reg  value_test ;
    reg  value_general, value_embed, value_single ,cc;
 
    //signal source
    initial begin
        value_test        = 0 ;
        #25 ;      value_test        = 1 ;
        #35 ;      value_test        = 0 ;        //absolute 60ns
        #40 ;      value_test        = 1 ;        //absolute 100ns
        #10 ;      value_test        = 0 ;        //absolute 110ns
    end
 
    //(1)general delay control
    initial begin
        value_general     = 1;
        #10 value_general  = value_test ; //10ns, value_test=0
        #45 value_general  = value_test ; //55ns, value_test=1
        #30 value_general  = value_test ; //85ns, value_test=0
        #20 value_general  = value_test ; //105ns, value_test=1
    end
    
        initial begin
        cc     = 1;
        #10 cc  = 0 ; 
        #45 cc  = 1 ; 
        #30 cc  =0 ; 
        #20 cc  = 1 ; 
    end
 
    //(2)embedded delay control
    initial begin
        value_embed       = 1;
        value_embed  = #10 value_test ; //0ns, value_test=0
        value_embed  = #45 value_test ; //10ns, value_test=0
        value_embed  = #30 value_test ; //55ns, value_test=1
        value_embed  = #20 value_test ; //85ns, value_test=0
    end
 
    //(3)single delay control
    initial begin
        value_single      = 1;
        #10 ;
        value_single = value_test ; //10ns, value_test=0
        #45 ;
        value_single = value_test ; //55ns, value_test=1
        #30 ;
        value_single = value_test ; //85ns, value_test=0
        #20 ;
        value_single = value_test ; //105ns, value_test=1
    end
 
    always begin
        #10;
        if ($time >= 150) begin
            $finish ;
        end
    end
 
endmodule

我用的是 vivado 21 秒学会 vivado 仿真

Verilog 时序控制_sed

仿真结果如下,由图可知:

  • (1)一般延时的两种表达方式执行的结果都是一致的。
  • (2)一般时延赋值方式:遇到延迟语句后先延迟一定的时间,然后将当前操作数赋值给目标信号,并没有"惯性延迟"的特点,不会漏掉相对较窄的脉冲。
  • (3)内嵌时延赋值方式:遇到延迟语句后,先计算出表达式右端的结果,然后再延迟一定的时间,赋值给目标信号。

Verilog 时序控制_事件控制_02

下面分析下内嵌延时的赋值过程:

value_embed  = #10 value_test ; //0ns, value_test=0

0ns 时,执行此延时语句。

先将 0 赋值给信号 value_embed, 延迟 10ns 输出为 0;

value_embed  = #45 value_test ; //10ns, value_test=0

10ns 时,执行此延时语句。

由于此时 value_test 仍然为 0,所以 value_embed 值不变。

即到 55ns 时,value_embed 值仍然保持为 0。

value_embed  = #30 value_test ; //55ns, value_test=1

同理,55ns 时,value_test 值为 1,将其赋值给 value_embed 并延迟 30ns 输出。

所以 85ns 时,value_embed 输出为 1。

value_embed  = #20 value_test ; //85ns, value_test=0

同理,105ns 时,value_embed 输出为 0。

边沿触发事件控制

在 Verilog 中,事件是指某一个 reg 或 wire 型变量发生了值的变化。

基于事件触发的时序控制又主要分为以下几种。

一般事件控制

事件控制用符号 @

语句执行的条件是信号的值发生特定的变化。

关键字 posedge 指信号发生边沿正向跳变,negedge 指信号发生负向边沿跳变,未指明跳变方向时,则 2 种情况的边沿变化都会触发相关事件。例如:


实例



//信号clk只要发生变化,就执行q<=d,双边沿D触发器模型
  
 
  always 
  @
  (clk
  ) q 
  <= d 
  ;                
  
 
  //在信号clk上升沿时刻,执行q<=d,正边沿D触发器模型
  
 
  always 
  @
  (
  posedge clk
  ) q 
  <= d 
  ;  
  
 
  //在信号clk下降沿时刻,执行q<=d,负边沿D触发器模型
  
 
  always 
  @
  (
  negedge clk
  ) q 
  <= d 
  ; 
  
 
  //立刻计算d的值,并在clk上升沿时刻赋值给q,不推荐这种写法
  
 q 
  = 
  @
  (
  posedge clk
  ) d 
  ;


命名事件控制

->


实例


event     start_receiving 
  ;
  
 
  always 
  @
  ( 
  posedge clk_samp
  ) 
  begin
  
         
  -> start_receiving 
  ;       
  //采样时钟上升沿作为时间触发时刻
  
 
  end
  
  
  
 
  always 
  @
  (start_receiving
  ) 
  begin
  
     data_buf 
  = 
  {data_if
  [
  0
  ]
  , data_if
  [
  1
  ]
  } 
  ; 
  //触发时刻,对多维数据整合
  
 
  end

敏感列表

or 连接多个事件或信号。这些事件或信号组成的列表称为"敏感列表"。当然,or 也可以用逗号 ,


实例


//带有低有效复位端的D触发器模型
  
 
  always 
  @
  (
  posedge clk 
  or 
  negedge rstn
  )    
  begin      
  
 
  //always @(posedge clk , negedge rstn)    begin      
  
 
  //也可以使用逗号陈列多个事件触发
  
     
  if(
  ! rstn)
  begin
  
         q 
  <= 
  1'b 
  ;      
  
     
  end
  
     
  else 
  begin
  
         q 
  <= d 
  ;
  
     
  end
  
 
  end

@* 或 @(*),表示对语句块中的所有输入变量的变化都是敏感的。例如:


实例


always 
  @
  (
  *
  ) 
  begin
  
 
  //always @(a, b, c, d, e, f, g, h, i, j, k, l, m) begin 
  
 
  //两种写法等价
  
     
  assign s 
  = a
  ? b
  +c 
  : d 
  ? e
  +f 
  : g 
  ? h
  +i 
  : j 
  ? k
  +l 
  : m 
  ;
  
 
  end


电平敏感事件控制

@+敏感列表

Verilog 中还支持使用电平作为敏感信号来控制时序,即后面语句的执行需要等待某个条件为真。Verilog 中使用关键字 wait 来表示这种电平敏感情况。例如:


实例



initial 
  begin
  
     
  wait 
  (start_enable
  ) 
  ;      
  //等待 start 信号
  
     
  forever 
  begin
  
         
  //start信号使能后,在clk_samp上升沿,对数据进行整合
  
         
  @
  (
  posedge clk_samp
  )  
  ;
  
         data_buf 
  = 
  {data_if
  [
  0
  ]
  , data_if
  [
  1
  ]
  } 
  ;      
  
     
  end
  
 
  end

源码下载

Download


举报

相关推荐

0 条评论