1. 程式人生 > 實用技巧 >FPGA:verilogHDL簡單小結

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語句的判斷先判斷條件,執行語句的優先順序都是相同的;