對於FPGA的結構原理,先不進行全面的了解,先能根據教程程序看得懂,寫得出來跑起來。慢慢的了解程序運行的原理,各種語法的使用。
今天對流水的程序有一個認識,熟悉軟件的使用,語法規則,原理。以正點原子的例程為例,代碼如下
1 // Created by: 正點原子 2 3 module flow_led( 4 input sys_clk , //系統時鍾 5 input sys_rst_n, //系統復位,低電平有效 6 7 output reg [3:0] led //4個LED燈 8 ); 9 10 //reg define 11 reg [23:0] counter; 12 13 // main code 14 //計數器對系統時鍾計數,計時0.2秒 15 always @(posedge sys_clk or negedge sys_rst_n) begin 16 if (!sys_rst_n) 17 counter <= 24'd0; 18 else if (counter < 24'd1000_0000) 19 counter <= counter + 1'b1; 20 else 21 counter <= 24'd0; 22 end 23 24 //通過移位寄存器控制IO口的高低電平,從而改變LED的顯示狀態 25 always @(posedge sys_clk or negedge sys_rst_n) begin 26 if (!sys_rst_n) 27 led <= 4'b0001; 28 else if(counter == 24'd1000_0000) 29 led[3:0] <= {led[2:0],led[3]}; 30 else 31 led <= led; 32 end 33 34 endmodule
這個代碼的功能是點亮流水燈。代碼的內容是:用一個定時器計時,到了0.2s就自動清零,否則自動+1計數。另一方面,判斷是否到了0.2s,到了就換另一個led亮。程序寫好后,燒寫在芯片里,會形成這樣的電路,功能是並行的。不像是stm32需要用C語言寫代碼,生成指令,cpu取指執行。FPGA會快一些。
代碼的整體結構,頭和尾有 module 和 endmodule 這是模塊的開始和結束。如果工程中有其他的功能代碼,寫在一個.v文件,也會有這個 module 。就是一個功能或者一個模塊卸載一個.v 文件,這樣便於管理。
input sys_clk , 可以理解為定義一個輸入類型的變量。
output reg [3:0] led, 可以理解為,定義一個輸出類型的變量,他占據了4個bit。
always @(posedge sys_clk or negedge sys_rst_n) begin 可理解為,復位了或者有新的始終進來就做下面的代碼。
led <= 4'b0001; 這個是賦值,和C語言有些不一樣。
4'b0001 是一個數,b:二進制表示,0001。 4' 是指這個數是4位的。
補充一下流水燈的形成 led[3:0] <= {led[2:0],led[3]}; 意思是:4位循環右移。即,3位放到0位,210位放到321位。led初始化0001,用形象的描述如下:
位 3210
初始值 0001
第一次 0010
第二次 0100
第三次 1000
....................
高電平點亮。形成流水燈。
然后這個程序實現了點燈。但是有一點,他是怎么控制的引腳呢,led是接在引腳少了,這里面也沒涉及到引腳啊。在下面這里:
通過這個圖可以看到,我們在工程里面的變量,需要“綁定”引腳。使使之一一對應,舉個例子led變量是4個bit的,把0 1 2 3 分別對應板子上led連接的引腳上,操作led就相當於操作引腳了。
幾天重新研究了一下這個流水燈的例程,覺得主要部分代碼還要再仔細的分析一下,時序代碼部分:
1 always @(posedge sys_clk or negedge sys_rst_n) begin 2 if (!sys_rst_n) 3 counter <= 24'd0; 4 else if (counter < 24'd1000_0000) 5 counter <= counter + 1'b1; 6 else 7 counter <= 24'd0; 8 end
其中關鍵字 always 是時序邏輯電路,在這個語句的下面,賦值的話用非阻塞賦值 “ <= ” ,非阻塞可以理解為:相互不干擾、不堵塞,是並行的。而阻塞賦值是串行的,相互之間有順序干擾的,一個一個的。
第一行解釋:如果有 sys_clk 時鍾信號上升沿或者 sys_rst_n 下降沿的話,就開始執行下面的代碼一直到 end 結束。
這是一個或的關系,兩個條件滿足一個即可。第一條 if 判斷是時鍾信號上升沿還是復位重啟,如果是復位重啟的話,就給 counter 計數器賦初值 0 。如果不是復位那就是時鍾洗好上升沿,此時,是一個脈沖過來了,執行下一個(類似單片機的下一條指令),那么就看計數器是否達到最大,沒達到的話,就 繼續 +1 ,達到了最大的話,就置為0。以便於重新計數。
流水燈部分:
1 always @(posedge sys_clk or negedge sys_rst_n) begin 2 if (!sys_rst_n) 3 led <= 4'b0001; 4 else if(counter == 24'd1000_0000) 5 led[3:0] <= {led[2:0],led[3]}; 6 else 7 led <= led; 8 end
這段代碼看着和上一段時序部分非常像。主要功能是,實現流水燈,根據上面的時序部分“定時器”,克制外部晶振50MHz,周期是 20ns。0.2s流動一次的話,計數器就要計數到 10^7 個,從0 到10^7-1,就是 24'd1000_0000。在流水燈部分,判斷計數器是否為24'd1000_0000 是的話就是到了0.2s。流動一次。
總結:
1. 掌握verilog的語法:
1.1 always 和 assign的區別
1.2 阻塞賦值和非阻塞賦值的區別
1.3 數據格式
1.4 程序代碼的框架