安德鲁

[文档].艾米电子 - 编码器与译码器.[ModelSim][Quartus II][Verilog]

0
阅读(4783)

对读者的假设

已经掌握:

内容

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 `timescale1ns/1ns
02 moduleencoder_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 moduleencoder(
02 input[3:0] iA,
03 outputreg[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 moduleencoder(
02 input[3:0] iA,
03 outputreg[1:0] oQ
04 );
05
06 always@ (*)
07 if(iA[3])
08 oQ =2'b11;
09 elseif(iA[2])
10 oQ =2'b10;
11 elseif(iA[1])
12 oQ =2'b01;
13 elseif(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 elseif(iA[2])
05 oQ =2'b10;
06 elseif(iA[1])
07 oQ =2'b01;
08 elseif(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 `timescale1ns/1ns
02 moduleencoder_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 moduledecoder(
02 input[1:0] iD,
03 outputreg[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 `timescale1ns/1ns
02 moduledecoder_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 moduleseg_transcoder(
02 input[3:0] iNum,
03 outputreg[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 `timescale1ns/1ns
02 moduleseg_transcoder_tb;
03 reg[3:0] i_num;
04 wire[7:0] o_seg;
05
06 initialbegin
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

Baidu
map