当前位置:   article > 正文

Verilog:【4】脉冲发生器(pulse_gen.sv)_verilog产生脉冲信号

verilog产生脉冲信号

碎碎念:

明明是周四,这周竟然不开组会_(:з)∠)_

那我可以继续愉快地学习人家的代码了,这篇博客介绍的是脉冲发生器,脉冲和Killer Queen是不是很配呢hhh

目录

1 模块功能

2 模块代码

3 模块思路

4 TestBench与仿真结果


1 模块功能

通过设置参数cntr_max与cntr_low,可以产生任意周期数与占空比的脉冲信号。

2 模块代码

  1. //------------------------------------------------------------------------------
  2. // pulse_gen.sv
  3. // Konstantin Pavlov, pavlovconst@gmail.com
  4. //------------------------------------------------------------------------------
  5. // INFO ------------------------------------------------------------------------
  6. // Pulse generator module, ver.2
  7. //
  8. // - generates one or many pulses of given width and period
  9. // - generates constant HIGH, constant LOW, or impulse output
  10. // - features buffered inputs, so inputs can change continiously during pulse period
  11. // - generates LOW when idle
  12. //
  13. // - Pulse period is (cntr_max[]+1) cycles
  14. // - If you need to generate constant LOW pulses, then CNTR_WIDTH should allow
  15. // setting cntr_low[]>cntr_max[]
  16. //
  17. // Example 1:
  18. // let CNTR_WIDTH = 8
  19. // let cntr_max[7:0] = 2^CNTR_WIDTH-2 = 254, pulse period is 255 cycles
  20. // cntr_low[7:0]==255 then output will be constant LOW
  21. // 0<cntr_low[7:0]<=cntr_max[7:0] then output will be generating pulse(s)
  22. // cntr_low[7:0]==0 then output will be constant HIGH
  23. //
  24. // Example 2:
  25. // let CNTR_WIDTH = 9
  26. // let cntr_max[8:0] = 255, pulse period is 256 cycles
  27. // cntr_low[8:0]>255 then output will be constant LOW
  28. // 0<cntr_low[8:0]<=cntr_max[8:0] then output will be generating pulse(s)
  29. // cntr_low[8:0]==0 then output will be constant HIGH
  30. //
  31. // In Example 2 constant LOW state can be acheived also by disabling start
  32. // condition or holding reset input, so cntr_low[8:0] and cntr_max[8:0]
  33. // can be left 8-bit-wide actually
  34. /* --- INSTANTIATION TEMPLATE BEGIN ---
  35. pulse_gen #(
  36. .CNTR_WIDTH( 8 )
  37. ) pg1 (
  38. .clk( clk ),
  39. .nrst( nrst ),
  40. .start( 1'b1 ),
  41. .cntr_max( 255 ),
  42. .cntr_low( 2 ),
  43. .pulse_out( ),
  44. .start_strobe,
  45. .busy( )
  46. );
  47. --- INSTANTIATION TEMPLATE END ---*/
  48. module pulse_gen #( parameter
  49. CNTR_WIDTH = 32
  50. )(
  51. input clk, // system clock
  52. input nrst, // negative reset
  53. input start, // enables new period start
  54. input [CNTR_WIDTH-1:0] cntr_max, // counter initilization value, should be > 0
  55. input [CNTR_WIDTH-1:0] cntr_low, // transition to LOW counter value
  56. output logic pulse_out, // active HIGH output
  57. // status outputs
  58. output logic start_strobe = 1'b0,
  59. output busy
  60. );
  61. logic [CNTR_WIDTH-1:0] seq_cntr = '0;
  62. logic seq_cntr_0;
  63. assign seq_cntr_0 = (seq_cntr[CNTR_WIDTH-1:0] == '0);
  64. // delayed one cycle
  65. logic seq_cntr_0_d1;
  66. always_ff @(posedge clk) begin
  67. if( ~nrst) begin
  68. seq_cntr_0_d1 <= 0;
  69. end else begin
  70. seq_cntr_0_d1 <= seq_cntr_0;
  71. end
  72. end
  73. // first seq_cntr_0 cycle time belongs to pulse period
  74. // second and further seq_cntr_0 cycles are idle
  75. assign busy = ~(seq_cntr_0 && seq_cntr_0_d1);
  76. // buffering cntr_low untill pulse period is over to allow continiously
  77. // changing inputs
  78. logic [CNTR_WIDTH-1:0] cntr_low_buf = '0;
  79. always_ff @(posedge clk) begin
  80. if( ~nrst ) begin
  81. seq_cntr[CNTR_WIDTH-1:0] <= '0;
  82. cntr_low_buf[CNTR_WIDTH-1:0] <= '0;
  83. start_strobe <= 1'b0;
  84. end else begin
  85. if( seq_cntr_0 ) begin
  86. // don`t start if cntr_max[] is illegal value
  87. if( start && (cntr_max[CNTR_WIDTH-1:0]!='0) ) begin
  88. seq_cntr[CNTR_WIDTH-1:0] <= cntr_max[CNTR_WIDTH-1:0];
  89. cntr_low_buf[CNTR_WIDTH-1:0] <= cntr_low[CNTR_WIDTH-1:0];
  90. start_strobe <= 1'b1;
  91. end else begin
  92. start_strobe <= 1'b0;
  93. end
  94. end else begin
  95. seq_cntr[CNTR_WIDTH-1:0] <= seq_cntr[CNTR_WIDTH-1:0] - 1'b1;
  96. start_strobe <= 1'b0;
  97. end
  98. end // ~nrst
  99. end
  100. always_comb begin
  101. if( ~nrst ) begin
  102. pulse_out <= 1'b0;
  103. end else begin
  104. // busy condition guarantees LOW output when idle
  105. if( busy &&
  106. (seq_cntr[CNTR_WIDTH-1:0] >= cntr_low_buf[CNTR_WIDTH-1:0]) ) begin
  107. pulse_out <= 1'b1;
  108. end else begin
  109. pulse_out <= 1'b0;
  110. end
  111. end // ~nrst
  112. end
  113. endmodule

3 模块思路

在明确了整个模块的功能之后,也就比较看出主要思路就是利用计数器的原理,利用对计数值的判断,来控制输出脉冲的信号,下面来具体进行说明。

1.模块接口定义部分(58-73行)

包含一个内部寄存器宽度参数CNTR_WIDTH;五个输入端口分别是时钟clk、低电平复位信号nrst、新周期开始信号start、起点计数值cntr_max、跳变计数值cntr_low;三个输出端口分别是脉冲输出信号pulse_out、输出状态信号start_strobe、忙碌信号busy。

这里同样使用了logic类型,在前几期的内容也有涉及到。

2.中间变量定义(76-79行)

定义了存储计数值的寄存器seq_cntr、以及标志其是否为0的寄存器seq_cntr_0。

3.延时一个周期(81-89行)

利用always_ff构建D触发器,从而获得seq_cntr_0延时一周期后的信号seq_cntr_0_d1。

4.输出busy信号的逻辑(93行)

当本周期与上一周期计数值都是0的时候,此时表示不在输出有效时间段,即busy=0;当start恒等于1时,因为计数值一定会变化,因此busy始终都是1。

读者可以结合仿真结果来看,我认为这一设计还是比较巧妙的,利用组合逻辑与延时的处理,让busy信号和计数器的结果就没有出现那种会错开一周期的情况(我本人有时会遇到_(:з)∠)_)。

5.计数器运行逻辑(96-119行)

利用always_ff构建D触发器,来实现整个运行的逻辑。当seq_cntr_0=1时,表示此时计数值为0,因此作为脉冲的起点,开始修改cntr_low等参数。

重点关注107行,当start信号出现一周期的高电平,同时cntr_max信号是有效值(大于0)时,将cntr_max的值赋值给seq_cntr;将cntr_low的值赋值给cntr_low_buf;start_strobe置为到1(持续一周期),表示开始启动脉冲的发生器。

之后start信号变为低电平,此时开始每周期将计数器结果减1。

在这里,cntr_max就控制了脉冲发生器的周期;cntr_low相对于cntr_max的大小,就控制了输出的占比空。

6.脉冲信号输出逻辑(121-133行)

这一部分就是比较简单的组合逻辑啦,使用always_comb搭建。通过判断当前的计数值,来控制脉冲信号的输出。

从代码可以看出:seq_cntr大于等于cntr_low_buf时,输出是1;seq_cntr小于cntr_low_buf时,输出是0。

TestBench与仿真结果

  1. //------------------------------------------------------------------------------
  2. // pulse_gen_tb.sv
  3. // Konstantin Pavlov, pavlovconst@gmail.com
  4. //------------------------------------------------------------------------------
  5. // INFO ------------------------------------------------------------------------
  6. // testbench for pulse_gen.sv module
  7. `timescale 1ns / 1ps
  8. module pulse_gen_tb();
  9. logic clk200;
  10. initial begin
  11. #0 clk200 = 1'b0;
  12. forever
  13. #2.5 clk200 = ~clk200;
  14. end
  15. // external device "asynchronous" clock
  16. logic clk33;
  17. initial begin
  18. #0 clk33 = 1'b0;
  19. forever
  20. #15.151 clk33 = ~clk33;
  21. end
  22. logic rst;
  23. initial begin
  24. #0 rst = 1'b0;
  25. #10.2 rst = 1'b1;
  26. #5 rst = 1'b0;
  27. //#10000;
  28. forever begin
  29. #9985 rst = ~rst;
  30. #5 rst = ~rst;
  31. end
  32. end
  33. logic nrst;
  34. assign nrst = ~rst;
  35. logic rst_once;
  36. initial begin
  37. #0 rst_once = 1'b0;
  38. #10.2 rst_once = 1'b1;
  39. #5 rst_once = 1'b0;
  40. end
  41. logic nrst_once;
  42. assign nrst_once = ~rst_once;
  43. logic [31:0] DerivedClocks;
  44. clk_divider #(
  45. .WIDTH( 32 )
  46. ) cd1 (
  47. .clk( clk200 ),
  48. .nrst( nrst_once ),
  49. .ena( 1'b1 ),
  50. .out( DerivedClocks[31:0] )
  51. );
  52. logic [31:0] E_DerivedClocks;
  53. edge_detect ed1[31:0] (
  54. .clk( {32{clk200}} ),
  55. .anrst( {32{nrst_once}} ),
  56. .in( DerivedClocks[31:0] ),
  57. .rising( E_DerivedClocks[31:0] ),
  58. .falling( ),
  59. .both( )
  60. );
  61. logic [31:0] RandomNumber1;
  62. c_rand rng1 (
  63. .clk( clk200 ),
  64. .rst( 1'b0 ),
  65. .reseed( rst_once ),
  66. .seed_val( DerivedClocks[31:0] ^ (DerivedClocks[31:0] << 1) ),
  67. .out( RandomNumber1[15:0] )
  68. );
  69. c_rand rng2 (
  70. .clk( clk200 ),
  71. .rst( 1'b0 ),
  72. .reseed( rst_once ),
  73. .seed_val( DerivedClocks[31:0] ^ (DerivedClocks[31:0] << 2) ),
  74. .out( RandomNumber1[31:16] )
  75. );
  76. logic start;
  77. initial begin
  78. #0 start = 1'b0;
  79. #100 start = 1'b1;
  80. #20 start = 1'b0;
  81. end
  82. // Modules under test ==========================================================
  83. // simple static test
  84. /*pulse_gen #(
  85. .CNTR_WIDTH( 8 )
  86. ) pg1 (
  87. .clk( clk200 ),
  88. .nrst( nrst_once ),
  89. .start( start ),
  90. .cntr_max( 15 ),
  91. .cntr_low( 0 ),
  92. .pulse_out( ),
  93. .busy( )
  94. );
  95. */
  96. logic [31:0] in_high_width = '0;
  97. logic out;
  98. logic out_rise;
  99. // random test
  100. pulse_gen #(
  101. .CNTR_WIDTH( 8 )
  102. ) pg1 (
  103. .clk( clk200 ),
  104. .nrst( nrst_once ),
  105. .start( start ),
  106. .cntr_max( 16 ),
  107. .cntr_low( {4'b0,RandomNumber1[3:0]} ),
  108. .pulse_out( out ),
  109. .busy( )
  110. );
  111. edge_detect out_ed (
  112. .clk( clk200 ),
  113. .anrst( nrst_once ),
  114. .in( out ),
  115. .rising( out_rise ),
  116. .falling( ),
  117. .both( )
  118. );
  119. always_ff @(posedge clk200) begin
  120. if( ~nrst_once ) begin
  121. in_high_width[31:0] <= 1'b0;
  122. end else begin
  123. if( out_rise ) begin
  124. in_high_width[31:0] <= in_high_width[31:0] + 1'b1;
  125. end
  126. end
  127. end
  128. // PWM test
  129. /*pulse_gen #(
  130. .CNTR_WIDTH( 8 )
  131. ) pg1 (
  132. .clk( clk200 ),
  133. .nrst( nrst_once ),
  134. .start( 1'b1 ),
  135. .cntr_max( 15 ),
  136. .cntr_low( {4'b0,in_high_width[3:0]} ),
  137. .pulse_out( out ),
  138. .busy( )
  139. );*/
  140. endmodule

下面开始学习一下TestBench的写法,其中我认为值得关注的地方有这几点:

1.#号表示延迟

此前我很少见到使用小数进行延迟的,看来在具有特殊频率要求的情况下,这也是必要的。

2.复用之前的随机数模块

同样对之前的随机数模块以及时钟分频电路进行了复用,从而提高测试的说服力。同时使用到了边沿检测器,用来对输出的脉冲进行上升沿检测。对随机数产色模块以及边沿检测不了解的,可以跳转:

  1. Verilog:【1】时钟分频电路(clk_divider.sv)
  2. Verilog:【2】伪随机数生成器(c_rand.v)
  3. Verilog:【3】边沿检测器(edge_detect.sv)

 同时注意到,为了生成32位的随机数,使用了两个16位随机数的生成器,将结果进行了拼接,应该是有降低资源使用量的考虑,毕竟过于高位的乘法器综合起来还是开销比较大的。

最后可以注意到这一TestBench包含两个测试,测试1是单独一次脉冲的结果(即下图);测试2是代码中最后被注释的部分,由于start恒为1,因此会持续不断产生受到随机数调制的PWM信号。

下面来看一下测试1的仿真波形,可以看到cntr_max的值是16,cntr_low的值是13,当start信号来一个周期的高电平时,cntr_low将值存储到cntr_low_buff中。

seq_cntr开始从16依次减小到0;busy这段时间为高电平,表示正在输出信号;seq_cntr变为16时,start_strobe出现一个高电平,表示开始输出的起点;pulse_out在计数值大于等于13时,为高电平,其余为低电平。seq_cntr_0与seq_cntr_0_d1对busy信号的产生就非常巧妙,我认为很有趣。

最终证明,波形的展示与我们的分析是保持一致的~


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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/411193
推荐阅读
相关标签
  

闽ICP备14008679号