碎碎念:

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

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

目录

1 模块功能

2 模块代码

3 模块思路

4 TestBench与仿真结果


1 模块功能

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

2 模块代码

//------------------------------------------------------------------------------
// pulse_gen.sv
// Konstantin Pavlov, pavlovconst@gmail.com
//------------------------------------------------------------------------------// INFO ------------------------------------------------------------------------
// Pulse generator module, ver.2
//
// - generates one or many pulses of given width and period
// - generates constant HIGH, constant LOW, or impulse output
// - features buffered inputs, so inputs can change continiously during pulse period
// - generates LOW when idle
//
// - Pulse period is (cntr_max[]+1) cycles
// - If you need to generate constant LOW pulses, then CNTR_WIDTH should allow
//   setting cntr_low[]>cntr_max[]
//
// Example 1:
//      let CNTR_WIDTH = 8
//      let cntr_max[7:0] = 2^CNTR_WIDTH-2 = 254, pulse period is 255 cycles
//      cntr_low[7:0]==255              then output will be constant LOW
//      0<cntr_low[7:0]<=cntr_max[7:0]  then output will be generating pulse(s)
//      cntr_low[7:0]==0                then output will be constant HIGH
//
// Example 2:
//      let CNTR_WIDTH = 9
//      let cntr_max[8:0] = 255, pulse period is 256 cycles
//      cntr_low[8:0]>255               then output will be constant LOW
//      0<cntr_low[8:0]<=cntr_max[8:0]  then output will be generating pulse(s)
//      cntr_low[8:0]==0                then output will be constant HIGH
//
//      In Example 2 constant LOW state can be acheived also by disabling start
//      condition or holding reset input, so cntr_low[8:0] and cntr_max[8:0]
//      can be left 8-bit-wide actually/* --- INSTANTIATION TEMPLATE BEGIN ---pulse_gen #(.CNTR_WIDTH( 8 )
) pg1 (.clk( clk ),.nrst( nrst ),.start( 1'b1 ),.cntr_max( 255 ),.cntr_low( 2 ),.pulse_out(  ),.start_strobe,.busy(  )
);--- INSTANTIATION TEMPLATE END ---*/module pulse_gen #( parameterCNTR_WIDTH = 32
)(input clk,                          // system clockinput nrst,                         // negative resetinput start,                        // enables new period startinput [CNTR_WIDTH-1:0] cntr_max,    // counter initilization value, should be > 0input [CNTR_WIDTH-1:0] cntr_low,    // transition to LOW counter valueoutput logic pulse_out,                   // active HIGH output// status outputsoutput logic start_strobe = 1'b0,output busy
);logic [CNTR_WIDTH-1:0] seq_cntr = '0;logic seq_cntr_0;
assign seq_cntr_0 = (seq_cntr[CNTR_WIDTH-1:0] == '0);// delayed one cycle
logic seq_cntr_0_d1;
always_ff @(posedge clk) beginif( ~nrst) beginseq_cntr_0_d1 <= 0;end else beginseq_cntr_0_d1 <= seq_cntr_0;end
end// first seq_cntr_0 cycle time belongs to pulse period
// second and further seq_cntr_0 cycles are idle
assign busy = ~(seq_cntr_0 && seq_cntr_0_d1);// buffering cntr_low untill pulse period is over to allow continiously
//  changing inputs
logic [CNTR_WIDTH-1:0] cntr_low_buf = '0;
always_ff @(posedge clk) beginif( ~nrst ) beginseq_cntr[CNTR_WIDTH-1:0] <= '0;cntr_low_buf[CNTR_WIDTH-1:0] <= '0;start_strobe <= 1'b0;end else beginif( seq_cntr_0 ) begin// don`t start if cntr_max[] is illegal valueif( start && (cntr_max[CNTR_WIDTH-1:0]!='0) ) beginseq_cntr[CNTR_WIDTH-1:0] <= cntr_max[CNTR_WIDTH-1:0];cntr_low_buf[CNTR_WIDTH-1:0] <= cntr_low[CNTR_WIDTH-1:0];start_strobe <= 1'b1;end else beginstart_strobe <= 1'b0;endend else beginseq_cntr[CNTR_WIDTH-1:0] <= seq_cntr[CNTR_WIDTH-1:0] - 1'b1;start_strobe <= 1'b0;endend // ~nrst
endalways_comb beginif( ~nrst ) beginpulse_out <= 1'b0;end else begin// busy condition guarantees LOW output when idleif( busy &&(seq_cntr[CNTR_WIDTH-1:0] >= cntr_low_buf[CNTR_WIDTH-1:0]) ) beginpulse_out <= 1'b1;end else beginpulse_out <= 1'b0;endend // ~nrst
endendmodule

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。

