Verilog设计实例(2)一步一步实现一个多功能通用计数器
博文目录
- 写在前面
- 正文
- 普通的二进制计数器
- 电路设计
- 行为仿真
- 普通的格雷码计数器
- 电路设计
- 行为仿真
- LFSR
- 电路设计
- 行为仿真
- 多功能计数器
- 电路设计
- 行为仿真
- 生成语句实现方式
- 电路设计
- 行为仿真
- 注意事项
- 参考资料
- 交个朋友
写在前面
相关博文
博客首页
注:学习交流使用!
正文
多功能计数器,英文名为:Versatile Counter;所谓多功能,这里包括二进制计数,格雷码计数以及线性反馈移位寄存器(LFSR)三种,本文通过从普通的计数器开始,也就是单个功能的计数器开始,一步一步过渡到多功能计数器。
作为对以下相关博文的延伸练习:
Verilog设计实例(1)线性反馈移位寄存器(LFSR)
FPGA设计心得(8)Verilog中的编译预处理语句
普通的二进制计数器
这个作为开头,不必多说,计数就完事了。
电路设计
设计文件:
`timescale 1ns/1ps
//
// Engineer: Reborn Lee
// Module Name: binary counter
// Additional Comments:
//
//
module binary_counter#(parameter N_BITS = 4)(input i_clk,input i_rst,output [N_BITS - 1 : 0] o_cnt,output o_cnt_done);reg [N_BITS - 1 : 0] bin_cnt = 0;always@(posedge i_clk) beginif(i_rst) beginbin_cnt <= 0;endelse beginbin_cnt <= bin_cnt + 1;endendassign o_cnt_done = (bin_cnt == 0)? 1:0;assign o_cnt = bin_cnt;endmodule
行为仿真
tb文件:
`timescale 1ns/1ps
module bin_cnt_tb;parameter N_BITS = 4;reg i_clk;reg i_rst;wire [N_BITS - 1 : 0] o_cnt;wire o_cnt_done;initial begini_clk = 0;forever begin# 2 i_clk = ~ i_clk;endendinitial begini_rst = 1;# 8i_rst = 0;endbinary_counter #(.N_BITS(N_BITS))inst_bin_cnt(.i_rst(i_rst),.i_clk(i_clk),.o_cnt(o_cnt),.o_cnt_done(o_cnt_done));
endmodule
仿真图:
普通的格雷码计数器
任意位宽的格雷码计数器,实现的方式通常是设计一个普通的二进制计数器,同时将计数结果转化为格雷码。
二进制与格雷码的转换方式,详情见:格雷码和二进制转换。
为了方便给出原理图:
伪代码描述为:
assign gray_value = binary_value ^ (binary_value>>1);
或者:
assign gray_cnt = { bin_cnt[N_BITS - 1], bin_cnt[N_BITS - 1 : 1]^bin_cnt[N_BITS - 2 : 0]};
电路设计
一种简单的设计方式为:
`timescale 1ns / 1ps
//
// Engineer: Reborn Lee
// Create Date: 2020/06/02 13:46:10
// Module Name: gray_counter
// Additional Comments: common gray counter
//
//module gray_counter #(parameter N_BITS = 4)(input i_clk,input i_rst,output [N_BITS - 1 : 0] o_cnt,output o_cnt_done);reg [N_BITS - 1 : 0] bin_cnt = 0;reg [N_BITS - 1 : 0] gray_cnt;always@(posedge i_clk) beginif(i_rst) beginbin_cnt <= 0;gray_cnt <= 0;endelse beginbin_cnt <= bin_cnt + 1;// translate binary counter into gray counter gray_cnt <= bin_cnt ^ bin_cnt >>> 1; //or // gray_cnt <= { bin_cnt[N_BITS - 1], bin_cnt[N_BITS - 1 : 1]^bin_cnt[N_BITS - 2 : 0]};//or // for(int i = 0; i < N_BITS - 1; i = i + 1) begin// gray_cnt[i] <= bin_cnt[i+1]^bin_cnt[i];// end// gray_cnt[N_BITS - 1] <= bin_cnt[N_BITS - 1];endendassign o_cnt = gray_cnt;// or assign o_cnt_done = (gray_cnt == 0) ? 1 : 0;endmodule
注释部分解释:
for(int i = 0; i < N_BITS - 1; i = i + 1) begingray_cnt[i] <= bin_cnt[i+1]^bin_cnt[i];endgray_cnt[N_BITS - 1] <= bin_cnt[N_BITS - 1];
以及:
gray_cnt <= { bin_cnt[N_BITS - 1], bin_cnt[N_BITS - 1 : 1]^bin_cnt[N_BITS - 2 : 0]};
均在always块内,因此使用非阻塞赋值。
又和二进制计数在一起always内,且紧邻分布,因此计数相较于二进制慢一拍,但毫无影响(不影响计数总数)。
注: 三种二进制转换为格雷码的实现原理一致,效果等价。
行为仿真
TestBench设计:
`timescale 1ns/1ps
module gray_cnt_tb;parameter N_BITS = 4;reg i_clk;reg i_rst;wire [N_BITS - 1 : 0] o_cnt;wire o_cnt_done;initial begini_clk = 0;forever begin# 2 i_clk = ~ i_clk;endendinitial begini_rst = 1;# 8i_rst = 0;endgray_counter #(.N_BITS(N_BITS))inst_gray_cnt(.i_rst(i_rst),.i_clk(i_clk),.o_cnt(o_cnt),.o_cnt_done(o_cnt_done));
endmodule
行为仿真波形:
局部放大:
LFSR
这个请参考上篇博文,单独做了一篇博客:
Verilog设计实例(1)线性反馈移位寄存器(LFSR)
为了方便不跳转另外一个链接,这里给出设计:
电路设计
`timescale 1ns / 1ps
//
// Company:
// Engineer: Reborn Lee
// Create Date: 2020/06/01 12:50:38
// Design Name:
// Module Name: lfsr
// Revision 0.01 - File Created
// Additional Comments:
//
//module lfsr #(parameter NUM_BITS = 3)(input i_Clk,input i_Enable,// data validinput i_Seed_DV,// Optional Seed Valueinput [NUM_BITS-1:0] i_Seed_Data,output [NUM_BITS-1:0] o_LFSR_Data,output o_LFSR_Done);// internal variablesreg [NUM_BITS:1] r_LFSR = 0;reg r_XNOR;// Purpose: Load up LFSR with Seed if Data Valid (DV) pulse is detected.// Othewise just run LFSR when enabled.always @(posedge i_Clk)beginif (i_Enable == 1'b1)beginif (i_Seed_DV == 1'b1)r_LFSR <= i_Seed_Data;elser_LFSR <= {r_LFSR[NUM_BITS-1:1],r_XNOR}; //left rightendend// Create Feedback Polynomials. Based on Application Note:// http://www.xilinx.com/support/documentation/application_notes/xapp052.pdfalways @(*)begincase (NUM_BITS)3: beginr_XNOR = r_LFSR[3] ^~ r_LFSR[2];end4: beginr_XNOR = r_LFSR[4] ^~ r_LFSR[3];end5: beginr_XNOR = r_LFSR[5] ^~ r_LFSR[3];end6: beginr_XNOR = r_LFSR[6] ^~ r_LFSR[5];end7: beginr_XNOR = r_LFSR[7] ^~ r_LFSR[6];end8: beginr_XNOR = r_LFSR[8] ^~ r_LFSR[6] ^~ r_LFSR[5] ^~ r_LFSR[4];end9: beginr_XNOR = r_LFSR[9] ^~ r_LFSR[5];end10: beginr_XNOR = r_LFSR[10] ^~ r_LFSR[7];end11: beginr_XNOR = r_LFSR[11] ^~ r_LFSR[9];end12: beginr_XNOR = r_LFSR[12] ^~ r_LFSR[6] ^~ r_LFSR[4] ^~ r_LFSR[1];end13: beginr_XNOR = r_LFSR[13] ^~ r_LFSR[4] ^~ r_LFSR[3] ^~ r_LFSR[1];end14: beginr_XNOR = r_LFSR[14] ^~ r_LFSR[5] ^~ r_LFSR[3] ^~ r_LFSR[1];end15: beginr_XNOR = r_LFSR[15] ^~ r_LFSR[14];end16: beginr_XNOR = r_LFSR[16] ^~ r_LFSR[15] ^~ r_LFSR[13] ^~ r_LFSR[4];end17: beginr_XNOR = r_LFSR[17] ^~ r_LFSR[14];end18: beginr_XNOR = r_LFSR[18] ^~ r_LFSR[11];end19: beginr_XNOR = r_LFSR[19] ^~ r_LFSR[6] ^~ r_LFSR[2] ^~ r_LFSR[1];end20: beginr_XNOR = r_LFSR[20] ^~ r_LFSR[17];end21: beginr_XNOR = r_LFSR[21] ^~ r_LFSR[19];end22: beginr_XNOR = r_LFSR[22] ^~ r_LFSR[21];end23: beginr_XNOR = r_LFSR[23] ^~ r_LFSR[18];end24: beginr_XNOR = r_LFSR[24] ^~ r_LFSR[23] ^~ r_LFSR[22] ^~ r_LFSR[17];end25: beginr_XNOR = r_LFSR[25] ^~ r_LFSR[22];end26: beginr_XNOR = r_LFSR[26] ^~ r_LFSR[6] ^~ r_LFSR[2] ^~ r_LFSR[1];end27: beginr_XNOR = r_LFSR[27] ^~ r_LFSR[5] ^~ r_LFSR[2] ^~ r_LFSR[1];end28: beginr_XNOR = r_LFSR[28] ^~ r_LFSR[25];end29: beginr_XNOR = r_LFSR[29] ^~ r_LFSR[27];end30: beginr_XNOR = r_LFSR[30] ^~ r_LFSR[6] ^~ r_LFSR[4] ^~ r_LFSR[1];end31: beginr_XNOR = r_LFSR[31] ^~ r_LFSR[28];end32: beginr_XNOR = r_LFSR[32] ^~ r_LFSR[22] ^~ r_LFSR[2] ^~ r_LFSR[1];endendcase // case (NUM_BITS)end // always @ (*)assign o_LFSR_Data = r_LFSR[NUM_BITS:1];// Conditional Assignment (?)assign o_LFSR_Done = (r_LFSR[NUM_BITS:1] == i_Seed_Data) ? 1'b1 : 1'b0;endmodule
行为仿真
`timescale 1ns / 1ps
module lfsr_tb ();parameter c_NUM_BITS = 4;reg r_Clk = 1'b0;wire [c_NUM_BITS-1:0] w_LFSR_Data;wire w_LFSR_Done;lfsr #(.NUM_BITS(c_NUM_BITS)) LFSR_inst(.i_Clk(r_Clk),.i_Enable(1'b1),.i_Seed_DV(1'b0),.i_Seed_Data({c_NUM_BITS{1'b0}}), // Replication.o_LFSR_Data(w_LFSR_Data),.o_LFSR_Done(w_LFSR_Done));always @(*)#10 r_Clk <= ~r_Clk; endmodule // LFSR_TB
仿真波形:
多功能计数器
有了上面三种计数器的单独设计,下面该考虑组合起来了,是用什么样的方式组合?
用户可以选择,可以通过定义条件编译的方式,定义了某个宏就执行某种计数器,计数位宽可选择,通过参数化的方式实现。
电路设计
本设计用到了上面的三个模块,例化到本模块使用;
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2020/06/02 16:22:52
// Design Name:
// Module Name: versatile_counter
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//// `define LFSR_MACRO
`define GRAY
// `define BIN
module versatile_counter #(parameter N_BITS = 4)(input i_clk,input i_rst,output [N_BITS - 1 : 0] o_out,output o_out_done);`ifdef LFSR_MACROlfsr #(.NUM_BITS(N_BITS)) LFSR_inst(.i_Clk(i_clk),.i_Enable(1'b1),.i_Seed_DV(1'b0),.i_Seed_Data({N_BITS{1'b0}}), // Replication.o_LFSR_Data(o_out),.o_LFSR_Done(o_out_done));`elsif GRAYgray_counter #(.N_BITS(N_BITS))inst_gray_cnt(.i_rst(i_rst),.i_clk(i_clk),.o_cnt(o_out),.o_cnt_done(o_out_done));`elsebinary_counter #(.N_BITS(N_BITS))inst_bin_cnt(.i_rst(i_rst),.i_clk(i_clk),.o_cnt(o_out),.o_cnt_done(o_out_done));`endifendmodule
这里约定定义了宏GRAY,就是跑格雷码的代码,定义了宏BIN,就是跑二进制的代码,定义了LFSR_MACRO,就是跑LFSR的程序。
行为仿真
先假设定义了宏GRAY,仿真程序通用,如下:
`timescale 1ns/1ps
module sim_versatile_counter;parameter N_BITS = 4;reg i_clk;reg i_rst;wire [N_BITS - 1 : 0] o_out;wire o_out_done;initial begini_clk = 0;forever begin# 2 i_clk = ~ i_clk;endendinitial begini_rst = 1;# 8i_rst = 0;endversatile_counter #(.N_BITS(N_BITS))inst_vc(.i_rst(i_rst),.i_clk(i_clk),.o_out(o_out),.o_out_done(o_out_done));
endmodule
仿真波形 :
确实是格雷码计数器 ,放大:
如果定义了宏BIN,
// `define LFSR_MACRO
// `define GRAY
`define BIN
则仿真图如下:
放大观测:
如果定义了宏LFSR_MACRO,则输出LFSR计数:
`define LFSR_MACRO
// `define GRAY
//`define BIN
生成语句实现方式
这里使用生成语句,generate case来实现多功能计数器,我们需要定义一个参数SEL,当SEL为0的时候,输出为LFSR;当SEL为1时,输出为格雷码计数器;当SEL为2时候,输出为二进制计数器。
电路设计
`timescale 1ns / 1ps
//
// Create Date: 2020/06/02 16:22:52
// Design Name:
// Module Name: versatile_counter
// Revision 0.01 - File Created
// Additional Comments:
// Reborn Lee
//
module versatile_counter #(parameter N_BITS = 4,parameter SEL = 0)(input i_clk,input i_rst,output [N_BITS - 1 : 0] o_out,output o_out_done);generatecase(SEL)0: beginlfsr #(.NUM_BITS(N_BITS)) LFSR_inst(.i_Clk(i_clk),.i_Enable(1'b1),.i_Seed_DV(1'b0),.i_Seed_Data({N_BITS{1'b0}}), // Replication.o_LFSR_Data(o_out),.o_LFSR_Done(o_out_done));end1: begingray_counter #(.N_BITS(N_BITS))inst_gray_cnt(.i_rst(i_rst),.i_clk(i_clk),.o_cnt(o_out),.o_cnt_done(o_out_done));end2: beginbinary_counter #(.N_BITS(N_BITS))inst_bin_cnt(.i_rst(i_rst),.i_clk(i_clk),.o_cnt(o_out),.o_cnt_done(o_out_done));endendcaseendgenerateendmodule
行为仿真
SEL为0,也即LFSR:
仿真文件例化改为:
versatile_counter #(.N_BITS(N_BITS),.SEL(0))inst_vc(.i_rst(i_rst),.i_clk(i_clk),.o_out(o_out),.o_out_done(o_out_done));
放大:
SEL为1,也即格雷码计数器:
仿真文件例化改为:
versatile_counter #(.N_BITS(N_BITS),.SEL(1))inst_vc(.i_rst(i_rst),.i_clk(i_clk),.o_out(o_out),.o_out_done(o_out_done));
放大:
SEL为2,也即 二进制计数器:
仿真文件例化改为:
versatile_counter #(.N_BITS(N_BITS),.SEL(2))inst_vc(.i_rst(i_rst),.i_clk(i_clk),.o_out(o_out),.o_out_done(o_out_done));
放大:
注意事项
关于多功能计数器的注意事项,这里不做多说,
在debug的过程中,可以先进行elaborated design,
如果没有错误,在进行行为仿真;
其次需要注意,在宏定义过程中,别忘了`这个符号。
就这样吧,感觉如此实现也不是太难,我还记得使用OPENCORES里的那个设计实例,编译预处理指令一大堆,看得我头皮发麻,最关键的是我还没编译通过。
工程要不要分享了呢?暂时算了吧,反正代码已经贴出来了,实在需要的可以说一声。
工具 :vivado 2019.1
参考资料
参考资料1
参考资料2
参考资料3
交个朋友
FPGA/IC技术交流2020
Verilog设计实例(2)一步一步实现一个多功能通用计数器相关推荐
- Verilog设计实例(3)基于Verilog的单端口同步读写RAM设计
文章目录 写在前面 正文 电路设计 行为仿真 交个朋友 写在前面 为什么要写单端口同步读写RAM呢? 没有那么多为什么?就是因为简单.基础,能清晰说明单端口RAM的原理,顺手给出设计,也能说明你的设计 ...
- Verilog设计实例(8)按键防抖设计之软件防抖
博文目录 写在前面 正文 背景介绍及回顾 单个按键 单按键的其他设计版本 多个按键 写在最后 参考资料 交个朋友 写在前面 个人微信公众号: FPGA LAB 个人博客首页 注:学习交流使用! 正文 ...
- Verilog设计实例(7)基于Verilog的数字电子钟设计
博文目录 写在前面 正文 设计要求 设计思想 设计文件 仿真文件 参考资料 交个朋友 写在前面 前段时间,有几个小伙伴向我请教数字电子钟设计的问题,这个问题我在之前的BCD计数器以及数码管显示问题中已 ...
- Verilog设计实例(5)详解全类别加法器(二)
文章目录 写在前面 正文 超前进位加法器 4位超前进位加法器 任意位宽的超前进位加法器 参考资料 交个朋友 写在前面 相关博文 个人博客首页 正文 超前进位加法器 超前加法器由许多级联在一起的全加法器 ...
- Verilog设计实例(6)基于Verilog的各种移位寄存器实现
文章目录 写在前面 正文 左移位寄存器 右移位寄存器 串行输入并行输出移位寄存器 并行输入串行输出移位寄存器 参考资料 交个朋友 写在前面 个人微信公众号:FPGA LAB 个人博客首页 注:学习交流 ...
- Verilog设计实例(4)详解全类别加法器(一)
博文目录 写在前面 正文 半加器 设计代码 测试文件 行为仿真波形图 全加器 设计文件 设计完整文件 行为仿真 纹波进位加法器 2bit数据等波纹加法设计 参数化的等波纹加法器设计 参考资料 交个朋友 ...
- 一步一步学Silverlight 2系列(18):综合实例之RSS阅读器
概述 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支持框架语言Visual Basic, Visual C#, IronRuby, ...
- (转)一步一步Asp.Net MVC系列_权限管理设计起始篇
原文地址:http://www.cnblogs.com/mysweet/archive/2012/07/26/2610793.html 前一段时间,写了一步一步asp.net的一系列博客,最近,也快要 ...
- 如何一步一步用DDD设计一个电商网站(七)—— 实现售价上下文
本系列所有文章 如何一步一步用DDD设计一个电商网站(一)-- 先理解核心概念 如何一步一步用DDD设计一个电商网站(二)-- 项目架构 如何一步一步用DDD设计一个电商网站(三)-- 初涉核心域 如 ...
最新文章
- 如果现在只能用汇编和 Goto 编程......
- RTMPdump(libRTMP) 源代码分析 3: AMF编码
- tcp 发送数据长度比预设缓存大_一文秒懂 TCP/IP实际五层结构(下篇)
- [Cypress] install, configure, and script Cypress for JavaScript web applications -- part3
- 使用XAMPP轻松建站(上)
- 第三次握手为什么没有序列号_TCP三次握手机制-深入浅出(实例演示)
- 说明使用tc编程的一般步骤 c语言,TC编程手册详解-完整版.doc
- hibernate反向工程
- Cadence元件库介绍
- user interface(用户界面)
- SCARA、通用6轴机器人奇异点位置与问题分析
- 中国的高校计算机教育存在哪些问题?
- 产品经理告诉你什么是PMF?什么是MVP?
- matlab加载xls文件报错,服务器出现意外情况,远程过程调用失败
- L2TP更改网络运营商后导致连接不了
- 大数据Spark实战第七集 机器学习和数据处理
- bean login not found within scope
- 二弟机器人_2017中国机器人技能大赛今天在南京理工大学举行
- 微信小程序——wxs脚本
- SEO竞争对手分析及网站SEO优化方案设计分析
热门文章
- 气泡形提示控件grumble.js
- python 动画场景_Python GUI教程(十五):在PyQt5中使用动画
- BufferedInputStream的read方法原理
- android文字广告的循环滚动,android怎样写一个循环文字滚动的TextView
- html随机播放不同的音乐,如何随机播不同的背景音乐
- python的format输出报文_python构造IP报文实例
- 第十六届智能车竞赛参赛队员提问与回答 |2021年7月12
- 我们遇到什么困难都不要怕,微笑着面对它
- 无线电能接收初步测试
- 电容触摸按键IC AT42QT1070