碎碎念:
今日学习,可没时间碎碎念(bushi)
这两天涨了几个粉丝,谢谢大家的关注!我一定认真更新,我们一起进步!
目录
设置参数WIDTH,从而可以将接收到的单周期脉冲,展宽为脉宽为WIDTH的脉冲信号。
- //--------------------------------------------------------------------------------
- // pulse_stretch.sv
- // Konstantin Pavlov, pavlovconst@gmail.com
- //--------------------------------------------------------------------------------
-
- // 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
-
据代码介绍所说,利用简单的延时或计数器来实现脉冲的展宽处理,下面来具体分析一下实现过程以及值得关注的点。
1.USE_CNTR(第29行)
这里注释提到,可以自行选择是通过延时线还是计数器来实现本模块的功能,这就直接容易想到利用System Verilog中的generate(42行)来实现,前面在边沿检测器我们对这一结构进行过介绍:传送门。
2.$clog2函数(第40行)
$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。不过也同样会有上面延时线实现时会遇到的相同的问题,可能作者本身就是这样设计的。
- //------------------------------------------------------------------------------
- // pulse_stretch_tb.sv
- // Konstantin Pavlov, pavlovconst@gmail.com
- //------------------------------------------------------------------------------
- // 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个时钟周期的脉冲信号。与我们对代码的分析说一致的。
这就是本期的全部内容啦,如果你喜欢我的文章,不要忘了点赞+收藏+关注,分享给身边的朋友哇~