第08课 · 计数器
时序逻辑二进制计数器BCDJohnson
📌 学习目标:实现二进制计数器、BCD 计数器(0-9循环)和 Johnson 计数器,理解不同计数模式的原理,并通过 Verilator 验证。
一、二进制计数器
二进制计数器是最基本的计数器,每个时钟周期加 1,到达最大值后回绕到 0。
N 位二进制计数器的计数范围:0 ~ 2ᴺ-1,模 = 2ᴺ
1.1 4位二进制计数器
计数序列:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, ...
1.2 带使能和加载的计数器
| rst | en | load | CLK ↑ | Q(next) |
| 1 | X | X | X | 0 |
| 0 | 0 | X | ↑ | Q(保持) |
| 0 | 1 | 1 | ↑ | D(加载) |
| 0 | 1 | 0 | ↑ | Q+1(计数) |
二、BCD 计数器
BCD(Binary-Coded Decimal)计数器按十进制计数,到 9 后回绕到 0。广泛用于数字显示。
计数序列:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, ...
三、Johnson 计数器
Johnson 计数器(扭环计数器)将最高位取反反馈到最低位。N 位 Johnson 计数器的模为 2N。
4位 Johnson 计数序列:
| 步骤 | Q3 | Q2 | Q1 | Q0 |
| 0 | 0 | 0 | 0 | 0 |
| 1 | 0 | 0 | 0 | 1 |
| 2 | 0 | 0 | 1 | 1 |
| 3 | 0 | 1 | 1 | 1 |
| 4 | 1 | 1 | 1 | 1 |
| 5 | 1 | 1 | 1 | 0 |
| 6 | 1 | 1 | 0 | 0 |
| 7 | 1 | 0 | 0 | 0 |
Johnson 计数器的优点:每次只有一个位变化(无毛刺),译码简单。
四、Verilog 实现
module binary_counter (
input clk, rst, en, load,
input [3:0] din,
output reg [3:0] q
);
always @(posedge clk) begin
if (rst)
q <= 4'b0;
else if (en) begin
if (load)
q <= din;
else
q <= q + 1;
end
end
endmodule
module bcd_counter (
input clk, rst, en,
output reg [3:0] q
);
always @(posedge clk) begin
if (rst)
q <= 4'b0;
else if (en) begin
if (q == 4'd9)
q <= 4'b0;
else
q <= q + 1;
end
end
endmodule
module johnson_counter (
input clk, rst, en,
output reg [3:0] q
);
always @(posedge clk) begin
if (rst)
q <= 4'b0;
else if (en)
q <= {~q[0], q[3:1]};
end
endmodule
4.1 测试台
module tb_counter;
reg clk, rst, en, load;
reg [3:0] din;
wire [3:0] bin_q, bcd_q, john_q;
binary_counter u_bin (.clk(clk), .rst(rst), .en(en), .load(load), .din(din), .q(bin_q));
bcd_counter u_bcd (.clk(clk), .rst(rst), .en(en), .q(bcd_q));
johnson_counter u_john (.clk(clk), .rst(rst), .en(en), .q(john_q));
integer pass = 0, fail = 0;
always #5 clk = ~clk;
initial begin
clk = 0; rst = 1; en = 0; load = 0; din = 0;
#12; rst = 0; en = 1;
#10; if (bin_q !== 1) begin $display("FAIL bin:1 got %0d", bin_q); fail=fail+1; end else pass=pass+1;
#10; if (bin_q !== 2) begin $display("FAIL bin:2 got %0d", bin_q); fail=fail+1; end else pass=pass+1;
#10; if (bin_q !== 3) begin $display("FAIL bin:3 got %0d", bin_q); fail=fail+1; end else pass=pass+1;
load = 1; din = 4'd14; #10;
if (bin_q !== 14) begin $display("FAIL load:14 got %0d", bin_q); fail=fail+1; end else pass=pass+1;
load = 0;
#10; if (bin_q !== 15) begin $display("FAIL bin:15 got %0d", bin_q); fail=fail+1; end else pass=pass+1;
#10; if (bin_q !== 0) begin $display("FAIL bin:wrap got %0d", bin_q); fail=fail+1; end else pass=pass+1;
rst = 1; #10; rst = 0;
repeat(7) #10;
if (bcd_q !== 7) begin $display("FAIL bcd:7"); fail=fail+1; end else pass=pass+1;
#10; if (bcd_q !== 8) begin $display("FAIL bcd:8"); fail=fail+1; end else pass=pass+1;
#10; if (bcd_q !== 9) begin $display("FAIL bcd:9"); fail=fail+1; end else pass=pass+1;
#10; if (bcd_q !== 0) begin $display("FAIL bcd:wrap got %0d", bcd_q); fail=fail+1; end else pass=pass+1;
rst = 1; #10; rst = 0;
if (john_q !== 4'b0000) begin $display("FAIL john:0"); fail=fail+1; end else pass=pass+1;
#10; if (john_q !== 4'b0001) begin $display("FAIL john:1 got %b", john_q); fail=fail+1; end else pass=pass+1;
#10; if (john_q !== 4'b0011) begin $display("FAIL john:3 got %b", john_q); fail=fail+1; end else pass=pass+1;
#10; if (john_q !== 4'b0111) begin $display("FAIL john:7"); fail=fail+1; end else pass=pass+1;
#10; if (john_q !== 4'b1111) begin $display("FAIL john:F"); fail=fail+1; end else pass=pass+1;
#10; if (john_q !== 4'b1110) begin $display("FAIL john:E"); fail=fail+1; end else pass=pass+1;
$display("========================================");
$display("计数器测试: PASS=%0d FAIL=%0d", pass, fail);
if (fail == 0) $display("✅ 二进制+BCD+Johnson 计数器验证通过!");
else $display("❌ 存在失败!");
$display("========================================");
$finish;
end
endmodule
五、Verilator 编译命令
verilator --cc counter.v --exe tb_counter.v \
--build --top-module tb_counter
./obj_dir/Vtb_counter
🤔 思考题:RISC-V CPU 中的 PC(程序计数器)是哪种计数器?它有什么特殊之处?
💡 提示:PC 通常是"计数+偏移"型,每周期 PC+4(顺序执行)或 PC+imm(分支跳转)
🏆 成就解锁:计数器专家
✅ Verilator 仿真验证通过
✅ 二进制计数器:计数、加载、回绕验证正确
✅ BCD 计数器:0-9 循环验证正确
✅ Johnson 计数器:扭环序列验证正确
🎯 下一目标:有限状态机 → 第09课:FSM