日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > > ZYNQ
		


01verilog模塊的結構


verilog語法中最基本的元素就是模塊了,主要包括模塊聲明以及模塊內容。首先是模塊的聲明,具體的聲明結構如下


module Hello_World(<端口信號列表>); //注意這里要有分號 <邏輯代碼>endmodule


接下來是模塊內容,主要包括I/O口的說明,內部信號的說明,功能的定義三個結構。


(1)I/O口的說明


輸入端口: input[信號位寬-1:0] 端口1; input[信號位寬-1:0] 端口2; ...輸出端口: output[信號位寬-1:0] 端口1; output[信號位寬-1:0] 端口2; ...輸入/輸出端口: inout[信號位寬-1:0] 端口1; inout[信號位寬-1:0] 端口2; ...


(2)內部信號的說明


reg [width-1:0] 變量1,變量2,...;wire [width-1:0] 變量1,變量2,...;


(3)功能定義


主要有三種最基本的功能定義方法,分別是always,assign,initial。一個module里面可以寫多個always,assign,initial,這些功能在電路通電之后也是同時開始執(zhí)行的。


·initial用在仿真。主要有以下兩種功能,一方面可以在仿真開始時對各變量進行初始化,這個初始化過程不需要任何仿真時間,即在 0 ns 時間內,便可以完成初始化工作;另一方面可以用來生成激勵波形作為電路的測試仿真信號。


// 完成初始化initial begin areg = 0; //初始化一個寄存器 for(index=0;indexindex=index+1) memory[index] = 0; //初始化一個memoryend// 完成激勵波形的生成initial begin inputs = 8'b0000_0000; #10 inputs = 8'b0110_0101; #10 inputs = 8'b1111_0101;end


·assign用于實現組合邏輯,直接互聯不同的信號,或者變量值。


assign  = <變量或者常量>;// 右邊也可以使用 (判斷條件)?(判斷條件為真時的邏輯處理):(判斷條件為假時的邏輯處理)// 完成更加簡潔的if...else語句


·always語句可以實現組合邏輯,也可以實現時序邏輯。它是一直運行的,但是后面跟著的過程塊是否執(zhí)行,就要看它的觸發(fā)條件是否滿足了,這個觸發(fā)條件就寫在括號中。可以由信號的邊沿觸發(fā),也可以由信號的變化觸發(fā)。


// 所有信號變換都可以觸發(fā),是組合邏輯always@(*) begin ...end// 信號a與信號b的變化都可以觸發(fā),是組合邏輯always@(a or b) begin ...end// 時鐘的上升沿或者復位信號的下降沿可以觸發(fā),是時序邏輯always@(posedge clk or negedge rst_n) begin ...end


(4)引用模塊


在引用時既可以按照模塊定義的端口順序來連接,不用標明原模塊定義時規(guī)定的端口名,也可以在引用時采用“.”符號,這樣就不用完全按照模塊定義的端口順序來連接了,一般還是使用“.”來引用比較好,這樣邏輯比較清晰。


...MyDesign M1(.sin(in),.pout(out));...


MyDesign 是已經定義的另一個模塊,M1是自己取的一個名字,sin與pout是MyDesign模塊里面端口的名字,in與out是在正在寫的模塊中定義的兩個信號,這兩個信號分別連接到sin與pout端口。


02數據類型


數據主要可以分為兩種類型,一種是常量類型的數據,另一種是變量類型的數據。


(1)常量類型的數據


·整數:二進制(b),十進制(d),十六進制(h)。


·x和z值:

·x代表未定值:不確定狀態(tài)為高還是為低,旨在告訴綜合工具設計者不關心它的電平是多少,是0是1都可以。

·z代表高阻值:輸出引腳與內部電路斷開,不導通,其電平隨外部電平高低而定,表示設計者不驅動這個信號。

·一個x可以用來定義十六進制數的4位二進制數的狀態(tài)或者二進制數的1位。建議在寫case語句中使用這些值。

·負數:在位寬表達式前加一個減號就可以定義一個負數。

·下劃線:主要用來分隔開數的表達式以提高程序的可讀性,它只能用在具體的數字之間。


