安德鲁

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

0
阅读(5162)

对读者的假设

已经掌握:

内容

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