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

碎碎念:

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

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

目录

1 模块功能

2 模块代码

3 模块思路

4 TestBench与仿真结果


1 模块功能

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

2 模块代码

//--------------------------------------------------------------------------------
// pulse_stretch.sv
// Konstantin Pavlov, [email protected]
//--------------------------------------------------------------------------------

// INFO --------------------------------------------------------------------------------
// Pulse stretcher/extender module
//   this implementftion uses a simple delay line or counter to stretch pulses
//   WIDTH parameter sets output pulse width
//   if you need variable output poulse width, see pulse_gen.sv module


/* --- INSTANTIATION TEMPLATE BEGIN ---

pulse_stretch #(
  .WIDTH( 8 ),
  .USE_CNTR( 0 )
) ps1 (
  .clk( clk ),
  .nrst( nrst ),
  .in(  ),
  .out(  )
);

--- INSTANTIATION TEMPLATE END ---*/

module pulse_stretch #( parameter
  WIDTH = 8,
  USE_CNTR = 0      // ==0  - stretcher is implemented on delay line
                    // ==1  - stretcher is implemented on counter
)(
  input clk,
  input nrst,

  input in,
  output out
);


localparam CNTR_WIDTH = $clog2(WIDTH) + 1;

generate

  if ( WIDTH == 0 ) begin
    assign out = 0;

  end else if( WIDTH == 1 ) begin
    assign out = in;

  end else begin
    if( USE_CNTR == '0 ) begin
      // delay line

      logic [WIDTH-1:0] shifter = '0;
      always_ff @(posedge clk) begin
        if( ~nrst ) begin
          shifter[WIDTH-1:0] <= '0;
        end else begin
          // shifting
          shifter[WIDTH-1:0] <= {shifter[WIDTH-2:0],in};
        end // nrst
      end // always

      assign out = (shifter[WIDTH-1:0] != '0);

    end else begin
      // counter

      logic [CNTR_WIDTH-1:0] cntr = '0;
      always_ff @(posedge clk) begin
        if( ~nrst ) begin
          cntr[CNTR_WIDTH-1:0] <= '0;
        end else begin
          if( in ) begin
            // setting counter
            cntr[CNTR_WIDTH-1:0] <= CNTR_WIDTH'(WIDTH);
          end else if( out ) begin
            // decrementing counter
            cntr[CNTR_WIDTH-1:0] <= cntr[CNTR_WIDTH-1:0] - 1'b1;
          end
        end // nrst
      end // always

      assign out = (cntr[CNTR_WIDTH-1:0] != '0);

    end
  end // if WIDTH
endgenerate


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与仿真结果

//------------------------------------------------------------------------------
// pulse_stretch_tb.sv
// Konstantin Pavlov, [email protected]
//------------------------------------------------------------------------------

// INFO ------------------------------------------------------------------------
// testbench for pulse_stretch.sv module


`timescale 1ns / 1ps

module pulse_stretch_tb();

logic clk200;
initial begin
  #0 clk200 = 1'b0;
  forever
    #2.5 clk200 = ~clk200;
end

logic rst;
initial begin
  #0 rst = 1'b0;
  #10.2 rst = 1'b1;
  #5 rst = 1'b0;
  //#10000;
  forever begin
    #9985 rst = ~rst;
    #5 rst = ~rst;
  end
end

logic nrst;
assign nrst = ~rst;

logic rst_once;
initial begin
  #0 rst_once = 1'b0;
  #10.2 rst_once = 1'b1;
  #5 rst_once = 1'b0;
end

logic start;
initial begin
  #0 start = 1'b0;
  #100 start = 1'b1;
  #5 start = 1'b0;
end

// Modules under test ==========================================================

pulse_stretch #(
  .WIDTH( 8 ),
  .USE_CNTR( 0 )
) ps1 (
  .clk( clk200 ),
  .nrst( nrst ),
  .in( start ),
  .out(  )
);

pulse_stretch #(
  .WIDTH( 8 ),
  .USE_CNTR( 1 )
) ps2 (
  .clk( clk200 ),
  .nrst( nrst ),
  .in( start ),
  .out(  )
);

endmodule

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

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

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


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

猜你喜欢

转载自blog.csdn.net/Alex497259/article/details/126286909
今日推荐