<位寬><進制><數字>8'b10101100 // 位寬為8的數的二進制表示,'b表示二進制8'ha2 // 位寬為8的數的十六進制表示,'h表示十六進制<數字>10 //默認采用十進制,并且數字的位寬采用默認位寬(與機器系統(tǒng)相關,最少32位) 4'b10x0 //位寬為4的二進制數,從低位數起第2位是不定值8'h4x //位寬為8的二進制數,其低四位值為不定值 -8'd5 //這個表達式代表5的補數 16'b1010_1011_1111_1010


·參數:使用parameter來定義常量,注意在引用實例的時候可以通過參數的傳遞來改變定義時規(guī)定的參數。


//parameter可以直接定義一系列常量參數parameter a=2,b=2.3,c=a-1;//可以通過傳參修改module中定義的parametermodule Decode(A,B); parameter width=1,polarity=1; ...endmodulemodule Decode_top; wire[3:0] A1; wire[3:0] B1; wire[3:0] A2; wire[3:0] B2; Decode #(4,0) D1(A1,B1); // D1實例引用的width=4,polarity=0 Decode #(5) D2(A2,B2); // D2實例引用的width=5,polarity=1endmodule


(2)變量類型的數據


·wire型:常用來表示用以assign關鍵字指定的組合邏輯信號。verilog程序模塊中輸入、輸出信號類型默認時自動定義為wire型。wire型信號可以用做任何方程式的輸入,也可以用作assign語句的輸出。

·reg型:常用來表示always模塊內的指定信號,在always模塊內被賦值的每一個信號都必須定義成reg型。reg的默認初始值是不定值。

·memory型:從編程角度可以理解成一個多維數組,從物理角度可以理解成RAM型存儲器或者ROM存儲器,從實現角度可以理解成是reg型數據的擴展。


wire [3:0] a,b; //定義了兩個4位的wire型數據reg [3:0] a,b; //定義了兩個4位的reg型數據reg [n-1:0] memory [m-1:0] //[n-1:0]定義存儲器中每一個存儲單元的大小,[m-1:0]定義了 //存儲器中存儲單元的個數也可以理解成各存儲單元的地址memory[3] = 0; //注意對memory中的存儲單元進行讀寫操作的時候必須指定該單元在存儲器中的地址



03運算符及表達式


·算術運算符


+ //加- //減* //乘/ //除 % //求模


進行整數除法運算時結果值略去小數部分,只留下整數部分,取模運算要求%兩側均為整數。


·位運算符


~ //取反& //按位與| //按位或^ //按位異或~^ //按位同或


不同長度的數據在進行位運算的時候,系統(tǒng)會自動地將兩者按右端對齊,位數少的操作數會在相應的高位用0填滿。


·邏輯運算符


&& //邏輯與|| //邏輯或! //邏輯非


·關系運算符


a//小于a>b //大于a<=b //小于等于a>=b //大于等于


·等式運算符


== //等于!= //不等于


·移位運算符


a>>n //a右移n位a<//a左移n位


兩種移位運算都用0來填補移出的空位。


·位拼接運算符


