• <li id="00i08"><input id="00i08"></input></li>
  • <sup id="00i08"><tbody id="00i08"></tbody></sup>
    <abbr id="00i08"></abbr>
  • 新聞中心

    EEPW首頁 > 嵌入式系統 > 設計應用 > Verilog語言:還真的是人格分裂的語言

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

    作者: 時間:2017-06-04 來源:網絡 收藏

    本文引用地址:http://www.czjhyjcfj.com/article/201706/348649.htm

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


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


    “‘reg’是什么?”最微軟的回答是:注冊表文件。這個自然沒錯,但是違背了“到哪座山,唱哪里歌”的原則。一般的“標準”答案是:寄存器型變量。看看‘reg’,不就是‘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. 電平觸發,組合實現
    和reg“孟不離焦,焦不離孟”的是關鍵詞always,這個要記清楚。人家wire和assign是夫妻,reg和always是一對,千萬不要搞混了,這不是能拉郎配的季節。


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


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


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


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


    其中,triger1等為觸發信號。當觸發信號電平變化時,說明敏感列表里面條件符合。如果觸發信號是向量,則其中一個比特的信號變化,就認為信號電平有變化。到了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模塊,總叫人覺得就是組合邏輯了。再次強調數字電路是并行工作的,注意不要用“執行”這個詞,不準確。對應的所有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,如同用水果的標準評價蜜餞。”,但是他們的確很多寫法有類似,這是很容易誤導學習者的地方。


    前文書關于“? :”選擇操作哪里介紹過,選擇與分枝在一般系統中是不可少的。那里介紹的代碼方法,顯然會產生閱讀困難----尤其是在條件比較多的時候。為了改善這一問題,也同時可以更加符合大家以前的習慣,這一講書老朽給貴客介紹“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”的形式)。當需要“保持”的時候,純組合電路是無法滿足的。所以,綜合器會引入“鎖存器”。不是綜合器自作主張,這是代碼的要求。“天作孽猶可恕,自作孽不可活”,只能怪你自己了,哭吧!這個對應的器件是鎖存器,不是這里的重點,會在以后介紹。這里要說的是:要產生組合邏輯,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]) + 7h01;
    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分支項中的常數稱為分支表達式。控制表達式通常表示為控制信號的某些位,分支表達式則用這些控制信號的具體狀態值來表示,因此分支表達式又可以稱為常量表達式。當控制表達式的值與分支表達式的值相等時,就執行分支表達式后面的語句。如果所有的分支表達式的值都沒有與控制表達式的值相匹配的,就執行default后面的語句。


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


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


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


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


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


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


    【例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)
    2b00:
    begin
    result = number + 8b0000_0001;
    end
    2b01:
    begin
    result = number;
    end
    2b10:
    begin
    result = number - 8b0000_0001;
    end
    default:
    begin
    result = 8b0000_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比特實現上的區別。如果眼睛還沒有貴恙的話,可以看出來多位數據選擇器就是若干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
    這正是:

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

    與非網原創內容,謝絕轉載!

    系列匯總:

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

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

    之三:數字邏輯不容小窺,電路門一統江湖



    關鍵詞: verilog傳奇

    評論


    技術專區

    關閉
    主站蜘蛛池模板: 乌鲁木齐市| 新绛县| 五家渠市| 泾源县| 江源县| 广宗县| 达尔| 高碑店市| 海阳市| 栾城县| 闽侯县| 定南县| 宣汉县| 南康市| 元阳县| 防城港市| 英德市| 黄浦区| 五华县| 溧阳市| 吉木乃县| 淮安市| 贡嘎县| 酒泉市| 尤溪县| 泉州市| 民勤县| 灯塔市| 清徐县| 原平市| 张家川| 维西| 新晃| 泗阳县| 兴和县| 维西| 双鸭山市| 琼结县| 林周县| 滁州市| 尤溪县|