前面已經學習了時序邏輯電路中的基本單元:觸發器,這次就用其來整點活,實現計數器的設計,計數器可以說是任何和時序有關的設計都會用到他。
一. 計數器原理
計數器的功能主要就是計數,在數字系統中主要對脈沖的個數進行計數,以實現測量,計數和控制的功能,同時也可以做到分頻等功能。 同時,其也是FPGA設計中最常用的一種時序邏輯電路,根據計數器的計數值可以精確控制各個信號的狀態。
二. 實用
用計數器計數1s間隔,實現led燈每隔1s閃爍的結果。
本設計十分簡單,用一個模塊附帶簡單的輸入輸出便可以完成。 如下所示:
sys_clk:輸入時鐘,為系統時鐘
sys_rst_n:復位信號,低電平有效
led_out:輸出信號,控制LED燈的亮滅
我們的邏輯是這樣:計數器進行計數,計數到1s便給LED燈賦值,讓其發亮,就可以達到1s亮滅的結果。 所以需要計算一下,計1s需要計數多少次,如果時鐘是50MHz,也就是0.000_000_02s,計數一秒需要計數50_000_000個數才可以,不過在設計時刻視為從0開始計數,所以是49_999_999個。
接下來的設計時,為了節省寄存器資源,只計數24_999_999個,也就是1s內閃爍一次(0.5秒一次)。 這需要25位寬的計數器。
下面介紹兩種方式的計數器; 帶標志位和不帶標志位。
不帶標志位 :設置一個cnt信號位,時鐘的每個上升沿到來時刻,cnt便自動+1,當計數器計到24_999_999的時候,清0,led_out信號取反,只要不復位,變一直計數下去。
帶標志位: 在上面方案的基礎上,添加一個cnt_flag信號,當計數器計數到24_999_999的時候先不取反,而是讓cnt_flag產生一個高脈沖,當led_out 的信號檢測到cnt_flag拉高的時候取反。
關于標志位有什么用,先挖個坑,以后說。
圖1
圖2
注意一下上面兩個圖(取自野火教材),標志位拉高的時刻并不一樣,下面的圖在N-1時刻拉高,led_out的狀態是剛剛好在0時刻轉換(慢一拍),上面的圖實際上到后來是計數計多了。 對于這種簡單的設計這個問題不痛不癢,但是對于一些要求比較高的設計來說,一秒也不能差,所以這個地方需要注意。
下面給出代碼(帶信號標志位)
module counter
#(
parameter CNT_MAX = 25'd24_999_999
)
(
input wire sys_clk , //系統時鐘 50Mh
input wire sys_rst_n , //全局復位
output reg led_out //輸出控制 led 燈
);
//reg define
reg [24:0] cnt ; //經計算得需要 25 位寬的寄存器才夠 500ms
reg cnt_flag;
//cnt:計數器計數,當計數到 CNT_MAX 的值時清零
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 25'b0;
else if(cnt == CNT_MAX)
cnt <= 25'b0;
else
cnt <= cnt + 1'b1;
//cnt_flag:計數到最大值產生的標志信號,每當計數滿標志信號有效時取反
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_flag <= 1'b0;
else if(cnt == CNT_MAX – 25'b1)
cnt_flag <= 1'b1;
else
cnt_flag <= 1'b0;
//led_out:輸出控制一個 LED 燈
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
led_out <= 1'b0;
else if(cnt_flag == 1'b1)
led_out <= ~led_out;
endmodule
測試平臺:
module tb_counter(
);
reg sys_clk;
reg sys_rst_n;
wire led_out;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
counter
#(
.CNT_MAX (25'd24) //實例化帶參數的模塊時候,當我們想要修改常數在此模塊的值的時候,直接實例化參數名后面的括號中修改即可
)
counter_inst(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.led_out(led_out)
);
endmodule
仿真波形:
到這里,問題還沒解決,在7系列的FPGA的板上,并不輸出單端口的時鐘,其是200MHz的差分時鐘。
AD11和AD12差分時鐘輸出
經過查閱,調用PLL鎖相環的IP核進行差分時鐘的單端口時鐘輸出。
調用時鐘IP核,并且選擇PLL,在下面選擇差分時鐘接口。 output時鐘設置50MHz。
重新寫頂層代碼,例化時鐘。
module counter
(
//input wire sys_clk,
input wire sys_rst_n,
input clk_p,
input clk_n,
output reg led_out
);
reg [24:0] cnt;
reg cnt_flag;
parameter CNT_MAX = 25'd24_999_999;
wire locked;
wire sys_clk;
clk_wiz_0 diff_2_single
(
.clk_out1(sys_clk),
.reset(1'b0),
.locked(locked),
.clk_in1_p(clk_p),
.clk_in1_n(clk_n)
);
//cnt:計數器計數,當計數到cnt_max的時候值清0
always @(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
cnt <= 25'b0;
else if(cnt == CNT_MAX)
cnt <= 25'b0;
else
cnt <= cnt+1'b1;
//cnt_flag:計數到最大值產生的標志位信號,每當計數滿標志信號有效時取反
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_flag <= 1'b0;
else if (cnt == CNT_MAX - 25'b1)
cnt_flag <= 1'b1;
else
cnt_flag <= 1'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
led_out <= 1'b0;
else if (cnt_flag == 1'b1)
led_out <= ~led_out;
endmodule
RTL電路如下所示:
關于kintex7差分時鐘引腳約束的問題需要注意:
這個地方只需要約束其中之一即可,另外一個不用管,如果是不清楚電平標準可以在Tcl控制臺輸入指令查詢:
-
FPGA
+關注
關注
1630文章
21796瀏覽量
605225 -
led
+關注
關注
242文章
23347瀏覽量
662433 -
計數器
+關注
關注
32文章
2261瀏覽量
94876 -
觸發器
+關注
關注
14文章
2003瀏覽量
61289 -
時序邏輯電路
+關注
關注
2文章
94瀏覽量
16575
發布評論請先 登錄
相關推薦
評論