一、module
?:
? ? ? ?定義:?是構建數字系統的基本單元,用于封裝電路的結構和行為。它可以表示從簡單的邏輯門到復雜的處理器等任何硬件組件。
1.?module
?的基本定義
module 模塊名 (端口列表);// 端口聲明input [位寬] 輸入端口1;output [位寬] 輸出端口1;inout [位寬] 雙向端口1; // 雙向I/O(如三態總線)// 內部信號聲明wire [位寬] 內部連線;reg [位寬] 寄存器;// 邏輯功能實現// - 連續賦值語句(assign)// - 過程塊(always)// - 模塊實例化// - 任務(task)和函數(function)endmodule
- 特點:
- 硬件抽象:每個模塊對應實際電路的一個功能單元。
- 層次性:模塊可以嵌套實例化,構建復雜系統。
- 端口隔離:模塊通過端口與外部通信,內部實現對外部透明。
2. 簡單模塊示例
示例 1:2 輸入與門
module and_gate (input a, // 輸入端口ainput b, // 輸入端口boutput y // 輸出端口y
);assign y = a & b; // 連續賦值語句,實現邏輯與endmodule
示例 2:帶寄存器的計數器
module counter (input clk, // 時鐘信號input reset, // 復位信號(高有效)input enable, // 使能信號output reg [7:0] count // 8位計數器輸出(聲明為reg類型)
);always @(posedge clk) beginif (reset)count <= 8'b0; // 復位時計數器清零else if (enable)count <= count + 1; // 使能時每個時鐘周期加1
endendmodule
3. 模塊實例化與連接
模塊通過實例化(Instantiation)嵌入到其他模塊中,形成層次化設計。
示例 3:使用 and_gate 構建更復雜的電路
module top_module (input x, y, z,output f
);wire w1, w2; // 內部連線// 實例化and_gate模塊(兩個與門)and_gate u1 (.a(x), // 連接到輸入x.b(y), // 連接到輸入y.y(w1) // 輸出連接到內部信號w1);and_gate u2 (.a(w1), // 輸入連接到u1的輸出w1.b(z), // 輸入連接到輸入z.y(w2) // 輸出連接到內部信號w2);// 最終輸出assign f = w2;endmodule
4. 參數化模塊
使用?parameter
?可以定義可配置的常量,增強模塊的靈活性。
示例 4:參數化的計數器
module param_counter #(parameter WIDTH = 8, // 計數器位寬,默認8位parameter INIT = 0 // 初始值,默認0
) (input clk, reset, enable,output reg [WIDTH-1:0] count
);always @(posedge clk) beginif (reset)count <= INIT; // 復位時加載初始值else if (enable)count <= count + 1;
endendmodule
assign
?是連續賦值語句,用于描述組合邏輯(無記憶,輸出實時跟隨輸入變化)。- 功能:將輸入?
a
?和?b
?相加,結果賦值給輸出?c
。Verilog 會自動處理位寬擴展(2 位 + 2 位 = 3 位,匹配?c
?的位寬)。 -
模塊實例化部分(
adder u1(.a(in1), .b(in2), .c(in3));
)這部分是復用已定義的模塊(
adder
),并將其端口與外部信號連接,類似 “在電路板上焊接芯片并接線”。 adder
:要實例化的模塊名(必須和前面定義的?module adder
?一致)。u1
:實例名(自定義,同一模塊可實例化多次,如?u2
、u3
?等,用于區分不同實例)。.端口名(外部信號)
:命名端口映射,把模塊的內部端口和外部信號一一對應:.a(in1)
:模塊的?a
?端口 ? 外部信號?in1
(in1
?需是 2 位信號)。.b(in2)
:模塊的?b
?端口 ? 外部信號?in2
(in2
?需是 2 位信號)。.c(in3)
:模塊的?c
?端口 ? 外部信號?in3
(in3
?需是 3 位信號)。
- 外部信號的要求
- 聲明:
in1
、in2
、in3
?必須在當前作用域(比如上層模塊)中提前聲明,例如:verilog
-
-
wire [1:0] in1, in2; // 2位輸入信號(wire類型,因為是組合邏輯連接) wire [2:0] in3; // 3位輸出信號(wire類型,因為模塊輸出是wire)
-
二、數據類型及常量變量
1、Verilog HDL有四種基本的值
(1)其中x和z不區分大小寫;
(2)z也可以使用?表示,雖然不懂為什么不是表示未知……
2、Verilog HDL三類常量
?(1)整型:直接用數字序列表示的整數,默認采用十進制。
??????????????1,-2;
- 特點:書寫簡單,但無法明確指定數值的位寬和進制。
- 注意:負數只能用十進制表示,不能用基數表示法表示負數。
B、基數表示:<位寬>’<進制><數字>?
??
b
?或?B
:二進制o
?或?O
:八進制d
?或?D
:十進制h
?或?H
:十六進制
?
下劃線(_):可以在數字中插入下劃線,提高可讀性,但不影響數值
- 無符號數:默認情況下,整型常量是無符號數。
- 有符號數:在常量前加負號(-),但負數只能用十進制表示。
-
-8'd10 // 錯誤!不能用基數表示法表示負數 -10 // 正確!十進制負數
- 不定值(x)和高阻值(z):在二進制、八進制和十六進制中,可以使用?
x
?或?z
?表示不定值或高阻值。
(2)實數型:實數型常量用于表示帶有小數部分的數值,有兩種表示形式:
1. 十進制形式
直接用數字和小數點表示的實數。
3.14159 // 圓周率
-0.001 // 負數
0.5 // 小數
5. // 等同于5.0
2. 科學計數法形式
使用指數表示的實數,格式為:<數字>e<指數>
?或?<數字>E<指數>
。
3.6e4 // 3.6×10?,即36000
-1.23e-2 // -1.23×10?2,即-0.0123
5E7 // 5×10?,即50000000
3. 實數與整數的轉換
- 實數賦值給整數變量時,會自動截斷小數部分,只保留整數部分。
integer a;
a = 3.9; // a的值為3(截斷小數部分)
- 整數賦值給實數變量時,會自動轉換為實數。
real b;
b = 10; // b的值為10.0(轉換為實數)
三、字符串型常量
字符串型常量用于表示一串字符,用雙引號(")括起來。
"Hello, World!" // 字符串常量
"Verilog HDL" // 包含空格的字符串
"12345" // 包含數字的字符串
1. 轉義字符
在字符串中,可以使用轉義字符表示特殊字符。
"\n" // 換行符
"\t" // 制表符
"\"" // 雙引號
"\\" // 反斜杠
2. 字符串的存儲
- 字符串在 Verilog 中被存儲為一系列的 ASCII 字符,每個字符占用 8 位(1 字節)。
- 字符串的長度由字符的個數決定,不包括雙引號。
"ABC" // 長度為3的字符串,占用24位(3×8)
3. 字符串操作
- 字符串可以賦值給 reg 類型的變量,但變量的位寬必須足夠容納字符串的所有字符。
reg [7:0] str [0:2]; // 聲明一個3個字符的字符串數組
initial beginstr[0] = "A"; // 或 8'd65 或 8'h41str[1] = "B"; // 或 8'd66 或 8'h42str[2] = "C"; // 或 8'd67 或 8'h43
endinitial begin$sformatf(str, "%s", "ABC"); // 格式化字符串到數組$display("str = %s", str); // 輸出:str = ABC
end
reg [7:0]
:每個元素是 8 位寬的寄存器(對應 1 個 ASCII 字符)。str [0:2]
:數組索引范圍是?0
?到?2
,共?3 個元素。- 等效理解:這是一個?3 行 8 列?的二維數組,可存儲 3 個 ASCII 字符。
- Verilog 不直接支持字符串的拼接、比較等操作,需要使用系統任務或函數。
$display("The value is %d", 10); // 系統任務輸出字符串和變量
四、WIRE類型
(1)wire類型是一種基本的數據類型,用于表示硬件電路中的物理連線。它是組合邏輯設計的核心元素,下面詳細介紹其定義、用法和注意事項:
一、核心定義
? wire
?用于建模信號連接,類似電路板上的導線,主要特點:
- 信號載體:用于傳遞和連接模塊、門電路之間的信號。
- 被動賦值:不能存儲值,必須由驅動源(如?
assign
、門級實例、模塊輸出)持續驅動。 - 默認狀態:若無驅動,值為高阻態?
z
。 - 硬件對應:直接映射到物理連線或組合邏輯輸出。
二、聲明語法
wire [位寬] 信號名1, 信號名2, ...;
- 位寬(可選):指定信號的二進制位數,默認為 1 位。
- 示例:
wire clk; // 1位wire信號(時鐘)
wire [7:0] data; // 8位向量(數據總線)
wire [3:0] addr, enable; // 同時聲明多個wire
三、常見用法
1.?連續賦值(assign
?語句)
通過表達式持續驅動?wire
:
wire a, b, c;
assign c = a & b; // c等于a和b的邏輯與wire [3:0] x, y, z;
assign z = x + y; // z等于x和y的和(組合邏輯加法)
2.?門級實例化連接
作為門電路的輸入 / 輸出:
wire a, b, c;
and gate1(c, a, b); // 與門實例化,輸出到c
3.?模塊間信號連接
連接不同模塊的端口:
module top;wire clk, rst, data_in, data_out;// 實例化時鐘生成模塊clock_gen clk_gen (.clk(clk), .rst(rst));// 實例化數據處理模塊data_proc proc (.clk(clk), .rst(rst), .in(data_in), .out(data_out));
endmodule
4.?三態門驅動
用于總線共享場景:
wire data_bus;
wire enable;// 三態緩沖器
assign data_bus = enable ? data_out : 1'bz; // 使能時輸出數據,否則高阻
五、REG類型
? ?REG
?是一種基本的數據類型,主要用于存儲數值,對應硬件中的觸發器(Flip-Flop)或鎖存器(Latch)。下面詳細介紹其定義、用法和注意事項:
一、核心定義
reg
?用于表示存儲元件,具有以下特點:
- 存儲能力:能保持當前值,直到被新值覆蓋。
- 主動賦值:通過過程塊(
initial
?或?always
)賦值。 - 默認狀態:初始值為不定態?
x
(除非顯式初始化)。 - 硬件對應:綜合后通常映射為觸發器(時序邏輯)或鎖存器(組合邏輯)。
?二、聲明語法
reg [位寬] 信號名1, 信號名2, ...;
- 位寬(可選):指定信號的二進制位數,默認為 1 位。
示例:
reg clk; // 1位reg信號
reg [7:0] data; // 8位向量(可存儲0~255)
reg [3:0] count; // 4位計數器
三、常見用法
1.?時序邏輯(觸發器)
在時鐘邊沿觸發的?always
?塊中使用:
reg [3:0] counter;always @(posedge clk or negedge rst_n) beginif (!rst_n) // 異步復位(低電平有效)counter <= 4'b0;else if (en) // 使能時計數counter <= counter + 1;
end
2.?組合邏輯(鎖存器)
在電平敏感的?always
?塊中使用(需謹慎,易產生鎖存器):
reg data_out;always @(*) beginif (sel)data_out = data_in; // 組合邏輯賦值(=)// 缺少else分支 → 綜合出鎖存器(不推薦)
end
3.?初始化(initial
?塊)
僅用于仿真,不可綜合:
reg [7:0] mem [0:15]; // 存儲器數組initial begin// 初始化存儲器內容mem[0] = 8'hAA;mem[1] = 8'h55;// ...
end
?4.?函數和任務中的變量
function [7:0] multiply(input [3:0] a, b);reg [7:0] result; // 函數內部變量beginresult = a * b;multiply = result;end
endfunction
?四、與?wire
?的對比
特性 | reg | wire |
---|---|---|
存儲能力 | 有存儲能力(保持值直到下一次賦值) | 無存儲,僅傳遞信號 |
賦值方式 | 只能在?initial ?或?always ?塊中賦值 | 由?assign 、門或模塊驅動 |
適用場景 | 時序邏輯(如觸發器)、變量存儲 | 組合邏輯連接、模塊間通信 |
默認值 | 不定值?x | 高阻態?z |
賦值符號 | 非阻塞賦值(<= )或阻塞賦值(= ) | 僅連續賦值(assign ) |
硬件對應 | 觸發器、鎖存器等存儲元件 | 物理連線、組合邏輯輸出 |
五、使用注意事項
(1)在always語句和initial語句中的賦值對象只能是reg類型,reg類型信號也只能在always語句和initial語句中被賦值,
(2)所以,always、initial塊外的賦值對象和連線用wire型信號,always、initial塊內的賦值對象用reg型
六、運算符與運算表達式
1、算術運算符
用于數值計算,支持整數和定點數運算。
運算符 | 描述 | 示例 |
---|---|---|
+ | 加法 | c = a + b; |
- | 減法 | c = a - b; |
* | 乘法 | c = a * b; |
/ | 除法 | c = a / b; (整數除法) |
% | 取模(取余) | c = a % b; |
** | 乘方(SystemVerilog) | c = a ** b; (a 的 b 次方) |
2、邏輯運算符
符號:=
(阻塞賦值)、<=
(非阻塞賦值)
功能:將值賦給變量,核心區別在于執行時序。
類型 | 語法 | 應用場景 | 關鍵特性 |
---|---|---|---|
阻塞賦值 | reg_a = reg_b; | 組合邏輯(always @(*) ) | 立即賦值,語句順序影響結果(如?a=1; b=a; ?中b 會立即等于 1)。 |
非阻塞賦值 | reg_a <= reg_b; | 時序邏輯(always @(posedge clk) ) |
3、關系運算符
符號:>
(大于)、<
(小于)、>=
(大于等于)、<=
(小于等于)
功能:比較兩數大小,結果為1 位布爾值(1
/0
/X
)。
示例 | 說明 |
---|---|
a > b | 按無符號數比較(Verilog 默認,SystemVerilog 可通過signed 聲明有符號比較)。 |
a <= b | 若操作數含X /Z ,結果可能為X (如?4'b10x0 <= 4'b1000 ?結果為X )。 |
4、相等運算符
符號:==
(邏輯相等)、!=
(邏輯不等)
功能:比較兩數是否相等(==
)或不等(!=
),不嚴格匹配X
/Z
。
示例 | 說明 |
---|---|
a == b | 僅比較0 /1 位,若含X /Z ,結果可能為X (如?4'b10x0 == 4'b1000 ?結果為X )。 |
a != b | 只要有一位0 /1 不同,結果為1 ;含X 時結果可能為X 。 |
擴展:=== (全等,嚴格匹配X /Z )和!== (非全等),圖中未列但常用。 |
5、邏輯運算符
符號:&&
(邏輯與)、||
(邏輯或)、!
(邏輯非)
功能:對布爾值(非零視為真
,零為假
)進行邏輯運算,結果為1 位。
示例 | 說明 |
---|---|
(a>0) && (b<5) | 兩邊均為真 (非零)時,結果為1 ;否則為0 。 |
!(a==0) | 若a 非零,結果為1 ;否則為0 。 |
與位運算符的區別: |
- 邏輯運算:結果 1 位;位運算(如
&
/|
)逐位處理,結果位寬同操作數。 - 示例:
wire log = (a && b);
(邏輯與) vs?wire bit = a & b;
(位與)。
6、位運算符
符號:~
(按位取反)、&
(按位與)、|
(按位或)、^
(按位異或)
功能:對操作數逐位進行邏輯運算,結果位寬與操作數一致。
運算符 | 示例 | 說明 | |||
---|---|---|---|---|---|
~ | ~4'b1010 = 4'b0101 | 每一位取反(1→0 ,0→1 )。 | |||
& | 4'b1010 & 4'b1100 = 4'b1000 | 逐位與(1&1=1 ,否則0 )。 | |||
` | ` | `4'b1010 | 4'b1100 = 4'b1110` | 逐位或(`0 | 0=0,否則 1`)。 |
^ | 4'b1010 ^ 4'b1100 = 4'b0110 | 逐位異或(相同為0 ,不同為1 )。 | |||
擴展:同或(~^ ?或?^~ ),即異或非(如?4'b1010 ~^ 4'b1100 = 4'b1001 )。 |
7、移位運算符
符號:<<
(邏輯左移)、>>
(邏輯右移)
功能:將操作數按位移動,空位補 0(邏輯移位)。
運算符 | 示例 | 說明 |
---|---|---|
<< | 4'b1010 << 1 = 4'b0100 | 左移 1 位,低位補 0(值變為原來的 2 倍,注意位寬截斷)。 |
>> | 4'b1010 >> 1 = 4'b0101 | 右移 1 位,高位補 0(值變為原來的 1/2,整數除法)。 |
注意: |
- Verilog 的
>>
對有符號數也會補 0(邏輯移位),SystemVerilog 的>>>
才是算術移位(補符號位)。 - 移位位數需為常量(綜合要求),如?
a << 2
(合法),a << b
(非法,b
需是常量)。
8、條件運算符
符號:?:
(三元運算符)
語法:condition ? expr1 : expr2
功能:條件為真
(非零)時,結果為expr1
;否則為expr2
。
示例 | 說明 |
---|---|
assign mux = sel ? a : b; | sel=1 選a ,sel=0 選b (實現多路選擇器)。 |
assign max = (a>b) ? a : b; | 選a 和b 中的較大者(支持嵌套,如三數取最大)。 |
注意:expr1 和expr2 需位寬兼容,否則會截斷(如16'b0 和8'b1 拼接會報錯,需顯式擴展位寬)。 |
9、連接和復制操作符
符號:{}
(大括號)
功能:
- 連接(Concatenation):將多個信號按位拼接。
- 復制(Replication):將信號重復拼接(格式:
{n{expr}}
,n
為常量)。
1. 連接示例
// 示例1:簡單拼接
wire [3:0] a = 4'b1010, b = 4'b0011;
wire [7:0] c = {a, b}; // 結果:8'b1010_0011(a是高4位,b是低4位)// 示例2:位逆序(圖中案例)
assign bus[3:0] = {bus[0], bus[1], bus[2], bus[3]};
// 原bus[3:0] = 4'b1010(位3=1,位2=0,位1=1,位0=0)→ 拼接后為{0,1,0,1} → 4'b0101(位序反轉)。
2. 復制示例?
// 示例1:信號復制
wire [1:0] a = 2'b10;
wire [5:0] b = {3{a}}; // 結果:6'b10_10_10(a重復3次)// 示例2:圖中案例
assign bus[3:0] = {2{bus[0]}, 2{bus[3]}};
// 假設bus[0]=1(1位),bus[3]=0(1位)→ 復制后:{1,1,0,0} → 4'b1100。
九、begin_end
1、定義:
??? 用于將多條語句組合成一個順序執行的代碼塊,類似于 C 語言中的花括號{}
。它主要用于always
、initial
、task
和function
等過程塊中,確保語句按順序執行。
begin語句1;語句2;// ... 更多語句
end
- 順序執行:塊內語句按書寫順序依次執行(僅適用于過程塊)。
- 命名塊:可通過
begin: 塊名
為塊命名,用于調試或聲明局部變量。
?2. 在always
塊中的應用
示例 1:組合邏輯(阻塞賦值)
always @(*) beginif (sel == 1'b0)out = a;elseout = b;
end
- 說明:
begin-end
將if-else
語句組合為一個整體。- 使用阻塞賦值(
=
),確保語句按順序執行。
示例 2:時序邏輯(非阻塞賦值)
always @(posedge clk or posedge reset) beginif (reset)q <= 1'b0; // 異步復位elseq <= d; // 時鐘上升沿更新
end
- 說明:
- 使用非阻塞賦值(
<=
),避免競爭冒險。 begin-end
包裹復位和賦值操作。
- 使用非阻塞賦值(
3. 在initial
塊中的應用
示例:測試平臺的初始化
initial begin// 初始化信號clk = 0;reset = 1;data = 8'h00;// 控制時序#10 reset = 0; // 10個時間單位后釋放復位#20 data = 8'h55; // 再20個時間單位后寫入數據#30 $finish; // 結束仿真
end
initial
塊僅執行一次,常用于測試平臺(Testbench)。begin-end
組合多條初始化和延時語句。
4. 在task
和function
中的應用
示例:任務(task)中的順序操作
???????
2、語句:順序塊
(1)塊內的語句順序執行
(2)每條語句的延時為相對前一句
(3)最后一句執行完,才能跳出該塊
十、fork_join語句:并行塊
(1)塊內語句同時執行
(2)每條語句的延時為相對于進入塊仿真的時間
(較為少用)
十一、if else語句(需要在always塊中使用)
if(表達式) 語句;
else if(表達式) 語句;
else 語句; (多個語句需放在begin end間)
十二、case語句:多分支語句(需要在always塊中使用)
case(表達式)分支:語句……default:語句;endcase
十三、forever連續執行,常用于產生時鐘信號
十四、while執行語句
十五、repeat
??? 連續執行語句n次
??? repeat(表達式),在此表達式通常為常量表達式,表示重復次數。
??? begin語句;end
十六、for
十七、initial
????????initial:是一種用于初始化和仿真控制的過程塊,主要用于測試平臺(Testbench)和仿真場景。initial
?塊的特點如下:
- 執行一次:在仿真開始時執行一次,執行完畢后不再執行。
- 仿真專用:不可綜合為硬件,僅用于仿真驗證。
- 并行執行:多個?
initial
?塊并行執行(與代碼順序無關)。 - 行為描述:用于控制信號時序、生成激勵或監控輸出。
二、語法格式
initial begin// 語句序列
end
begin-end
:可選,若有多條語句則必須使用;單條語句可省略。
示例:
initial begina = 1'b0; // 初始化信號a為0#10 a = 1'b1; // 延遲10個時間單位后,a置為1#20 $finish; // 再延遲20個時間單位后,結束仿真
end
#10
?是 Verilog 的延遲語句,表示暫停執行 10 個時間單位(時間單位由仿真器或?timescale
?指令定義)。- 延遲結束后,執行?
a = 1'b1
。
#20 $finish;
- 功能:再延遲 20 個時間單位后,調用系統任務?
$finish
?結束仿真。 - 語法:
$finish
?是 Verilog 的系統任務,用于終止當前仿真。- 整個?
initial
?塊的執行時間為?30 個時間單位(10 + 20)。
三、常見用法
1.?信號初始化
在仿真開始時設置信號初始值:
reg clk, rst_n;
reg [7:0] data;initial beginclk = 1'b0; // 初始化時鐘為0rst_n = 1'b0; // 初始化復位信號為低(復位狀態)data = 8'h00; // 初始化數據為0
end
clk = 1'b0
:??1'b0
?表示 1 位二進制數(1'b
?是 Verilog 的二進制字面量格式)
將時鐘信號初始化為低電平。通常配合后續的時鐘生成代碼(如forever #5 clk = ~clk
)。rst_n = 1'b0
:
將復位信號初始化為低電平。大多數設計采用異步低電平復位,即rst_n=0
時電路進入復位狀態。data = 8'h00
:
將數據信號初始化為 0。8'h00
表示 8 位十六進制數00
(二進制0000_0000
)。
2.?生成時鐘信號
通過循環生成周期性時鐘:
initial beginforever begin#5 clk = ~clk; // 每5個時間單位翻轉一次,生成周期為10的時鐘end
end
3.?產生測試激勵
按特定時序提供輸入信號:
initial begin// 復位序列rst_n = 1'b0;#20 rst_n = 1'b1; // 20個時間單位后釋放復位// 輸入數據序列#10 data = 8'hAA; // 釋放復位后10個時間單位,發送數據AA#20 data = 8'h55; // 再20個時間單位后,發送數據55#30 $finish; // 結束仿真
end
4.?文件操作
讀取測試數據或寫入仿真結果:
integer file;
reg [7:0] data;initial beginfile = $fopen("input.txt", "r"); // 打開輸入文件if (file == 0) begin$display("Error: Cannot open file!");$finish;endwhile (!$feof(file)) begin$fscanf(file, "%h", data); // 從文件讀取16進制數據#10; // 等待10個時間單位end$fclose(file);
end
5.?仿真控制
使用系統任務控制仿真流程:
initial begin#1000 $finish; // 1000個時間單位后自動結束仿真// 打印關鍵信息$display("Simulation started at time %0t", $time);$monitor("At time %0t: a=%b, b=%b, sum=%b", $time, a, b, sum);
end
十八、always?塊
????????是用于描述時序邏輯或組合邏輯的核心結構。它允許代碼在特定條件下重復執行,是構建硬件電路行為模型的基礎。
1.?always
?塊的基本定義
always @(觸發條件) begin// 過程化語句
end
- 功能:
always
?塊會在觸發條件滿足時執行,執行完畢后等待下一次觸發。 - 觸發條件(敏感列表):
- 邊沿觸發(時序邏輯):
@(posedge clk)
(上升沿)或?@(negedge clk)
(下降沿)。 - 電平觸發(組合邏輯):
@(a or b)
?或?@(*)
(自動推斷所有輸入信號)。
- 邊沿觸發(時序邏輯):
????????????????1.?@(a or b)
?的含義
????????????????????????觸發條件:當信號a
或b
的值發生變化時,always
塊內的代碼會立即執行。??
? ? ?2、@(*)
?的含義
????????????????????????自動推斷:編譯器會自動分析always
塊內的代碼,將所有讀操作的輸入信號添加到敏感列表中。
always @(*) begin // 等價于 @(a or b or c)out = a & b | c; // 自動推斷敏感信號為a、b、c
end
2. 時序邏輯中的?always
?塊
用途:描述觸發器、寄存器等時序元件。
示例 1:同步復位的 D 觸發器
always @(posedge clk) beginif (reset) // 同步復位(高電平有效)q <= 1'b0; // 復位時輸出清零elseq <= d; // 時鐘上升沿時,d的值賦給q
end
- 特點:
- 使用非阻塞賦值(
<=
)保證正確的時序行為。 - 僅在時鐘上升沿觸發,復位信號?
reset
?必須與時鐘同步。
- 使用非阻塞賦值(
示例 2:帶使能的計數器
reg [7:0] count;
always @(posedge clk) beginif (reset)count <= 8'b0;else if (enable)count <= count + 1; // 每個時鐘周期加1
end
?3. 組合邏輯中的?always
?塊
用途:描述邏輯門、多路選擇器等組合電路。
示例 3:4 選 1 多路選擇器
always @(*) begin // 敏感列表為*,自動包含所有輸入case (sel)2'b00: out = a;2'b01: out = b;2'b10: out = c;2'b11: out = d;default: out = 1'bx; // 避免鎖存器生成endcase
end
第 1 行:always @(*)
- 觸發條件:
*
表示敏感列表自動包含所有在塊內被讀取的信號(即sel
、a
、b
、c
、d
)。 - 組合邏輯標志:
always @(*)
是 Verilog 中定義組合邏輯的標準寫法。
第 2-7 行:case
語句
- 選擇邏輯:
- 當
sel
為2'b00
時,輸出a
。 - 當
sel
為2'b01
時,輸出b
。 - 當
sel
為2'b10
時,輸出c
。 - 當
sel
為2'b11
時,輸出d
。
- 當
第 8 行:default: out = 1'bx
- 默認分支:處理
sel
為非法值(如2'bx
或2'bz
)的情況。 - 避免鎖存器:明確指定所有可能的輸入場景,防止綜合工具生成不必要的鎖存器。
- 特點:
- 使用阻塞賦值(
=
)保證語句按順序執行。 - 敏感列表需包含所有輸入信號(或用?
*
?自動推斷),否則可能導致仿真與綜合結果不一致。
- 使用阻塞賦值(
示例 4:組合邏輯實現的加法器
always @(a or b) begin // 等價于 @(*)sum = a + b;
end
4. 混合邏輯中的?always
?塊
用途:同時包含時序和組合邏輯的復雜電路。
示例 5:帶保持功能的寄存器
always @(posedge clk or posedge reset) beginif (reset)q <= 1'b0;else if (load)q <= d; // 加載新數據elseq <= q; // 保持原數據(等價于不操作)
end
5. 無限循環的?always
?塊
用途:生成時鐘信號(僅用于仿真)。
示例 6:時鐘生成
reg clk;
always #5 clk = ~clk; // 生成10個時間單位周期的時鐘(50%占空比)
6. 敏感列表的注意事項
-
組合邏輯必須包含所有輸入:
// 錯誤示例:敏感列表遺漏b,可能導致仿真錯誤
always @(a) beginout = a & b; // 當b變化時,out不會更新
end// 正確寫法:
always @(a or b) begin ... end // 或使用 @(*)
時序邏輯通常僅對時鐘邊沿敏感:?
// 同步復位的正確寫法
always @(posedge clk) beginif (reset) ...
end// 異步復位的寫法(復位信號獨立觸發)
always @(posedge clk or posedge reset) beginif (reset) ...
end
7. 阻塞賦值和非阻塞賦值類型對比
特性 | 阻塞賦值(=) | 非阻塞賦值(<=) |
---|---|---|
執行順序 | 立即執行,按順序執行 | 并行執行,在時間步結束時更新 |
適用場景 | 組合邏輯 | 時序邏輯 |
硬件對應 | 邏輯門 | 觸發器 / 寄存器 |
?
十九、function
用于定義可重復使用的組合邏輯單元,類似于軟件中的函數。它接收輸入參數,執行計算,并返回單個結果。
1.?function
?的基本定義
function [返回值位寬] 函數名;input [參數位寬] 參數1;input [參數位寬] 參數2;// 局部變量聲明reg [位寬] 變量名;// 函數體begin// 計算邏輯函數名 = 表達式; // 將結果賦給函數名本身end
endfunction
- 特點:
- 組合邏輯:函數內不能包含時序控制(如?
#延遲
、@事件
)。 - 立即返回:執行完畢后立即返回結果。
- 單一返回值:通過函數名本身賦值返回結果。
- 組合邏輯:函數內不能包含時序控制(如?
2. 簡單函數示例
?示例 1:計算兩個數的和
function [7:0] add_numbers;input [7:0] a;input [7:0] b;
beginadd_numbers = a + b; // 返回a和b的和
end
endfunction
調用方式:
result = add_numbers(10, 20); // result = 30
示例 2:判斷奇偶性?
function bit is_odd;input [7:0] num;
beginis_odd = (num % 2 == 1); // 返回1(奇數)或0(偶數)
end
endfunction
調用方式:??
result = is_odd(number); // result = 1'b1
3. 帶局部變量的函數
function [7:0] max_of_three;input [7:0] a, b, c;reg [7:0] temp; // 局部變量
begintemp = (a > b) ? a : b;max_of_three = (temp > c) ? temp : c; // 返回三者中的最大值
end
endfunction
調用方式:?
max_value = max_of_three(a, b, c); // max_value = 25
?4. 多維數組作為參數
function [7:0] sum_array;input [7:0] array [0:3]; // 4元素的數組參數reg [7:0] i, sum;
beginsum = 0;for (i = 0; i < 4; i = i + 1) beginsum = sum + array[i];endsum_array = sum; // 返回數組元素的和
end
endfunction
?調用方式:
initial begin// 初始化數組元素my_array[0] = 8'd10;my_array[1] = 8'd20;my_array[2] = 8'd30;my_array[3] = 8'd40;// 調用函數計算數組總和result = sum_array(my_array); // result = 100 (8'd100)
5. 函數的遞歸調用
// 計算階乘(n!)
function [31:0] factorial;input [7:0] n;
beginif (n == 0)factorial = 1;elsefactorial = n * factorial(n-1); // 遞歸調用
end
endfunction
??調用方式:
module test_factorial;reg [7:0] input_n; // 輸入值reg [31:0] result; // 存儲結果initial begininput_n = 5; // 計算 5!// 調用函數result = factorial(input_n); // result = 120 (5! = 120)$display("%d的階乘是: %d", input_n, result); // 輸出: 120end
二十、task:
????????用于定義可復用的代碼塊,類似于function
,但功能更強大。與function
只能實現組合邏輯不同,task
可以包含時序控制(如延時、事件觸發),支持多輸出參數,且不要求立即返回結果。
1.?task
的基本定義
task 任務名;input [位寬] 輸入參數1; // 輸入參數(可選)output [位寬] 輸出參數1; // 輸出參數(可選)inout [位寬] 雙向參數1; // 雙向參數(可選)reg [位寬] 局部變量; // 局部變量(可選)// 任務體begin// 可包含時序控制和過程化語句#延遲; // 延時語句@(事件); // 事件觸發wait(條件); // 等待條件滿足// 邏輯操作輸出參數1 = 表達式; // 賦值給輸出參數end
endtask
- 特點:
- 時序支持:可包含
#延遲
、@事件
、wait
等時序控制語句。 - 多參數傳遞:通過
output
和inout
參數返回多個值。 - 過程化執行:任務內的語句按順序執行。
- 時序支持:可包含
2. 簡單任務示例
示例 1:帶延時的信號生成
task generate_pulse;input [7:0] width; // 脈沖寬度(時間單位)output pulse; // 輸出脈沖信號
beginpulse = 1'b1; // 脈沖置高#width; // 保持width個時間單位pulse = 1'b0; // 脈沖置低
end
endtask
調用方式:
reg my_pulse;
generate_pulse(10, my_pulse); // 生成寬度為10的脈沖
示例 2:帶輸入輸出的任務
task add_numbers;input [7:0] a, b; // 輸入兩個數output [8:0] sum; // 輸出和(9位以避免溢出)
beginsum = a + b; // 計算和
end
endtask
?調用方式:
reg [7:0] x = 5, y = 10;
reg [8:0] result;
add_numbers(x, y, result); // result = 15 (9'd15)
?3. 包含時序控制的任務
task wait_and_check;input signal; // 待監測的信號input [7:0] timeout; // 超時時間output success; // 檢查結果(成功/失敗)
beginsuccess = 1'b0; // 默認失敗// 等待信號變高或超時forkbegin@(posedge signal); // 等待信號上升沿success = 1'b1; // 成功endbegin#timeout; // 超時等待endjoin_anydisable fork; // 終止未完成的線程
end
endtask
調用方式:
reg flag, check_result;
wait_and_check(flag, 100, check_result); // 等待flag上升沿,最多100個時間單位
4. 在模塊中調用任務
module test_task;reg clk, reset, enable;reg [7:0] data_in, data_out;initial begin// 初始化信號clk = 0;reset = 1;enable = 0;// 調用任務initialize_system(); // 初始化系統generate_clock(10); // 生成周期為10的時鐘// 執行操作#50;reset = 0; // 釋放復位#20;process_data(8'd42, data_out); // 處理數據end// 任務定義task initialize_system;begin// 系統初始化操作reset = 1;enable = 0;#20; // 保持復位20個時間單位endendtasktask generate_clock;input period;beginforever begin#(period/2) clk = ~clk; // 生成時鐘endendendtasktask process_data;input [7:0] input_data;output [7:0] output_data;begin@(posedge clk); // 等待時鐘上升沿if (!reset && enable) begin// 數據處理邏輯output_data = input_data * 2;endendendtask
endmodule
函數與任務(task)的對比
特性 | 函數(function) | 任務(task) |
---|---|---|
返回值 | 必須有一個返回值(通過函數名賦值) | 可以沒有返回值,或通過 output 參數返回 |
時序控制 | 不能包含(如?# 、@ 、wait ) | 可以包含時序控制語句 |
調用 | 可在表達式中直接調用 | 必須單獨作為一條語句調用 |
適用場景 | 組合邏輯計算 | 時序邏輯或復雜操作 |