【接口时序】2、Verilog实现流水灯及与C语言的对比
一、 軟件平臺與硬件平臺
軟件平臺:
1、操作系統:Windows-8.1
2、開發套件:ISE14.7
3、仿真工具:ModelSim-10.4-SE
硬件平臺:
1、FPGA型號:XC6SLX45-2CSG324
二、 原理介紹
我的開發板上有4個LED燈,原理圖如下:
由原理圖可知僅當FPGA的對應管腳輸入低電平時LED才會亮,流水燈的效果可以輪流讓四個對應管腳輸出低電平來產生。
三、 目標任務
編寫四個LED流水的Verilog代碼并用ModelSim進行仿真,仿真通過以后下載到開發板進行測試,要求開發板上每個LED亮的時間為1s。
四、 設計思路與Verilog代碼編寫
由于每個LED亮的時間為1s,所以首先很自然想到產生一個1s的時鐘用來驅動后續邏輯,有了這個1s的時鐘以后,就可以在這個1s時鐘的節拍下對LED的輸出進行以移位操作來產生流水燈的效果。
? 1、1s時鐘的分頻邏輯
由于主時鐘是50MHz,周期為20ns,所以可以利用50MHz主時鐘驅動一個計數器,當計數器的值每次到達24999999時,消耗的時間為25000000*20ns=0.5s,這時把分頻器的輸出反轉,并把計數值清0,這樣分頻器的輸出就會每隔0.5s翻轉一次,產生了一個1s的時鐘。
Verilog代碼如下:
// // 功能:產生1s的時鐘 // always @(posedge I_clk or negedge I_rst_n) beginif(!I_rst_n)beginR_cnt_ls <= 32'd0 ; R_clk_ls_reg <= 1'b1 ;end else if(R_cnt_ls == 32'd24_999_999)beginR_cnt_ls <= 32'd0 ;R_clk_ls_reg <= ~R_clk_ls_reg ; endelseR_cnt_ls <= R_cnt_ls + 1'b1 ; endassign W_clk_ls = R_clk_ls_reg ;?
2、移位邏輯
有了1s的時鐘信號以后,就在這個1s時鐘信號的驅動下對輸出的LED寄存器進行移位操作產生流水效果。
Verilog代碼如下:
// // 功能:對輸出寄存器進行移位產生流水效果 // always @(posedge W_clk_ls or negedge I_rst_n) beginif(!I_rst_n) R_led_out_reg <= 4'b0001 ; else if(R_led_out_reg == 4'b1000)R_led_out_reg <= 4'b0001 ;else R_led_out_reg <= R_led_out_reg << 1 ; endassign O_led_out = ~R_led_out_reg ;五、 ModelSim仿真
寫好邏輯以后,為了確定時序是正確的,最好寫一個測試文件對功能進行仿真,為了加快仿真速度,修改分頻邏輯計數器的計數值為24,然后編寫測試文件,測試文件中激勵產生的Verilog代碼如下:
initial begin// Initialize InputsI_clk = 0;I_rst_n = 0;// Wait 100 ns for global reset to finish#100;I_rst_n = 1;// Add stimulus hereendalways #10 I_clk = ~I_clk ;?
仿真的時序圖如下圖所示:
?
可以看到時序完全正確,接下來就是綁定管腳,生成bit文件下載到開發板測試了。
六、 進一步思考——C語言流水燈與Verilog流水燈區別
看完網上《Verilog那些事》系列博文以后,作者提出了一種“仿順序操作”方法,其實以前自己寫代碼的時候無形之中一直在用這種思想,但是一直沒有提煉出來,看完作者的介紹以后才發現確實是有那個“仿順序”的味道。詳細的博文請參考博客園博主akuei2的系列博文。這里我在總結一遍,給以后留個印象。
C語言實現流水燈的大致代碼框架如下:
while(1)
{
1、讓第1個LED亮,其他的滅;
2、延時1s
3、讓第2個LED亮,其他的滅
4、延時1s
5、讓第3個LED亮,其他的滅;
6、延時1s
7、讓第4個LED亮,其他的滅
8、延時1s
??? }
在while(1)里面代碼是一行一行的執行,最后一行執行完畢以后在回到第一行重新開始新一輪的執行。就這樣產生了流水的效果。
看到這里,有人應該突然明白了吧,這不正好就是Verilog中的一個狀態機么。對應的Verilog代碼也可以寫出來了
always @(posedge I_clk)
begin
?????? case(R_state)
????????????? 第1個狀態:讓第1個LED亮,其他的滅,下一狀態是第2個狀態;
????????????? 第2個狀態:延時1s,下一狀態是第3個狀態;
????????????? 第3個狀態:讓第2個LED亮,其他的滅,下一狀態是第4個狀態;
????????????? 第4個狀態:延時1s,下一狀態是第5個狀態;
????????????? 第5個狀態:讓第3個LED亮,其他的滅,下一狀態是第6個狀態;
????????????? 第6個狀態:延時1s,下一狀態是第7個狀態;
????????????? 第7個狀態:讓第4個LED亮,其他的滅,下一狀態是第8個狀態;
????????????? 第8個狀態:延時1s,下一狀態是第1個狀態;
????????????? default????????? : ;
?????? endcase
end
具體的代碼如下:
// // 功能:“仿順序操作” // always @(posedge I_clk or negedge I_rst_n) beginif(!I_rst_n)beginR_state <= 3'b000 ; R_cnt_ls <= 32'd0 ;endelsebegin case(R_state)C_S0:beginR_led_out_reg <= 4'b0001 ;R_state <= C_S1 ; endC_S1:beginif(R_cnt_ls == C_CNT_1S)beginR_cnt_ls <= 32'd0 ;R_state <= C_S2 ;endelseR_cnt_ls <= R_cnt_ls + 1'b1 ; endC_S2:beginR_led_out_reg <= 4'b0010 ;R_state <= C_S3 ; endC_S3:beginif(R_cnt_ls == C_CNT_1S)beginR_cnt_ls <= 32'd0 ;R_state <= C_S4 ;endelseR_cnt_ls <= R_cnt_ls + 1'b1 ; endC_S4:beginR_led_out_reg <= 4'b0100 ;R_state <= C_S5 ; endC_S5:beginif(R_cnt_ls == C_CNT_1S)beginR_cnt_ls <= 32'd0 ;R_state <= C_S6 ;endelseR_cnt_ls <= R_cnt_ls + 1'b1 ; endC_S6:beginR_led_out_reg <= 4'b1000 ;R_state <= C_S7 ; endC_S7:beginif(R_cnt_ls == C_CNT_1S)beginR_cnt_ls <= 32'd0 ;R_state <= C_S0 ;endelseR_cnt_ls <= R_cnt_ls + 1'b1 ; end default: R_state <= 3'b000 ; endcase end endassign O_led_out = ~R_led_out_reg ;時序圖如下圖:
時序圖仍然正確,實現了流水燈的效果
七、 總結
1、所謂的“仿順序操作”實際上就是一個狀態機,通過狀態的跳變實現“順序執行”的效果。這種思想在后面寫接口時序的時候還是挺管用的,今后可以多多琢磨琢磨。
2、 C語言的while(1)和Verilog語言的always @(posedge I_clk)有類似的地方,只要CPU的時鐘存在,它們就一直執行下去。書上都說C語言是一種串行語言,Verilog是一種并行語言,實際上這里也能有體會:C語言里只能有1個while(1)語句,進入while(1)以后CPU就出不來了,而Verilog中可以有多個always @(posedge I_clk)語句,并且每個always @(posedge I_clk)同時運行的,這就是兩種語言最大的區別吧。
?
八、 附錄
1、分頻1s產生流水燈的完整代碼
module led_work_top (input I_clk ,input I_rst_n ,output [3:0] O_led_out );reg [31:0] R_cnt_ls ; wire W_clk_ls ; reg R_clk_ls_reg ; reg [3:0] R_led_out_reg ;// // 功能:產生1s的時鐘 // always @(posedge I_clk or negedge I_rst_n) beginif(!I_rst_n)beginR_cnt_ls <= 32'd0 ; R_clk_ls_reg <= 1'b1 ;end else if(R_cnt_ls == 32'd24_999_999)beginR_cnt_ls <= 32'd0 ;R_clk_ls_reg <= ~R_clk_ls_reg ; endelseR_cnt_ls <= R_cnt_ls + 1'b1 ; endassign W_clk_ls = R_clk_ls_reg ;// // 功能:對輸出寄存器進行移位產生流水效果 // always @(posedge W_clk_ls or negedge I_rst_n) beginif(!I_rst_n) R_led_out_reg <= 4'b0001 ; else if(R_led_out_reg == 4'b1000)R_led_out_reg <= 4'b0001 ;else R_led_out_reg <= R_led_out_reg << 1 ; endassign O_led_out = ~R_led_out_reg ;endmodule?
2、 “仿順序操作”產生流水燈完整代碼
module led_work_top (input I_clk ,input I_rst_n ,output [3:0] O_led_out ); reg [31:0] R_cnt_ls ; reg [3:0] R_led_out_reg ; reg [2:0] R_state ;parameter C_CNT_1S = 32'd49_999_999 ; parameter C_S0 = 3'b000 ,C_S1 = 3'b001 ,C_S2 = 3'b010 ,C_S3 = 3'b011 ,C_S4 = 3'b100 ,C_S5 = 3'b101 ,C_S6 = 3'b110 ,C_S7 = 3'b111 ;// // 功能:仿順序操作 // always @(posedge I_clk or negedge I_rst_n) beginif(!I_rst_n)beginR_state <= 3'b000 ; R_cnt_ls <= 32'd0 ;endelsebegin case(R_state)C_S0:beginR_led_out_reg <= 4'b0001 ;R_state <= C_S1 ; endC_S1:beginif(R_cnt_ls == C_CNT_1S)beginR_cnt_ls <= 32'd0 ;R_state <= C_S2 ;endelseR_cnt_ls <= R_cnt_ls + 1'b1 ; endC_S2:beginR_led_out_reg <= 4'b0010 ;R_state <= C_S3 ; endC_S3:beginif(R_cnt_ls == C_CNT_1S)beginR_cnt_ls <= 32'd0 ;R_state <= C_S4 ;endelseR_cnt_ls <= R_cnt_ls + 1'b1 ; endC_S4:beginR_led_out_reg <= 4'b0100 ;R_state <= C_S5 ; endC_S5:beginif(R_cnt_ls == C_CNT_1S)beginR_cnt_ls <= 32'd0 ;R_state <= C_S6 ;endelseR_cnt_ls <= R_cnt_ls + 1'b1 ; endC_S6:beginR_led_out_reg <= 4'b1000 ;R_state <= C_S7 ; endC_S7:beginif(R_cnt_ls == C_CNT_1S)beginR_cnt_ls <= 32'd0 ;R_state <= C_S0 ;endelseR_cnt_ls <= R_cnt_ls + 1'b1 ; end default: R_state <= 3'b000 ; endcase end endassign O_led_out = ~R_led_out_reg ;endmodule3、測試記錄文件完整代碼
module tb_led_work_top;// Inputsreg I_clk;reg I_rst_n;// Outputswire [3:0] O_led_out;// Instantiate the Unit Under Test (UUT) led_work_top U_led_work_top (.I_clk(I_clk), .I_rst_n(I_rst_n), .O_led_out(O_led_out));initial begin// Initialize InputsI_clk = 0;I_rst_n = 0;// Wait 100 ns for global reset to finish#100;I_rst_n = 1;// Add stimulus hereendalways #5 I_clk = ~I_clk ;endmodule?
歡迎關注我的公眾號:FPGA之禪
轉載于:https://www.cnblogs.com/liujinggang/p/9463589.html
總結
以上是生活随笔為你收集整理的【接口时序】2、Verilog实现流水灯及与C语言的对比的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 逆Laplace数值逆变换
- 下一篇: 【pyqt5】 读取numpy arrr