第12章:组合逻辑电路设计之译码器
本章对应原书第 338~342 页,属于「第四篇 数字电路篇」。
本章在讲什么?(先看这个)
上一章的选择器是"多选一",这一章的译码器则反过来——用很少的输入组合,去控制很多路输出。3-8 译码器就是用 3 个输入位的 8 种组合,一一对应地点亮 8 路输出中的一路。"译码"这个词很形象:把压缩过的二进制编码"翻译"回一条条具体的控制信号。本章继续用"框图 → 真值表 → 波形图 → 代码 → 仿真"的套路,重点学习位拼接符 {} 的用法,并强化 case 语句的规范写法。
核心概念拆解
译码器简介
译码器(Decoder)是一种多输入、多输出的组合逻辑电路器件。它的工作原理是:将输入的二进制代码的状态信息转换成对应的输出控制信号。译码器广泛应用于数字电路和通信系统中,具体应用包括内存地址译码、数字显示、数据选择等。
打个比方:酒店房间号 101~108 是 3 位"编码",前台(译码器)根据你报的号码,精确点亮对应房间的灯——3 位编码能表示 8 个房间。
译码器的分类包括二进制译码器、BCD 译码器和七段显示译码器。其中二进制译码器实现 n 位二进制输入到 2^n 路输出的转换,典型如 74HC138 型 3-8 译码器。
3-8 译码器属于二进制译码器,也称为变量译码器:它将 3 位二进制输入信号转换为 8 个输出信号中的一个有效电平,每个输出信号对应输入值的一个唯一组合。
硬件设计
由于 3-8 译码器的上板验证需要用到 8 个 LED 灯,而开发板的 LED 灯数量不够,所以本实验只进行仿真验证,不进行上板测试。
实验任务
设计一个 3-8 译码器并进行仿真验证。
程序设计——模块设计
3-8 译码器借助 3 个输入信号形成 8 种二进制组合,进而控制 8 个输出端口呈现 8 种不同的状态。模块框图如下:

模块端口与功能描述:
| 信号名 | 方向 | 端口说明 |
|---|---|---|
| A | 输入 | 输入信号A |
| B | 输入 | 输入信号B |
| C | 输入 | 输入信号C |
| out | 输出 | 译码后的输出信号out |
绘制波形图
输入为 3 个 1bit 信号,任意二进制组合共 8 种,每种组合与 out 输出 8bit 的 8 种状态一一对应。先列真值表,再画波形:
| A | B | C | out |
|---|---|---|---|
| 0 | 0 | 0 | 0000_0001 |
| 0 | 0 | 1 | 0000_0010 |
| 0 | 1 | 0 | 0000_0100 |
| 0 | 1 | 1 | 0000_1000 |
| 1 | 0 | 0 | 0001_0000 |
| 1 | 0 | 1 | 0010_0000 |
| 1 | 1 | 0 | 0100_0000 |
| 1 | 1 | 1 | 1000_0000 |

