?
“人氣腹語術(shù)師天愿在現(xiàn)場(chǎng)披露了被人偶搭檔奪取靈魂的腹語術(shù)師將妻子殺害的表演節(jié)目。天愿真的陷入了多重人格,命令自己殺害妻子和子的人偶的人格出現(xiàn)了。為了不(讓自己)殺害和弟子登川有外遇的妻子,天愿提出委托想要監(jiān)視,然而第二天早上,和子真的被殺害的事件發(fā)生了。天愿坦白很可能是在自己的意識(shí)失去的時(shí)候殺害的……”(----“真相只有一個(gè)”《名偵探柯南》一向是老衲喜歡的動(dòng)畫片)這個(gè)是第 806 回《腹語師的錯(cuò)覺》的介紹。
人有雙重人格,或者叫人格分裂,那么語言呢?Verilog 語言還真的是人格分裂的語言。前回書已經(jīng)說到了,不能簡(jiǎn)單地把 wire 類型映射為組合邏輯,同時(shí)把 reg 類型映射為時(shí)序邏輯。事實(shí)上,這兩個(gè)概念會(huì)交叉的。也就是說,wire 類型極可能被綜合為組合邏輯也可能綜合為時(shí)序邏輯,reg 類型也是這樣。
“‘reg’是什么?”最微軟的回答是:注冊(cè)表文件。這個(gè)自然沒錯(cuò),但是違背了“到哪座山,唱哪里歌”的原則。一般的“標(biāo)準(zhǔn)”答案是:寄存器型變量??纯础畆eg’,不就是‘register’(寄存器)的縮寫嗎?大多數(shù)中文教材中都是這樣說的。
下面為了說明白這樁事情,請(qǐng)?jiān)试S老僧引用 IEEE 有關(guān) 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.”
為了強(qiáng)調(diào),表 1 里面給出了 wire、reg 類型和組合邏輯、時(shí)序邏輯之間的映射關(guān)系。
表 1 wire、reg 類型和組合邏輯、時(shí)序邏輯之間的映射關(guān)系
? |
wire |
reg |
組合邏輯 |
可 |
可 |
時(shí)序邏輯 |
可 |
可 |
可見 reg 是“雙面間諜”的工作性質(zhì),為了能夠“左右逢源”,自然用法要比 wire 來的復(fù)雜。類型 wire 被綜合為時(shí)序邏輯一般還真是寫錯(cuò)了,不必細(xì)表。
1. 電平觸發(fā),組合實(shí)現(xiàn)
和 reg“孟不離焦,焦不離孟”的是關(guān)鍵詞 always,這個(gè)要記清楚。人家 wire 和 assign 是夫妻,reg 和 always 是一對(duì),千萬不要搞混了,這不是能拉郎配的季節(jié)。
“always”的語法結(jié)構(gòu)是:
always @(sensitive_tabel)
其中,sensitive_tabel 稱作敏感列表,其中包含 always 內(nèi)部操作的一個(gè)或者多個(gè)觸發(fā)條件。字符“@”發(fā)音是“艾特”(at),大伙兒發(fā)電子郵件(e-mail)的時(shí)候常用,不羅嗦。
正如上面提到的、標(biāo)準(zhǔn)立面的說法可以是邊沿敏感和電平敏感兩種。對(duì)于組合邏輯電路,這個(gè)敏感列表里面所有條件均為電平敏感。邏輯上,當(dāng)敏感列表里面的條件符合的時(shí)候,always 內(nèi)部的操作可以進(jìn)行。但是,很多邏輯上可行的代碼,由于沒有實(shí)際電路的支持,是無法實(shí)現(xiàn)的。
在 Verilog 語言 95 版本里面,電平觸發(fā)的敏感列表的寫法是
triger1 or triger2 or triger3……
其中,triger1 等為觸發(fā)信號(hào)。當(dāng)觸發(fā)信號(hào)電平變化時(shí),說明敏感列表里面條件符合。如果觸發(fā)信號(hào)是向量,則其中一個(gè)比特的信號(hào)變化,就認(rèn)為信號(hào)電平有變化。到了 Verilog 2001 版本,這個(gè)寫法被更加簡(jiǎn)化了:“,”和“or”都可以用來分割敏感事件;并且,可以用“*”代表所有輸入信號(hào),這可以防止遺漏。例 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 模塊,總叫人覺得就是組合邏輯了。再次強(qiáng)調(diào)數(shù)字電路是并行工作的,注意不要用“執(zhí)行”這個(gè)詞,不準(zhǔn)確。對(duì)應(yīng)的所有 assign 以及 always 帶領(lǐng)的快都是并行的,其在代碼中前后順序與輸出結(jié)果無關(guān)。也就是說例 2 里面的兩段代碼是等效的。代碼中,敏感列表 sensitive_table1 和 sensitive_table2 對(duì)應(yīng)操作 Operation_A 和操作 Operation_B。
【例 2】always 所帶操作順序與輸出結(jié)果無關(guān)
always @(sensitive_table1) Operation_A always @( sensitive_table2) Operation_B |
always @( sensitive_table2) ??? Operation_B always @(sensitive_table1) Operation_A |
理論上,assign 后面只有一行,對(duì)于這個(gè)并行工作的理解不難,不會(huì)產(chǎn)生誤解。到了 always 這里,一般其后的代碼就有很多行了,一不注意就會(huì)出錯(cuò)。
?
2. 條件判斷,分枝多多
“用 C 語言的標(biāo)準(zhǔn)評(píng)價(jià) Verilog,如同用水果的標(biāo)準(zhǔn)評(píng)價(jià)蜜餞?!?,但是他們的確很多寫法有類似,這是很容易誤導(dǎo)學(xué)習(xí)者的地方。
前文書關(guān)于“? :”選擇操作哪里介紹過,選擇與分枝在一般系統(tǒng)中是不可少的。那里介紹的代碼方法,顯然會(huì)產(chǎn)生閱讀困難 ---- 尤其是在條件比較多的時(shí)候。為了改善這一問題,也同時(shí)可以更加符合大家以前的習(xí)慣,這一講書老朽給貴客介紹“if”和“case”這兩位大家熟悉的陌生人。
先看眼里的代碼,條件語句 if 的形式有如表 2 中的三種。其中,condition 等表示選擇的條件,operation 等表示對(duì)應(yīng)的操作。請(qǐng)注意,這里的表達(dá)式“選擇”,目的是和電路對(duì)應(yīng),不是故意和別人不一樣的嘩眾取寵。
表 2 條件語句 if 的格式
? |
無分枝 |
單級(jí)分枝 |
多級(jí)分枝 |
形式 |
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 |
對(duì)應(yīng)電路 |
時(shí)序電路 |
時(shí)序電路 |
時(shí)序電路 組合電路 |
表 .2 中“對(duì)應(yīng)電路”一行也請(qǐng)施主們注意,if 語句中條件的所有路徑覆蓋不全面,可能會(huì)產(chǎn)生時(shí)序電路的。對(duì)于 reg 類型的變量,需要滿足“條件不滿足的時(shí)候,保持原值”;同時(shí),組合電路不可“自賦值”(也就是類似“a <= a”的形式)。當(dāng)需要“保持”的時(shí)候,純組合電路是無法滿足的。所以,綜合器會(huì)引入“鎖存器”。不是綜合器自作主張,這是代碼的要求?!疤熳髂酹q可恕,自作孽不可活”,只能怪你自己了,哭吧!這個(gè)對(duì)應(yīng)的器件是鎖存器,不是這里的重點(diǎn),會(huì)在以后介紹。這里要說的是:要產(chǎn)生組合邏輯,if 的條件路徑必須全覆蓋。
if 語句中條件的所有路徑覆蓋不全面,可能會(huì)產(chǎn)生時(shí)序電路的。這個(gè)對(duì)應(yīng)的器件是鎖存器,不是這里的重點(diǎn),以后介紹。這里要說的是:要產(chǎn)生組合邏輯,if 的條件路徑必須全覆蓋。
例 3 是一個(gè)單級(jí)條件語句 if 應(yīng)用的例子,其功能是求有符號(hào)數(shù)絕對(duì)值。其中,輸入為 8 比特有符號(hào)數(shù),編碼方式為補(bǔ)碼;輸出是輸入數(shù)值的絕對(duì)值。具體算法是:
?
【例 3】絕對(duì)值運(yùn)算模塊
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. 多種情況,并列判決
在條件很多的時(shí)候,用 if 語句來寫還是很麻煩的,搞不好就是是一個(gè)條件路徑覆蓋不完全。這個(gè)時(shí)候,可以選擇 case 套餐。case 語句是一種多分支選擇語句, Verilog 語言提供的 case 語句直接處理多分支選擇。多分支的 case 有三種形式,如表 3 所示。
表 3 條件語句 case 的形式
? |
case |
casex |
casez |
比較方式 |
敏感表達(dá)式與各項(xiàng)值之間的比較,是一種全等比較 |
如果分支表達(dá)式某些位的值為高阻 z,那么對(duì)這些位的比較就會(huì)忽略,不予考慮,而只關(guān)注其他位的比較結(jié)果。 casez 會(huì)把 z/?匹配成任意,也會(huì)把任意匹配成 z/? |
在 casex 語句中,則把這種處理方式進(jìn)一步擴(kuò)展到對(duì) x 的處理,即如果比較雙方有一方的某些位的值是 z 或 x,那么這些位的比較就不予考慮。 casex 會(huì)把 z/?x 匹配成任意,也會(huì)把任意匹配成 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 項(xiàng) |
各個(gè) constant 項(xiàng)為確定寬度的常數(shù)值,不包含 x 和 z; 可以用“?”表示不關(guān)心該位數(shù)值 |
各個(gè) constant 項(xiàng)為確定寬度的常數(shù)值,可包含 x 但不能包含 z |
各個(gè) constant 項(xiàng)為確定寬度的常數(shù)值,可包含 z 但不能包含 z |
constant 例子 |
3’b000:3 比特寬度全 0; 3’b0?0:3 比特寬度第二比特不關(guān)心,其他比特為 0 |
3’b000:3 比特寬度全 0; 3’b0?0:3 比特寬度第二比特不關(guān)心,其他比特為 0; 3’b00x:3 比特寬度最低比特為 x,其他比特為 0 |
3’b000:3 比特寬度全 0; 3’b0?0:3 比特寬度第二比特不關(guān)心,其他比特為 0; 3’b00z:3 比特寬度最低比特為不關(guān)心,其他比特為 0 |
可綜合性 |
可綜合 |
依賴綜合軟件 |
依賴綜合軟件 |
case 括弧內(nèi)的變量稱為控制表達(dá)式,case 分支項(xiàng)中的常數(shù)稱為分支表達(dá)式。控制表達(dá)式通常表示為控制信號(hào)的某些位,分支表達(dá)式則用這些控制信號(hào)的具體狀態(tài)值來表示,因此分支表達(dá)式又可以稱為常量表達(dá)式。當(dāng)控制表達(dá)式的值與分支表達(dá)式的值相等時(shí),就執(zhí)行分支表達(dá)式后面的語句。如果所有的分支表達(dá)式的值都沒有與控制表達(dá)式的值相匹配的,就執(zhí)行 default 后面的語句。
default 項(xiàng)可有可無,一個(gè) case 語句里只能有一個(gè) default 項(xiàng)。 當(dāng)分支表達(dá)式可以覆蓋控制表達(dá)式全部分枝路徑時(shí),default 可以不寫。但是,有時(shí)候這個(gè)全覆蓋不是那么容易看出來的,所以建議最好寫上 default,哪怕有冗余這個(gè) default 永遠(yuǎn)不可能被實(shí)現(xiàn)。也請(qǐng)大家放心,這種冗余綜合軟件會(huì)大伙兒去掉的,不必?fù)?dān)心浪費(fèi)電路資源。
每一個(gè) case 分項(xiàng)的分支表達(dá)式的值必須互不相同,否則就會(huì)出現(xiàn)矛盾現(xiàn)象(對(duì)表達(dá)式的同一個(gè)值,有多種執(zhí)行方案)。
執(zhí)行完 case 分項(xiàng)后的語句,則跳出該 case 語句結(jié)構(gòu),終止 case 語句的執(zhí)行。(精通 C 語言的大蝦們請(qǐng)?zhí)貏e注意這點(diǎn),這里 case 操作執(zhí)行完之后不必寫 break 了。)
在用 case 語句表達(dá)式進(jìn)行比較的過程中,只有當(dāng)信號(hào)的對(duì)應(yīng)位的值能明確進(jìn)行比較時(shí),比較才能成功,因此要詳細(xì)說明 case 分項(xiàng)的分支表達(dá)式的值。
case 語句的所有表達(dá)式的值的位寬必須相等,只有這樣控制表達(dá)式和分支表達(dá)式才能進(jìn)行對(duì)應(yīng)位的比較。一個(gè)經(jīng)常犯的錯(cuò)誤是用'bx、'bz 來替代 n'bx、n'bz,這樣寫是不對(duì)的,因?yàn)樾盘?hào) x、z 的缺省寬度是機(jī)器的字節(jié)寬度,通常是 32 位(此處 n 是 case 控制表達(dá)式的位寬)。
當(dāng)分支表達(dá)式不完全覆蓋控制表達(dá)式全部分枝路徑時(shí),您老有偷懶沒有寫 default 的情況下,可能產(chǎn)生時(shí)序邏輯的鎖存器的,這點(diǎn)和條件 if 語句類似。例 4 是一個(gè)例子,說明了 default 的重要性。但是,圖 1 中的“l(fā)d”是鎖存器已經(jīng)是時(shí)序電路的元件了,超越了本章的范圍。
【例 4】case 語句條件覆蓋不全產(chǎn)生會(huì)綜合出鎖存器
代碼 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. 多路選擇,一個(gè)例子
數(shù)據(jù)選擇器(也稱為:多路復(fù)用器,英文:multiplexer,簡(jiǎn)寫:MUX),是一種從多路輸入信號(hào)中選擇一個(gè)信號(hào)作為輸出的器件。電器符號(hào)如圖 2 所示。
圖 2 數(shù)據(jù)選擇器的電氣符號(hào)
數(shù)據(jù)選擇器的的邏輯功能是:
注意,其中輸入 I0、I1 和 SEL 以及輸出 O 都是 1 比特位寬的信號(hào)。對(duì)應(yīng)布爾邏輯表達(dá)式是
對(duì)應(yīng) Verilog 代碼為:
1) 利用? :表達(dá)式
input SEL;
input I0;
input I1;
output O
assign O =(SEL) ? (I0) : (I1);
代碼中關(guān)鍵的部分是?? :表達(dá)式,其語法結(jié)構(gòu)為 (邏輯表達(dá)式) ?? (值 0) : (值 1);作用是
所以,以上代碼滿足了數(shù)字電路里面數(shù)據(jù)選擇器的功能。
2) 利用 if 關(guān)鍵詞
If (SEL == 1’b0)
begin
?? O = I0;
end
else
begin
?? O = I1;
end
3) 利用 case 關(guān)鍵詞
case (SEL)
??? 1’b0:
????? begin
??????? O = I0;
????? end
??? 1’b1:
????? begin
???????? O = I1;
????? end
endcase
在很多情況下,需要選擇的輸入位寬大于 1,這個(gè)時(shí)候只要兩個(gè)待選擇的輸入與輸出的位寬一致,照樣可以實(shí)現(xiàn)功能(以下按照 8 比特輸入為例)。此時(shí) Verilog 代碼除了模塊的接口位寬,其他部分幾乎沒有變化:
input SEL;
input[7:0] I0;
input[7:0]? I1;
output[7:0]? O
assign O =(SEL) ? (I0) : (I1);
當(dāng)然用 if 或者 case 語句也可以實(shí)現(xiàn),相信讀者舉一反三的能力,就不羅列了。
很多讀者或許會(huì)感覺到筆者十分啰嗦,實(shí)則不然,圖 3 是多位輸入的數(shù)據(jù)選擇器的電氣原理圖。
圖 3 多位數(shù)據(jù)選擇器的電路原理圖
上圖看起來是順理成章的。這里之所以筆者還不厭其煩的畫出來,是為了叫讀者看到多位與 1 比特實(shí)現(xiàn)上的區(qū)別。如果眼睛還沒有貴恙的話,可以看出來多位數(shù)據(jù)選擇器就是若干 1 比特?cái)?shù)據(jù)選擇器的并行排列??紤]到前面內(nèi)容介紹的時(shí)延問題,需要提醒讀者注意的是這個(gè)位數(shù)很高(當(dāng)然不是例子里面的 8 比特)的時(shí)候,輸入和輸出信號(hào)的 skew(線間時(shí)延)可能會(huì)給設(shè)計(jì)帶來麻煩。
另一種常見的情況是輸入不止有兩個(gè)信號(hào),或者說需要在不僅僅兩個(gè)信號(hào)里面進(jìn)行選擇,這個(gè)叫做高階數(shù)據(jù)選擇器(一般吧 SEL 的比特?cái)?shù)稱為數(shù)據(jù)選擇器的階數(shù))。通常輸入個(gè)數(shù)是 2 的冪,此時(shí)選擇信號(hào) SEL 也就不僅是 1 比特信號(hào)了,這個(gè)很容易理解。在理論上,可以通過展開布爾邏輯表達(dá)式的方法,完成高速的高階數(shù)據(jù)選擇器。例如,2 階(也就是有 4 個(gè)輸入信號(hào),SEL 為 2 比特變量)的隨機(jī)選擇的布爾邏輯表達(dá)式為:
?
其中,I0、I1、I2 和 I3 為器件的輸入,S0 和 S1 為 SEL 信號(hào)的低比特和高比特。
這個(gè)式子已經(jīng)不簡(jiǎn)單了,如果是 10 階神馬的數(shù)據(jù)選擇器,那樣的式子的長(zhǎng)度不難想象。所以,在工程上,一般利用低階數(shù)據(jù)選擇器的串聯(lián)來實(shí)現(xiàn)高階數(shù)據(jù)選擇器。圖 4 是一個(gè)用 3 個(gè) 1 階數(shù)據(jù)選擇器實(shí)現(xiàn) 2 階數(shù)據(jù)選擇器的例子。
圖 4 利用低階數(shù)據(jù)選擇器實(shí)現(xiàn)高階數(shù)據(jù)選擇器
對(duì)于高階數(shù)據(jù)選擇器的 Verilog 代碼,一般建議利用 case 的形式。例如圖 3 里面的 2 階數(shù)據(jù)選擇器可以用以下代碼實(shí)現(xiàn):
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
這正是:
“
組合邏輯大融合,關(guān)鍵語法有心得。不論理論數(shù)學(xué)河,電路優(yōu)化靠綜合。
鄙人說書自有樂,撬行老僧沙彌哥。報(bào)告整理嫉妒惹,大乘渡人笑呵呵。
”
?
與非網(wǎng)原創(chuàng)內(nèi)容,謝絕轉(zhuǎn)載!
系列匯總: