计数器是非常基本的使用,没有计数器就无法处理时序。我在学习时发现市面上有几种不同的计数器写法,非常有趣,在此记录下来:

一、时序逻辑和组合逻辑彻底分开

1.代码

 //======================================================================
// --- 名称 : Count_1
// --- 作者 : xianyu_FPGA
// --- 日期 : 2018-12-10
// --- 描述 : 模10计数器,0到10循环累加
//====================================================================== module Count_1
(
input clk ,
input rst_n ,
output reg [ :] cnt
); //----------------------------------------------------------------------
//-- 组合电路
//----------------------------------------------------------------------
reg [ :] cnt_n ; always @(*)begin
if(cnt == 'd9)
cnt_n = 'd0;
else
cnt_n = cnt + 'b1;
end //----------------------------------------------------------------------
//-- 时序电路
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 'b0;
else
cnt <= cnt_n;
end endmodule /*
//----------------------------------------------------------------------
//-- 组合电路也可以这样写
//----------------------------------------------------------------------
wire [ 3:0] cnt_n ; assign cnt_n = (cnt==4'd9)? 4'd0 : cnt+1'b1; */

2.写法1的RTL视图

3.写法2的RTL视图

二、最常见的写法

1.代码

 //======================================================================
// --- 名称 : Count_2
// --- 作者 : xianyu_FPGA
// --- 日期 : 2018-12-10
// --- 描述 : 模10计数器,0到10循环累加
//====================================================================== module Count_2
(
input clk ,
input rst_n ,
output reg [ :] cnt
); always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 'd0;
else if(cnt=='d9)
cnt <= 'd0;
else
cnt <= cnt + 'b1;
end endmodule

2.RTL视图

三.代码片段写法

1.代码

 //======================================================================
// --- 名称 : Count_3
// --- 作者 : xianyu_FPGA
// --- 日期 : 2018-12-10
// --- 描述 : 模10计数器,0到10循环累加
//====================================================================== module Count_3
//---------------------<端口声明>---------------------------------------
(
input clk ,
input rst_n ,
output reg [ :] cnt
);
//---------------------<信号定义>---------------------------------------
wire add_cnt ;
wire end_cnt ; //----------------------------------------------------------------------
//-- 0-9计数
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 'd0;
else if(add_cnt)begin
if(end_cnt)
cnt <= 'd0;
else
cnt <= cnt + 'b1;
end
else
cnt <= cnt;
end assign add_cnt = ;
assign end_cnt = add_cnt && cnt==10-1; endmodule

2.RTL视图

四、自减计数器(较少用到)

1.代码

 //======================================================================
// --- 名称 : Count_4
// --- 作者 : xianyu_FPGA
// --- 日期 : 2018-12-19
// --- 描述 : 模10自减计数器,10到0循环累减
//====================================================================== module Count_4
//---------------------<端口声明>---------------------------------------
(
input clk ,
input rst_n ,
output reg [ :] cnt
);
//---------------------<参数定义>---------------------------------------
parameter CNT_MAX = 10 , //----------------------------------------------------------------------
//-- 10到0循环累减
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n) begin
cnt <= ;
end
else if(cnt==) begin
cnt <= CNT_MAX;
end
else begin
cnt <= cnt - ;
end
end endmodule

2.RTL视图

3.仿真波形

五、新学到的一种非常简洁的计数器

  本以为计数器就是这样了,近来学习开源骚客《SDRAM那些事儿》系列教程,又发现一种新的写法,对于特定功能的实现上非常简洁。

要求:

  现在对 OV5640 摄像头进行上电控制,由数据手册得到上电控制的时序图如下所示,用Verilog代码实现其波形。

1、代码片段法

  代码片段法还是比较好用的,我平时用的最多,要实现这个时序图,我肯定会这样写:

 module power_ctrl
//========================< 端口 >==========================================
(
//system --------------------------------------------
input wire clk , // 50MHz
input wire rst_n ,
//ov5640 --------------------------------------------
output reg ov5640_pwdn , // ov5640上电
output reg ov5640_rst_n , // ov5640复位
output reg power_done // power_ctrl全面有效,SCCB可以开始工作
);
//========================< 参数 >==========================================
localparam T2_6MS = 30_0000 ; // T2>5ms
localparam T3_2MS = 10_0000 ; // T3>1ms
localparam T4_21MS = 105_0000 ; // T4>20ms
//========================< 信号 >==========================================
reg [:] cnt_6ms ;
wire add_cnt_6ms ;
wire end_cnt_6ms ;
reg [:] cnt_2ms ;
wire add_cnt_2ms ;
wire end_cnt_2ms ;
reg [:] cnt_21ms ;
wire add_cnt_21ms ;
wire end_cnt_21ms ; //==========================================================================
//== ov5640_pwdn
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt_6ms <= 'd0;
else if(add_cnt_6ms) begin
if(end_cnt_6ms)
cnt_6ms <= 'd0;
else
cnt_6ms <= cnt_6ms + ;
end
end assign add_cnt_6ms = ov5640_pwdn == 'b1;
assign end_cnt_6ms = add_cnt_6ms && cnt_6ms== T2_6MS-; always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
ov5640_pwdn <= 'b1;
end
else if(end_cnt_6ms) begin
ov5640_pwdn <= 'b0;
end
end //==========================================================================
//== ov5640_rst_n
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt_2ms <= 'd0;
else if(add_cnt_2ms) begin
if(end_cnt_2ms)
cnt_2ms <= 'd0;
else
cnt_2ms <= cnt_2ms + 'b1;
end
end assign add_cnt_2ms = ov5640_rst_n == 'b0 && ov5640_pwdn == 1'b0;
assign end_cnt_2ms = add_cnt_2ms && cnt_2ms== T3_2MS-; always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
ov5640_rst_n <= 'b0;
end
else if(end_cnt_2ms) begin
ov5640_rst_n <= 'b1;
end
end //==========================================================================
//== power_done
//==========================================================================
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_21ms <= 'd0;
else if(add_cnt_21ms) begin
if(end_cnt_21ms)
cnt_21ms <= 'd0;
else
cnt_21ms <= cnt_21ms + 'b1;
end
end assign add_cnt_21ms = power_done == 'b0 && ov5640_rst_n == 1'b1;
assign end_cnt_21ms = add_cnt_21ms && cnt_21ms== T4_21MS-; always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
power_done <= 'b0;
end
else if(end_cnt_21ms) begin
power_done <= 'b1;
end
end endmodule

  可以看到代码还是不复杂的,条理也比较清晰,仿真后得到如下波形,和时序图一致,设计正确。

2、简洁计数器(by 开源骚客)

  开源骚客实现这段时序时也是采用计数器,可是代码却非常简洁!他的写法如下:

 module power_ctrl
//========================< 端口 >==========================================
(
//system --------------------------------------------
input wire clk , // 50MHz
input wire rst_n ,
//ov5640 --------------------------------------------
output wire ov5640_pwdn , // ov5640上电
output wire ov5640_rst_n , // ov5640复位
output wire power_done // power_ctrl全面有效,SCCB可以开始工作
);
//========================< 参数 >==========================================
localparam T2_6MS = 30_0000 ; // T2>5ms
localparam T3_2MS = 10_0000 ; // T3>1ms
localparam T4_21MS = 105_0000 ; // T4>20ms
//========================< 信号 >==========================================
reg [:] cnt_6ms ;
reg [:] cnt_2ms ;
reg [:] cnt_21ms ; //==========================================================================
//== ov5640_pwdn
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_6ms <= 'd0;
end
else if(ov5640_pwdn == 'b1) begin
cnt_6ms <= cnt_6ms + 'b1;
end
end assign ov5640_pwdn = (cnt_6ms >= T2_6MS) ? 'b0 : 1'b1; //==========================================================================
//== ov5640_rst_n
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_2ms <= 'd0;
end
else if(ov5640_rst_n == 'b0 && ov5640_pwdn == 1'b0) begin
cnt_2ms <= cnt_2ms + 'b1;
end
end assign ov5640_rst_n = (cnt_2ms >= T3_2MS) ? 'b1 : 1'b0; //==========================================================================
//== power_done
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_21ms <= 'd0;
end
else if(power_done == 'b0 && ov5640_rst_n == 1'b1) begin
cnt_21ms <= cnt_21ms + 'b1;
end
end assign power_done = (cnt_21ms >= T4_21MS) ? 'b1 : 1'b0; endmodule

  可以看到,代码非常简洁且条理清晰,比我自己的写法要省很多代码。同样对其仿真得到如下波形,和时序图一致,也设计正确。

  由两个仿真波形图可以发现,我自己的写法是计数器计满了就清0,大多时候计数器也确实是这样用的。而开源骚客的计数器是计数器和信号配合产生,计满了就保持,写法非常简洁!在设计简单时序时,这种计数器思路非常受用。看来计数器虽然简单,但是里面包含的学问可真不少啊。

参考资料:

[1]锆石科技FPGA教程

[2]小梅哥FPGA教程

[3]明德扬FPGA教程

[4]开源骚客《SDRAM那些事儿》

计数器的Verilog写法的更多相关文章

  1. 状态机的Verilog写法

    “硬件设计很讲究并行设计思想,虽然用Verilog描述的电路大都是并行实现的,但是对于实际的工程应用,往往需要让硬件来实现一些具有一定顺序的工作,这就要用到状态机思想.什么是状态机呢?简单的说,就是通 ...

  2. 基础数字电路的Verilog写法

    Verilog是硬件描述电路,我对此一直稀里糊涂,于是将锆石科技开发板附带的的一些基础数字电路Verilog程序整理记录下来,并且查看他们的RTL视图,总算有点理解了. 1.基本运算符 module ...

  3. 对Verilog 初学者比较有用的整理(转自它处)

    *作者: Ian11122840    时间: 2010-9-27 09:04                                                              ...

  4. Verilog HDL那些事_建模篇笔记(实验七:数码管电路驱动)

    1.同步动态扫描 多个数码管的显示采用的是同步动态扫描方法,同步动态扫描指的是:行信号和列信号同步扫描,是一种并行操作. 2.数码管驱动电路实现思路      如果要求数码管显示我们想要的数字,首先需 ...

  5. 使用PowerShell收集多台服务器的性能计数器

    写在前面     当管理多台Windows Server服务器时(无论是DB.AD.WEB以及其他的应用服务器),当出现性能或其他问题后,参阅性能计数器都是一个非常好的维度从而推测出问题可能出现的原因 ...

  6. Modelsim——显示状态机名称的方法

    方法在本人博客<状态机的Verilog写法>已经写明,为了方便查看,特意拎出来. 方法1: Testbench 设计文件含有状态机时,对应的仿真文件testbench里增加一段参数转ASC ...

  7. React Hook上车

    React Hook 是 v16.8 的新功能,自诞生以来,受到广泛的好评,在 React 版本更新中具有里程碑的意义.现在都2020年了,再不上车 React Hook 就真的 out 了... H ...

  8. 一个简单的Verilog计数器模型

    一个简单的Verilog计数器模型 功能说明: 向上计数 向下计数 预装载值 一.代码 1.counter代码(counter.v) module counter( input clk, input ...

  9. 【CPU微架构设计】利用Verilog设计基于饱和计数器和BTB的分支预测器

    在基于流水线(pipeline)的微处理器中,分支预测单元(Branch Predictor Unit)是一个重要的功能部件,它负责收集和分析分支/跳转指令的执行结果,当处理后续分支/跳转指令时,BP ...

随机推荐

  1. cube.js 学习(十)cube 来自官方的学习网站

    尽管cube.js 包含了一个doc 站点,但是资料不是很全,同时如果查看了cube github 代码中的一些demo的话,发现还是很不错的 但是一些实践没有在文档展现出来,还好我们可以从cube ...

  2. CSS块元素

    一.典型代表: Div h1-h6 p ul li 二.特点: 独占一行 可以设置宽高 嵌套(包含)下,子块元素宽度(没有定义情况下)和父块元素宽度默认一致. <style type=" ...

  3. 洛谷P2659 美丽的序列

    题目 该题目可以用辅助数组l[i], r[i]来指向以data[i]为最小值的左端点和右端点.然后最后枚举每个data[i]寻找每个data[i]的美丽值的最大值. 然后辅助数组可以用单调栈求出. # ...

  4. Java面试集合(三)-30道面试题

    前言 大家好,我是 Vic,今天给大家带来Java面试集合(三)的概述,希望你们喜欢 三 1.在Java中是否可以含有多个类?答:可以含有多个类,但只有一个是public类,public类的类名与文件 ...

  5. Clion下同时编写多个main函数

    在你的CMakeLists.txt文件下配置,使用add_executable(),前面的一定要不一样 红色部分是描述main的,配置后运行处可以选择:

  6. Linux crontab命令:循环执行定时任务(详解版)

    前面学习了 at 命令,此命令在指定的时间仅能执行一次任务,但在实际工作中,系统的定时任务一般是需要重复执行的.而 at 命令显然无法满足需求,这是就需要使用 crontab 命令来执行循环定时任务. ...

  7. 安装与配置HSDIS与JITWatch

    本作者的系统: 操作系统版本及位数可通过uname -a命令查看,如下: Linux ubuntu 3.13.0-32-generic #57~precise1-Ubuntu SMP Tue Jul ...

  8. Spring Cloud Feign踩坑记录(二)

    注意,以下的Feign遇到的坑,在高版本中有些已经修复. 某些项目由于历史包袱原因,无法进行全面升级,才需要修补这些坑. 1.启动报错:not annotated with HTTP method t ...

  9. 刷题记录:[网鼎杯]Fakebook

    目录 刷题记录:[网鼎杯]Fakebook 一.涉及知识点 1.敏感文件泄露 2.sql注入 二.解题方法 刷题记录:[网鼎杯]Fakebook 题目复现链接:https://buuoj.cn/cha ...

  10. torch

    1.从数据直接构建tensor x = torch.tensor([5.5,3]) 2.从已有的tensor构建一个tensor.这些方法会重用原来tensor的特征. x = x.new_ones( ...