Lattice MXO2: 時鐘分頻
時鐘分頻
在之前的實驗中我們已經(jīng)熟悉了小腳丫的各種外設(shè),掌握了verilog的組合邏輯設(shè)計,接下來我們將學(xué)習(xí)時序邏輯的設(shè)計。
硬件說明
時鐘信號的處理是FPGA的特色之一,因此分頻器也是FPGA設(shè)計中使用頻率非常高的基本設(shè)計之一。一般在FPGA中都有集成的鎖相環(huán)可以實現(xiàn)各種時鐘的分頻和倍頻設(shè)計,但是通過語言設(shè)計進行時鐘分頻是最基本的訓(xùn)練,在對時鐘要求不高的設(shè)計時也能節(jié)省鎖相環(huán)資源。在本實驗中我們將實現(xiàn)任意整數(shù)的分頻器,分頻的時鐘保持50%占空比。
1,偶數(shù)分頻:偶數(shù)倍分頻相對簡單,比較容易理解。通過計數(shù)器計數(shù)是完全可以實現(xiàn)的。如進行N倍偶數(shù)分頻,那么通過時鐘觸發(fā)計數(shù)器計數(shù),當計數(shù)器從0計數(shù)到N/2-1時,輸出時鐘進行翻轉(zhuǎn),以此循環(huán)下去。
2,奇數(shù)分頻: 如果要實現(xiàn)占空比為50%的奇數(shù)倍分頻,不能同偶數(shù)分頻一樣計數(shù)器記到一半的時候輸出時鐘翻轉(zhuǎn),那樣得不到占空比50%的時鐘。以待分頻時鐘CLK為例,如果以偶數(shù)分頻的方法來做奇數(shù)分頻,在CLK上升沿觸發(fā),將得到不是50%占空比的一個時鐘信號(正周期比負周期多一個時鐘或者少一個時鐘);但是如果在CLK下降沿也觸發(fā),又得到另外一個不是50%占空比的時鐘信號,這兩個時鐘相位正好相差半個CLK時鐘周期。通過這兩個時鐘信號進行邏輯運算我們可以巧妙的得到50%占空比的時鐘。
總結(jié)如下:對于實現(xiàn)占空比為50%的N倍奇數(shù)分頻,首先進行上升沿觸發(fā)進行模N計數(shù),計數(shù)選定到某一個值進行輸出時鐘翻轉(zhuǎn),然后經(jīng)過(N-1)/2再次進行翻轉(zhuǎn)得到一個占空比非50%奇數(shù)n分頻時鐘。再者同時進行下降沿觸發(fā)的模N計數(shù),到和上升沿觸發(fā)輸出時鐘翻轉(zhuǎn)選定值相同值時,進行輸出時鐘時鐘翻轉(zhuǎn),同樣經(jīng)過(N-1)/2時,輸出時鐘再次翻轉(zhuǎn)生成占空比非50%的奇數(shù)n分頻時鐘。兩個占空比非50%的n分頻時鐘進行邏輯運算(正周期多的相與,負周期多的相或),得到占空比為50%的奇數(shù)n分頻時鐘。
Verilog代碼
// ******************************************************************** // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< // ******************************************************************** // File name : divide.v // Module name : divide // Author : STEP // Description : clock divider // Web : www.stepfpga.com // // -------------------------------------------------------------------- // Code Revision History : // -------------------------------------------------------------------- // Version: |Mod. Date: |Changes Made: // V1.0 |2017/03/02 |Initial ver // -------------------------------------------------------------------- // Module Function:任意整數(shù)時鐘分頻 module divide ( clk,rst_n,clkout); input clk,rst_n; //輸入信號,其中clk連接到FPGA的C1腳,頻率為12MHz output clkout; //輸出信號,可以連接到LED觀察分頻的時鐘 //parameter是verilog里常數(shù)語句 parameter WIDTH = 3; //計數(shù)器的位數(shù),計數(shù)的最大值為 2**WIDTH-1 parameter N = 5; //分頻系數(shù),請確保 N < 2**WIDTH-1,否則計數(shù)會溢出 reg [WIDTH-1:0] cnt_p,cnt_n; //cnt_p為上升沿觸發(fā)時的計數(shù)器,cnt_n為下降沿觸發(fā)時的計數(shù)器 reg clk_p,clk_n; //clk_p為上升沿觸發(fā)時分頻時鐘,clk_n為下降沿觸發(fā)時分頻時鐘 //上升沿觸發(fā)時計數(shù)器的控制 always @ (posedge clk or negedge rst_n ) //posedge和negedge是verilog表示信號上升沿和下降沿 //當clk上升沿來臨或者rst_n變低的時候執(zhí)行一次always里的語句 begin if(!rst_n) cnt_p<=0; else if (cnt_p==(N-1)) cnt_p<=0; else cnt_p<=cnt_p+1; //計數(shù)器一直計數(shù),當計數(shù)到N-1的時候清零,這是一個模N的計數(shù)器 end //上升沿觸發(fā)的分頻時鐘輸出,如果N為奇數(shù)得到的時鐘占空比不是50%;如果N為偶數(shù)得到的時鐘占空比為50% always @ (posedge clk or negedge rst_n) begin if(!rst_n) clk_p<=0; else if (cnt_p<(N>>1)) //N>>1表示右移一位,相當于除以2去掉余數(shù) clk_p<=0; else clk_p<=1; //得到的分頻時鐘正周期比負周期多一個clk時鐘 end //下降沿觸發(fā)時計數(shù)器的控制 always @ (negedge clk or negedge rst_n) begin if(!rst_n) cnt_n<=0; else if (cnt_n==(N-1)) cnt_n<=0; else cnt_n<=cnt_n+1; end //下降沿觸發(fā)的分頻時鐘輸出,和clk_p相差半個時鐘 always @ (negedge clk) begin if(!rst_n) clk_n<=0; else if (cnt_n<(N>>1)) clk_n<=0; else clk_n<=1; //得到的分頻時鐘正周期比負周期多一個clk時鐘 end assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p; //條件判斷表達式 //當N=1時,直接輸出clk //當N為偶數(shù)也就是N的最低位為0,N(0)=0,輸出clk_p //當N為奇數(shù)也就是N最低位為1,N(0)=1,輸出clk_p&clk_n。正周期多所以是相與 endmodule
測試文件,進行功能仿真時需要編寫testbench測試文件。verilog里的testbench文件和源文件一樣也是.v文件,仿真能讓我們更直觀的觀察信號波形,可以先閱讀Diamond的使用了解如何使用Diamond中集成的仿真工具。
// ******************************************************************** // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< // ******************************************************************** // File name : divide_tb.v // Module name : divide_tb // Author : STEP // Description : clock divider // Web : www.stepfpga.com // // -------------------------------------------------------------------- // Code Revision History : // -------------------------------------------------------------------- // Version: |Mod. Date: |Changes Made: // V1.0 |2017/03/02 |Initial ver // -------------------------------------------------------------------- // Module Function:divide.v時鐘分頻器的測試文件 `timescale 1ns/100ps //仿真時間單位/時間精度,時間單位要大于或者等于時間精度 module divide_tb(); //測試文件也是一個module,因為用于仿真所以無需輸入輸出信號 reg clk,rst_n; //需要產(chǎn)生的激勵信號定義,激勵信號需要過程塊產(chǎn)生所以定義為reg型變量 wire clkout; //需要觀察的輸出信號定義,定義為wire型變量 //初始化過程塊 initial begin clk = 0; rst_n = 0; #25 //#表示延時25個時間單位 rst_n = 1; //產(chǎn)生了一個初始25ns低電平,然后變高電平的復(fù)位信號 end always #10 clk = ~clk; //每隔10ns翻轉(zhuǎn)一次clk信號,也就是產(chǎn)生一個時鐘周期20ns的clk,頻率為50MHz //module調(diào)用例化格式 divide #(.WIDTH(4),.N(11)) u1 ( //#后面的()中為參數(shù)傳遞,如果不傳遞參數(shù)就是所調(diào)用模塊中的參數(shù)默認值 //divide表示所要例化的module名稱,u1是我們定義的例化名稱,必須以字母開頭 .clk (clk), //輸入輸出信號連接。 .clk表示module本身定義的信號名稱;(clk)表示我們在這里定義的激勵信號 .rst_n (rst_n), //在testbench里定義的信號名稱可以與所要調(diào)用module的端口信號名稱不同 .clkout (clkout) ); endmodule
引腳分配
小腳丫上的系統(tǒng)時鐘連接到FPGA的C1腳,時鐘為12MHz。你可以通過仿真波形觀察分頻時鐘(注意仿真的時間是有限的,所以分頻時鐘頻率需要較高)。如果我們想通過眼睛觀察LED的閃爍,那么需要設(shè)置參數(shù)N和WIDTH得到一個頻率較低的時鐘(例如N=12000000,WIDTH=24,分頻時鐘周期為1秒)。
信號 | 引腳 |
---|---|
clk | C1 |
rst_n | L14 |
clkout | N13 |
修改程序中的分頻系數(shù)和計數(shù)器位數(shù)就能夠調(diào)整LED閃爍速度(注意計數(shù)的最大值一定要保證超過分頻系數(shù)N)。
小結(jié)
在本實驗學(xué)習(xí)了如何進行任意整數(shù)的分頻設(shè)計,我們產(chǎn)生各種時鐘,通過修改程序還能實驗調(diào)整輸出時鐘的頻率、相位以及占空比,非常靈活。同時學(xué)習(xí)了如何編寫testbench文件,了解verilog中如何例化module,在后面的學(xué)習(xí)中將會經(jīng)常用到。在下個實驗我們將進一步了解時序邏輯,如何利用時鐘來進一步設(shè)計,請看最常見的流水燈。
評論