[文档].艾米电子 - 编码器与译码器.[ModelSim][Quartus II][Verilog]
0赞对读者的假设
已经掌握:
内容
1 编码器
1.1 二进制编码器
二进制编码器,即把来自2^n条输入线路的信息,编码转换成二进制码。其输入为独热码,输出为二进制码。此处以4~2编码器为例。
表1 4~2编码器的真值表
代码1.1.1 使用case语句描述的4~2编码器(可综合
01 module encoder( 02 input [3:0] iA, 03 output reg [1:0] oQ 04 ); 05 06 always @ (*) 07 case(iA) 08 4'b0001 : oQ = 2'b00; 09 4'b0010 : oQ = 2'b01; 10 4'b0100 : oQ = 2'b10; 11 4'b1000 : oQ = 2'b11; 12 default : oQ = 2'b00; 13 endcase 14 15 endmodule
于输入的选项并没有优先级,因此第6~7行,采用case语句来描述编码器。如前文所述,对于不完全的case选项,为了避免前后仿真不一致及锁存器的生成,第12行加入了deflaut语句赋初值。实际上在这种情况下,在敏感列表之后赋初值的效果是一样的。
1 |
always @ (*) |
2 |
case (iA) |
3 |
4'b0001 : oQ = 2'b00 ; |
4 |
4'b0010 : oQ = 2'b01 ; |
5 |
4'b0100 : oQ = 2'b10 ; |
6 |
4'b1000 : oQ = 2'b11 ; |
7 |
default : oQ = 2'b00 ; |
8 |
endcase |
图1.1.1 4~2编码器的RTL视图
代码1.1.2 4~2编码器的testbench(不可综合,仅用于仿真)
01 |
` timescale 1ns/1ns |
02 |
module encoder_tb; |
03 |
reg [3:0] i_a; |
04 |
wire [1:0] o_q; |
05 |
06 |
initial |
07 |
begin |
08 |
i_a = 4'b0000 ; |
09 |
#20 i_a = 4'b0001 ; |
10 |
#20 i_a = 4'b0010 ; |
11 |
#20 i_a = 4'b0100 ; |
12 |
#20 i_a = 4'b1000 ; |
13 |
#20 $ stop ; |
14 |
end |
15 |
16 |
encoder encoder_inst( |
17 |
.iA(i_a), |
18 |
.oQ(o_q) |
19 |
); |
20 |
|
21 |
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优先编码器(可综合)
01 |
module encoder( |
02 |
input [3:0] iA, |
03 |
output reg [1:0] oQ |
04 |
); |
05 |
06 |
always @ (*) |
07 |
casex (iA) |
08 |
4'b0001 : oQ = 2'b00 ; |
09 |
4'b001 ? : oQ = 2'b01 ; |
10 |
4'b01 ?? : oQ = 2'b10 ; |
11 |
4'b1 ??? : oQ = 2'b11 ; |
12 |
default : oQ = 2'b00 ; |
13 |
endcase |
14 |
15 |
endmodule |
第6~13行,采用casez来描述优先编码器。注意:对于无关项输入的描述,使用“?”描述优于使用“x”,详情见参考1;在可综合的代码中,不要使用casex。
1 |
always @ (*) |
2 |
casex (iA) |
3 |
4'b0001 : oQ = 2'b00 ; |
4 |
4'b001 ? : oQ = 2'b01 ; |
5 |
4'b01 ?? : oQ = 2'b10 ; |
6 |
4'b1 ??? : oQ = 2'b11 ; |
7 |
default : oQ = 2'b00 ; |
8 |
endcase |
图1.2.1 使用case语句描述的4~2优先编码器RTL视图
代码1.2.2 使用if-else-if语句描述的4~2优先编码器(可综合)
01 |
module encoder( |
02 |
input [3:0] iA, |
03 |
output reg [1:0] oQ |
04 |
); |
05 |
06 |
always @ (*) |
07 |
if (iA[3]) |
08 |
oQ = 2'b11 ; |
09 |
else if (iA[2]) |
10 |
oQ = 2'b10 ; |
11 |
else if (iA[1]) |
12 |
oQ = 2'b01 ; |
13 |
else if (iA[0]) |
14 |
oQ = 2'b00 ; |
15 |
else |
16 |
oQ = 2'b00 ; |
17 |
|
18 |
endmodule |
对于带优先级的电路,使用if-else-if来描述,应该是轻而易举的事情。注意:优先级高的,放在前面描述;else不要丢。
01 |
always @ (*) |
02 |
if (iA[3]) |
03 |
oQ = 2'b11 ; |
04 |
else if (iA[2]) |
05 |
oQ = 2'b10 ; |
06 |
else if (iA[1]) |
07 |
oQ = 2'b01 ; |
08 |
else if (iA[0]) |
09 |
oQ = 2'b00 ; |
10 |
else |
11 |
oQ = 2'b00 ; |
图1.2.2 使用if-else-if语句描述的4~2优先编码器
代码1.2.3 4~2优先编码器的testbench(不可综合,仅用于仿真)
01 |
` timescale 1ns/1ns |
02 |
module encoder_tb; |
03 |
reg [3:0] i_a; |
04 |
wire [1:0] o_q; |
05 |
06 |
initial |
07 |
begin |
08 |
i_a = 4'b0 ; |
09 |
forever |
10 |
begin |
11 |
if (i_a<16) |
12 |
#20 i_a <= i_a + 1'b1 ; |
13 |
else |
14 |
#20 i_a <= 4'b0 ; |
15 |
end |
16 |
end |
17 |
18 |
initial #330 $ stop ; |
19 |
20 |
encoder encoder_inst( |
21 |
.iA(i_a), |
22 |
.oQ(o_q) |
23 |
); |
24 |
|
25 |
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语句描述的译码器(可综合)
01 |
module decoder( |
02 |
input [1:0] iD, |
03 |
output reg [3:0] oQ |
04 |
); |
05 |
06 |
always @ (*) |
07 |
begin |
08 |
oQ = 4'b0000 ; |
09 |
case (iD) |
10 |
2'b00 : oQ = 4'b0001 ; |
11 |
2'b01 : oQ = 4'b0010 ; |
12 |
2'b10 : oQ = 4'b0100 ; |
13 |
2'b11 : oQ = 4'b1000 ; |
14 |
endcase |
15 |
end |
16 |
17 |
endmodule |
第6~15行,由于输入刚好是2^n个选项,因此没有加入default语句(没有其他选项可能出现),而在敏感列表之后给输出寄存器赋初值(避免生成锁存器)这一良好的代码风格一定要提现出来。
01 |
always @ (*) |
02 |
begin |
03 |
oQ = 4'b0000 ; |
04 |
case (iD) |
05 |
2'b00 : oQ = 4'b0001 ; |
06 |
2'b01 : oQ = 4'b0010 ; |
07 |
2'b10 : oQ = 4'b0100 ; |
08 |
2'b11 : oQ = 4'b1000 ; |
09 |
endcase |
10 |
end |
图2.1 使用case语句描述的译码器的RTL视图
代码2.2 使用case语句描述的译码器的testbench(不可综合,仅用于仿真)
01 |
` timescale 1ns/1ns |
02 |
module decoder_tb; |
03 |
reg [1:0] i_d; |
04 |
wire [3:0] o_q; |
05 |
06 |
initial |
07 |
begin |
08 |
i_d = 2'b00 ; |
09 |
#20 i_d = 2'b01 ; |
10 |
#20 i_d = 2'b10 ; |
11 |
#20 i_d = 2'b11 ; |
12 |
#20 $ stop ; |
13 |
end |
14 |
15 |
decoder decoder_inst( |
16 |
.iD(i_d), |
17 |
.oQ(o_q) |
18 |
); |
19 |
20 |
endmodule |
图2.2 使用case语句描述的译码器的功能仿真波形
3 码型转换器
有了编码器和译码器的基础,下面我们举一小例来讨论一下码型转换器。此处以共阳的七段数码管段码查找表为例。
图3.1 七段数码管
表3.1 七段数码管段码查找表
代码3.1 七段数码管段码查找表(可综合)
01 |
module seg_transcoder( |
02 |
input [3:0] iNum, |
03 |
output reg [7:0] oSeg |
04 |
); |
05 |
06 |
always @ (*) |
07 |
begin |
08 |
case (iNum) |
09 |
4'h0 : oSeg = 8'hC0 ; |
10 |
4'h1 : oSeg = 8'hF9 ; |
11 |
4'h2 : oSeg = 8'hA4 ; |
12 |
4'h3 : oSeg = 8'hB0 ; |
13 |
4'h4 : oSeg = 8'h99 ; |
14 |
4'h5 : oSeg = 8'h92 ; |
15 |
4'h6 : oSeg = 8'h82 ; |
16 |
4'h7 : oSeg = 8'hF8 ; |
17 |
4'h8 : oSeg = 8'h80 ; |
18 |
4'h9 : oSeg = 8'h90 ; |
19 |
4'hA : oSeg = 8'h88 ; |
20 |
4'hB : oSeg = 8'h83 ; |
21 |
4'hC : oSeg = 8'hC6 ; |
22 |
4'hD : oSeg = 8'hA1 ; |
23 |
4'hE : oSeg = 8'h86 ; |
24 |
4'hF : oSeg = 8'h8E ; |
25 |
default : oSeg = 8'hFF ; |
26 |
endcase |
27 |
end |
28 |
29 |
endmodule |
图3.1 七段数码管段码查找表的RTL视图
代码3.2 七段数码管段码查找表的testbench(不可综合,仅用于仿真)
01 |
` timescale 1ns/1ns |
02 |
module seg_transcoder_tb; |
03 |
reg [3:0] i_num; |
04 |
wire [7:0] o_seg; |
05 |
06 |
initial begin |
07 |
i_num = 4'h0 ; |
08 |
for (i_num=0; i_num< 4'hF ; i_num=i_num+ 1'b1 ) |
09 |
#20 ; |
10 |
end |
11 |
12 |
initial #330 $ stop ; |
13 |
14 |
seg_transcoder seg_transcoder_inst( |
15 |
.iNum(i_num), |
16 |
.oSeg(o_seg) |
17 |
); |
18 |
19 |
endmodule |
第8~9行,使用for语句来产生激励数据。注意for内不能没有表达式哟。for语句主要用作复制电路,没有语句,就像此处一样,用“;”表示无需复制电路。
1 |
for (i_num=0; i_num< 4'hF ; i_num=i_num+ 1'b1 ) |
2 |
#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