{信號1的某幾位,信號2的某幾位,...}{a,b[3:0],w,3'b101}{4{w}} //位拼接可以用來簡化表達式,這樣寫等同于{w,w,w,w}{b,{3{a,b}}} //這樣寫等同于 {b,a,b,a,b,a,b}


位拼接表達式不允許存在沒有指明位數的信號。


·縮減運算符


reg [3:0] B;reg C;C = &B; // 相當于 C=((B[0] & B[1])&B[2])&B[3]


與之前的位運算不同,縮減運算是對單個操作數各位的邏輯操作,最終的運算結果是1位二進制數。


04條件語句及循環(huán)語句


(1)條件語句


條件語句主要有兩種寫法,一種是if else的寫法,另一種是case的寫法。


// if else 的寫法if(a==2'b00) begin <具體邏輯>endelse if(a==2'b01) begin <具體邏輯>endelse begin <具體邏輯>end// case 的寫法case(a) 2'b00:<具體邏輯> 2'b01:<具體邏輯> default:<具體邏輯>endcase


還有用法與case類似的casex與casez,這兩者可以用來處理比較過程中不必考慮的情況。其中casez語句用來處理不必考慮高阻值z的比較過程,casex語句則將高阻值z和不定值都視為不必關心的情況。所謂不必關心的情況就是說,在表達式進行比較時不將該位的狀態(tài)考慮在內。


(2)循環(huán)語句


總體來說循環(huán)語句用到的不是很多,這里就列舉兩個最常用的forever和for循環(huán)。


·forever循環(huán)語句常用來產生周期性的波形,用來作為仿真信號,必須要寫在initial塊中。

·for循環(huán)語句的一般形式是for(<變量名>=<初值>;<判斷表達式>;<變量名>=<新值>)。for可以綜合的,for幾次就相當于把你的電路復制幾次。


initial begin clk = 0; forever begin clk = ~clk; #5; endend initial begin counter2 = 'b0 ; for (i=0; i<=10; i=i+1) begin #10 ; counter2 = counter2 + 1'b1 ; endend


05塊語句


·順序塊:采用begin end語句,塊內的語句是按照順序執(zhí)行的,前面的語句執(zhí)行完才輪到后面的語句執(zhí)行,每條語句的延遲時間是相對于前一條語句的仿真時間定的。

·并行塊:采用fork join語句,塊內的語句是并行執(zhí)行的,每條語句的延遲時間是相對于程序進入塊內的時間。并行塊是不可綜合的,只能用在仿真中。


module begin_tb(); reg [7:0] r; reg clk;  initial begin:begin_tb //這里可以將塊名寫上 clk = 0; #50 r = 8'h35; #30 r = 8'h12; #40 r = 8'h41; end always #5 clk = ~clk;endmodule module fork_tb(); reg [7:0] r; reg clk;  initial fork:fork_tb //這里可以將塊名寫上 clk = 0; #50 r = 8'h35; #30 r = 8'h12; #40 r = 8'h41; join always #5 clk = ~clk;endmodule



上面是begin end的仿真時序圖,下面是fork join的仿真時序圖。從仿真時序圖中可以明顯的看到,begin end是串行的時序而fork join是并行的時序。


·生成塊:采用generate endgenerate語句,這一聲明語句方便參數化模塊的生成。當對矢量中的多個位進行重復操作時,或者當進行多個模塊的實例引用的重復操作時,或者在根據參數的定義來確定程序中是都應該包括某段verilog代碼的時候,使用生成語句能夠大大簡化程序的編寫過程。


1、for:在generate中的應用主要是用來減少重復操作。


//-------------module-------------//// 頂層模塊包含N個半加器module top(a,b,sum,cout); parameter N=2; input [N-1:0] a, b; output [N-1:0] sum, cout;  // 聲明一個暫時的循環(huán)變量 genvar i;  // 生成N次 generate for (i = 0; i < N; i = i + 1) begin half_add u0(a[i], b[i], sum[i], cout[i]); end endgenerateendmodule // 定義一個半加器module half_add(a, b, sum, cout); input a,b; output sum,cout;  assign sum  = a ^ b; assign cout = a & b;endmodule//-------------testbench-------------//`timescale 1ns / 1nsmodule top_tb; parameter N = 2; reg [N-1:0] a, b; wire [N-1:0] sum, cout;  top instance1( .a(a), .b(b), .sum(sum), .cout(cout));  initial begin a <= 0; b <= 0;  #10 a <= 'h2; b <= 'h3; #20 b <= 'h4; #10 a <= 'h5; endendmodule



具體生成的RTL圖以及仿真結果如上圖所示。


2、if:在generate中主要用來根據參數的定義來確定程序。


//-------------module-------------//module top(a, b, sel, out); input a,b,sel; output out; parameter USE_CASE = 0; // 使用 generate 塊選擇使用 mux_case 或者 mux_assign generate if (USE_CASE) mux_case m1 (.a(a), .b(b), .sel(sel), .out(out)); else mux_assign m2 (.a(a), .b(b), .sel(sel), .out(out)); endgenerate endmodule// Design #1: 使用assignmodule mux_assign ( input a, b, sel, output out); assign out = sel ? a : b; // 這個display可以在仿真時標明在用哪一個design initial $display ("mux_assign is instantiated");endmodule  // Design #2: 使用casemodule mux_case (input a, b, sel, output reg out); always @ (a or b or sel) begin case (sel) 0 : out = a; 1 : out = b; endcase end // 這個display可以在仿真時標明在用哪一個design initial $display ("mux_case is instantiated");endmodule//-------------testbench-------------//`timescale 1ns / 1nsmodule top_tb; reg a, b, sel; wire out; integer i; // 使用 USE_CASE 選擇用哪個 design initial  $display("USE_CASE = %0d",0); top #(.USE_CASE(0)) u0 ( .a(a), .b(b), .sel(sel), .out(out));  initial begin a <= 0; b <= 0; sel <= 0;  for (i = 0; i <= 2; i = i + 1) begin #10 a <= $random; b <= $random; sel <= $random; $display ("i=%0d a=0x%0h b=0x%0h sel=0x%0h out=0x%0h", i, a, b, sel, out); end endendmodule



上面是仿真結果在tcl的輸出,可以看到使用不同的USE_CASE可以選取不同的design。


3、case:在generate中主要用來根據參數的定義來確定程序。


//-------------module-------------//module top (a, b, cin, sum, cout); input a,b,cin; output sum,cout;  parameter ADDER_TYPE = 1;  generate case(ADDER_TYPE) 0 : ha u0 (.a(a), .b(b), .sum(sum), .cout(cout)); 1 : fa u1 (.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout)); endcase endgenerateendmodule// Design #1: 半加器module ha (input a, b, output reg sum, cout); always @ (a or b) {cout, sum} = a + b;  initial $display ("Half adder instantiation");endmodule // Design #2: 全加器module fa (input a, b, cin, output reg sum, cout); always @ (a or b or cin) {cout, sum} = a + b + cin;  initial $display ("Full adder instantiation");endmodule //-------------testbench-------------//`timescale 1ns / 1nsmodule top_tb; reg a, b, cin; wire sum, cout;  initial  $display("ADDER_TYPE = %0d",0); top #(.ADDER_TYPE(0)) u0 (.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));  initial begin a <= 0; b <= 0; cin <= 0;  $monitor("a=0x%0h b=0x%0h cin=0x%0h cout=0%0h sum=0x%0h", a, b, cin, cout, sum);  for (int i = 0; i<=2; i = i + 1) begin #10 a <= $random; b <= $random; cin <= $random; end endendmodule



上面是仿真結果在tcl的輸出,可以看到使用不同的ADDER_TYPE可以選取不同的design。


06任務


任務要寫在task endtask中,有點像matlab中函數這個概念,其中可以有input、output、inout端口作為出入口參數。注意任務中不能出現initial語句和always語句, 但任務調用語句可以在initial語句和always語句中使用(這些都是對static task來說的,automatic task略有不同,一般默認是static task)。


module top_tb; reg [7:0] x, y, z;  task sum; input [7:0] a, b; output [7:0] c; begin c = a + b; endendtask  initial begin:test {x,y} = {8'd2,8'd3};  sum (x, y, z); #10 {x,y} = {8'd5,8'd1}; sum (x, y, z);end endmodule


x 和 y 是輸入值,最后計算結果存儲在 z 中。



07常用的系統(tǒng)任務


(1)格式化輸出函數


·$display


reg [3:0] rval;initial begin rval = 3; $display("rval = %h hex, rval = %d decimal, rval = %b binary", rval, rval, rval); $display("current scope is %m"); // 輸出等級層次的名字 $display("%s", "Hello World");  //在%和表示輸出格式的字符之間插入一個0自動調整顯示輸出的數據寬度  $display("Simulation time is %0d", $time);end



·$strobe


與display不同,它可以確保所有在同一時鐘沿賦值的其它語句在執(zhí)行完畢之后才顯示數據。


reg [3:0] a,b;wire [4:0] y = a + b;// 采用display的方式輸出initial begin a = 3; b = 2; $display("$display: time =%0d, a= %d, b=%d, y= %d ",$time ,a, b, y); #10 a = 4; $display("$display: time =%0d, a= %d, b=%d, y= %d ",$time,a, b, y); b = 5; $display("$display: time =%0d, a= %d, b=%d, y= %d ",$time ,a, b, y); #10;end// 采用strobe的方式輸出initial begin a = 3; b = 2; $strobe("$strobe: time =%0d, a= %d, b=%d, y= %d ",$time ,a, b, y); #10 a = 4; $strobe("$strobe: time =%0d, a= %d, b=%d, y= %d ",$time,a, b, y); b = 5; $strobe("$strobe: time =%0d, a= %d, b=%d, y= %d ",$time ,a, b, y); #10;end



從上面演示的例子可以看出,當打印輸出連續(xù)賦值語句或module例化的輸出結果時,在當前的時間點內,$display無法輸出當前運算結果,但 $strobe卻可以顯示組合邏輯結果。因此, 如果希望在當前時間點內打印連續(xù)賦值語句或module例化的輸出結果推薦使用$strobe, 而其他情況下使用$display可以顯示更多的細節(jié)。


·$monitor


一般在initial塊中調用,可以不間斷地對所設定的信號進行監(jiān)視。一般與時間度量系統(tǒng)函數$time一起使用,具體的應用實例在下面時間度量系統(tǒng)函數給出。


(2)時間度量系統(tǒng)函數


·$time


該函數可以返回一個64位整數來表示當前的仿真時刻,該時刻受時間尺度比例的影響,例如在下面的例子中,時間尺度是10ns,16ns與32ns就要變成1.6與3.2,同時因為$time的輸出要是整數,因此實際的輸出為2和3。


`timescale 10ns / 1nsmodule top_tb (); reg set;initial begin $monitor("time=%0d",$time," ","set=",set); #1.6 set = 0; #1.6 set = 1;endendmodule




(3)仿真暫停與退出函數


·$finish


該系統(tǒng)函數可以退出仿真器,返回主操作系統(tǒng)結束仿真過程,并且可以輸出當前的仿真時刻和位置。




·$stop


該系統(tǒng)函數暫停模擬并將模擬器置于交互模式。


(4)文件讀取到寄存器函數


·readmemh


$readmemh("<數據文件名>",<存儲器名>);$readmemh("<數據文件名>",<存儲器名>,<起始地址>);$readmemh("<數據文件名>",<存儲器名>,<起始地址>,<終止地址>);


·<數據文件名> 是指向一個文本文件,用來保存仿真的數據。每一行代表一個十六進制的數據。

·<存儲器名> 為仿真文件中例化的存儲器的名稱。

·<起始地址>,<終止地址> 指示將文本文件中的數據存儲到存儲器的位置段。


`timescale 1ns / 1psmodule top_tb(); reg clk = 0;always clk = #10 ~clk;reg [7:0] ram[0:127];localparam FILE_NAME = "../../../led_sim.sim";initial begin $readmemh (FILE_NAME, ram, 2, 8); //從第二個16進制數據讀起,但是由于ram是8位的因此每次存入8endinteger i;initialbegin #20; for(i = 0; i < 16; i = i + 1) $display("ram[%02d] = 0x%h ", i, ram[ i ] ); #8000; $stop;end endmodule



上圖左邊是文件中的數據,右邊是仿真中的輸出值,可以看到ram[00],ram[01],ram[09]-ram[15]的值都是0xxx(未初始化的值),真正初始化的只有ram[02]- ram[08]。


(5)隨機數生成函數


·$random


一般用法是$random%b,其中b>0,它給出了一個范圍在(-b+1):(b-1)中的隨機數。也可以通過位拼接操作{$random}%b,生成0:(b-1)之間的隨機數。


reg [23:0] rand;rand = $random%60; // 生成一個范圍在 [-59,59] 之間的隨機數rand = {$random}%60;// 生成一個范圍在 [0,59] 之間的隨機數


08編譯預處理


·宏定義 `define


`define WORDSIZE 8modulereg [`WORDSIZE-1:0] data; //相當于定義[7:0],注意在引用時也要加`...endmodule


·文件包含 `include


`include相當于將被引用的文件全部囊括進引用文件中??梢詫⒁恍┏S玫暮甓x命令或任務(task)組成一個文件,然后用`include命令將這些宏定義包含到自己所寫的源文件中。


·時間尺度 `timescale


該命令的格式如下:`timescale<時間單位>/<時間精度>,時間單位參量用來定義模塊中仿真時間和延遲時間的基準單位,時間精度參量用來定義該模塊仿真時間的精確程度。


`timescale 10ns/1nsparameter d = 1.55;#d //時間單位為10ns,時間精度為1ns,1.55*10=15.5,再根據精度得到16,因此#d實際上是延時16ns
本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內容真實性等。需要轉載請聯系該專欄作者,如若文章內容侵犯您的權益,請及時聯系本站刪除。
關閉