[文档].艾米电子 - 多路选择器与多路分解器.[ModelSim][Quartus II][Verilog]
0赞对读者的假设
已经掌握:
内容
1 多路选择器Multiplexer
此处所说的多路选择器,为组合逻辑电路中的多路多路选择器:多路输入,一路输出。
1.1 不带优先级的多路选择器
1.1.1 使用case语句描述
此处以4选1多路选择器为例:
代码1.1 4选1多路选择器(可综合)
01 |
` timescale 1ns/1ns |
02 |
module multiplexer( |
03 |
input iA, |
04 |
input iB, |
05 |
input iC, |
06 |
input iD, |
07 |
input [1:0] iSel, |
08 |
output reg oQ |
09 |
); |
10 |
11 |
always @ (*) |
12 |
case (iSel) |
13 |
2'b00 : oQ = iA; |
14 |
2'b01 : oQ = iB; |
15 |
2'b10 : oQ = iC; |
16 |
2'b11 : oQ = iD; |
17 |
endcase |
18 |
19 |
endmodule |
第10~16行,使用case语句来实现4选1多路选择器。因为是2^n个case选项,所以此处没有使用default语句。下面我会使用一个3选1的多路选择器来说明default的作用。
1 |
always @ (*) |
2 |
case (iSel) |
3 |
2'b00 : oQ = iA; |
4 |
2'b01 : oQ = iB; |
5 |
2'b10 : oQ = iC; |
6 |
2'b11 : oQ = iD; |
7 |
endcase |
图1.1 4选1多路选择器的RTL视图
由图1.1所示,在2^n个case选项时,没有加上default语句,其综合的结果为并行的MUX。
代码1.2 4选1多路选择器testbench(不可综合,仅用于仿真)
01 |
` timescale 1ns/1ns |
02 |
module multiplexer_tb; |
03 |
reg i_a, i_b, i_c, i_d; |
04 |
reg [1:0] i_sel; |
05 |
wire o_q; |
06 |
07 |
initial begin |
08 |
i_a = 1; i_b = 0; i_c = 0; i_d = 0; |
09 |
#20 i_a = 1; i_b = 0; i_c = 1; i_d = 1; |
10 |
#20 i_a = 0; i_b = 0; i_c = 1; i_d = 0; |
11 |
#20 i_a = 1; i_b = 1; i_c = 1; i_d = 0; |
12 |
end |
13 |
14 |
initial begin |
15 |
i_sel = 2'b00 ; |
16 |
#20 i_sel = 2'b01 ; |
17 |
#20 i_sel = 2'b10 ; |
18 |
#20 i_sel = 2'b11 ; |
19 |
#20 $ stop ; |
20 |
end |
21 |
22 |
multiplexer multiplexer_inst( |
23 |
.iA (i_a), |
24 |
.iB (i_b), |
25 |
.iC (i_c), |
26 |
.iD (i_d), |
27 |
.iSel (i_sel), |
28 |
.oQ (o_q) |
29 |
); |
30 |
31 |
endmodule |
第3~5行声明了一些变量,使用的标准为:映射为所需测试模块的输入信号,即需要使用initial或always来给出激励,因此多声明为reg类型;而映射为所需测试模块的输出信号,不需要激励,声明为wire类型即可。
1 |
reg i_a, i_b, i_c, i_d; |
2 |
reg [1:0] i_sel; |
3 |
wire o_q; |
第21行,$stop表示仿真在此时刻终止。更多$打头的函数,请参考Verilog语法书。
1 |
#20 $ stop ; |
图1.2 4选1多路选择器的功能仿真波形
图1.3 4选1多路选择器的门级仿真波形
由图1.2和图1.3所示,在2^n个case选项时,没有加上default语句,其功能仿真和门级仿真保持一致。
1.2 怎么多出个锁存器?
此处以3选1多路选择器为例。
代码1.3 3选1多路选择器1(可综合)
01 |
module multiplexer( |
02 |
input iA, |
03 |
input iB, |
04 |
input iC, |
05 |
input [1:0] iSel, |
06 |
output reg oQ |
07 |
); |
08 |
09 |
always @ (*) |
10 |
case (iSel) |
11 |
2'b00 : oQ = iA; |
12 |
2'b01 : oQ = iB; |
13 |
2'b10 : oQ = iC; |
14 |
endcase |
15 |
16 |
endmodule |
图1.4 3选1多路选择器1的RTL视图
如图1.4所示,综合出来了锁存器。下面我们讨论如何消除这个锁存器。
方法1 加(* full_case *)修饰
代码1.4 3选1多路选择器2(可综合)
01 |
module multiplexer( |
02 |
input iA, |
03 |
input iB, |
04 |
input iC, |
05 |
input [1:0] iSel, |
06 |
output reg oQ |
07 |
); |
08 |
09 |
always @ (*) |
10 |
(* full_case *) case (iSel) |
11 |
2'b00 : oQ = iA; |
12 |
2'b01 : oQ = iB; |
13 |
2'b10 : oQ = iC; |
14 |
endcase |
15 |
16 |
endmodule |
第10行,加(* full_case *)修饰,强制综合成full case。
1 |
(* full_case *) case (iSel) |
图1.5 3选1多路选择器2的RTL视图
方法2 使用default语句
代码1.5 3选1多路选择器3(可综合)
01 |
module multiplexer( |
02 |
input iA, |
03 |
input iB, |
04 |
input iC, |
05 |
input [1:0] iSel, |
06 |
output reg oQ |
07 |
); |
08 |
09 |
always @ (*) |
10 |
case (iSel) |
11 |
2'b00 : oQ = iA; |
12 |
2'b01 : oQ = iB; |
13 |
2'b10 : oQ = iC; |
14 |
default : oQ = 1'b0 ; |
15 |
endcase |
16 |
17 |
endmodule |
第14行,加上default语句,来描述缺省动作。
1 |
default : oQ = 1'b0 ; |
图1.6 3选1多路选择器3的RTL视图
方法3 在敏感列表后,赋初值
代码1.6 3选1多路选择器4(可综合)
01 |
module multiplexer( |
02 |
input iA, |
03 |
input iB, |
04 |
input iC, |
05 |
input [1:0] iSel, |
06 |
output reg oQ |
07 |
); |
08 |
09 |
always @ (*) |
10 |
begin |
11 |
oQ = 1'b0 ; |
12 |
case (iSel) |
13 |
2'b00 : oQ = iA; |
14 |
2'b01 : oQ = iB; |
15 |
2'b10 : oQ = iC; |
16 |
endcase |
17 |
end |
18 |
19 |
endmodule |
注意第9~17,在代码1.3的基础上,于敏感列表之后,加入oQ的初值。
1 |
always @ (*) |
2 |
begin |
3 |
oQ = 1'b0 ; |
4 |
case (iSel) |
5 |
2'b00 : oQ = iA; |
6 |
2'b01 : oQ = iB; |
7 |
2'b10 : oQ = iC; |
8 |
endcase |
9 |
end |
图1.7 3选1多路选择器4的RTL视图
方法123的比较
代码1.7 3选1多路选择器的testbench(不可综合,仅用于仿真)
01 |
` timescale 1ns/1ns |
02 |
module multiplexer_tb; |
03 |
reg i_a, i_b, i_c; |
04 |
reg [1:0] i_sel; |
05 |
wire o_q; |
06 |
07 |
initial begin |
08 |
i_a = 0; i_b = 0; i_c = 0; |
09 |
#20 i_a = 1; i_b = 0; i_c = 0; |
10 |
#20 i_a = 1; i_b = 0; i_c = 1; |
11 |
#20 i_a = 0; i_b = 0; i_c = 1; |
12 |
#20 i_a = 1'bx ; i_b = 1'bx ; i_c = 1'bx ; |
13 |
end |
14 |
15 |
initial begin |
16 |
i_sel = 2'bxx ; |
17 |
#20 i_sel = 2'b00 ; |
18 |
#20 i_sel = 2'b01 ; |
19 |
#20 i_sel = 2'b10 ; |
20 |
#20 i_sel = 2'b11 ; |
21 |
#20 $ stop ; |
22 |
end |
23 |
24 |
multiplexer multiplexer_inst( |
25 |
.iA (i_a), |
26 |
.iB (i_b), |
27 |
.iC (i_c), |
28 |
.iSel (i_sel), |
29 |
.oQ (o_q) |
30 |
); |
31 |
32 |
endmodule |
图1.8 方法1的功能仿真波形
图1.9 方法1的门级仿真波形
由图1.8和图1.9所示,虽然方法1加了(* full_case *)修饰后,3选1多路选择器的综合结果没有产生锁存器,但是其功能仿真波形却当作锁存器来处理,而其门级仿真波形则是当作无关项来处理。即方法1,其前 后仿真结果是不一致的。至于其他的特点,本文暂不讨论。想要了解更多full case的信息,请阅读参考2。
图1.10 方法2的功能仿真波形
图1.11 方法2的门级仿真波形
如图1.10和1.11所示,方法2使用了default语句,其前后仿真和综合的RTL视图一致。萧鸿森大哥提醒我们:正确的做法是,default指定输出0或者某个选项。
图1.12 方法3的功能仿真波形
图1.13 方法3的门级仿真波形
如图1.12和1.13所示,方法3在敏感列表后,赋初值也可以很好解决锁存器的问题。
1.1.3 结论
虽然方法123都可以解决锁存器的问题,但是:方法1的前后仿真结果不同;方法3的其他特点暂时没有研究。因此此处只推荐大家使用方法2,即要实现并行的mux,当case选项不是2^n个的时候,加上default指定输出0或某个选项是最安全的(虽然Verilog语法没有强制加入default)。
1.2 带优先级的多路选择器
1.2.1 使用if-else-if描述
此处以3选1多路选择器为例。
代码2.1 使用if-else-if描述的3选1多路选择器(可综合)
01 |
module multiplexer( |
02 |
input iA, |
03 |
input iB, |
04 |
input iC, |
05 |
input [1:0] iSel, |
06 |
output reg oQ |
07 |
); |
08 |
09 |
always @ (*) |
10 |
if (iSel == 2'b0 ) |
11 |
oQ = iA; |
12 |
else if (iSel == 2'b01 ) |
13 |
oQ = iB; |
14 |
else |
15 |
oQ = iC; |
16 |
17 |
endmodule |
第9~15行,使用if-else-if描述带优先级的多路选择器。有人看到我的代码会有一点小疑问?为什么always块没有加begin- end的。其实呢这个begin-end就相当于C语言里面的{},与C语言类似,当后面的语句条数为1时,可以省略begin-end。而if- else-if恰好为一个完整的语句。
1 |
always @ (*) |
2 |
if (iSel == 2'b0 ) |
3 |
oQ = iA; |
4 |
else if (iSel == 2'b01 ) |
5 |
oQ = iB; |
6 |
else |
7 |
oQ = iC |
图1.14 使用if-else-if描述的3选1多路选择器的RTL视图
1.2.2 使用“: ?”描述
既然使用if-else-if可以描述带优先级的多路选择器,那么使用“: ?”当然也是可行的。
代码2.2 使用“: ?”描述的3选1多路选择器(可综合)
01 |
module multiplexer( |
02 |
input iA, |
03 |
input iB, |
04 |
input iC, |
05 |
input [1:0] iSel, |
06 |
output oQ |
07 |
); |
08 |
09 |
assign oQ = (iSel == 2'b00 ) ? iA : |
10 |
(iSel == 2'b01 ) ? iB : iC; |
11 |
|
12 |
endmodule |
第9~10行,使用“: ?”描述带优先级的多路选择器。
1 |
assign oQ = (iSel == 2'b00 ) ? iA : |
2 |
(iSel == 2'b01 ) ? iB : iC; |
实际上等同于,为什么使用上面这种写法呢?因为“: ?”的运算优先级比较低,所以不必加括号。
1 |
assign oQ = (iSel == 2'b00 ) ? iA : |
2 |
((iSel == 2'b01 ) ? iB : iC); |
注意:第6行的声明,由于是使用assign语句赋值,因此无需声明成reg类型。
1 |
output oQ |
图1.15 使用“: ?”描述的3选1多路选择器的RTL视图
如图1.15所示,使用“: ?”描述多路选择器与if-else-if描述完全一致,因此两种描述方式可以互换。
2 多路分解器De-Multiplexer
多路分解器,也叫数据选择器,顾名思义即多路输入,一路输出。有了前面的基础,那么描述一个多路分解器,便是轻而易举的事情了。
代码2.1 使用case描述多路分解器
01 |
module de_multiplexer( |
02 |
input iA, |
03 |
input [1:0] iSel, |
04 |
output reg oQ_0, |
05 |
output reg oQ_1, |
06 |
output reg oQ_2 |
07 |
); |
08 |
09 |
always @ (*) |
10 |
begin |
11 |
oQ_0 = 1'b0 ; |
12 |
oQ_1 = 1'b0 ; |
13 |
oQ_2 = 1'b0 ; |
14 |
case (iSel) |
15 |
2'b00 : oQ_0 = iA; |
16 |
2'b01 : oQ_1 = iA; |
17 |
2'b10 : oQ_2 = iA; |
18 |
default : ; |
19 |
endcase |
20 |
end |
21 |
22 |
endmodule |
第9~20行,在敏感列表后,追加初始值,可以有效地避免生成锁存器。
01 |
always @ (*) |
02 |
begin |
03 |
oQ_0 = 1'b0 ; |
04 |
oQ_1 = 1'b0 ; |
05 |
oQ_2 = 1'b0 ; |
06 |
case (iSel) |
07 |
2'b00 : oQ_0 = iA; |
08 |
2'b01 : oQ_1 = iA; |
09 |
2'b10 : oQ_2 = iA; |
10 |
default : ; |
11 |
endcase |
12 |
end |
1 |
|
图2.1 代码2.1综合后的RTL视图
代码2.2 代码2.1的testbench
01 |
` timescale 1ns/1ns |
02 |
module de_multiplexer_tb; |
03 |
reg i_a; |
04 |
reg [1:0] i_sel; |
05 |
wire o_q_0, o_q_1, o_q_2; |
06 |
07 |
initial begin |
08 |
i_a = 0; |
09 |
forever #10 i_a = ~i_a; |
10 |
end |
11 |
12 |
initial begin |
13 |
i_sel = 2'b00 ; |
14 |
#20 i_sel = 2'b01 ; |
15 |
#20 i_sel = 2'b10 ; |
16 |
#20 i_sel = 2'b11 ; |
17 |
#20 $ stop ; |
18 |
end |
19 |
20 |
de_multiplexer de_multiplexer_inst( |
21 |
.iA(i_a), |
22 |
.iSel(i_sel), |
23 |
.oQ_0(o_q_0), |
24 |
.oQ_1(o_q_1), |
25 |
.oQ_2(o_q_2) |
26 |
); |
27 |
28 |
endmodule |
图2.1 代码2.1的功能仿真波形
图2.2 代码2.1的门级仿真波形
图2.1和图2.2的所示波形与代码2.1一致。
辅助阅读
1. Wikipedia.Multiplexer
2. Altera.Recommended HDL Coding Styles
参考
1. 刘福奇, 刘波.Verilog HDL应用程序设计实例精讲.电子工业出版社
2. Cliff Cummings' Award-Winning Verilog & SystemVerilog Papers. "full_case parallel_case", the Evil Twins of Verilog Synthesis
3. 萧鸿森.(原創) 多工器MUX coding style整理 (SOC) (Verilog) (Quartus II)