加入星計劃,您可以享受以下權益:

  • 創(chuàng)作內容快速變現
  • 行業(yè)影響力擴散
  • 作品版權保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
  • 相關推薦
  • 電子產業(yè)圖譜
申請入駐 產業(yè)圖譜

Verilog語言:還真的是人格分裂的語言

2016/07/19
30
  • 1評論
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

?

“人氣腹語術師天愿在現場披露了被人偶搭檔奪取靈魂的腹語術師將妻子殺害的表演節(jié)目。天愿真的陷入了多重人格,命令自己殺害妻子和子的人偶的人格出現了。為了不(讓自己)殺害和弟子登川有外遇的妻子,天愿提出委托想要監(jiān)視,然而第二天早上,和子真的被殺害的事件發(fā)生了。天愿坦白很可能是在自己的意識失去的時候殺害的……”(----“真相只有一個”《名偵探柯南》一向是老衲喜歡的動畫片)這個是第 806 回《腹語師的錯覺》的介紹。


人有雙重人格,或者叫人格分裂,那么語言呢?Verilog 語言還真的是人格分裂的語言。前回書已經說到了,不能簡單地把 wire 類型映射為組合邏輯,同時把 reg 類型映射為時序邏輯。事實上,這兩個概念會交叉的。也就是說,wire 類型極可能被綜合為組合邏輯也可能綜合為時序邏輯,reg 類型也是這樣。


“‘reg’是什么?”最微軟的回答是:注冊表文件。這個自然沒錯,但是違背了“到哪座山,唱哪里歌”的原則。一般的“標準”答案是:寄存器型變量??纯础畆eg’,不就是‘register’(寄存器)的縮寫嗎?大多數中文教材中都是這樣說的。


下面為了說明白這樁事情,請允許老僧引用 IEEE 有關 Verilog 語言里面的原文:
“Assignments to a reg are made by procedural assignments (see 6.2 and 9.2). Since the reg holds a value between assignments, it can be used to model hardware registers. Edge-sensitive (i.e., flip-flops) and level sensitive (i.e., RS and transparent latches) storage elements can be modeled. A reg need not represent a hardware storage element since it can also be used to represent combinatorial logic.”


為了強調,表 1 里面給出了 wire、reg 類型和組合邏輯、時序邏輯之間的映射關系。


表 1 wire、reg 類型和組合邏輯、時序邏輯之間的映射關系

?

wire

reg

組合邏輯

時序邏輯


可見 reg 是“雙面間諜”的工作性質,為了能夠“左右逢源”,自然用法要比 wire 來的復雜。類型 wire 被綜合為時序邏輯一般還真是寫錯了,不必細表。


1. 電平觸發(fā),組合實現
和 reg“孟不離焦,焦不離孟”的是關鍵詞 always,這個要記清楚。人家 wire 和 assign 是夫妻,reg 和 always 是一對,千萬不要搞混了,這不是能拉郎配的季節(jié)。


“always”的語法結構是:
always @(sensitive_tabel)


其中,sensitive_tabel 稱作敏感列表,其中包含 always 內部操作的一個或者多個觸發(fā)條件。字符“@”發(fā)音是“艾特”(at),大伙兒發(fā)電子郵件(e-mail)的時候常用,不羅嗦。


正如上面提到的、標準立面的說法可以是邊沿敏感和電平敏感兩種。對于組合邏輯電路,這個敏感列表里面所有條件均為電平敏感。邏輯上,當敏感列表里面的條件符合的時候,always 內部的操作可以進行。但是,很多邏輯上可行的代碼,由于沒有實際電路的支持,是無法實現的。


在 Verilog 語言 95 版本里面,電平觸發(fā)的敏感列表的寫法是
triger1 or triger2 or triger3……


其中,triger1 等為觸發(fā)信號。當觸發(fā)信號電平變化時,說明敏感列表里面條件符合。如果觸發(fā)信號是向量,則其中一個比特的信號變化,就認為信號電平有變化。到了 Verilog 2001 版本,這個寫法被更加簡化了:“,”和“or”都可以用來分割敏感事件;并且,可以用“*”代表所有輸入信號,這可以防止遺漏。例 1 給出了一些符合語法的 always 的例子。


【例 1】always 與敏感列表
always @ (triger1 or triger2 or triger3)??? //Version 95 and Version 2001
always @ (triger1 , triger2 , triger3)??? //Version 2001
always @ (*)??? //Version 2001
具有完全電平敏感列表的 always 模塊,總叫人覺得就是組合邏輯了。再次強調數字電路是并行工作的,注意不要用“執(zhí)行”這個詞,不準確。對應的所有 assign 以及 always 帶領的快都是并行的,其在代碼中前后順序與輸出結果無關。也就是說例 2 里面的兩段代碼是等效的。代碼中,敏感列表 sensitive_table1 和 sensitive_table2 對應操作 Operation_A 和操作 Operation_B。
【例 2】always 所帶操作順序與輸出結果無關

