学习的文章:https://mp.weixin.qq.com/s/hfrnaK4QJRN7JE_Eieuu7Q
仅作个人学习记录,侵权即删。
原理:
具体要求:以clk为基准,设计一个秒计数器,在指定的计数值产生中断,实时输出当前的秒数计数值。已知clk的频率32.768kHz,rst_n低电平有效,收到start后,秒计数器sec_cnt从零开始以秒为单位来计数,计数到alarm[7:0]指定的数值后,产生一个脉冲int,然后秒数计数器回零并停止。
解决:意思是通过clk分频一个周期为1s的时钟,拿这个时钟来检测alarm的数值,比如alarm是3,那么就是有3秒。
clk=32768Hz=215Hz,那么1s就需要计数(1/(1/clk))=215次。
还有一个这里的计数到32768,就意味着从0计数到32767,那么就是15'b111_1111_1111_1111,如果计数到这个值就会占用很多资源,所以改成了&cnt_1s==1'b1。
代码:
综合代码:
module sec_cnt #( parameter cnt_1s_width=4'd14 ) ( input clk, input rst_n, input start, input [7:0]alarm, output reg int, output reg [31:0]sec_cnt //计数计算到第sec_cnt个1s ); reg cnt_en; //将start寄存到一个寄存器,这样可以控制它的状态 reg [cnt_1s_0]cnt_1s; //计数到1s所用的时间 always @(posedge clk or negedge rst_n) if(!rst_n) cnt_en<=1'b0; else if(start) cnt_en<=1'b1; else if(sec_cnt==alarm && &cnt_1s==1'b1) cnt_en<=1'b0; always @(posedge clk or negedge rst_n) if(!rst_n) cnt_1s<=15'd0; else if(cnt_en) cnt_1s<=cnt_1s+1'b1; else cnt_1s<=15'd0; always @(posedge clk or negedge rst_n) if(!rst_n) sec_cnt<=32'd0; else if(sec_cnt==alarm && &cnt_1s==1'b1) //这两个不能换过来 sec_cnt<=32'd0; else if(&cnt_1s==1'b1 && cnt_en==1'b1) //这两个不能换过来 sec_cnt<=sec_cnt+1'b1; always @(posedge clk or negedge rst_n) if(!rst_n) int<=1'b0; else if(sec_cnt==alarm && &cnt_1s==1'b1) int<=1'b1; else int<=1'b0; endmodule
仿真代码:仿真这里按照着原理,把宽度parameter从14换成2,省略了很多计数,只是数值计算就变成了7,所以&cnt_1s==1'b1就是cnt_1s==3'b111。
`timescale 1ns/1ns module sec_cnt_tb; reg clk; reg rst_n; reg start; reg [7:0]alarm; wire int; wire [31:0]sec_cnt; initial begin clk<=1'b1; rst_n<=1'b0; start<=1'b0; alarm<=8'd0; #10 rst_n<=1'b1; end always #10 clk<=~clk; initial begin #40 start<=1'b1; alarm<=8'd3; #20 start<=1'b0; #1000 start<=1'b1; alarm<=8'd4; #20 start<=1'b0; end sec_cnt #( .cnt_1s_width(4'd2) //这里就是把宽度parameter值由15变成2,所以计数&cnt_1s==1'b1就是3'b111 ) u_sec_cnt( .clk (clk), .rst_n (rst_n), .start (start), .alarm (alarm), .int (int), .sec_cnt (sec_cnt) ); endmodule
仿真效果:
部分放大一点: