0 前言
0.1 使用環境
- EDA工具:Vivado 2017.4
- 硬件描述語言:Verilog HDL
0.2 涉及知識
- 數字邏輯
- Verilog
1 基礎模塊:一位四選一數據選擇器
1.1 設計部分:層次建模
1.1.1 需求分析
設計一個一位的四選一數據選擇器,有一個低電平輸入有效的使能端E
1.1.2 層次建模
1.1.3 功能分析
- 輸入端口
- 數據輸入端 X 4
- D0~D3(Date)
- 地址輸入端 X 2(因為2個端口可以有2^2=4種狀態)
- A1,A0(Address)
- 使能端 X 1
- E(Enable)
- 數據輸入端 X 4
- 輸入端口
- 數據輸出端 X 1
- OUT
- 數據輸出端 X 1
一位四選一數據選擇器的簡化真值表為:
E的bar | A1 | A0 | OUT |
---|---|---|---|
0 | x | x | 0 |
1 | 0 | 0 | D0 |
1 | 0 | 1 | D1 |
1 | 1 | 0 | D2 |
1 | 1 | 1 | D3 |
邏輯表達式為:
OUT = (A1bar·A0bar·D0 + A1bar·A0·D1 + A1·A0bar·D2 + A1·A0·D3)·E
1.2 實現部分:設計的實現
1.2.1 設計塊
根據1.1.3節的邏輯表達式,使用數據流建模,直接得到設計塊部分。注意,這里并沒有使用前面所示的端口名稱,這并不影響,不過不要學習我,還是保持一致比較好!
// 使用數據流建模,實現一位的四選一數據選擇器
module choose_4to1(input i0,i1,i2,i3, // 數據輸入端input s1,s0, // 地址輸入端input e, // 使能端output out // 輸出);assign out = ((~s1)&(~s0)&i0 | (~s1)&s0&i1 | s1&(~s0)&i2 | s1&s0&i3) & e;endmodule
1.2.2 激勵塊
調用設計塊實例,施加激勵信號,利用系統函數顯示結果,驗證設計塊的正確性。
module test();reg I0 = 1,I1 = 0,I2 = 1,I3 = 0;reg S1,S0;reg E = 1;wire OUT;choose_4to1 CT0 (I0,I1,I2,I3,S1,S0,E,OUT);initial#1 $monitor("S1 = %b, S2 = %b, OUT = %b\n",S1,S0,OUT);initialbegin$display("I0 = %b, I1 = %b, I2 = %b, I3 = %b\n\n",I0,I1,I2,I3);#1 S1 = 0; S0 = 0;#1 S1 = 0; S0 = 1;#1 S1 = 1; S0 = 0;#1 S1 = 1; S0 = 1;endendmodule
1.2.3 驗證
根據輸出結果,驗證設計塊是否正確,若不正確,則改正后再次驗證,直到正確為止。
經過驗證,設計塊是正確的!
1.3 科技黑箱1:1位四選一數據選擇器
目前,一位四選一數據選擇器,已經能夠直接使用,我們根據起端口和功能,設計出更復雜的功能模塊。
2 向量型擴展:多位四選一數據選擇器
我們知道,數據在計算機中采用二進制信息進行表示,對于第1節中的數據選擇器,輸入數據只能是1個二進制位,這顯然滿足不了現實需求,因此我們對其進行擴展,例如,能夠實現100,266等類似數據的選擇。
這里為了簡單器件,說明設計思想,我們選擇將1位擴展為4位
2.1 設計部分:4位四選一數據選擇器
2.1.1 層次建模
2.1.2 設計模型
2.1.3 設計思想:分治思想
舉例,假設選擇的結果的5
合(十進制) | 分(二進制) | |||
---|---|---|---|---|
9 | 1 | 0 | 0 | 1 |
5 | 0 | 1 | 0 | 1 |
7 | 0 | 1 | 1 | 1 |
8 | 1 | 0 | 0 | 0 |
處理(選) | 0 | 1 | 0 | 1 |
合(十進制) | 5 |
簡單解釋一下:
- 合: 將十進制的數字拆成4位二進制數
- 分: 4個數的第一位進入第一個數據選擇器,第二位進入第二個數據選擇器……
- 選: 每個數據選擇器分別選出一個二進制數
- 合: 將得到的4位二進制數轉換為十進制數輸出
2.1.4 模型類比
從一位全加器到四位全加器,也是同樣的過程。
2.2 實現部分:設計的實現
2.2.1 設計塊
`timescale 1ns / 1ps// 使用數據流建模,實現一位的四選一數據選擇器
module choose_4to1(input i0,i1,i2,i3, // 數據輸入端input s1,s0, // 地址輸入端input e, // 使能端output out // 輸出);assign out = ((~s1)&(~s0)&i0 | (~s1)&s0&i1 | s1&(~s0)&i2 | s1&s0&i3) & e;endmodule// 向量型擴展,實現4位四選一數據選擇器
module choose_4to1_4sizes(input [3:0] d0,d1,d2,d3,input s1,s0,input e,output [3:0] out);// 4個1位四選一數據選擇器實例choose_4to1 DT0 (d0[0],d1[0],d2[0],d3[0],s1,s0,e,out[0]);choose_4to1 DT1 (d0[1],d1[1],d2[1],d3[1],s1,s0,e,out[1]);choose_4to1 DT2 (d0[2],d1[2],d2[2],d3[2],s1,s0,e,out[2]);choose_4to1 DT3 (d0[3],d1[3],d2[3],d3[3],s1,s0,e,out[3]);endmodule
2.2.2 激勵塊
`timescale 1ns / 1psmodule test();reg [3:0] I0 = 9,I1 = 5,I2 = 7,I3 = 8;reg S1,S0;reg E = 1;wire [3:0] OUT;choose_4to1_4sizes CT0 (I0,I1,I2,I3,S1,S0,E,OUT);initial#1 $monitor("S1 = %b, S2 = %b, OUT = %d\n",S1,S0,OUT);initialbegin$display("I0 = %d, I1 = %d, I2 = %d, I3 = %d\n\n",I0,I1,I2,I3);#1 S1 = 0; S0 = 0;#1 S1 = 0; S0 = 1;#1 S1 = 1; S0 = 0;#1 S1 = 1; S0 = 1;endendmodule
2.2.3 結果驗證
2.3 科技黑箱2:4位四選一數據選擇器
依然還是這個模型,區別是:這里的數據輸入和輸出端口,支持4個二進制位
3 數組型擴展:1位八選一數據選擇器
利用使能端,通過屏蔽理論,將一位四選一數據選擇器,擴展為一位八選一數據選擇器。
3.0 數組型擴展的核心原則
考慮所有的端口
- 該分開的分開,注意連接順序
- 該連接的連接,注意連接方法
3.1 設計部分:1位八選一數據選擇器
3.1.1 層次建模
3.1.2 設計模型
產生的變化:
- 地址輸入端:增加一個
- 利用使能端
- E為1時,高4位有效
- E為0時,低4位有效
- 兩個四選一數據選擇器:屏蔽理論——利用非門
- 高4位
- 低4位
- 輸出端口要合并,使用邏輯或
- 共同的部分連一起
注意對應關系:例如,高4位的0,1,2,3要分別對應4,5,6,7,順序不能顛倒!
對于RTL級建模來說,這個邏輯圖,不畫也罷,直接寫出其邏輯關系即可!
另外,這里也要為以后考慮,也就是,需要再這個模型上,再加上一個使能端E,以便于后續的擴展。
3.2 設計塊
// 一位八選一數據選擇器
module choose_8to1 (input i0,i1,i2,i3,i4,i5,i6,i7, // 數據輸入端input s2,s1,s0, // 地址輸入端input e, // 新的使能端output out // 數據輸出端);wire out1,out2; // 兩個四選一的輸出choose_4to1 DT4 (i4,i5,i6,i7,s1,s0,s2,out1); // 高4位choose_4to1 DT5 (i0,i1,i2,i3,s1,s0,~s2,out2); // 低4位assign out = (out1 | out2) & e;endmodule
3.3 激勵塊
module test();reg I0 = 1,I1 = 0,I2 = 1,I3 = 0,I4 = 1,I5 = 0,I6 = 1,I7 = 0;reg S2,S1,S0;reg E = 1;wire OUT;choose_8to1 CT0 (I0,I1,I2,I3,I4,I5,I6,I7,S2,S1,S0,E,OUT);initial#1 $monitor("S2 = %b, S1 = %b, S0 = %b, OUT = %b\n",S2,S1,S0,OUT);initialbegin$display("I0 = %b, I1 = %b, I2 = %b, I3 = %b\nI4 = %b, I5 = %b, I6 = %b, I7 = %b\n\n",I0,I1,I2,I3,I4,I5,I6,I7);#1 S2 = 0; S1 = 0; S0 = 0;#1 S2 = 0; S1 = 0; S0 = 1;#1 S2 = 0; S1 = 1; S0 = 0;#1 S2 = 0; S1 = 1; S0 = 1;#1 S2 = 1; S1 = 0; S0 = 0;#1 S2 = 1; S1 = 0; S0 = 1;#1 S2 = 1; S1 = 1; S0 = 0;#1 S2 = 1; S1 = 1; S0 = 1;endendmodule
4 混合型擴展:多位八選一數據選擇器
- 先擴展為4位四選一數據選擇器
- 再擴展為4位八選一數據選擇器
4.1 層次建模
4.2 模塊實現
4.2.1 設計塊
// 4位八選一數據選擇器
module choose_8to1_4sizes (input [3:0] i0,i1,i2,i3,i4,i5,i6,i7, // 數據輸入端input s2,s1,s0, // 地址輸入端input [3:0] e, // 新的使能端output [3:0] out // 數據輸出端);wire [3:0] out1,out2; // 兩個四選一的輸出choose_4to1_4sizes DT4_0 (i4,i5,i6,i7,s1,s0,s2,out1); // 高4位choose_4to1_4sizes DT4_1 (i0,i1,i2,i3,s1,s0,~s2,out2); // 低4位assign out = (out1 | out2) & e;endmodule
4.2.2 激勵塊
module test();reg [3:0] I0 = 1,I1 = 2,I2 = 3,I3 = 4,I4 = 5,I5 = 6,I6 = 7,I7 = 8;reg S2,S1,S0;reg [3:0] E = 4'b1111;wire [3:0] OUT;choose_8to1_4sizes CT0 (I0,I1,I2,I3,I4,I5,I6,I7,S2,S1,S0,E,OUT);initial#1 $monitor("S2 = %b, S1 = %b, S0 = %b, OUT = %d\n",S2,S1,S0,OUT);initialbegin$display("I0 = %b, I1 = %b, I2 = %b, I3 = %b\nI4 = %b, I5 = %b, I6 = %b, I7 = %b\n\n",I0,I1,I2,I3,I4,I5,I6,I7);#1 S2 = 0; S1 = 0; S0 = 0;#1 S2 = 0; S1 = 0; S0 = 1;#1 S2 = 0; S1 = 1; S0 = 0;#1 S2 = 0; S1 = 1; S0 = 1;#1 S2 = 1; S1 = 0; S0 = 0;#1 S2 = 1; S1 = 0; S0 = 1;#1 S2 = 1; S1 = 1; S0 = 0;#1 S2 = 1; S1 = 1; S0 = 1;endendmodule
4.2.3 結果驗證
4.3 重要知識點與思維方法
4.3.1 解決錯誤的方法論
- 分析程序的行為是什么,而不是為什么程序不按照你想的來
- 注意使用單元測試
- 保證低層模塊沒有問題,再實現高層模塊
- 如果高層模塊出了問題,先從高層找問題
- 再逐一找到之前沒有問題的低層模塊,進行修正
4.3.2 按位運算與邏輯運算
- 按位運算是二進制位的每一位進行運算,其結果位數為:兩個操作數的最小位數
- 邏輯運算把操作數都當成0,1,x,z,其結果也是0,1,x,z(這里是粗略說明,詳細內容可以百度一下對應真值表)
4.3.2.1 按位運算:位寬不匹配時的注意事項
當按位運算的兩操作數位寬不相等時,短位寬數會在左側補0,使兩操作數位寬一致。
在上述4位八選一數據選擇器的設計塊中,模塊結尾處有這樣一條語句:assign out = (out1 | out2) & e;
并且,我在前面使用的是位寬為4的使能端,這里,我想有必要詳細解釋一下為什么這樣做,并且我會告訴你更好的解決方案。
我們知道以下的事實:
- 使能端的作用: 它就相當于選擇器的開關,打開它選擇器才能正常工作,關閉它選擇器就不能工作。
- 開關的兩種形式: 低電平有效和高電平有效,這里我采用的是高電平有效,也就是
e = 1
的時候才有效(但是事實上,大多數情況我們都采用低電平有效的使能端,可是,你想過為什么嗎?我在后面將解釋為什么要這樣做)
接下來,我將會講解為什么使用4位的使能端:
4位的使能端,只有當e = 4b'_1111
的時候才能啟動選擇器,如果它是1位的,我們則只能為他賦值e = 1'b_1
,此后它將與4位的數據進行邏輯與運算,我們知道,位寬不匹配的時候,短位寬將會自動補0再運算,此時的e將會被虛擬地增加3個0e = 4'b_0001
,顯然這樣運算的結果是錯誤的。
我想你能夠明白為什么高電平有效的使能端,在這里要使用4位位寬了。
不過,這樣會對未來造成麻煩,我非常不建議你這樣做,試想一下,如果用這樣的八選一數據選擇器,去擴展為十六選一的,將會出現一些問題。
那么,如何保證使能端依然是1位呢?
請注意一個事實,那就是,位寬不匹配的時候,系統會自動補0,因為補的是0,所以,我們應該采用低電平有效的使能端,將前面的邏輯表達式改一下就可以assign out = (out1 | out2) & (~e);
5 組合型擴展:一位/多位雙四選一數據選擇器
這個就是簡單的組合而已,沒什么好說的,除了使能端共用,輸入輸出端口依然還是各自獨立的。
6 深入淺出:將數據選擇器講給孩子聽
嘗試將專業的知識將給孩子聽,并讓他聽懂,就說明你真的掌握了,我給你一些提示:
- 輸入:香蕉,蘋果,橘子,大鴨梨
- 控制:開關 X 1,控制端 X 2
- 輸出:某一個水果
7 一句話總結
數據選擇器就是,將多組數據輸入,通過地址控制和開關控制,輸出其中一組指定的數據。
8 科技黑箱:直接應用“數據選擇器”解決現實問題
——現實生活中有項目需求,但是沒有問題,也沒有答案
8.0 對數據選擇器新的理解
使用數據選擇器,實現:數據輸入端的并聯,不同的地址,輸出同一個結果
8.1 需求:紅綠燈故障檢測系統
- 正常情況:三盞燈只有一個是亮的
- 故障情況:正常情況均為故障
現要求,能夠實現故障的檢測,出現故障的時候系統會自動報警,請使用數據選擇器設計邏輯電路
8.2 設計部分:組合邏輯電路
8.2.1 信息符號化
兩個核心要點:
- 信息的符號表示
- 邏輯值的含義
- 三盞燈 1代表亮,0代表不亮
- 紅燈 R
- 黃燈 Y
- 綠燈 G
- 故障檢測 F
- F = 1,代表故障
- F = 0,代表沒有故障
- 檢測開關:使能端E,低電平有效
8.2.2 求邏輯表達式
真值表如下:
Ebar | R | Y | G | 輸出:Fbar |
---|---|---|---|---|
1 | x | x | x | x |
0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 1 | 1 |
0 | 0 | 1 | 0 | 1 |
0 | 0 | 1 | 1 | 0 |
0 | 1 | 0 | 0 | 1 |
0 | 1 | 0 | 1 | 0 |
0 | 1 | 1 | 0 | 0 |
0 | 1 | 1 | 1 | 0 |
0 | x | x | x | x |
邏輯表達式如下:
Fbar = Rbar·Ybar·G + Rbar·Y·Gbar + R·Ybar·Gbar
= m0·G + m1·Gbar + m2·Gbar + m3·0
8.2.3 器件選擇
使用四選一數據選擇器
- 地址輸入端:R,Y
- 數據輸入端:G,Gbar
- 使能端:E
- 數據輸出端:F
8.3 實現部分:Verilog實現邏輯電路
8.3.0 構建模型
8.3.1 設計塊
// 紅綠燈故障檢測
module RYG_Flault_test (input G, // 數據輸入input R,Y, // 地址輸入input E, // 使能端output F); wire F1;choose_4to1 RYG_test (G,~G,~G,0,R,Y,E,F1); // 為什么不能輸出端口不能連接“~F”??assign F = ~F1;
endmodule
特別注意:輸出端口不能連接~F,只能多加一步導一下
8.3.2 激勵塊
// 紅綠燈故障檢測系統測試
module test;reg R,Y;reg G;reg E = 1;wire F;RYG_Flault_test RYG_Test(G,R,Y,E,F); // 紅綠燈檢測模塊實例initial$monitor($time," R = %b, Y = %b, G = %b, F = %b\n",R,Y,G,F); // 顯示紅綠燈的狀況initialbegin#1 R = 0; Y = 0; G = 0;#1 R = 0; Y = 0; G = 1;#1 R = 0; Y = 1; G = 0;#1 R = 0; Y = 1; G = 1;#1 R = 1; Y = 0; G = 0;#1 R = 1; Y = 0; G = 1;#1 R = 1; Y = 1; G = 0;#1 R = 1; Y = 1; G = 1;end endmodule
8.3.3 結果驗證
9 深入剖析數據選擇器的本質
數據選擇器的本質,是多路選擇器!,它可以應用于任何需要做出功能選擇的電路中去,它是非常常見并且實用的,數據選擇器的設計思想可以推廣到任意功能的選擇上去,而不僅僅是數據的選擇。
10 組合邏輯電路的設計
各種器件的本質,不過是邏輯表達式,對于期間的使用,需要進行實際需求表達式與器件邏輯表達式的對應關系即可!
這個部分我將會在其他文章闡述,敬請期待。
11 Verilog之行為級建模的魅力
事實上,對于以上的Verilog代碼,并不需要那樣繁瑣,也不需要那么多行,為什么我依然堅持使用呢?
- 充分深入底層邏輯,并通過實戰幫助你理解數據選擇器,為后面使用多路選擇器打基礎
- 充分使用層次建模思想解決問題,鍛煉核心思維
請記住,即便有了更為簡單的方法,我們依然不應該完全放棄底層知識的學習,沒有底層積累,是不能設計出優秀的頂層設計的。
行為級仿真代碼留給讀者自行完成。