HDLBits答案(7)_Verilog多路选择器
Verilog多路选择器
定义
多路选择器(Multiplexer,简称MUX)是数字系统中最常用的电路之一。简单来说,它就像一个单刀多掷开关——根据选择信号的不同,从多个输入中选一个送到输出端。
让我们用更正式的语言来定义:
多路选择器是一个多输入、单输出的组合逻辑电路。它可以根据地址码(选择码)的不同,从多个输入数据流中选取一个,让其输出到公共的输出端。
打个生活中的比方:多路选择器就像一个电梯的楼层选择器——你按下想去的楼层按钮(选择信号),电梯就会把你带到对应的楼层(选择对应的输入)。
部分练习题
让我们通过一系列的题目来掌握多路选择器的设计。
题目1:2-to-1多路选择器(1位宽)
题目描述1:实现一个位宽为1的2-to-1多路选择器。当 sel = 0,选择 a;当 sel = 1,选择 b。
Solution1:
1 | module top_module( |
小贴士:2选1多路选择器用三目运算符写起来最简洁!
题目2:2-to-1多路选择器(100位宽)
题目描述2:实现一个位宽为100的2-to-1多路选择器。当 sel = 0,选择 a;当 sel = 1,选择 b。
Solution2:
1 | module top_module( |
看到了吗?和1位的情况几乎一模一样,只是位宽变了而已!这就是Verilog的方便之处——向量操作可以大大简化代码。
题目3:9-to-1多路选择器(16位宽)
题目描述3:创建一个位宽为16的9-to-1多路选择器。
sel = 0选择asel = 1选择b- ……
sel = 8选择i
对于未使用的 sel 值(sel = 9~15),将所有输出位设置为1。
Solution3:
1 | module top_module( |
小贴士:选择分支超过2个时,用case语句比嵌套if-else或三目运算符更清晰!
题目4:256-to-1多路选择器(1位宽)
题目描述4:创建一个位宽为1的256-to-1多路选择器。256个输入都被打包成一个256位的输入向量。
sel = 0表示选择in[0]sel = 1表示选择in[1]- ……
Solution4:
1 | module top_module( |
Verilog允许直接用变量作为向量的索引,这在设计多路选择器时特别方便!
题目5:256-to-1多路选择器(4位宽)
题目描述5:创建一个位宽为4的256-to-1多路选择器。256个4位输入都被打包成一个1024位的输入向量。
sel = 0选择in[3:0]sel = 1选择in[7:4]sel = 2选择in[11:8]- ……
这个题目稍微有点挑战性!因为每个输入是4位,我们需要选择连续的4位。
这时候就需要用到Verilog的一个特殊语法:+:和-:操作符。
先看一下这两个操作符的用法:
1 | data[0 +: 8] // 等价于 data[7:0] |
格式说明:
起始位置 +: 位宽:从起始位置开始,向下(索引增大的方向)选指定位宽起始位置 -: 位宽:从起始位置开始,向上(索引减小的方向)选指定位宽
Solution5:
1 | module top_module( |
是不是很优雅?用+:操作符一行就搞定了!
入门者避坑指南
多路选择器虽然简单,但初学者也容易踩一些坑:
错误1:没有覆盖所有情况
错误表现:1
2
3
4
5
6
7
8
9
10
11
12
13
14module bad_example (
input [1:0] sel,
input [3:0] a, b, c,
output reg [3:0] y
);
always @(*) begin
case(sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
// 漏掉了sel == 2'b11的情况!
endcase
end
endmodule
错误原因:
这会生成锁存器!因为当sel是2’b11时,y保持原值不变。
正确做法:1
2
3
4
5
6
7
8
9
10
11
12
13
14module good_example (
input [1:0] sel,
input [3:0] a, b, c,
output reg [3:0] y
);
always @(*) begin
case(sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
2'b11: y = 4'd0; // 加上default情况
endcase
end
endmodule
或者用先赋初值的方法:
1 | module good_example2 ( |
调试技巧:
- 写case语句时,先把default写上,然后再填其他分支
- 或者,在always块开头先给所有输出赋初值
错误2:索引越界
错误表现:1
2
3
4
5
6
7
8module bad_example (
input [99:0] in,
input [7:0] sel,
output out
);
// sel是8位,可能的值是0-255,但in只有100位!
assign out = in[sel];
endmodule
错误原因:
当sel >= 100时,索引越界了!虽然有些仿真器和综合工具可能会处理,但这是不安全的。
正确做法:1
2
3
4
5
6
7
8module good_example (
input [99:0] in,
input [7:0] sel,
output out
);
// 先检查sel是否在有效范围内
assign out = (sel < 8'd100) ? in[sel] : 1'b0;
endmodule
或者:
1 | module good_example2 ( |
错误3:误用+:和-:
错误表现:1
2
3
4
5
6
7
8module bad_example (
input [1023:0] in,
input [7:0] sel,
output [3:0] out
);
// 错误!`+:`的右边应该是位宽,不是结束位置
assign out = in[sel*4 +: sel*4 + 3];
endmodule
错误原因:+:和-:的第二个操作数是位宽,不是结束位置!
正确做法:1
2
3
4
5
6
7
8module good_example (
input [1023:0] in,
input [7:0] sel,
output [3:0] out
);
// 正确:sel*4是起始位置,4是位宽
assign out = in[sel*4 +: 4];
endmodule
调试技巧:
- 记住:
+:和-:后面跟的是位宽,不是结束索引 - 如果记不住,可以用传统的方式,但要注意位宽是否固定
错误4:选择方式不恰当
错误表现:1
2
3
4
5
6
7
8
9
10module bad_example (
input [1:0] sel,
input [3:0] a, b, c, d,
output [3:0] y
);
// 2选1用三目运算符没问题,但4选1还嵌套就太乱了
assign y = (sel == 2'd0) ? a :
(sel == 2'd1) ? b :
(sel == 2'd2) ? c : d;
endmodule
虽然语法上没问题,但可读性很差!
正确做法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15module good_example (
input [1:0] sel,
input [3:0] a, b, c, d,
output reg [3:0] y
);
always @(*) begin
case(sel)
2'd0: y = a;
2'd1: y = b;
2'd2: y = c;
2'd3: y = d;
default: y = 4'd0;
endcase
end
endmodule
调试技巧:
- 2选1:推荐用三目运算符
- 3选1及以上:推荐用case语句
- 如果有优先级:用if-else
错误5:位宽不匹配
错误表现:1
2
3
4
5
6
7
8module bad_example (
input [3:0] a, b,
input sel,
output [2:0] y
);
// 错误!a和b是4位,y是3位,位宽不匹配
assign y = sel ? b : a;
endmodule
错误原因:
虽然有些工具会自动截断高位,但这可能导致意外的结果。最好显式指定位宽匹配。
正确做法:1
2
3
4
5
6
7
8module good_example (
input [3:0] a, b,
input sel,
output [2:0] y
);
// 显式取低3位
assign y = sel ? b[2:0] : a[2:0];
endmodule
调试技巧:
- 确保选择器的输入和输出位宽一致
- 如果位宽不同,显式说明如何处理(截断、补零等)
本章小结
这一章我们学习了多路选择器的设计:
- 多路选择器的定义:根据选择信号,从多个输入中选一个输出
- 几种实现方式:
- 2选1:三目运算符最简洁
sel ? b : a - 多选一:case语句最清晰
- 索引选择:直接用变量作为索引
in[sel]
- 2选1:三目运算符最简洁
- 高级技巧:
+:和-:操作符,用于选择连续的多位 - 注意事项:
- 覆盖所有选择情况,避免生成锁存器
- 注意索引不要越界
- 选择合适的实现方式,保证代码可读性
多路选择器是数字设计中最基础、最常用的模块之一,一定要熟练掌握!




