文章目錄
對讀者的假設
已經掌握:
- 可程式化邏輯基礎
- Verilog HDL基礎
- 使用Verilog設計的Quartus II入門指南
- 使用Verilog設計的ModelSIm入門指南
內容1 編碼器1.1 二進位編碼器
二進位編碼器,即把來自2^n條輸入線路的資訊,編碼轉換成二進位碼。其輸入為獨熱碼,輸出為二進位碼。此處以4~2編碼器為例。
表1 4~2編碼器的真值表
代碼1.1.1 使用case語句描述的4~2編碼器(可綜合)
module encoder( input [3:0] iA, output reg [1:0] oQ);always @ (*) case(iA) 4'b0001 : oQ = 2'b00; 4'b0010 : oQ = 2'b01; 4'b0100 : oQ = 2'b10; 4'b1000 : oQ = 2'b11; default : oQ = 2'b00; endcaseendmodule
由於輸入的選項並沒有優先順序,因此第6~7行,採用case語句來描述編碼器。如前文所述,對於不完全的case選項,為了避免前後模擬不一致及鎖存器的產生,第12行加入了deflaut語句賦初值。實際上在這種情況下,在敏感列表之後賦初值的效果是一樣的。
always @ (*) case(iA) 4'b0001 : oQ = 2'b00; 4'b0010 : oQ = 2'b01; 4'b0100 : oQ = 2'b10; 4'b1000 : oQ = 2'b11; default : oQ = 2'b00; endcase
圖1.1.1 4~2編碼器的RTL視圖
代碼1.1.2 4~2編碼器的testbench(不可綜合,僅用於模擬)
`timescale 1ns/1nsmodule encoder_tb;reg [3:0] i_a;wire [1:0] o_q;initialbegin i_a = 4'b0000; #20 i_a = 4'b0001; #20 i_a = 4'b0010; #20 i_a = 4'b0100; #20 i_a = 4'b1000; #20 $stop;end encoder encoder_inst( .iA(i_a), .oQ(o_q)); endmodule
圖1.1.2 使用case語句描述的4~2編碼器的功能模擬波形
1.2 優先編碼器
如表2所示,優先順序分別為:i3 > i2 > i1 > i0;對於這種有優先順序的編碼器,我們可以採用casez語句來描述。
表2.1 4~2優先編碼器的真值表
代碼1.2.1 使用case語句描述的4~2優先編碼器(可綜合)
module encoder( input [3:0] iA, output reg [1:0] oQ);always @ (*) casez(iA) 4'b0001 : oQ = 2'b00; 4'b001? : oQ = 2'b01; 4'b01?? : oQ = 2'b10; 4'b1??? : oQ = 2'b11; default : oQ = 2'b00; endcaseendmodule
第6~13行,採用casez來描述優先編碼器。注意:對於無關項輸入的描述,使用“?”描述優於使用“x”,詳情見參考1;在可綜合的代碼中,不要使用casex。
always @ (*) casez(iA) 4'b0001 : oQ = 2'b00; 4'b001? : oQ = 2'b01; 4'b01?? : oQ = 2'b10; 4'b1??? : oQ = 2'b11; default : oQ = 2'b00; endcase
圖1.2.1 使用case語句描述的4~2優先編碼器RTL視圖
代碼1.2.2 使用if-else-if語句描述的4~2優先編碼器(可綜合)
module encoder( input [3:0] iA, output reg [1:0] oQ);always @ (*) if (iA[3]) oQ = 2'b11; else if (iA[2]) oQ = 2'b10; else if (iA[1]) oQ = 2'b01; else if (iA[0]) oQ = 2'b00; else oQ = 2'b00; endmodule
對於帶優先順序的電路,使用if-else-if來描述,應該是輕而易舉事情。注意:優先順序高的,放在前面描述;else不要丟。
always @ (*) if (iA[3]) oQ = 2'b11; else if (iA[2]) oQ = 2'b10; else if (iA[1]) oQ = 2'b01; else if (iA[0]) oQ = 2'b00; else oQ = 2'b00;
圖1.2.2 使用if-else-if語句描述的4~2優先編碼器
代碼1.2.3 4~2優先編碼器的testbench(不可綜合,僅用於模擬)
`timescale 1ns/1nsmodule encoder_tb;reg [3:0] i_a;wire [1:0] o_q;initialbegin i_a = 4'b0; forever begin if (i_a<16) #20 i_a <= i_a + 1'b1; else #20 i_a <= 4'b0; endendinitial #330 $stop; encoder encoder_inst( .iA(i_a), .oQ(o_q)); endmodule
圖1.2.3 使用case語句描述的4~2優先編碼器的功能模擬波形
圖1.2.4 使用if-else-if語句描述的4~2優先編碼器的功能模擬波形
圖1.2.3和圖1.2.4的波形一致,說明兩種描述方式雖然RTL視圖有所不同,但其功能上是一致的。
2 解碼器
解碼器剛好與編碼器相反,即輸入二進位碼,輸出獨熱碼。此處以2~4解碼器為例。
代碼2.1 使用case語句描述的解碼器(可綜合)
module decoder( input [1:0] iD, output reg [3:0] oQ);always @ (*)begin oQ = 4'b0000; case(iD) 2'b00: oQ = 4'b0001; 2'b01: oQ = 4'b0010; 2'b10: oQ = 4'b0100; 2'b11: oQ = 4'b1000; endcaseendendmodule
第6~15行,由於輸入剛好是2^n個選項,因此沒有加入default語句(沒有其他選項可能出現),而在敏感列表之後給輸出寄存器賦初值(避免產生鎖存器)這一良好的代碼風格一定要提現出來。
always @ (*)begin oQ = 4'b0000; case(iD) 2'b00: oQ = 4'b0001; 2'b01: oQ = 4'b0010; 2'b10: oQ = 4'b0100; 2'b11: oQ = 4'b1000; endcaseend
圖2.1 使用case語句描述的解碼器的RTL視圖
代碼2.2 使用case語句描述的解碼器的testbench(不可綜合,僅用於模擬)
`timescale 1ns/1nsmodule decoder_tb;reg [1:0] i_d;wire [3:0] o_q;initialbegin i_d = 2'b00; #20 i_d = 2'b01; #20 i_d = 2'b10; #20 i_d = 2'b11; #20 $stop;end decoder decoder_inst( .iD(i_d), .oQ(o_q));endmodule
圖2.2 使用case語句描述的解碼器的功能模擬波形
3 碼型轉換器
有了編碼器和解碼器的基礎,下面我們舉一小例來討論一下碼型轉換器。此處以共陽的七段數位管段碼尋找表為例。
圖3.1 七段數位管
表3.1 七段數位管段碼尋找表
代碼3.1 七段數位管段碼尋找表(可綜合)
module seg_transcoder( input [3:0] iNum, output reg [7:0] oSeg);always @ (*)begin case(iNum) 4'h0 : oSeg = 8'hC0; 4'h1 : oSeg = 8'hF9; 4'h2 : oSeg = 8'hA4; 4'h3 : oSeg = 8'hB0; 4'h4 : oSeg = 8'h99; 4'h5 : oSeg = 8'h92; 4'h6 : oSeg = 8'h82; 4'h7 : oSeg = 8'hF8; 4'h8 : oSeg = 8'h80; 4'h9 : oSeg = 8'h90; 4'hA : oSeg = 8'h88; 4'hB : oSeg = 8'h83; 4'hC : oSeg = 8'hC6; 4'hD : oSeg = 8'hA1; 4'hE : oSeg = 8'h86; 4'hF : oSeg = 8'h8E; default : oSeg = 8'hFF; endcaseendendmodule
圖3.1 七段數位管段碼尋找表的RTL視圖
代碼3.2 七段數位管段碼尋找表的testbench(不可綜合,僅用於模擬)
`timescale 1ns/1nsmodule seg_transcoder_tb;reg [3:0] i_num;wire [7:0] o_seg;initial begin i_num = 4'h0; for(i_num=0; i_num<4'hF; i_num=i_num+1'b1) #20 ;endinitial #330 $stop; seg_transcoder seg_transcoder_inst( .iNum(i_num), .oSeg(o_seg));endmodule
第8~9行,使用for語句來產生激勵資料。注意for內不能沒有運算式喲。for語句主要用作複製電路,沒有語句,就像此處一樣,用“;”表示無需複製電路。
for(i_num=0; i_num<4'hF; i_num=i_num+1'b1) #20 ;
右鍵所需觀察的訊號,選擇Radix>Hexadecimal,以十六進位形式查看訊號值。3.2所示。
圖3.2 以十六進位形式查看訊號值
圖3.3 七段數位管段碼尋找表的功能模擬波形
4 小結
經過上面的討論,我們發現,一般情況下,可綜合的代碼僅僅使用了if-else-if和case語句,幾乎涉及不到其他語句。由於可用綜合的語句較少,因此描述起來也比較靈活,這就使得代碼風格顯得尤為重要。然而,好的代碼風格需要長時間方能練就,作為初學者,我們不僅要經常比對自己所描述電路綜合和模擬效果是否和想法一致,還需要多讀一些官方推薦的文檔及一些大師的論文,因為我們個人的能力畢竟有限,我們要善於借鑒已有的經驗以消化利用。所謂心中有點路,此話應當這樣理解,心中本無電路,練得多了,電路才能紮根於我們的思維中,彼時我們才能遊刃有餘地來描述所需電路。
輔助閱讀
1. Altera.Recommended HDL Coding Styles
參考
1. Cliff Cummings' Award-Winning Verilog & SystemVerilog Papers. "full_case parallel_case", the Evil Twins of Verilog Synthesis
2. Wikipedia.Seven-segment display
另見
[與艾米一起學FPGA/SOPC].[邏輯實驗文檔連載計劃]