always @(sensitive_table1)

Operation_A

always @( sensitive_table2)

Operation_B

always @( sensitive_table2)

??? Operation_B

always @(sensitive_table1)

Operation_A

理論上,assign 后面只有一行,對于這個并行工作的理解不難,不會產生誤解。到了 always 這里,一般其后的代碼就有很多行了,一不注意就會出錯。

?


2. 條件判斷,分枝多多
“用 C 語言的標準評價 Verilog,如同用水果的標準評價蜜餞。”,但是他們的確很多寫法有類似,這是很容易誤導學習者的地方。


前文書關于“? :”選擇操作哪里介紹過,選擇與分枝在一般系統(tǒng)中是不可少的。那里介紹的代碼方法,顯然會產生閱讀困難 ---- 尤其是在條件比較多的時候。為了改善這一問題,也同時可以更加符合大家以前的習慣,這一講書老朽給貴客介紹“if”和“case”這兩位大家熟悉的陌生人。


先看眼里的代碼,條件語句 if 的形式有如表 2 中的三種。其中,condition 等表示選擇的條件,operation 等表示對應的操作。請注意,這里的表達式“選擇”,目的是和電路對應,不是故意和別人不一樣的嘩眾取寵。


表 2 條件語句 if 的格式

?

無分枝

單級分枝

多級分枝

形式

if (condition )

begin

?? ?operations

end

if (condition )

begin

??? operations_1

end

else

begin

??? operations_2

end

if (condition_1 )

begin

??? operations_1

end

else if (condition_2 )

begin

??? operations_2

end

else if……

……

begin

??? operations_m

end

對應電路

時序電路

時序電路

組合電路

時序電路

組合電路


表 .2 中“對應電路”一行也請施主們注意,if 語句中條件的所有路徑覆蓋不全面,可能會產生時序電路的。對于 reg 類型的變量,需要滿足“條件不滿足的時候,保持原值”;同時,組合電路不可“自賦值”(也就是類似“a <= a”的形式)。當需要“保持”的時候,純組合電路是無法滿足的。所以,綜合器會引入“鎖存器”。不是綜合器自作主張,這是代碼的要求?!疤熳髂酹q可恕,自作孽不可活”,只能怪你自己了,哭吧!這個對應的器件是鎖存器,不是這里的重點,會在以后介紹。這里要說的是:要產生組合邏輯,if 的條件路徑必須全覆蓋。


if 語句中條件的所有路徑覆蓋不全面,可能會產生時序電路的。這個對應的器件是鎖存器,不是這里的重點,以后介紹。這里要說的是:要產生組合邏輯,if 的條件路徑必須全覆蓋。


例 3 是一個單級條件語句 if 應用的例子,其功能是求有符號數絕對值。其中,輸入為 8 比特有符號數,編碼方式為補碼;輸出是輸入數值的絕對值。具體算法是:
?


【例 3】絕對值運算模塊
module abs
? (
??? input[7:0] signed_value,
??? output reg[6:0] result
? );

//Definition for Variables in the module


//Load other module(s)

//Logical
always @(signed_value)
begin
??? if ( signed_value[7])
? //Negative number input
? begin
????? result <= (~signed_value[6:0]) + 7'h01;
? end
? else
? //Positive number or zero input
? begin
????? result <= signed_value[6:0];
? end
end

endmodule

?


3. 多種情況,并列判決
在條件很多的時候,用 if 語句來寫還是很麻煩的,搞不好就是是一個條件路徑覆蓋不完全。這個時候,可以選擇 case 套餐。case 語句是一種多分支選擇語句, Verilog 語言提供的 case 語句直接處理多分支選擇。多分支的 case 有三種形式,如表 3 所示。


表 3 條件語句 case 的形式

?

case

casex

casez

比較方式

敏感表達式與各項值之間的比較,是一種全等比較

如果分支表達式某些位的值為高阻 z,那么對這些位的比較就會忽略,不予考慮,而只關注其他位的比較結果。

casez 會把 z/?匹配成任意,也會把任意匹配成 z/?

在 casex 語句中,則把這種處理方式進一步擴展到對 x 的處理,即如果比較雙方有一方的某些位的值是 z 或 x,那么這些位的比較就不予考慮。

casex 會把 z/?x 匹配成任意,也會把任意匹配成 z/?/x,即直接忽略 z/?/x

形式

case (variable)

costant_1:

??? begin

??????? operations_1

??? end

costant_2:

??? begin

??????? operations_2

??? end

……

default:

??? begin

??????? operations_m

end

endcase

case (variable)

costant_1:

??? begin

??????? operations_1

??? end

costant_2:

??? begin

??????? operations_2

??? end

……

