• Verilog 时序控制


    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 ;
     
        //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
     
        //(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

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

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

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

    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(事件)类型的变量,并触发该变量来识别该事件是否发生。命名事件用关键字 event 来声明,触发信号用 -> 表示。例如:

    实例

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

    敏感列表

    当多个信号或事件中任意一个发生变化都能够触发语句的执行时,Verilog 中使用"或"表达式来描述这种情况,用关键字 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

     

  • 相关阅读:
    处理目标检测中的类别不均衡问题
    js前端获取农历日期
    web前端期末大作业 html+css+javascript火影忍者网页设计实例 动漫网站制作
    地质灾害防治可视化智慧管理系统平台
    Java 数据结构篇-实现双链表的核心API
    智慧公厕:探索未来公共厕所的创新设计
    Git 和 Github 的使用
    特征选择技术总结
    关于CSS 盒子模型的基础教程
    Vue开发实例(七)Axios的安装与使用
  • 原文地址:https://blog.csdn.net/qq_36613247/article/details/125530799