4 TestBench与仿真结果

//------------------------------------------------------------------------------
// pulse_gen_tb.sv
// Konstantin Pavlov, pavlovconst@gmail.com
//------------------------------------------------------------------------------// INFO ------------------------------------------------------------------------
// testbench for pulse_gen.sv module`timescale 1ns / 1psmodule pulse_gen_tb();logic clk200;
initial begin#0 clk200 = 1'b0;forever#2.5 clk200 = ~clk200;
end// external device "asynchronous" clock
logic clk33;
initial begin#0 clk33 = 1'b0;forever#15.151 clk33 = ~clk33;
endlogic 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
endlogic 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;
endlogic nrst_once;
assign nrst_once = ~rst_once;logic [31:0] DerivedClocks;
clk_divider #(.WIDTH( 32 )
) cd1 (.clk( clk200 ),.nrst( nrst_once ),.ena( 1'b1 ),.out( DerivedClocks[31:0] )
);logic [31:0] E_DerivedClocks;
edge_detect ed1[31:0] (.clk( {32{clk200}} ),.anrst( {32{nrst_once}} ),.in( DerivedClocks[31:0] ),.rising( E_DerivedClocks[31:0] ),.falling(  ),.both(  )
);logic [31:0] RandomNumber1;
c_rand rng1 (.clk( clk200 ),.rst( 1'b0 ),.reseed( rst_once ),.seed_val( DerivedClocks[31:0] ^ (DerivedClocks[31:0] << 1) ),.out( RandomNumber1[15:0] )
);c_rand rng2 (.clk( clk200 ),.rst( 1'b0 ),.reseed( rst_once ),.seed_val( DerivedClocks[31:0] ^ (DerivedClocks[31:0] << 2) ),.out( RandomNumber1[31:16] )
);logic start;
initial begin#0 start = 1'b0;#100 start = 1'b1;#20 start = 1'b0;
end// Modules under test ==========================================================// simple static test
/*pulse_gen #(.CNTR_WIDTH( 8 )
) pg1 (.clk( clk200 ),.nrst( nrst_once ),.start( start ),.cntr_max( 15 ),.cntr_low( 0 ),.pulse_out(  ),.busy(  )
);
*/logic [31:0] in_high_width = '0;
logic out;
logic out_rise;// random test
pulse_gen #(.CNTR_WIDTH( 8 )
) pg1 (.clk( clk200 ),.nrst( nrst_once ),.start( start ),.cntr_max( 16 ),.cntr_low( {4'b0,RandomNumber1[3:0]} ),.pulse_out( out ),.busy(  )
);edge_detect out_ed (.clk( clk200 ),.anrst( nrst_once ),.in( out ),.rising( out_rise ),.falling(  ),.both(  )
);always_ff @(posedge clk200) beginif( ~nrst_once ) beginin_high_width[31:0] <= 1'b0;end else beginif( out_rise ) beginin_high_width[31:0] <= in_high_width[31:0] + 1'b1;endend
end// PWM test
/*pulse_gen #(.CNTR_WIDTH( 8 )
) pg1 (.clk( clk200 ),.nrst( nrst_once ),.start( 1'b1 ),.cntr_max( 15 ),.cntr_low( {4'b0,in_high_width[3:0]} ),.pulse_out( out ),.busy(  )
);*/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信号的产生就非常巧妙,我认为很有趣。

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


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

Verilog:【4】脉冲发生器(pulse_gen.sv)相关推荐

  1. matlab verilog 接口,使用SystemVerilog简化FPGA中的接口

    FPGA工程师们应该都会吐槽Verilog的语法,相当的不友好,尤其是对于有很多接口的模块,像AXI4/AXI-Lite这种常用的总线接口,动不动就好几十根线,写起来是相当费劲. 当然现在Xilinx ...

  2. verilog读入txt文件

    system verilog读入txt文件 sv利用系统函数读取txt文件 本目录下需要存在data_in.txt,才能读入该文件.data_in.txt存储的数据利用 空格 或者 回车 隔开,这两种 ...

  3. SV学习(1)——数据类型

    SV语法(1)--数据类型 1. 内建数据类型 2. 用户自定义 2. 枚举类型 4. 定宽数组 5. 动态数组 6. 关联数组 7. 队列 8. 字符串 8.1. 非组合型数组(unpacked) ...

  4. gvim【二】【ab命令快速制作verilog模板】

    文章目录 一.前言 二.使用方法 1.将以下代码拷贝到 `_vimrc文件`中 2.打开gvim,敲入模板名字,敲回车即可 3. 自己制作模板方法 4. 注意事项 5. 下载 三.本系列其他博客 一. ...

  5. IC前端数字验证导学

    前言: 20年,真的很特殊.开学到校,需要等待:毕业答辩,需要等待:研究生复试,也是等待.漫长的等待,使得自己思考了很多,其中之一就是对"中国芯"的考量.在与师兄师姐交流后,意识到 ...

  6. DC基础学习(二)Synthesis Flow1

    Design Compiler(以下简称DC)是Synopsys公司用于做电路综合的核心工具,可以将HDL描述的电路转换为基于工艺库的门级网表.本系列主要介绍综合相关的知识以及DC工具的使用. 整体的 ...

  7. VCS makefile文件

    SYNOPSYS VCS Makefile文件编写与研究 这个Makefile是synopsys提供的模板,看上去非常好用,你只要按部就班提供实际项目的参数就可以了.我们来看这个文件的头部说明:  m ...

  8. 无招胜有招-Vivado非工程模式下的FPGA设计流程

    参考:UG892 UG835 Vivado集成开发工具为设计者提供了非工程模式下的FPGA设计流程.在Vivado非工程模式下,FPGA开发人员可以更加灵活地对设计过程的每个阶段进行控制,从而进一步提 ...

  9. Synopsys Design Compiler基础

    目录 引言 综合环境 启动文件 系统库变量 DC设计对象.变量和属性 对象 变量 属性 找寻设计对象 数据组织 引言 Synopsys Design Compiler (DC) 和 Design Vi ...

最新文章

  1. python 的文件读写方法:read readline readlines wirte   writelines
  2. JavaScript(八)
  3. 汇编:ZF(zero flag)标志位
  4. yar java_Yar 的传输协议学习以及 Java 版本的实现
  5. __stdcall、__cdecl 、CALLBACK 几种函数修饰符
  6. java大作业斗地主游戏_Java集合练习:斗地主游戏
  7. Julia: rand
  8. js调用zk中zul页面表单值
  9. Flash遮罩之放大镜
  10. 抖音:运营与商业模式分析
  11. Windows XP/2000实现自动登陆(加入域和未加域两种情况)
  12. SQL从特定位置检索记录
  13. 【2022 年“SPSSPRO 杯”数学中国数学建模网络挑战赛】A题 人员的紧急疏散-第二阶段23页论文
  14. 数据源EPMSSqlDataSource的使用
  15. 如何查看服务器bmc信息,服务器查看bmcip地址
  16. 蹩脚的CSDN手机绑定
  17. 安卓使用http下载文件
  18. OSChina 周三乱弹 ——程序员从入职到跑路
  19. TensorFlow学习--LeNet5神经网络
  20. 运行老是跳转谷歌然后又加载不出来

热门文章

  1. 问道科技产业新变局,2022「甲子引力」年终盛典圆满举办|甲子引力
  2. 如果一份工作让你时常感到焦虑,你会不会立马辞职?
  3. Scratch案例-冒泡排序
  4. 【数据库】Navicat编写MySQL自定义函数详解
  5. 高等数学——多元函数的极值的条件
  6. SNTP获取时间源统一时间
  7. linux哪个版本支持tipc,Linux TIPC 2.0 Programmerapos;s Guide文档.doc
  8. ikbc c87 Win键失灵/锁定
  9. 5 个牛逼的算法设计,你知道几个?
  10. 浅谈表值函数和标量值函数