default:

??? begin

??????? operations_m

??? end

endcase

case (variable)

costant_1:

??? begin

??????? operations_1

??? end

costant_2:

??? begin

??????? operations_2

??? end

……

default:

??? begin

??????? operations_m

??? end

endcase

constant 項

各個 constant 項為確定寬度的常數值,不包含 x 和 z;

可以用“?”表示不關心該位數值

各個 constant 項為確定寬度的常數值,可包含 x 但不能包含 z

各個 constant 項為確定寬度的常數值,可包含 z 但不能包含 z

constant 例子

3’b000:3 比特寬度全 0;

3’b0?0:3 比特寬度第二比特不關心,其他比特為 0

3’b000:3 比特寬度全 0;

3’b0?0:3 比特寬度第二比特不關心,其他比特為 0;

3’b00x:3 比特寬度最低比特為 x,其他比特為 0

3’b000:3 比特寬度全 0;

3’b0?0:3 比特寬度第二比特不關心,其他比特為 0;

3’b00z:3 比特寬度最低比特為不關心,其他比特為 0

可綜合性

可綜合

依賴綜合軟件

依賴綜合軟件


case 括弧內的變量稱為控制表達式,case 分支項中的常數稱為分支表達式??刂票磉_式通常表示為控制信號的某些位,分支表達式則用這些控制信號的具體狀態(tài)值來表示,因此分支表達式又可以稱為常量表達式。當控制表達式的值與分支表達式的值相等時,就執(zhí)行分支表達式后面的語句。如果所有的分支表達式的值都沒有與控制表達式的值相匹配的,就執(zhí)行 default 后面的語句。


default 項可有可無,一個 case 語句里只能有一個 default 項。 當分支表達式可以覆蓋控制表達式全部分枝路徑時,default 可以不寫。但是,有時候這個全覆蓋不是那么容易看出來的,所以建議最好寫上 default,哪怕有冗余這個 default 永遠不可能被實現。也請大家放心,這種冗余綜合軟件會大伙兒去掉的,不必擔心浪費電路資源。


每一個 case 分項的分支表達式的值必須互不相同,否則就會出現矛盾現象(對表達式的同一個值,有多種執(zhí)行方案)。


執(zhí)行完 case 分項后的語句,則跳出該 case 語句結構,終止 case 語句的執(zhí)行。(精通 C 語言的大蝦們請?zhí)貏e注意這點,這里 case 操作執(zhí)行完之后不必寫 break 了。)


在用 case 語句表達式進行比較的過程中,只有當信號的對應位的值能明確進行比較時,比較才能成功,因此要詳細說明 case 分項的分支表達式的值。


case 語句的所有表達式的值的位寬必須相等,只有這樣控制表達式和分支表達式才能進行對應位的比較。一個經常犯的錯誤是用'bx、'bz 來替代 n'bx、n'bz,這樣寫是不對的,因為信號 x、z 的缺省寬度是機器的字節(jié)寬度,通常是 32 位(此處 n 是 case 控制表達式的位寬)。


當分支表達式不完全覆蓋控制表達式全部分枝路徑時,您老有偷懶沒有寫 default 的情況下,可能產生時序邏輯的鎖存器的,這點和條件 if 語句類似。例 4 是一個例子,說明了 default 的重要性。但是,圖 1 中的“l(fā)d”是鎖存器已經是時序電路的元件了,超越了本章的范圍。


【例 4】case 語句條件覆蓋不全產生會綜合出鎖存器
代碼 1:組合邏輯電路寫法
module case_full
? (
??? input[7:0] number,
? input[1:0] select,
??? output reg[7:0] result
? );
?
//Load other module(s)

//Definition for Variables in the module

//Logical
always @(*)
begin
??? case (select)
??????? 2'b00:
??????? begin
????????????? result <= number + 8'b0000_0001;
??????? end
??????? 2'b01:
??????? begin
????????????? result <= number;
??????? end
??????? 2'b10:
??????? begin
????????????? result <= number - 8'b0000_0001;
??????? end
??????? default:
??????? begin
????????????? result <= 8'b0000_0000;
??????? end
??? endcase
end?
endmodule


圖 1 例 4 綜合出的電路圖(全部為組合邏輯)

?


4. 多路選擇,一個例子
數據選擇器(也稱為:多路復用器,英文:multiplexer,簡寫:MUX),是一種從多路輸入信號中選擇一個信號作為輸出的器件。電器符號如圖 2 所示。


圖 2 數據選擇器的電氣符號


數據選擇器的的邏輯功能是:


注意,其中輸入 I0、I1 和 SEL 以及輸出 O 都是 1 比特位寬的信號。對應布爾邏輯表達式是


