FPGA和外围接口-第一章 爱上FPGA
第1章 爱上FPGA-
1.5 爱上FPGA从计数器开始
在这里感谢网上各位大神和前辈的指导资料,在此一一谢过,本系列文章主要是以交流和学习为主,欢迎各位转载,转载请注明下出处,谢谢!
PS:阅读过程中,有什么问题或者建议可以在微信公众号:OpenFPGA 后台留言,一定悉心听取各位前辈和大佬的建议。
计数器在FPGA设计中有着举足轻重的地位,把它理解通了,大部分时序都会很好理解,所以本节会介绍的很详细,予以一个完美的开始。 |
---|
1.5.3 计数器设计
如下案例,对时钟CLOCK进行计数,在en使能信号由高电平变换到低电平时,dout开始计数使能高电平,计数周期为10。整个计数器工作过程如下:当en使能信号由低电平变成高电平再变成低电平时(en发出脉冲信号),计数器开始计数,dout输出高电平,同时作为计数器的加一信号,当计数器计满10个时钟信号,达到结束条件时,dout输出低电平,同时计数结束并复位到0或者其他信号(看具体需求)。
这里还可以从另一方面去考虑加一条件,就是当计数器没有加满10个时钟信号就继续加一,这种方式其实和第一种方式一样,因为当计数器没有计满10个信号dout会一直处于高电平。但是第二种方式起始条件、加一条件和结束条件很难统一,所以我们在设计时会采用第一种方式,虽然复杂点,但是以后在设计时会很方便。
图 1 69 对时钟CLOCK进行计数
采用自顶向下的设计流程。
第一步 电路整体结构图
图1 70 计数器整体结构图
表1 29 counter_10clk.v结构图说明
说明:
1、 I/O引脚在以后的章节中,可能不会出现,因为在结构图中很方便就看出来;
2、 位宽除非必要和明确情况一般也不会说明;
第二步 时序分析
见表 1 29。
第三步 计数器结构
计数器结构如下图所示,这一步主要目的是为了后面分析计数器的起始条件和结束条件做准备,后面本步骤可以省略。
图1 71 计数器结构
第四步 计数器起始条件、加一条件和结束条件
计数器最重要的是起始条件、加一条件和结束条件,其中起始条件由时序图中很容易看到就是en发送脉冲信号;加一条件为dout为高电平;结束条件为cnt==9即计满十个时钟信号(cnt由0开始计数)。
表1-30 计数器工作条件
说明:H:高电平;L:低电平。
在这一步还需要定义一下特殊信号,比如由起始条件引申出来的起始信号,由结束条件引申出来的结束信号,见表1 30。在表中可以看到起始条件不需要特殊使用,因为加一条件和起始条件是统一在一起的。
由上表可以写出下列程序:
代码1-1
1. parameter counter_value= 4'd10;
2. always @ ( posedge CLOCK or negedge RST_n )
3. if( !RST_n )
4. begin
5. cnt <= 0;
6. end
7. else if(add_cnt)
8. begin
9. if(end_cnt)
10. cnt <= 0;
11. else
12. cnt <= cnt 1;
13. end
14.
15. assign add_cnt = dout == 1'b1;
16. assign end_cnt = add_cnt && cnt == counter_value-1;
程序说明:
parameter 定义了计数器的计数终值,在使用的时候只需要在这里修改add_cnt后面的加一条件和end_cnt结束条件,结束条件不需要修改,只需要修改parameter宏定义的值即可。
第五步 功能性代码
这里主要是dout信号怎么产生,先看一下dout信号的产生条件和结束条件,如下表所示:
表1-31 dout信号产生条件
代码 1-2
1. module counter_10clk
2. (
3. CLOCK, RST_n,
4. en,
5. dout
6. );
7.
8. input CLOCK, RST_n; //全局时钟信号输入
9. input en; //计数器使能信号
10. output reg dout; //计数器输出信号
11.
12. parameter counter_value= 4'd10; //计数时钟个数
13.
14.
15. reg [7:0] cnt;
16. wire add_cnt,end_cnt;
17.
18.
19. always @ ( posedge CLOCK or negedge RST_n )
20. if( !RST_n )
21. begin
22. cnt <= 0;
23. end
24. else if(add_cnt)
25. begin
26. if(end_cnt)
27. cnt <= 0;
28. else
29. cnt <= cnt 1;
30. end
31. always @ ( posedge CLOCK or negedge RST_n )
32. if( !RST_n )
33. begin
34. dout <= 1;
35. end
36. else if(en)
37. begin
38. dout <= 1;
39. end
40. else if(end_cnt)
41. begin
42. dout <= 0;
43. end
44.
45. assign add_cnt = dout == 1'b1;
46. assign end_cnt = add_cnt && cnt == counter_value-1;
47.
48. endmodule 49.
这样完整的计数器代码就写完了,后面只需要按照这个模板去修改参数就可以了,并不需要每次都去写程序。
编写TestBench,如下:
代码1-3
1.//****************************************************************************//
2. //# @Author: 碎碎思
3. //# @Date: 2019-06-12 22:54:31
4. //# @Last Modified by: zlk
5. //# @WeChat Official Account: OpenFPGA
6. //# @Last Modified time: 2019-06-12 23:41:12
7. //# Description:
8. //# @Modification History: 2019-06-12 23:41:12
9. //# Date By Version Change Description:
10. //# ============================================ #
11. //# 2019-06-12 23:41:12
12. //# ============================================ #
13. //# | | #
14. //# | OpenFPGA | #
15.//****************************************************************************//
16. `timescale 1 ns/ 1 ps
17. module counter_10clk_tb; // 申明TestBench名称
18.
19. reg clock;
20. reg reset; // 申明信号
21. reg en;
22. wire dout;
23.
24.
25. // 申明设计单元
26. counter_10clk dut
27. (
28. .CLOCK( clock ),
29. .RST_n( reset ),
30. .en( en ), //
31. .dout( dout )
32. );
33.
34. initial begin // 建立时钟
35. clock = 0;
36. forever #20 clock = ~clock;
37. end
38.
39. initial begin // 提供激励
40. reset = 0;
41. en = 0;
42. #200
43. reset = 1;
44. en = 1;
45. #210
46. en = 0;
47. #50000 $stop;
48. end
49.
50. endmodule
Modelsim仿真结果如下:
图1-72 10clk计数器仿真结果