owned this note
owned this note
Published
Linked with GitHub
# 9. Verilog 連續賦值
[參考網址:Verilog 中文詳細教程](https://www.runoob.com/w3cnote/verilog-tutorial.html)
###### tags: `verilog` `IC 設計`
---
[toc]
---
### :pushpin: 關鍵詞:assign
連續賦值語句是 Verilog 數據流建模的基本語句,用於對 wire 型變量進行賦值。 : 格式如下
```verilog=
assign LHS_target = RHS_expression ;
```
LHS(left hand side) 指賦值操作的左側,RHS(right hand side)指賦值操作的右側。 assign 為關鍵詞,任何已經聲明 wire 變量的連續賦值語句都是以 assign 開頭,例如:
```verilog=
wire Cout, A, B ;
assign Cout = A & B ; //实现计算A与B的功能
```
需要說明的是:
- LHS_target 必須是一個標量或者線型向量,而不能是寄存器類型。
- RHS_expression 的類型沒有要求,可以是標量或線型或存器向量,也可以是函數調用。
- 只要 RHS_expression 表達式的操作數有事件發生(值的變化)時,RHS_expression 就會立刻重新計算,同時賦值給 LHS_target。
Verilog 還提供了另一種對 wire 型賦值的簡單方法,即在 wire 型變量聲明的時候同時對其賦值。 wire 型變量只能被賦值一次,因此該種連續賦值方式也只能有一次。例如下面賦值方式和上面的賦值例子的賦值方式,效果都是一致的。
```verilog=
wire A, B ;
wire Cout = A & B ;
```
### :pushpin: 全加器
下面採用數據流描述方式,來設計一個 1bit 全加器。 設 Ai,Bi,Ci 分別為被加數、加數和相鄰低位的進位數,So, Co 分別為本位和與向相鄰高位的進位數。 真值表如下:

全加器的表達式為:
```verilog=
So = Ai ⊕ Bi ⊕ Ci ;
Co = AiBi + Ci(Ai+Bi)
```
rtl 代碼(full_adder1.v)如下:
```verilog=
module full_adder1(
input Ai, Bi, Ci,
output So, Co);
assign So = Ai ^ Bi ^ Ci ;
assign Co = (Ai & Bi) | (Ci & (Ai | Bi));
endmodule
```
當然,更為貼近加法器的代碼描述可以為:
```verilog=
module full_adder1(
input Ai, Bi, Ci
output So, Co);
assign {Co, So} = Ai + Bi + Ci ;
endmodule
```
testbench(test.sv)參考如下:
```verilog=
`timescale 1ns/1ns
module test ;
reg Ai, Bi, Ci ;
wire So, Co ;
initial begin
{Ai, Bi, Ci} = 3'b0;
forever begin
#10 ;
{Ai, Bi, Ci} = {Ai, Bi, Ci} + 1'b1;
end
end
full_adder1 u_adder(
.Ai (Ai),
.Bi (Bi),
.Ci (Ci),
.So (So),
.Co (Co));
initial begin
forever begin
#100;
//$display("---gyc---%d", $time);
if ($time >= 1000) begin
$finish ;
end
end
end
endmodule
```
仿真結果如下:

# 3.2 Verilog 時延
### :pushpin: 時延
連續賦值延時語句中的延時,用於控制任意操作數發生變化到語句左端賦予新值之間的時間延時。
時延一般是不可綜合的。
寄存器的時延也是可以控制的,這部分在時序控制裡加以說明。
連續賦值時延一般可分為普通賦值時延、隱式時延、聲明時延。
下面 3 個例子實現的功能是等效的,分別對應 3 種不同連續賦值時延的寫法。
```verilog=
//普通時延,A&B計算結果延時10個時間單位賦值給Z
wire Z, A, B ;
assign #10 Z = A & B ;
```
```verilog=
//隱式時延,聲明一個wire型變量時對其進行包含一定時延的連續賦值。
wire A, B;
wire #10 Z = A & B;
```
```verilog=
//聲明時延,聲明一個wire型變量是指定一個時延。因此對該變量所有的連續賦值都會被推遲到指定的時間。除非門級建模中,一般不推薦使用此類方法建模。
wire A, B;
wire #10 Z ;
assign Z =A & B
```
### :pushpin: 慣性時延
在上述例子中,A 或 B 任意一個變量發生變化,那麼在 Z 得到新的值之前,會有 10 個時間單位的時延。如果在這 10 個時間單位內,即在 Z 獲取新的值之前,A 或 B 任意一個值又發生了變化,那麼計算 Z 的新值時會取 A 或 B 當前的新值。所以稱之為慣性時延,即信號脈衝寬度小於時延時,對輸出沒有影響。 因此仿真時,時延一定要合理設置,防止某些信號不能進行有效的延遲。 對一個有延遲的與門邏輯進行時延仿真。
```verilog=
module time_delay_module(
input ai, bi,
output so_lose, so_get, so_normal);
assign #20 so_lose = ai & bi ;
assign #5 so_get = ai & bi ;
assign so_normal = ai & bi ;
endmodule
```
testbench 參考如下:
```verilog=
`timescale 1ns/1ns
module test ;
reg ai, bi ;
wire so_lose, so_get, so_normal ;
initial begin
ai = 0 ;
#25 ; ai = 1 ;
#35 ; ai = 0 ; //60ns
#40 ; ai = 1 ; //100ns
#10 ; ai = 0 ; //110ns
end
initial begin
bi = 1 ;
#70 ; bi = 0 ;
#20 ; bi = 1 ;
end
time_delay_module u_wire_delay(
.ai (ai),
.bi (bi),
.so_lose (so_lose),
.so_get (so_get),
.so_normal (so_normal));
initial begin
forever begin
#100;
//$display("---gyc---%d", $time);
if ($time >= 1000) begin
$finish ;
end
end
end
endmodule
```
仿真結果如下:
信號 so_normal 為正常的與邏輯。 由於所有的時延均大於 5ns,所以信號 so_get 的結果為與操作後再延遲 5ns 的結果。 信號 so_lose 前一段是與操作後再延遲 20ns 的結果。 由於信號 ai 第二個高電平持續時間小於 20ns,so_lose 信號會因慣性時延而漏掉對這個脈衝的延時檢測,所以後半段 so_lose 信號仍然為 0。