對應 Verilog 代碼為:
1) 利用? :表達式
input SEL;
input I0;
input I1;
output O
assign O =(SEL) ? (I0) : (I1);
代碼中關鍵的部分是?? :表達式,其語法結構為 (邏輯表達式) ?? (值 0) : (值 1);作用是


所以,以上代碼滿足了數字電路里面數據選擇器的功能。


2) 利用 if 關鍵詞
If (SEL == 1’b0)
begin
?? O = I0;
end
else
begin
?? O = I1;
end
3) 利用 case 關鍵詞
case (SEL)
??? 1’b0:
????? begin
??????? O = I0;
????? end
??? 1’b1:
????? begin
???????? O = I1;
????? end
endcase


在很多情況下,需要選擇的輸入位寬大于 1,這個時候只要兩個待選擇的輸入與輸出的位寬一致,照樣可以實現功能(以下按照 8 比特輸入為例)。此時 Verilog 代碼除了模塊的接口位寬,其他部分幾乎沒有變化:

input SEL;
input[7:0] I0;
input[7:0]? I1;
output[7:0]? O
assign O =(SEL) ? (I0) : (I1);


當然用 if 或者 case 語句也可以實現,相信讀者舉一反三的能力,就不羅列了。
很多讀者或許會感覺到筆者十分啰嗦,實則不然,圖 3 是多位輸入的數據選擇器的電氣原理圖。


圖 3 多位數據選擇器的電路原理圖


上圖看起來是順理成章的。這里之所以筆者還不厭其煩的畫出來,是為了叫讀者看到多位與 1 比特實現上的區(qū)別。如果眼睛還沒有貴恙的話,可以看出來多位數據選擇器就是若干 1 比特數據選擇器的并行排列??紤]到前面內容介紹的時延問題,需要提醒讀者注意的是這個位數很高(當然不是例子里面的 8 比特)的時候,輸入和輸出信號的 skew(線間時延)可能會給設計帶來麻煩。


另一種常見的情況是輸入不止有兩個信號,或者說需要在不僅僅兩個信號里面進行選擇,這個叫做高階數據選擇器(一般吧 SEL 的比特數稱為數據選擇器的階數)。通常輸入個數是 2 的冪,此時選擇信號 SEL 也就不僅是 1 比特信號了,這個很容易理解。在理論上,可以通過展開布爾邏輯表達式的方法,完成高速的高階數據選擇器。例如,2 階(也就是有 4 個輸入信號,SEL 為 2 比特變量)的隨機選擇的布爾邏輯表達式為:
?


其中,I0、I1、I2 和 I3 為器件的輸入,S0 和 S1 為 SEL 信號的低比特和高比特。
這個式子已經不簡單了,如果是 10 階神馬的數據選擇器,那樣的式子的長度不難想象。所以,在工程上,一般利用低階數據選擇器的串聯來實現高階數據選擇器。圖 4 是一個用 3 個 1 階數據選擇器實現 2 階數據選擇器的例子。


圖 4 利用低階數據選擇器實現高階數據選擇器


對于高階數據選擇器的 Verilog 代碼,一般建議利用 case 的形式。例如圖 3 里面的 2 階數據選擇器可以用以下代碼實現:
case (SEL)
?? 2’b00:
?????? begin
???????? O = I0;
?????? end
?? 2’b01:
?????? begin
???????? O = I1;
????? end
2’b01:
?????? begin
???????? O = I2;
?????? end
?? 2’b11:
?????? begin
???????? O = I3;
????? end

endcase
這正是:

組合邏輯大融合,關鍵語法有心得。不論理論數學河,電路優(yōu)化靠綜合。
鄙人說書自有樂,撬行老僧沙彌哥。報告整理嫉妒惹,大乘渡人笑呵呵。

?

與非網原創(chuàng)內容,謝絕轉載!

系列匯總:

之一:溫故而知新:從電路里來,到 Verilog 里去!

之二:Verilog 編程無法一蹴而就,語言層次講究“名正則言順”

之三:數字邏輯不容小窺,電路門一統(tǒng)江湖

相關推薦

電子產業(yè)圖譜

本名:吳濤,通信專業(yè)博士,畢業(yè)后十多年從事無線通訊產品的研發(fā)工作。了解W-CDMA、TDS-CDMA和LTE的標準協(xié)議、接收機算法以及系統(tǒng)架構和開發(fā)。從事過關于W-CDMA的FPGA IP core設計工作,也完成過W-CDMA和TDS-CDMA的接收機理論研究和鏈路仿真工作。綜合上面的工作,最終選擇了無線通訊的系統(tǒng)設計和標準設計工作。目前擁有100多個已授權的發(fā)明專利,是某通訊行業(yè)標準文件的第一作者,亦有專利思想被寫入3GPP協(xié)議。已出版FPGA設計專業(yè)著作《IP核芯志-數字邏輯設計思想》和《Verilog傳奇-從電路出發(fā)的HDL代碼設計》。