第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 带使能和加载的计数器

rstenloadCLK ↑Q(next)
1XXX0
00XQ(保持)
011D(加载)
010Q+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 计数序列:

步骤Q3Q2Q1Q0
00000
10001
20011
30111
41111
51110
61100
71000

Johnson 计数器的优点:每次只有一个位变化(无毛刺),译码简单。

四、Verilog 实现

// counter.v — 三种计数器 // 4位二进制计数器(带使能、加载、同步复位) 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 // BCD 计数器 (0-9 循环) 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 // 4位 Johnson 计数器 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 测试台

// tb_counter.v 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; // 二进制计数: 0→1→2→3→4→5 #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; // BCD 计数: 7→8→9→0→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; // Johnson 计数: 0→1→3→7→F→E→C→8→0 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