FPGA:verilogHDL簡單小結
FPGA(Field Programmable Gate Array)現場 可程式設計 邏輯閘 陣列;
是主要使用邏輯閘(LE)和查詢表(LUT)來生成邏輯電路的器件,還包含可程式設計邏輯,互連線,暫存器等資源;
veilog HDL(hardware description language)硬體描述語言是通過描述硬體來產生與之相對應的硬體電路的語言;是FPGA的主要語言之一;
硬體描述語言和軟體程式語言有什麼區別呢?
軟體程式語言編譯之後是工作在堆疊和記憶體上,是對堆疊和記憶體的資料處理,邏輯上資料的處理是單執行緒的;
硬體描述語言編譯之後得到的是硬體電路,是具體的物理電路連線,器件間訊號的傳遞可以實現並行執行;
1 verilog模組
verilog程式碼是以模組為最小模擬單位存在;可以將特定的邏輯功能封裝成模組,在頂層模組中對子模組例項化來呼叫子模組,組成完整的專案;
verilog模組主要由三部分組成:埠宣告A,內部訊號量宣告B,功能定義C;
每個.v檔案就是一個verilog模組,.v檔案的名字要與module中定義的名字相同;以下為兩個module舉例:
/***********頂層模組:trist1.v***********************/ /***********以下使用三態驅動器模組舉例*****************/ /***********以下的功能定義部分C1,C2,C3是並行執行的******/ module trist1 ( output out, //A:埠宣告; input in, //A: input enable //A:預設埠宣告變數是wire型; ); wire [2:0] con1; //B:內部訊號宣告; wire [7:0] con2; //B wire[7:0] con; //B reg [7:0] and_con; //B mytri tri_inst(out,in,enable); //C1:呼叫子模組mytri, assign con={con1[2:0],con2[4:0]} //C2:組合邏輯,always塊也屬於組合邏輯; and and_inst(and_sum,con1,con2); //C3:使用例項元件and, endmodule /******** 子模組 mytri.v***************************/ module mytri //子模組被頂層trist1.v呼叫時,會生成例項元件tri_inst;例項元件的名字必須具有唯一性; ( output out, input in, input enable ); assign out= enable? in:1'bz; //assign宣告組合邏輯;組合邏輯中被賦值的變數必須是net型;wire型屬於net型; endmodule
2資料型別
verilog共有19種資料型別,如large、medium、scalared、time、small、tri、trio、tri1、triand、trior、trireg、vectored、wand、wor等
其中reg、wire、interger、parameter為基本的四種資料型別;以下簡單說明一下;
2.1 parameter
parameter用來定義常量;先了解一下常量在verilog中的表達方式,然後在瞭解一下parameter如何使用;
2.1.1 常量的表示方式
/*常量由位寬,進位制,具體數值組成,中間不能加空格; **位寬表示當前常量用二進位制表示的具體位數, **進製表示後面的具體數值使用的進位制, **具體數值表示實際值;在數位電路中,x代表不定值,z代表高阻值; **常量的位寬和進位制預設時,預設是32位位寬,10進位制;*/ parameter NUM1 = 8'b11001000; // 8表示二進位制位數為8;'b表示為當前常量使用2進製表示; 1100 1000為具體數值; parameter NUM2 = 8'hc8; // 8表示為二進位制位數為8,'h表示當前常量使用16進製表示,c8為具體數值; parameter NUM3 = 4'b10x0; // 4表示二進位制位數為4,'b表示當前常量用2進製表示;10x0為具體數值,其中bit1為不定值; parameter NUM4 = 8'h4z; // 8表示二進位制位數為8,'h表示當前常量用16進製表示;4z為具體數值,其中bit[3:0]為高阻值; parameter NUM5 = 8'h4?; // 8表示二進位制位數為8,'h表示當前常量用16進製表示;4?為具體數值,其中bit[3:0]為高阻值;
2.1.2 parameter可以在模組內宣告,也可以在模組外宣告;這裡應該要補充一下原型和呼叫方式的,下面只有宣告原型的方式;
//在模組內部定義了一個常量OUT,作用域為當前模組;定義之後可以修改,也可以在被其他模組呼叫時修改值; module param1 ( input clk, output reg[3:0] sum ); parameter OUT= 4'b1100; always@(posedge clk) begin sum <= OUT; end endmodule //在模組外部定義模組使用的常量ADD2; module param2 #(parameter ADD2 = 2'd1) ( input clk, input [2:0]din, output reg [3:0]sum ); always@(posedge clk) begin sum <= din+ADD2; end endmodule
2.2 wire
wire屬於網路資料型別,相當於電路的物理連線;在邏輯綜合中,會被對映為真實的物理連線;
可以用作任何方程式的資料,組合邏輯或例項元件的輸出;
在assign語句中被賦值的變數必須是wire型別;例項元件被呼叫後的輸出必須賦值給wire型;預設預設輸入輸出為wire型;
wire clk; //定義了一個wire型別的變數clk; assign clk = sys_clk; //作為組合邏輯的輸出; wire [7:0] data; //定義了一個wire型別的變數data; ins instance1( .inst_output (data), //作為例項元件instance1的輸出; ... );
2.3 reg
reg是資料儲存單元的抽象,相當於晶片的暫存器;在邏輯綜合中,會被對映為真實的物理暫存器;
在always塊內被賦值的訊號都必須被定義成reg型別;reg資料預設值為x不定值;
reg[7:0] dataByte;always@(posedge clk) if(clk) dataByte <=revByte;//在always塊內被賦值的訊號都要為reg型的;
2.4 memory
通過組合reg型資料來表示儲存器memory,相當於晶片的ram、rom儲存器;儲存器深度只能為一維陣列,陣列下標同c一樣需要為常量;
上次寫程式碼的時候使用了一下,發現使用不了,不明所以;
reg[7:0] memFirst[255:0]; //定義了一個256位的暫存器組,其中每個暫存器組為8位的reg型; memFirst[3]= 0; //暫存器組的賦值需要給每個單元單獨賦值;
3 運算子
等式運算子:"=="和"!="為邏輯等式運算子,邏輯值可為不定值;而"==="和"!=="進行比較時對某些位的不定值x和高阻值z也進行比較,邏輯值非0即真;
阻塞賦值"=":阻塞賦值的意思是在當前塊中,第一條語句的執行會阻塞第二條語句的執行;在當前塊中語句順序賦值,由於電路時序約束可能會出現資料交換意外;
非阻塞賦值"<=":非阻塞賦值的意思是begin-end塊中,第一條語句的執行與否不會阻塞第二條語句的執行;塊中語句一起執行完之後再一起賦值,較為常用;
3.1 組合邏輯與阻塞賦值 " = "
組合邏輯電路表示輸出只與輸入有關,與電路當前狀態無關;根據電路特性,需要使用阻塞賦值;
對於阻塞賦值的訊號,如果訊號在跳變沿變化為狀態B,則跳變沿的取值為新的狀態B;其他訊號在跳變沿對其取樣的時候,取樣訊號為狀態B;
3.2 時序邏輯與非阻塞賦值" <= "
時序邏輯電路表示輸出不僅與輸入有關,還與電路當前狀態有關;根據電路特性,需要使用非阻塞賦值;
對於非阻塞賦值的訊號,如果訊號在跳變沿變化為狀態B,跳變沿的取值並沒有立即變化,而是為之前的狀態A;其他訊號在跳變沿對其取樣的時候,取樣訊號為狀態A;
4 testbench
quartus軟體具有多種模擬功能;可以檢視RTL檢視,可以編寫VWF波形檔案,可以硬體線上除錯,也可以通過modelsim等軟體模擬訊號;
硬體線上除錯:選單欄Tools >> SignalTap.. >>然後配置一下時鐘和檢視引數 >> 重新編譯工程載入進配置 >> 重新開啟SignalTap,載入執行即可檢視;
軟體模擬除錯:編寫好tb檔案 >> 選單欄Tools >> setting >> simulation >>配置模擬的testbench檔案 >>重新編譯後即可檢視RTL模擬訊號;
以下是testbencn檔案的基礎寫法舉例;
/**ipcore_tb.v 模組*****************************************/ `timescale 1ns/1ps //`時間單位/時間精度:時間單位表示#的延時單位,時間精度表示執行時間的誤差範圍; module ipcore_tb(); //當前模組為ipcore_tb,模組名等於檔名; reg sys_clk; reg sys_rst_n; initial begin //initial為testbench的語法功能,RTL模組中不可以使用; sys_rst_n = 1'b0; sys_clk = 1'b0; #40 sys_rst_n = 1'b1; //#40表示延時40ns; end
always #10 sys_clk <= ~sys_clk; //50MHz,20ns一個週期; inst instance_1 ( .sys_clk(sys_clk), .sys_rst_n(sys_rst_n) ); endmodule
5 簡單模組舉例
/**mux.v 二選一多路選擇器 ************************************** *** always@()中的*為萬用字元,表示當always塊內的輸入訊號發生變化,即執行當前塊;*/ module mux ( input in1, input in2, input sel, output reg[0:0] out ); always@(*) begin case(sel) 1'b0: out <= in1; 1'b1: out <= in2; default: out <= out; endcase end endmodule /**adder.v 加法器 **********************************************/ module adder ( input add1, input add2, output sum, output carry ); assign {carry,sum} = add1 + add2; endmodule /**decode.v 譯碼器:將輸入二進位制數,轉換成訊號輸出的邏輯器件;************ **3-8譯碼器:將輸入的3'bxxx轉換成二進位制後輸出8線;*********************** **8421BCD譯碼器:使用4個二進位制來表示10進位制數***************************/ module decode ( input reg[2:0] binary_in, output reg[7:0] out ); always@(*) begin case(binary_in) 3'b000: out <= 8'b0000_0001; 3'b001: out <= 8'b0000_0010; 3'b010: out <= 8'b0000_0100; 3'b011: out <= 8'b0000_1000; 3'b100: out <= 8'b0001_0000; 3'b101: out <= 8'b0010_0000; 3'b110: out <= 8'b0100_0000; 3'b111: out <= 8'b1000_0000; default: out <= out; endcase end endmodule //if語句的判斷是順序執行的,前面的判斷優先順序要稍微高於後面的語句; //case語句的判斷先判斷條件,執行語句的優先順序都是相同的;