• Verilog:【5】脉冲展宽器(pulse_stretch.sv)


    碎碎念:

    今日学习,可没时间碎碎念(bushi)

    这两天涨了几个粉丝,谢谢大家的关注!我一定认真更新,我们一起进步!

    目录

    1 模块功能

    2 模块代码

    3 模块思路

    4 TestBench与仿真结果


    1 模块功能

    设置参数WIDTH,从而可以将接收到的单周期脉冲,展宽为脉宽为WIDTH的脉冲信号。

    2 模块代码

    1. //--------------------------------------------------------------------------------
    2. // pulse_stretch.sv
    3. // Konstantin Pavlov, pavlovconst@gmail.com
    4. //--------------------------------------------------------------------------------
    5. // INFO --------------------------------------------------------------------------------
    6. // Pulse stretcher/extender module
    7. // this implementftion uses a simple delay line or counter to stretch pulses
    8. // WIDTH parameter sets output pulse width
    9. // if you need variable output poulse width, see pulse_gen.sv module
    10. /* --- INSTANTIATION TEMPLATE BEGIN ---
    11. pulse_stretch #(
    12. .WIDTH( 8 ),
    13. .USE_CNTR( 0 )
    14. ) ps1 (
    15. .clk( clk ),
    16. .nrst( nrst ),
    17. .in( ),
    18. .out( )
    19. );
    20. --- INSTANTIATION TEMPLATE END ---*/
    21. module pulse_stretch #( parameter
    22. WIDTH = 8,
    23. USE_CNTR = 0 // ==0 - stretcher is implemented on delay line
    24. // ==1 - stretcher is implemented on counter
    25. )(
    26. input clk,
    27. input nrst,
    28. input in,
    29. output out
    30. );
    31. localparam CNTR_WIDTH = $clog2(WIDTH) + 1;
    32. generate
    33. if ( WIDTH == 0 ) begin
    34. assign out = 0;
    35. end else if( WIDTH == 1 ) begin
    36. assign out = in;
    37. end else begin
    38. if( USE_CNTR == '0 ) begin
    39. // delay line
    40. logic [WIDTH-1:0] shifter = '0;
    41. always_ff @(posedge clk) begin
    42. if( ~nrst ) begin
    43. shifter[WIDTH-1:0] <= '0;
    44. end else begin
    45. // shifting
    46. shifter[WIDTH-1:0] <= {shifter[WIDTH-2:0],in};
    47. end // nrst
    48. end // always
    49. assign out = (shifter[WIDTH-1:0] != '0);
    50. end else begin
    51. // counter
    52. logic [CNTR_WIDTH-1:0] cntr = '0;
    53. always_ff @(posedge clk) begin
    54. if( ~nrst ) begin
    55. cntr[CNTR_WIDTH-1:0] <= '0;
    56. end else begin
    57. if( in ) begin
    58. // setting counter
    59. cntr[CNTR_WIDTH-1:0] <= CNTR_WIDTH'(WIDTH);
    60. end else if( out ) begin
    61. // decrementing counter
    62. cntr[CNTR_WIDTH-1:0] <= cntr[CNTR_WIDTH-1:0] - 1'b1;
    63. end
    64. end // nrst
    65. end // always
    66. assign out = (cntr[CNTR_WIDTH-1:0] != '0);
    67. end
    68. end // if WIDTH
    69. endgenerate
    70. endmodule

    3 模块思路

    据代码介绍所说,利用简单的延时或计数器来实现脉冲的展宽处理,下面来具体分析一下实现过程以及值得关注的点。

    1.USE_CNTR(第29行)

    这里注释提到,可以自行选择是通过延时线还是计数器来实现本模块的功能,这就直接容易想到利用System Verilog中的generate(42行)来实现,前面在边沿检测器我们对这一结构进行过介绍:传送门

    2.$clog2函数(第40行)

    参考博客:http://t.csdn.cn/JzRse

    $clog2这个计算是log2,就是求2对数,例如log2(8)=3。这里通过利用这一函数,计算出了对应WIDTH需要的计数器的位数大小CNTR_WIDTH,并作为常量进行保存。

    3.功能核心部分(42-88行)

    利用generate结构,实现对代码实现方式的控制。由于是两种实现方式,因此我就分为两个部分进行分别介绍。

            3.1 延时线实现(54-64行)

    这个地方的实现我个人认为非常巧妙。首先根据设定的WIDTH数值,建立一个长度为WIDTH的以移位寄存器shifter。利用always_ff构建D触发器,在非复位情况下,依次将之进行左移处理,并将输入信号in放到最低位,之后检测shifter是否全为0,来控制当前输出信号out的值。若shifter所有位不全为0,则输出为1。

    这种方式非常直接地将输出控制在了需要的宽度内,不过一个问题是如果所输入的脉冲不是一个周期的,则可能会导致输出展宽后的脉宽比WIDTH更宽。

            3.2 计数器实现(69-84行)

    首先,计数器的话就需要利用到上面调用log2函数获得的结果,构建出一个刚好满足要求的计数器。之后同样是利用always_ff构建D触发器,在非复位情况下,当检测到输入信号in为高电平时,会将WIDTH这个值存储到cntr中。当输出为高电平时,逐周期对计数器进行减一操作。

    输出信号out的控制逻辑,就是检测计数器结果cntr的值,如果不为0,则输出1,否则输出0。不过也同样会有上面延时线实现时会遇到的相同的问题,可能作者本身就是这样设计的。

    4 TestBench与仿真结果

    1. //------------------------------------------------------------------------------
    2. // pulse_stretch_tb.sv
    3. // Konstantin Pavlov, pavlovconst@gmail.com
    4. //------------------------------------------------------------------------------
    5. // INFO ------------------------------------------------------------------------
    6. // testbench for pulse_stretch.sv module
    7. `timescale 1ns / 1ps
    8. module pulse_stretch_tb();
    9. logic clk200;
    10. initial begin
    11. #0 clk200 = 1'b0;
    12. forever
    13. #2.5 clk200 = ~clk200;
    14. end
    15. logic rst;
    16. initial begin
    17. #0 rst = 1'b0;
    18. #10.2 rst = 1'b1;
    19. #5 rst = 1'b0;
    20. //#10000;
    21. forever begin
    22. #9985 rst = ~rst;
    23. #5 rst = ~rst;
    24. end
    25. end
    26. logic nrst;
    27. assign nrst = ~rst;
    28. logic rst_once;
    29. initial begin
    30. #0 rst_once = 1'b0;
    31. #10.2 rst_once = 1'b1;
    32. #5 rst_once = 1'b0;
    33. end
    34. logic start;
    35. initial begin
    36. #0 start = 1'b0;
    37. #100 start = 1'b1;
    38. #5 start = 1'b0;
    39. end
    40. // Modules under test ==========================================================
    41. pulse_stretch #(
    42. .WIDTH( 8 ),
    43. .USE_CNTR( 0 )
    44. ) ps1 (
    45. .clk( clk200 ),
    46. .nrst( nrst ),
    47. .in( start ),
    48. .out( )
    49. );
    50. pulse_stretch #(
    51. .WIDTH( 8 ),
    52. .USE_CNTR( 1 )
    53. ) ps2 (
    54. .clk( clk200 ),
    55. .nrst( nrst ),
    56. .in( start ),
    57. .out( )
    58. );
    59. endmodule

    下面开始介绍TestBench的内容,这里就比较简单没有涉及到对之前信号的复用问题,只是单纯通过构建时钟信号以及start模拟一个输入脉冲,从而实现对模块的测试。

    可以看到最后例化了两个待测试信号,分别通过修改参数,前者是通过延时线实现的方式,后者是通过计数器实现的方式。

    从仿真波形中可以看出,红色波形是使用延时线实现的方式,设定的WIDTH=8,当输入为一个周期的脉冲时,会输出宽度为8个时钟周期的脉冲信号。绿色波形是使用计数器实现的方式,放输入脉冲到达时,计数器会从8依次递减到1,从而输出输出宽度为8个时钟周期的脉冲信号。与我们对代码的分析说一致的。


    这就是本期的全部内容啦,如果你喜欢我的文章,不要忘了点赞+收藏+关注,分享给身边的朋友哇~

  • 相关阅读:
    9.吴恩达深度学习--机器翻译
    图扑智慧电力可视化大屏,赋能虚拟电厂精准减碳
    c++ || 二分查找
    2022 IDEA (学生邮箱认证)安装使用教程以及基础配置教程
    什么是APS生产排程系统?
    EMQX配置ssl/tls双向认证+EMQX http客户端设备认证(Java实现)_真实业务实践
    MVCC解决的问题是什么
    【ES6】require、export和import的用法
    山科山东科技大学 计网计算机网络期末题2022.1.7
    Parser用法
  • 原文地址:https://blog.csdn.net/Alex497259/article/details/126286909