• ASIC-WORLD Verilog(11)过程时序控制


    写在前面

            在自己准备写一些简单的verilog教程之前,参考了许多资料----Asic-World网站的这套verilog教程即是其一。这套教程写得极好,奈何没有中文,在下只好斗胆翻译过来(加了自己的理解)分享给大家。

            这是网站原文:Verilog Tutorial

            这是系列导航:Verilog教程系列文章导航


    过程块和时序控制(Procedural blocks and timing controls

    • 延时控制(Delay controls)
    • 边沿敏感的事件控制(Edge-Sensitive Event controls)
    • 电平敏感的事件控制(Level-Sensitive Event controls-Wait statements)
    • 特定事件控制(Named Events)

    延时控制

            通过指定特定的仿真时间来达到延时的目的,一般语法是这样的:

    #< time > < statement >;

            比如2个仿真时间单位后给复位信号赋值1;5个仿真时间单位后在给复位信号赋值0:

    #2 reset = 1; //2个时间单位后赋值为1

    #5 reset = 0; //5个时间单位后赋值为0

            下面是一个完整的例子,用来模拟一个复位,并通过 $monitor 来监控各个寄存器的值:

    1. module clk_gen ();
    2. reg clk, reset;
    3. initial begin
    4. $monitor ("TIME = %g RESET = %b CLOCK = %b", $time, reset, clk); //监控各个寄存器的值
    5. clk = 0;
    6. reset = 0;
    7. #2 reset = 1; //2个单位后复位赋值为1
    8. #5 reset = 0; //5个单位后复位赋值为0
    9. #10 $finish;
    10. end
    11. always #1 clk = ! clk; //每一个时间单位翻转一次时钟,即生成时钟信号,周期为2个时间单位
    12. endmodule

            这是窗口的仿真结果:

     TIME = 0  RESET = 0 CLOCK = 0
     TIME = 1  RESET = 0 CLOCK = 1
     TIME = 2  RESET = 1 CLOCK = 0
     TIME = 3  RESET = 1 CLOCK = 1
     TIME = 4  RESET = 1 CLOCK = 0
     TIME = 5  RESET = 1 CLOCK = 1
     TIME = 6  RESET = 1 CLOCK = 0
     TIME = 7  RESET = 0 CLOCK = 1
     TIME = 8  RESET = 0 CLOCK = 0
     TIME = 9  RESET = 0 CLOCK = 1
     TIME = 10 RESET = 0 CLOCK = 0
     TIME = 11 RESET = 0 CLOCK = 1
     TIME = 12 RESET = 0 CLOCK = 0
     TIME = 13 RESET = 0 CLOCK = 1
     TIME = 14 RESET = 0 CLOCK = 0
     TIME = 15 RESET = 0 CLOCK = 1
     TIME = 16 RESET = 0 CLOCK = 0

            这是仿真结果的波形图:


    边沿敏感的事件控制

            通过指定特定事件的边沿变化来控制时间(语句)的执行。一般语法是这样的:

    @ (< posedge >|< negedge > signal) < statement >;

            通过时钟信号的 上升沿/下降沿 来控制某个事件的执行就是很经典的边沿敏感型事件控制语句。比如在enable信号的上升沿后的5个时钟单位后触发trigger信号为1:

    1. always @ (posedge enable)begin
    2. trigger = 0;
    3. repeat (5) begin //重复5
    4. @ (posedge clk) ; //在上升沿被触发
    5. end
    6. trigger = 1; //触发其值为1
    7. end

            这个代码可以拓展一下,并加上相应的测试脚本:

    1. module edge_wait_example();
    2. reg enable, clk, trigger;
    3. //在每个enable上升沿的5个时钟后把trigger赋值为1
    4. always @ (posedge enable)
    5. begin
    6. trigger = 0;
    7. repeat (5) begin
    8. @ (posedge clk) ;
    9. end
    10. trigger = 1;
    11. end
    12. initial begin
    13. $monitor ("TIME : %g CLK : %b ENABLE : %b TRIGGER : %b",
    14. $time, clk,enable,trigger);
    15. clk = 0;
    16. enable = 0;
    17. //通过延时语句分别对enable赋值
    18. #5 enable = 1;
    19. #1 enable = 0;
    20. #10 enable = 1;
    21. #1 enable = 0;
    22. #10 $finish;
    23. end
    24. always #1 clk = ~clk;
    25. endmodule

            这是仿真结果:

     TIME : 0 CLK : 0 ENABLE : 0 TRIGGER : x
     TIME : 1 CLK : 1 ENABLE : 0 TRIGGER : x
     TIME : 2 CLK : 0 ENABLE : 0 TRIGGER : x
     TIME : 3 CLK : 1 ENABLE : 0 TRIGGER : x
     TIME : 4 CLK : 0 ENABLE : 0 TRIGGER : x
     TIME : 5 CLK : 1 ENABLE : 1 TRIGGER : 0
     TIME : 6 CLK : 0 ENABLE : 0 TRIGGER : 0
     TIME : 7 CLK : 1 ENABLE : 0 TRIGGER : 0
     TIME : 8 CLK : 0 ENABLE : 0 TRIGGER : 0
     TIME : 9 CLK : 1 ENABLE : 0 TRIGGER : 0
     TIME : 10 CLK : 0 ENABLE : 0 TRIGGER : 0
     TIME : 11 CLK : 1 ENABLE : 0 TRIGGER : 0
     TIME : 12 CLK : 0 ENABLE : 0 TRIGGER : 0
     TIME : 13 CLK : 1 ENABLE : 0 TRIGGER : 0
     TIME : 14 CLK : 0 ENABLE : 0 TRIGGER : 0
     TIME : 15 CLK : 1 ENABLE : 0 TRIGGER : 1
     TIME : 16 CLK : 0 ENABLE : 1 TRIGGER : 0
     TIME : 17 CLK : 1 ENABLE : 0 TRIGGER : 0
     TIME : 18 CLK : 0 ENABLE : 0 TRIGGER : 0
     TIME : 19 CLK : 1 ENABLE : 0 TRIGGER : 0
     TIME : 20 CLK : 0 ENABLE : 0 TRIGGER : 0
     TIME : 21 CLK : 1 ENABLE : 0 TRIGGER : 0
     TIME : 22 CLK : 0 ENABLE : 0 TRIGGER : 0
     TIME : 23 CLK : 1 ENABLE : 0 TRIGGER : 0
     TIME : 24 CLK : 0 ENABLE : 0 TRIGGER : 0
     TIME : 25 CLK : 1 ENABLE : 0 TRIGGER : 1
     TIME : 26 CLK : 0 ENABLE : 0 TRIGGER : 1

    电平敏感的事件控制

            当前条件为真时才执行接下来的语句,有点类似if语句。它的一般语法是这样的:

    wait (< expression >) < statement >; 

            比如当data_ready为真时,才把data_bus的值赋给data:

    wait (data_ready == 1)  data = data_bus; 

            这个代码可以拓展一下,并加上相应的测试脚本:

    1. module wait_example();
    2. reg mem_read, data_ready;
    3. reg [7:0] data_bus, data;
    4. always @ (mem_read or data_bus or data_ready) begin
    5. data = 0;
    6. while (mem_read == 1'b1) begin
    7. wait (data_ready == 1) #1 data = data_bus;
    8. end
    9. end
    10. // Testbench Code here
    11. initial begin
    12. $monitor ("TIME = %g READ = %b READY = %b DATA = %b",
    13. $time, mem_read, data_ready, data);
    14. data_bus = 0;
    15. mem_read = 0;
    16. data_ready = 0;
    17. #10 data_bus = 8'hDE;
    18. #10 mem_read = 1;
    19. #20 data_ready = 1;
    20. #1 mem_read = 1;
    21. #1 data_ready = 0;
    22. #10 data_bus = 8'hAD;
    23. #10 mem_read = 1;
    24. #20 data_ready = 1;
    25. #1 mem_read = 1;
    26. #1 data_ready = 0;
    27. #10 $finish;
    28. end
    29. endmodule

            这是仿真结果:

     TIME = 0  READ = 0 READY = 0 DATA = 00000000
     TIME = 20 READ = 1 READY = 0 DATA = 00000000
     TIME = 40 READ = 1 READY = 1 DATA = 00000000
     TIME = 41 READ = 1 READY = 1 DATA = 11011110
     TIME = 42 READ = 1 READY = 0 DATA = 11011110
     TIME = 82 READ = 1 READY = 1 DATA = 11011110
     TIME = 83 READ = 1 READY = 1 DATA = 10101101
     TIME = 84 READ = 1 READY = 0 DATA = 10101101 


    赋值内延迟语句(Intra-Assignment Timing Controls) 

            这是相对于 赋值间延迟语句(Inter-Assignment Timing Controls) 的概念,赋值间延迟语句就是我们平常最常用的延迟语句,也就是这种:

    #10 rega = regb;

            这种情况下,赋值语句需要等待一定时间,然后将计算结果(右侧值)赋值给目标信号(左侧值)。 

            而赋值内延迟语句的用法则是这样的:

    rega = #10 regb;

            它是先计算出右侧值,延时完成后再将结果赋给左侧。看看下面的例子:

    1. 1 module intra_assign();
    2. 2
    3. 3 reg a, b;
    4. 4
    5. 5 initial begin
    6. 6 $monitor("TIME = %g A = %b B = %b",$time, a , b);
    7. 7 a = 1;
    8. 8 b = 0;
    9. 9 a = #10 0;
    10. 10 b = a;
    11. 11 #20 $display("TIME = %g A = %b B = %b",$time, a , b);
    12. 12 $finish;
    13. 13 end
    14. 14
    15. 15 endmodule

            这是仿真结果:

     TIME = 0   A = 1  B = 0
     TIME = 10  A = 0  B = 0
     TIME = 30  A = 0  B = 0 


    使用连续赋值语句对组合逻辑建模

            组合逻辑就是无论右侧的结果何时发生了变化,左侧的值都会同样立即发生改变。

    例1 三态缓冲器

    1. module tri_buf_using_assign();
    2. reg data_in, enable;
    3. wire pad;
    4. assign pad = (enable) ? data_in : 1'bz;
    5. initial begin
    6. $monitor ("TIME = %g ENABLE = %b DATA : %b PAD %b",
    7. $time, enable, data_in, pad);
    8. #1 enable = 0;
    9. #1 data_in = 1;
    10. #1 enable = 1;
    11. #1 data_in = 0;
    12. #1 enable = 0;
    13. #1 $finish;
    14. end
    15. endmodule

            这个三态缓冲器也是经典的控制I2C、1-Wire等总线的一种方法。当enable为1时,就往总线上输出数据;当enable为0时,此时总线为高组态,就可以从总线上读取数据了。

            仿真结果:

     TIME = 0 ENABLE = x DATA : x PAD x
     TIME = 1 ENABLE = 0 DATA : x PAD z
     TIME = 2 ENABLE = 0 DATA : 1 PAD z
     TIME = 3 ENABLE = 1 DATA : 1 PAD 1
     TIME = 4 ENABLE = 1 DATA : 0 PAD 0
     TIME = 5 ENABLE = 0 DATA : 0 PAD z

    例2 多路选择器

            同样的,这样还可以实现多路选择器:

    1. module mux_using_assign();
    2. reg data_in_0, data_in_1;
    3. wire data_out;
    4. reg sel;
    5. assign data_out = (sel) ? data_in_1 : data_in_0;
    6. // Testbench code here
    7. initial begin
    8. $monitor("TIME = %g SEL = %b DATA0 = %b DATA1 = %b OUT = %b",
    9. $time,sel,data_in_0,data_in_1,data_out);
    10. data_in_0 = 0;
    11. data_in_1 = 0;
    12. sel = 0;
    13. #10 sel = 1;
    14. #10 $finish;
    15. end
    16. // Toggel data_in_0 at #1
    17. always #1 data_in_0 = ~data_in_0;
    18. // Toggel data_in_1 at #2
    19. always #2 data_in_1 = ~data_in_1;
    20. endmodule

            仿真结果很简单直观,看看就好:

     TIME = 0 SEL = 0 DATA0 = 0 DATA1 = 0 OUT = 0
     TIME = 1 SEL = 0 DATA0 = 1 DATA1 = 0 OUT = 1
     TIME = 2 SEL = 0 DATA0 = 0 DATA1 = 1 OUT = 0
     TIME = 3 SEL = 0 DATA0 = 1 DATA1 = 1 OUT = 1
     TIME = 4 SEL = 0 DATA0 = 0 DATA1 = 0 OUT = 0
     TIME = 5 SEL = 0 DATA0 = 1 DATA1 = 0 OUT = 1
     TIME = 6 SEL = 0 DATA0 = 0 DATA1 = 1 OUT = 0
     TIME = 7 SEL = 0 DATA0 = 1 DATA1 = 1 OUT = 1
     TIME = 8 SEL = 0 DATA0 = 0 DATA1 = 0 OUT = 0
     TIME = 9 SEL = 0 DATA0 = 1 DATA1 = 0 OUT = 1
     TIME = 10 SEL = 1 DATA0 = 0 DATA1 = 1 OUT = 1
     TIME = 11 SEL = 1 DATA0 = 1 DATA1 = 1 OUT = 1
     TIME = 12 SEL = 1 DATA0 = 0 DATA1 = 0 OUT = 0
     TIME = 13 SEL = 1 DATA0 = 1 DATA1 = 0 OUT = 0
     TIME = 14 SEL = 1 DATA0 = 0 DATA1 = 1 OUT = 1
     TIME = 15 SEL = 1 DATA0 = 1 DATA1 = 1 OUT = 1
     TIME = 16 SEL = 1 DATA0 = 0 DATA1 = 0 OUT = 0
     TIME = 17 SEL = 1 DATA0 = 1 DATA1 = 0 OUT = 0
     TIME = 18 SEL = 1 DATA0 = 0 DATA1 = 1 OUT = 1
     TIME = 19 SEL = 1 DATA0 = 1 DATA1 = 1 OUT = 1 


    •  📣您有任何问题,都可以在评论区和我交流📃!
    • 📣本文由 孤独的单刀 原创,首发于CSDN平台🐵,博客主页:wuzhikai.blog.csdn.net
    • 📣您的支持是我持续创作的最大动力!如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!

  • 相关阅读:
    面试经验分享
    Dubbo源码(五) - 服务目录
    【Python爬虫项目实战四】Chatgpt国内接口分享第一期
    四川轻化工大学计算机考研资料汇总
    konva系列教程3:自定义图形
    图文详细解决IDEA使用Debug模式启动项目一直转圈圈跑起不来(亲测可以)
    关于APS生产排产软件选择,有哪几个要素?
    8.12 矢量图层面要素单一符号使用二(仅渲染中心点)
    Flutter快学快用18 项目实战:实践 Flutter 交友功能
    暖阳脚本_ 将Agent技术的灵活性引入RPA,清华等发布自动化智能体ProAgent
  • 原文地址:https://blog.csdn.net/wuzhikaidetb/article/details/132014751