第06课 · 触发器
时序逻辑D触发器T触发器JK触发器
📌 学习目标:掌握 D/T/JK 三种触发器的原理、特性表和 Verilog 实现,通过 Verilator 验证时序行为。
一、从组合逻辑到时序逻辑
组合逻辑的输出仅取决于当前输入,没有记忆能力。时序逻辑引入了存储元件,输出取决于当前输入和历史状态。
触发器(Flip-Flop)是最基本的存储元件:
- 存储 1 位二进制信息
- 在时钟边沿采样输入、更新输出
- 是寄存器、计数器、状态机的基础
二、D 触发器(Data Flip-Flop)
D 触发器是最常用的触发器,在 RISC-V CPU 中广泛使用。
功能:在时钟上升沿,将 D 输入传递到 Q 输出。
| CLK 上升沿 | D | Q(下一状态) | Q̄ |
| ↑ | 0 | 0 | 1 |
| ↑ | 1 | 1 | 0 |
带异步复位和使能的 D 触发器:
| rst | en | CLK ↑ | D | Q(next) |
| 1 | X | X | X | 0 |
| 0 | 0 | ↑ | X | Q(保持) |
| 0 | 1 | ↑ | 0 | 0 |
| 0 | 1 | ↑ | 1 | 1 |
三、T 触发器(Toggle Flip-Flop)
T 触发器在 T=1 时翻转输出,T=0 时保持。常用于计数器设计。
| CLK ↑ | T | Q(next) |
| ↑ | 0 | Q(保持) |
| ↑ | 1 | Q̄(翻转) |
特征方程:Q(next) = T ⊕ Q
四、JK 触发器
JK 触发器是功能最全的触发器,可以实现 D 和 T 触发器的所有功能。
| CLK ↑ | J | K | Q(next) | 功能 |
| ↑ | 0 | 0 | Q | 保持 |
| ↑ | 0 | 1 | 0 | 复位 |
| ↑ | 1 | 0 | 1 | 置位 |
| ↑ | 1 | 1 | Q̄ | 翻转 |
特征方程:Q(next) = J·Q̄ + K̄·Q
五、Verilog 实现
module d_ff (
input clk, rst, en, d,
output reg q
);
always @(posedge clk or posedge rst) begin
if (rst)
q <= 1'b0;
else if (en)
q <= d;
end
endmodule
module t_ff (
input clk, rst, t,
output reg q
);
always @(posedge clk or posedge rst) begin
if (rst)
q <= 1'b0;
else if (t)
q <= ~q;
end
endmodule
module jk_ff (
input clk, rst, j, k,
output reg q
);
always @(posedge clk or posedge rst) begin
if (rst)
q <= 1'b0;
else
case ({j, k})
2'b00: q <= q;
2'b01: q <= 1'b0;
2'b10: q <= 1'b1;
2'b11: q <= ~q;
endcase
end
endmodule
5.1 测试台
module tb_flipflops;
reg clk, rst;
reg d_en, d_in;
wire d_q;
reg t_in;
wire t_q;
reg j_in, k_in;
wire jk_q;
d_ff u_dff (.clk(clk), .rst(rst), .en(d_en), .d(d_in), .q(d_q));
t_ff u_tff (.clk(clk), .rst(rst), .t(t_in), .q(t_q));
jk_ff u_jkff (.clk(clk), .rst(rst), .j(j_in), .k(k_in), .q(jk_q));
integer pass = 0, fail = 0;
task check_d; input expected; begin
if (d_q !== expected) begin
$display("FAIL D_FF: expected=%b got=%b", expected, d_q); fail=fail+1;
end else pass=pass+1; end
endtask
task check_t; input expected; begin
if (t_q !== expected) begin
$display("FAIL T_FF: expected=%b got=%b", expected, t_q); fail=fail+1;
end else pass=pass+1; end
endtask
task check_jk; input expected; begin
if (jk_q !== expected) begin
$display("FAIL JK_FF: expected=%b got=%b", expected, jk_q); fail=fail+1;
end else pass=pass+1; end
endtask
always #5 clk = ~clk;
initial begin
clk = 0; rst = 1;
d_en = 0; d_in = 0;
t_in = 0;
j_in = 0; k_in = 0;
#12; rst = 0;
d_en = 1; d_in = 1; #10; check_d(1);
d_en = 0; d_in = 0; #10; check_d(1);
d_en = 1; d_in = 0; #10; check_d(0);
t_in = 1; #10; check_t(1);
#10; check_t(0);
#10; check_t(1);
t_in = 0; #10; check_t(1);
j_in = 1; k_in = 0; #10; check_jk(1);
j_in = 0; k_in = 0; #10; check_jk(1);
j_in = 0; k_in = 1; #10; check_jk(0);
j_in = 1; k_in = 1; #10; check_jk(1);
#10; check_jk(0);
rst = 1; #10;
check_d(0); check_t(0); check_jk(0);
$display("========================================");
$display("触发器测试: PASS=%0d FAIL=%0d", pass, fail);
if (fail == 0) $display("✅ D/T/JK 触发器时序验证全部通过!");
else $display("❌ 存在失败!");
$display("========================================");
$finish;
end
endmodule
六、Verilator 编译命令
verilator --cc flipflops.v --exe tb_flipflops.v \
--build --top-module tb_flipflops
./obj_dir/Vtb_flipflops
七、触发器的关键概念
| 类型 | 特征方程 | 主要用途 |
| D FF | Q(n+1) = D | 数据寄存、流水线寄存 |
| T FF | Q(n+1) = T ⊕ Q | 计数器、分频器 |
| JK FF | Q(n+1) = JQ̄ + K̄Q | 通用触发器 |
🤔 思考题:如何用 D 触发器实现 T 触发器?如何用 JK 触发器实现 D 触发器?
💡 提示:D_FF + XOR = T_FF;JK_FF 令 K=J̄ 即为 D_FF
🏆 成就解锁:时序逻辑入门
✅ Verilator 仿真验证通过
✅ D 触发器:加载、保持、复位验证正确
✅ T 触发器:翻转、保持验证正确
✅ JK 触发器:置位、复位、保持、翻转验证正确
🎯 下一目标:多位寄存器 → 第07课:寄存器