• Verilog:【3】边沿检测器(edge_detect.sv)


    碎碎念:

    学习新知识的感觉还是比较快乐,下面要介绍的是一个全能的边沿检测器,可以识别上升、下降、以及同时检测两种边沿,不得不说人家的代码写得确实很优雅。

    目录

    1 模块功能

    2 模块代码

    3 模块思路

    4 TestBench与仿真结果


    1 模块功能

    多通道的边沿检测器,可以同时检测到输入信号的上升沿、下降沿以及上升/下降沿。

    2 模块代码

    1. //------------------------------------------------------------------------------
    2. // edge_detect.sv
    3. // published as part of https://github.com/pConst/basic_verilog
    4. // Konstantin Pavlov, pavlovconst@gmail.com
    5. //------------------------------------------------------------------------------
    6. // INFO ------------------------------------------------------------------------
    7. // Edge detector, ver.4
    8. //
    9. // (new!) Added WIDTH parameter to simplify instantiating arrays of edge detectors
    10. // (new!) Made reset to be asynchronous
    11. //
    12. // Added parameter to select combinational implementation (zero clocks delay)
    13. // or registered implementation (one clocks delay)
    14. //
    15. // In case when "in" port has toggle rate 100% (changes every clock period)
    16. // "rising" and "falling" outputs will completely replicate input
    17. // "both" output will be always active in this case
    18. //
    19. /* --- INSTANTIATION TEMPLATE BEGIN ---
    20. edge_detect #(
    21. .WIDTH( 32 ),
    22. .REGISTER_OUTPUTS( 1'b1 )
    23. ) in_ed (
    24. .clk( clk ),
    25. .anrst( 1'b1 ),
    26. .in( in[31:0] ),
    27. .rising( in_rise[31:0] ),
    28. .falling( ),
    29. .both( )
    30. );
    31. --- INSTANTIATION TEMPLATE END ---*/
    32. module edge_detect #( parameter
    33. bit [7:0] WIDTH = 1, // signal width
    34. bit [0:0] REGISTER_OUTPUTS = 1'b0 // 0 - comb. implementation (default)
    35. // 1 - registered implementation
    36. )(
    37. input clk,
    38. input anrst,
    39. input [WIDTH-1:0] in,
    40. output logic [WIDTH-1:0] rising,
    41. output logic [WIDTH-1:0] falling,
    42. output logic [WIDTH-1:0] both
    43. );
    44. // data delay line
    45. logic [WIDTH-1:0] in_d = '0;
    46. always_ff @(posedge clk or negedge anrst) begin
    47. if ( ~anrst ) begin
    48. in_d[WIDTH-1:0] <= '0;
    49. end else begin
    50. in_d[WIDTH-1:0] <= in[WIDTH-1:0];
    51. end
    52. end
    53. logic [WIDTH-1:0] rising_comb;
    54. logic [WIDTH-1:0] falling_comb;
    55. logic [WIDTH-1:0] both_comb;
    56. always_comb begin
    57. rising_comb[WIDTH-1:0] = {WIDTH{anrst}} & (in[WIDTH-1:0] & ~in_d[WIDTH-1:0]);
    58. falling_comb[WIDTH-1:0] = {WIDTH{anrst}} & (~in[WIDTH-1:0] & in_d[WIDTH-1:0]);
    59. both_comb[WIDTH-1:0] = {WIDTH{anrst}} & (rising_comb[WIDTH-1:0] | falling_comb[WIDTH-1:0]);
    60. end
    61. generate
    62. if( REGISTER_OUTPUTS==1'b0 ) begin
    63. // combinational outputs, no delay
    64. always_comb begin
    65. rising[WIDTH-1:0] = rising_comb[WIDTH-1:0];
    66. falling[WIDTH-1:0] = falling_comb[WIDTH-1:0];
    67. both[WIDTH-1:0] = both_comb[WIDTH-1:0];
    68. end // always
    69. end else begin
    70. // registered outputs, 1 cycle delay
    71. always_ff @(posedge clk or negedge anrst) begin
    72. if( ~anrst ) begin
    73. rising[WIDTH-1:0] <= '0;
    74. falling[WIDTH-1:0] <= '0;
    75. both[WIDTH-1:0] <= '0;
    76. end else begin
    77. rising[WIDTH-1:0] <= rising_comb[WIDTH-1:0];
    78. falling[WIDTH-1:0] <= falling_comb[WIDTH-1:0];
    79. both[WIDTH-1:0] <= both_comb[WIDTH-1:0];
    80. end // always
    81. end // if
    82. end // end else
    83. endgenerate
    84. endmodule

    3 模块思路

    重点分析一下其中我认为值得关注的地方。

    1.bit数据类型

    bit类型是System Verilog中新增的数据类型,用来表示一位的无符号整型数据,当然也可以像40行这样使用多位的定义。

    2.数据延时线(53-61行)

    如果有过编写边沿检测器的经验,应该对这个相当熟悉。利用D触发器的结构,将待检测的输入数据in延时一个周期存储到in_d中,通过分析本周期和上一周期的信号,即可判断出待检测信号的边沿状况。

    3.边沿检测逻辑(63-70行)

    这部分就是本模块的算法核心部分,通过检测复位信号anrst、本周期输入信号in以及上一周期输入信号in_d这三个信号的情况,来检测是否出现了边沿。为了实现多通道的检测,这里作者使用了位运算符,从而可以同时对多个通道进行检测。

    边沿类型anrstinin_d
    上升沿110
    下降沿101
    上升/下降沿1--

    如果需要检测上升/下降沿,只需要将上升沿与下降沿的检测结果按位或处理即可。

    4.always_comb(76-80行)

    参考博客:Systemverilog always_comb 过程块

    always_comb是System Verilog中用来指定综合结果是组合逻辑的语法,因此其不需要指定敏感表,同时在仿真的零时刻会进行自动求值,从而不会出现Verilog中没有触发信号导致仿真前期出现高阻态的情况,这种零时刻求值确实是很关键。

    5.generate(72-98行)

    参考博客:Verilog中generate的使用 - 知乎 (zhihu.com)

    generate模块在这里是用来构造条件结构,可以看到73行对REGISTER_OUTPUTS这一变量的值进行了检测,当其为0时,输出则使用always_comb构建组合逻辑电路;而当其值为1时,则会调用always_ff构建D触发器电路,从而将输出信号延迟一个周期。

    在这里使用generate模块,大大提升了代码在使用过程中的灵活性。

    4 TestBench与仿真结果

    1. //------------------------------------------------------------------------------
    2. // edge_detect_tb.sv
    3. // Konstantin Pavlov, pavlovconst@gmail.com
    4. //------------------------------------------------------------------------------
    5. // INFO ------------------------------------------------------------------------
    6. //
    7. `timescale 1ns / 1ps
    8. module edge_detect_tb();
    9. logic clk200;
    10. initial begin
    11. #0 clk200 = 1;
    12. forever
    13. #2.5 clk200 = ~clk200;
    14. end
    15. logic rst;
    16. initial begin
    17. #10.2 rst = 1;
    18. #5 rst = 0;
    19. //#10000;
    20. forever begin
    21. #9985 rst = ~rst;
    22. #5 rst = ~rst;
    23. end
    24. end
    25. logic nrst;
    26. assign nrst = ~rst;
    27. logic rst_once;
    28. initial begin // initializing non-X data before PLL starts
    29. #10.2 rst_once = 1;
    30. #5 rst_once = 0;
    31. end
    32. initial begin
    33. #510.2 rst_once = 1; // PLL starts at 500ns, clock appears, so doing the reset for modules
    34. #5 rst_once = 0;
    35. end
    36. logic nrst_once;
    37. assign nrst_once = ~rst_once;
    38. logic [31:0] DerivedClocks;
    39. clk_divider #(
    40. .WIDTH( 32 )
    41. ) CD1 (
    42. .clk( clk200 ),
    43. .nrst( nrst_once ),
    44. .out( DerivedClocks[31:0] )
    45. );
    46. logic [15:0] RandomNumber1;
    47. c_rand RNG1 (
    48. .clk( clk200 ),
    49. .rst( rst_once ),
    50. .reseed( 1'b0 ),
    51. .seed_val( DerivedClocks[31:0] ),
    52. .out( RandomNumber1[15:0] )
    53. );
    54. logic start;
    55. initial begin
    56. #0 start = 1'b0;
    57. #100.2 start = 1'b1;
    58. #5 start = 1'b0;
    59. end
    60. // Module under test ==========================================================
    61. logic [15:0] rise;
    62. logic [15:0] fall;
    63. logic [15:0] both;
    64. edge_detect ED1[15:0] (
    65. .clk( {16{clk200}} ),
    66. .anrst( {16{nrst_once}} ),
    67. .in( RandomNumber1[15:0] ),
    68. .rising(rise),
    69. .falling(fall),
    70. .both(both)
    71. );
    72. endmodule

    其中复用了前几期提到过的模块:

    1. Verilog:【1】时钟分频电路(clk_divider.sv)

    2. Verilog:【2】伪随机数生成器(c_rand.v)

    仔细阅读发现TestBench其实也有很多值得学习的地方,这里只提一点。

    通过将生成的伪随机数作为边沿检测的输入信号,这样可以很方便的对所需要检测的模块进行更加具有说服力的验证。

    从仿真结果中(最后四个信号,是我从并行信号中提取出来的,便于观察现象)可以看出,对于其中任意一路信号,当出现上升沿时,rise会在对应通道产生一个周期的高电平;下降沿以及上升下降沿是同理的。


    这就是本期的全部内容啦,我感觉一边学习一边总结真的是受益匪浅,开拓了我很多的思路。如果你喜欢我的文章,不要忘了点赞+收藏+关注,分享给身边的朋友哇~

  • 相关阅读:
    ESP8266-Arduino编程实例-MAX6675冷端补偿K热电偶数字转换器驱动
    hugetlb核心组件
    js之防抖、节流函数
    吃透Java线程安全问题
    DB2存储过程如何编写和执行
    jsp汽车销售管理软件Myeclipse开发mysql数据库web结构java编程计算机网页项目
    10-18 查询同专业的学生(MSSQL)
    【数据库】函数处理(文本处理函数、日期和时间处理函数、数值处理函数)
    操作系统·八股文背诵版V0.3
    应用层 HTTP 代理服务器转发消息时的相关头部 请求头 X-Forwarded-For
  • 原文地址:https://blog.csdn.net/Alex497259/article/details/126268298