规律很明显:out 中为 1 的那一位的位置,正好就是输入 ABC 组成的二进制数的值——这就是"译码"。
编写代码
使用上一章学习的 case 语句来实现:
module decoder3_8(
input A,
input B,
input C,
output reg [7:0] out
);
//*****************************************************
//** main code
//*****************************************************
//define wire
wire [2:0] sel;
//使用assign语句进行位拼接
assign sel = {A, B, C}; //使用"{}"位拼接符将3个1bit数据按照顺序拼成一个3bit数据
//out:根据输入的3bit sel信号选择输出对应的8bit out信号
always@(*) begin
case(sel)
3'b000 : out = 8'b0000_0001;
3'b001 : out = 8'b0000_0010;
3'b010 : out = 8'b0000_0100;
3'b011 : out = 8'b0000_1000;
3'b100 : out = 8'b0001_0000;
3'b101 : out = 8'b0010_0000;
3'b110 : out = 8'b0100_0000;
3'b111 : out = 8'b1000_0000;
default : ;
endcase
end
endmodule
代码解读:
- 第 13 行定义了一个位宽为 3 的信号 sel;
- 第 16 行使用 assign 语句,通过位拼接符
{}把三个 1bit 输入按 A、B、C 顺序拼成一个 3bit 数据赋给 sel。{A,B,C}中 A 是最高位、C 是最低位,这样 sel 的值就直接等于三个输入组成的二进制数; - 第 21~28 行用 case 语句把 8 种译码关系全部列举。因为情况已经全部覆盖,第 29 行的 default 其实可以省略,但为了代码规范性和良好习惯,默认都加上 default——可以指定任意一种情况,也可以像这里一样留空。
编写 TB 文件:
`timescale 1ns / 1ns
module tb_decoder_3_8();
//reg define
reg A;
reg B;
reg C;
//define wire
wire [7:0] out;
//信号初始化
initial begin
A = 0; B = 0; C = 0;
#200
A = 0; B = 0; C = 1;
#200
A = 0; B = 1; C = 0;
#200
A = 0; B = 1; C = 1;
#200
A = 1; B = 0; C = 0;
#200
A = 1; B = 0; C = 1;
#200
A = 1; B = 1; C = 0;
#200
A = 1; B = 1; C = 1;
#200
A = 0; B = 0; C = 0;
end
//例化3-8译码器模块
decoder3_8 decoder3_8_inst(
.A (A),
.B (B),
.C (C),
.out(out)
);
endmodule
第 1 行定义仿真单位和仿真精度都是 1ns;第 14~32 行的 initial 块先对三个输入赋初值 000,然后每隔 200ns 依次遍历 8 种输入组合(最后回到 000 形成闭环),正好覆盖全部译码情况。
仿真验证
打开 Modelsim 运行 10us:

波形中三个输入信号与输出信号 out 之间的对应关系和代码中的译码关系完全一致:输入 000 时 out 为 8'b0000_0001,输入 001 时 out 变为 8'b0000_0010……"1"的位置随输入值逐位左移,符合逻辑设计。
本章总结
本章讲解了如何用 Verilog 实现经典组合逻辑 3-8 译码器,并通过仿真验证。本章大部分语法(case、always@(*)、testbench 写法)都在上一章讲过,这里做了强化巩固,同时新增了两个要点:
- 位拼接
{}的使用:可以把多个信号按顺序拼成一个更宽的信号,本章正是用它把 3 个 1bit 输入拼成 3bit 选择信号,变相实现了"移位"式的译码选择; - case 语句的规范:即使条件已全部列举,也保留 default 分支。
图片对照
本章原文图片仅一张(模块框图与波形示意),已在模块设计小节插入。
初学者容易踩的坑
- 位拼接顺序搞反:
{A,B,C}中 A 在最高位,写成{C,B,A}译码结果就全错了,输出会对应到别的组合上。 - case 漏写 default:本章虽然 8 种情况全部列举,但养成写 default 的习惯非常重要,否则后续写不完整的 case 时会综合出锁存器。
- out 忘记声明为 reg:case 写在 always@(*) 块里,输出必须声明为
output reg [7:0]。 - 数字位宽不匹配:case 分支里的常量必须写足位宽,如
3'b000、8'b0000_0001,漏写位宽可能导致比较和赋值出错。 - 期望上板却无法上板:本实验需要 8 个 LED 而板载不足,只能仿真验证——这是正常的实验安排,不要误以为自己漏了步骤。
小结 & 下一步
本章通过 3-8 译码器巩固了组合逻辑设计流程,重点掌握了位拼接符 {} 和 case 语句的规范用法。建议动手拓展:上一章的 if 语句同样可以实现译码器——试着独立编写一个用 if-else 实现的 3-8 译码器代码,并仿真验证,对比两种写法的优缺点。