第5節(jié) 功能描述-組合邏輯
5.1 程序語句
5.1.1 assign 語句
assign 語句是連續(xù)賦值語句,一般是將一個變量的值不間斷地賦值給另一變量,兩個變量之間就類似于被導(dǎo)線連在了一起,習(xí)慣上當(dāng)做連線用。 assign 語句的基本格式是:
assign a = b (邏輯運算符) c …;
assign 語句的功能屬于組合邏輯的范疇,應(yīng)用范圍可以概括為一下幾點:
(1)持續(xù)賦值;
(2)連線;
(3) 對 wire 型變量賦值, wire 是線網(wǎng),相當(dāng)于實際的連接線,如果要用 assign 直接連接,就用 wire 型變量, wire 型變量的值隨時發(fā)生變化。
需要說明的是,多條 assign 連續(xù)賦值語句之間互相獨立、 并行執(zhí)行。
5.1.2 always 語句
always 語句是條件循環(huán)語句,執(zhí)行機制是通過對一個稱為敏感變量表的事件驅(qū)動來實現(xiàn)的,下面會具體講到。 always 語句的基本格式是:
always @(敏感事件)begin
程序語句
end
always 是“一直、總是”的意思, @后面跟著事件。整個 always 的意思是:當(dāng)敏感事件的條件滿足時,就執(zhí)行一次“程序語句”。敏感事件每滿足一次,就執(zhí)行“程序語句”一次。 (敏感事件中的敏感條件出現(xiàn)變化時,執(zhí)行always條件循環(huán)語句中的內(nèi)容)
這段程序的意思是: 當(dāng)信號 a 或者信號 b 或者信號 d 發(fā)生變化時,就執(zhí)行一次下面語句。在執(zhí)行該段語句時,首先判斷信號 sel 是否為 0,如果為 0,則執(zhí)行第 3 行代碼。 如果 sel 不為 0,則執(zhí)行第 5 行代碼。需要強調(diào)的是, a、 b、 c 任意一個發(fā)生變化一次, 2 行至 5 行也只執(zhí)行一次,不會執(zhí)行第二次。
此處需要注意,僅僅 sel 這個信號發(fā)生變化是不會執(zhí)行第 2 行到 5 行代碼的, 通常這并不符合設(shè)計者的想法。例如,一般設(shè)計者的想法是: 當(dāng) sel 為 0 時 c 的結(jié)果是 a+b;當(dāng) sel 不為 0 時 c 的結(jié)果是 a+d。但如果觸發(fā)條件沒有發(fā)生改變, 雖然 sel 由 0 變 1, 但此時 c 的結(jié)果仍是 a+b。 因此, 這并不是一個規(guī)范的設(shè)計思維。
因此,按照設(shè)計者的想法重新對代碼進行設(shè)計:當(dāng)信號 a 或者信號 b 或者信號 d 或者信號 sel發(fā)生變化時,就執(zhí)行 2 行至 5 行。這樣就可以確保 sel 信號值為 0 時, c 的結(jié)果一定為 a+b, 當(dāng) sel 不為 0 時, c 的結(jié)果一定是 a+d。 因此要在敏感列表中加入 sel, 其代碼如下所示。
當(dāng)敏感信號非常多時很容易就會把敏感信號遺漏,為避免這種情況可以用“ * ”來代替。這個“ *”是指“程序語句”中所有的條件信號, 即 a、 b、 d、 sel(不包括 c) , 也推薦這種寫法,其具體代碼如下所示。
這種條件信號變化結(jié)果立即變化的 always 語句被稱為“組合邏輯”。
上述代碼敏感列表是**“ posedge clk”,其中 posedge 表示上升沿**。也就是說, 當(dāng) clk 由 0 變成1 的瞬間執(zhí)行一次程序代碼,即第 2 至 5 行, 其他時刻 c 的值保持不變。要特別強調(diào)的是: 如果 clk沒有由 0 變成 1,那么即使 a、 b、 d、 sel 發(fā)生變化, c 的值也是不變的。
可以看到上述代碼的敏感列表是“ negedge clk”,其中 negedg 表示下降沿。也就是說,當(dāng) clk 由 1 變成 0 的瞬間執(zhí)行一次程序代碼,即第 2 至 5 行, 其他時刻 c 的值保持不變。要特別強調(diào)的是,如果 clk 沒有由 1 變成 0,那么即使 a、 b、 d、 sel 發(fā)生變化, c 的值也是不變的。
上述代碼的敏感列表是“ posedge clk or negedge rst_n”,也就是說,當(dāng) clk 由 0 變成 1 的瞬間,或者 rst_n 由 1 變化 0 的瞬間,執(zhí)行一次程序代碼,即第 2 至 8 行, 其他時刻 c 的值保持不變。這種信號邊沿觸發(fā),即信號上升沿或者下降沿才變化的 always, 被稱為“時序邏輯”, 此時信號 clk 是時鐘。注意: 識別信號是不是時鐘不是看名稱,而是看這個信號放在哪里,只有放在敏感列表并且是邊沿觸發(fā)的才是時鐘。而信號 rst_n 是復(fù)位信號, 同樣也不是看名字來判斷,而是放在敏感列表中且同樣邊沿觸發(fā),更關(guān)鍵的是“程序語句”首先判斷了 rst_n 的值, 這表示 rst_n 優(yōu)先級最高,一般都是用于復(fù)位。
設(shè)計時需要注意以下幾點:
1、*組合邏輯的 always 語句中敏感變量必須寫全,或者用“ ”代替。
2、組合邏輯器件的賦值采用阻塞賦值“ =, 時序邏輯器件的賦值語句采用非阻塞賦值“ <=”,
具體原因見“阻塞賦值和非阻塞賦值”一節(jié)內(nèi)容。
5.2 數(shù)字進制
5.2.1 數(shù)字表示方式
在 Verilog 中的數(shù)字表示方式,最常用的格式是: <位寬>’<基數(shù)><數(shù)值>,如 4’b1011。位寬:描述常量所含位數(shù)的十進制整數(shù),是可選項。例如 4’b1011 中的 4 就是位寬, 通俗理解就是 4 根線。如果沒有這一項可以通過常量的值進行推斷。例如’b1011 可知位寬是 4,而’b10010 可推斷出位寬為 5。
基數(shù):表示數(shù)值是多少進制??梢允?b, B, d, D, o, O, h 或者 H,分別表示二進制、十進制、八進制和十六進制。如果沒有此項,則缺省默認為十進制數(shù)。例如,二進制的 4’b1011 可以寫成十進制的 4’d11,也可以寫成十六進制的 4’hb 或者八進制的 4’o13,還可以不寫基數(shù)直接寫成 11。 綜上所述,只要二進數(shù)相同, 無論寫成十進制、八進制和十六進制都是同樣的數(shù)字。
數(shù)值:是由基數(shù)所決定的表示常量真實值的一串 ASCII 碼。如果基數(shù)定義為 b 或 B,數(shù)值可以是 0, 1, x, X, z 或 Z。如果基數(shù)定義為 o 或 O,數(shù)值可以是 2, 3, 4, 5, 6, 7。如果基數(shù)定義為h 或 H,數(shù)值可以是 8, 9, a, b, c, d, e, f, A, B, C, D, E, F。對于基數(shù)為 d 或者 D 的情況,數(shù)值符可以是任意的十進制數(shù): 0 到 9, 但不可以是 x 或 z。例如, 4’b12 是錯誤的,因為 b 表示二進制,數(shù)值只能是 0、 1、 x 或者 z,不包含 2。 32’h12 等同于 32’h00000012, 即數(shù)值未寫完整時,高位補 0。
5.2.2 二進制是基礎(chǔ)
在數(shù)字電路中如果芯片 A 給芯片 B 傳遞數(shù)據(jù),例如傳遞 0 或者 1 信息,可以將芯片 A 和芯片 B通過一個管腳進行相連,然后由芯片 A 控制該管腳輸出為高電平或者低電平,通過高低電平來表示 0和 1。芯片 B 檢測到該管腳為低電平時,表示收到 0, 芯片 B 檢測到該管腳為高電平時,表示收到 1。
反之, 如果用低電平表示收到 1,用高電平表示收到 0 可不可以呢?當(dāng)然可以,只要芯片 A 和芯片 B 事先協(xié)定, 芯片 A 要發(fā)數(shù)字 1 時會將該管腳置為低電平。芯片 B 檢測到該管腳為低電平, 表示收到了數(shù)字 1,通信完成。
一個管腳擁有高低電平兩種狀態(tài),可以分別表示數(shù)字 0 和 1 的兩種情況。如果芯片 A 要發(fā)數(shù)字0、 1、 2、 3 給芯片 B 又要如何操作呢?
可以讓芯片 A 和芯片 B 連接兩根管腳,即兩條線: a 和 b。當(dāng)兩條線都為低電平時,表示發(fā)送數(shù)字 0;當(dāng) a 為高電平 b 為低電平時,表示發(fā)送數(shù)字 1;當(dāng) a 為低電平 b 為高電平時,表示發(fā)送數(shù)字 2;當(dāng)兩條線都是高電平時,表示發(fā)送數(shù)字 3。
按照同樣的道理,芯片 A 要發(fā)送數(shù)據(jù) 4, 5, 6, 7 給芯片 B 時,只要再添加一條線就可以了。三根線一共有 8 種狀態(tài),可以表示 8 個數(shù)字。綜上所述,線的不同電平狀態(tài)可以表示不同的含義, 有多少種不同狀態(tài)就可以表示多少個數(shù)字。
下面來思考一下如果芯片 A 要發(fā)送+1, -1, 0, +2 等數(shù)字給芯片 B,這里的正負又該如何表示呢?參考前面的思路, 線的高低電平表示的含義是由芯片雙方向事先約定好的, 既然如此則可以單用一根線來表示符號,例如低電平表示正數(shù),高電平表示負數(shù)。
上圖所示的三根線中用線 c 表示正負, 其中 0 表示正數(shù), 1 表示負數(shù)。用線 a 和線 b 表示數(shù)值,以 3’b111 為例,其可以解釋為十進制數(shù) 7,也可以解釋為有符號數(shù)原碼“ -3”,也可以解釋為有符號數(shù)補碼“-1”, 如何解釋取決于工程師對二進制數(shù)的定義。只要該定義不影響到電路之間的通信就不會發(fā)生問題。 因此數(shù)字中的“ 0”和“1”不僅可以表示字面上的數(shù)值含義,也可以表示其他意義,如正負符號等。同樣的道理,在數(shù)字電路中二進制數(shù)是八進制、十進制、十六進制、有符號數(shù)、無符號數(shù)、小數(shù)等其他數(shù)制的根本。在 FPGA 設(shè)計中,不清楚小數(shù)、有符號數(shù)的計算方法的最根本原因是不清楚這些數(shù)據(jù)所對應(yīng)的二進制值, 只要理解了對應(yīng)的二進制值,很多問題都可以解決。
下面通過例子讓同學(xué)們更好的理解這一概念, 很多初學(xué)者經(jīng)常問, FPGA 中如何實現(xiàn)小數(shù)計算呢?以“0.5+0.25” 為例, 眾所周知 0.5+0.25 的結(jié)果為 0.75, 可以考慮 0.5、 0.25 和 0.75 用二進制該如何表示? 具體表示方法取決于工程師的做法,因為這種表示方法有很多種,例如定點小數(shù),浮點小數(shù),甚至如前面所討論,用幾根線自行來定義,只要能正常通信,那就沒有問題。假設(shè)某工程師用三根線自行定義了二進制值所表示的小數(shù)值,如下表所示。
二進制值 定義 二進制值 定義
3’b000 0.1 3’b100 0.25
3’b001 0.5 3’b101 0.3
3’b010 0.75 3’b110 0.8
3’b011 0.2 3’b111 0
為了說明二進制值的意義是可以隨便定義的,數(shù)字順序為亂序。 那為什么只有這幾種小數(shù)呢?這是因為假定中的系統(tǒng)就只有這幾種數(shù)字,如果想表示更多數(shù)字增加線的數(shù)量就可以了。完成上面定義之后,要實現(xiàn)“ 0.5+0.25”就很容易了,其實就是 3’b001 和 3’b100“相加”,期望得到 3’b010。 但是在該表中直接使用 3’b001 + 3’b100,結(jié)果為“101”, 這不是想要的結(jié)果,此時可以將代碼寫為:
當(dāng)然,這只是其中一種寫法, 只要能實現(xiàn)所對應(yīng)的功能且結(jié)果正確,任意寫法都可以。
此處可能存在疑慮, 0.1+0.8 應(yīng)該為 0.9,但上面的表格中并沒有 0.9 的表示。這其實是設(shè)計者定義的這個表格有缺陷,或者設(shè)計者認為不會出現(xiàn)這一情況。 此處要表達的是: 只要定義好對應(yīng)的二進制數(shù),很多功能都是很容易設(shè)計的。
當(dāng)然,實際的工程中通常會遵守約定成俗的做法,沒必要另辟蹊徑。例如, 下表是常用的定點小數(shù)的定義:
二進制值 定義 二進制值 定義
3’b000 0.0 3’b100 0.5
3’b001 0.125 3’b101 0.625
3’b010 0.25 3’b110 0.75
3’b011 0.3725 3’b111 0.8725
此時如果要實現(xiàn) 0+0.5=0.5,也就是 3’b000 和 3’b100 相加,期望能得到 3’b100。 可以發(fā)現(xiàn)直接用二進制 3’b000+3’b100 就能得到 3’b100。同樣地, 要實現(xiàn) 0.125+0.75=0.8725,也就是 3’b001 和 3’b110 相加,期望能得到 3’b111。 可以發(fā)現(xiàn)直接用二進制 3’b001+3’b110 就能得到 3’b111。
如果要實現(xiàn) 0.5+0.75=1.25 這一計算, 可以看出此時 1.25 已經(jīng)超出了表示范圍, 可以通過增加信號位寬或只表示小數(shù)位的做法解決這一問題。如果只是表示小數(shù)位則結(jié)果就是 0.25,即 3’b100 和3’b110 相加,期望得到 3’b010。不難發(fā)現(xiàn) 3’b100 + 3’b110 = 4’b1010,用 3 位表示就是 3’b010,也就是 0.25。綜上所述可以看出,定點小數(shù)的計算并不復(fù)雜,定義好定點小數(shù)與二進制值之間的關(guān)系后直接進行計算即可。
5.2.3 不定態(tài)
前文中講過數(shù)字電路只有高電平和低電平,分別表示 1 和 0。但代碼中經(jīng)常能看到 x 和 z,如 1’bx, 1’bz。那么這個 x 和 z 是什么電平呢?答案是并沒有實際的電平來對應(yīng)兩者。 x 和 z 更多地是用來表示設(shè)計者的意圖或者用于仿真目的, 旨在告訴仿真器和綜合器如何解釋這段代碼。
X 態(tài),稱之為不定態(tài), 其常用于判斷條件, 從而告訴綜合工具設(shè)計者不關(guān)心它的電平是多少,是0 還是 1 都可以。
上面的例子中可以看出判斷條件是 din== 4’b10x0, 該條件等價于 din== 4’b1000||din==4’b1010,其中“||”是“或”符號。
然而在設(shè)計中直接寫成 din== 4’b1000||din == 4’b1010 要好于寫成“din == 4’b10x0”, 因為這樣的寫法更加直接和簡單明了。
在仿真的過程中有些信號產(chǎn)生了不定態(tài),那么設(shè)計者就要認真分析這個不定態(tài)是不是合理的。如果真的不關(guān)心它是 0 還是 1,那么可以不解決。但建議所有信號都不應(yīng)該處于不定態(tài), 寫清楚其是 0還是 1,不要給設(shè)計添加“思考”的麻煩。
5.2.4 高阻態(tài)
Z 態(tài),一般稱之為高阻態(tài), 表示設(shè)計者不驅(qū)動這個信號(既不給 0 也不給 1),通常用于三態(tài)門接口當(dāng)中。
上圖就是三態(tài)總線的應(yīng)用案例, 圖中的連接總線對于 CPU 和 FPGA 來說既為輸入又為輸出,是雙向接口。一般的硬件電路中會將該線接上一個上拉電阻(弱上拉)或下拉電阻(弱下拉)。
當(dāng) CPU 和 FPGA 都不驅(qū)動該總線時, A 點保持為高電平。當(dāng) FPGA 不驅(qū)動該總線, CPU 驅(qū)動該總線時, A 點的值就由 CPU 決定。當(dāng) CPU 不驅(qū)動該總線, FPGA 驅(qū)動該總線時, A 點的值就由 FPGA 決定。 但 FPGA 和 CPU 不能同時驅(qū)動該總線,否則 A 的電平就不確定了, 通常 FPGA 和 CPU何時驅(qū)動總線是按事先協(xié)商的協(xié)議進行工作。
上圖是典型的 I2C 的時序。 I2C 的總線 SDA 就是一個三態(tài)信號。 I2C 協(xié)議已規(guī)定好上面的時間中,哪段時間是由主設(shè)備驅(qū)動,哪段時間是由從設(shè)備驅(qū)動,雙方都要遵守協(xié)議,不能存在同時驅(qū)動的情況。那么 FPGA 在設(shè)計中是如何做到“不驅(qū)動”這一行為呢?這是因為 FPGA 內(nèi)部有三態(tài)門。
三態(tài)門是一個硬件,上圖是它的典型結(jié)構(gòu)。三態(tài)門有四個接口,如上圖所示的寫使能 wr_en、寫數(shù)據(jù) wr_data、讀數(shù)據(jù) rd_data 以及與外面器件相連的三態(tài)信號 data。
需要注意的是寫使能信號,當(dāng)該信號有效時三態(tài)門會將 wr_data 的值賦給三態(tài)線 data,此時 data 的值由 wr_data 決定,當(dāng) wr_data 為 0 時 data 值為 0;當(dāng) wr_data 為 1 時 data 值為 1。 而當(dāng)寫使能信號無效時,則不論 wr_data 值是多少都不會對外面的 data 值有影響,也就是不驅(qū)動。
在 Verilog 中以上功能是通過如下代碼實現(xiàn)的:
當(dāng)綜合器看到這兩行代碼則知道要綜合成三態(tài)門了,高阻 z 的作用正在于此。 此外可以注意到硬件上用三態(tài)線是為了減少管腳,而在 FPGA 內(nèi)部沒有必要減少連線,所以使用三態(tài)信號是沒有意義的。 因此, 建議各位在進行設(shè)計時不要在 FPGA 內(nèi)部使用高阻態(tài)“ z”, 因為沒有必要給自己添加“思考”的麻煩。當(dāng)然, 如果設(shè)計中使用了高阻態(tài)也不會報錯,也可以實現(xiàn)功能。
總的來說高阻態(tài)“z”是表示“不驅(qū)動總線”這個行為,實際上數(shù)字電路就是高電平或者低電平,不存在其他電平的情況。
5.3 算術(shù)運算符
算術(shù)運算符包括加法“ +”、減法“-”、乘法“*”、除法“ / ”和求余“ % ”, 其中常用的算術(shù)運算符主要有 :加法“ + ”,減法“-”和乘法“ *”。
注意,常用的運算中不包括除法和求余運算符, 這是由于除法和求余不是簡單的門邏輯搭建起來的, 其所對應(yīng)的硬件電路比較大。加減是最簡單的運算,而乘法可以拆解成多個加法運算, 因此加減法、乘法所對應(yīng)的電路都比較小。而除法就不同了, 同學(xué)們可以回想一下除法的步驟, 其涉及到多次乘法、移位、加減法,所以除法對應(yīng)的電路是復(fù)雜的,這也同時要求設(shè)計師在進行 Verilog 設(shè)計時要慎用除法。
5.3.1 加法運算符
首先學(xué)習(xí)加法運算符, 在 Verilog 代碼中可以直接使用符號“+”:
其電路示意圖如下所示:
綜合器可以識別加法運算符并將其轉(zhuǎn)成如上圖所示的電路。二進制的加法運算和十進制的加法相似,十進制是逢十進一,而二進制是逢二進一。二進制加法的基本運算如下:
5.3.2 減法運算符
減法運算符,在 Verilog 代碼中可以直接使用符號“-”:
其電路示意圖如下所示:
綜合器可以識別減法運算符并將其直接轉(zhuǎn)成上圖所示的電路。
二進制的減法運算和十進制的減法運算是相似的,也有借位的概念。十進制是借一當(dāng)十,二進制則是借一當(dāng)二。 1 位減法基本運算如下:
5.3.3 乘法運算符
————————————————
版權(quán)聲明:本文為CSDN博主「原來如此呀」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/Royalic/article/details/121196365
-
FPGA
+關(guān)注
關(guān)注
1625文章
21628瀏覽量
601252 -
組合邏輯
+關(guān)注
關(guān)注
0文章
46瀏覽量
10026 -
功能描述
+關(guān)注
關(guān)注
0文章
2瀏覽量
8390
發(fā)布評論請先 登錄
相關(guān)推薦
評論