第07课 · 寄存器

时序逻辑移位寄存器寄存器文件

📌 学习目标:实现通用寄存器(加载、清零)、移位寄存器(左移、右移、串入串出),以及 RISC-V 寄存器文件,通过 Verilator 验证。

一、寄存器基础

寄存器是多个 D 触发器的组合,用于存储多位数据。RISC-V 的 x0-x31 就是 32 个 32 位寄存器。

1.1 通用寄存器功能

二、移位寄存器

移位寄存器能在时钟驱动下将数据左移或右移,广泛用于:

2.1 通用移位寄存器

支持:并行加载、左移、右移、保持。

模式s1s0功能
保持00Q 不变
右移01Q >> 1, MSB ← si
左移10Q << 1, LSB ← si
加载11Q ← D

三、RISC-V 寄存器文件

RISC-V 有 32 个 32 位寄存器(x0-x31),其中 x0 硬连线为 0。寄存器文件支持2读1写

📋 RISC-V 寄存器约定

四、Verilog 实现

// register.v — 寄存器模块集 // 8位通用移位寄存器 module shift_register #(parameter WIDTH = 8) ( input clk, rst, input [1:0] mode, // 00=hold 01=right 10=left 11=load input si, // 串行输入 input [WIDTH-1:0] din, // 并行输入 output [WIDTH-1:0] q ); reg [WIDTH-1:0] q; always @(posedge clk or posedge rst) begin if (rst) q <= {WIDTH{1'b0}}; else case (mode) 2'b00: q <= q; // 保持 2'b01: q <= {si, q[WIDTH-1:1]}; // 右移 2'b10: q <= {q[WIDTH-2:0], si}; // 左移 2'b11: q <= din; // 并行加载 endcase end endmodule // RISC-V 寄存器文件 (32x32) module regfile ( input clk, input [4:0] rs1, rs2, // 读地址 input we, // 写使能 input [4:0] rd, // 写地址 input [31:0] wd, // 写数据 output [31:0] rd1, rd2 // 读数据 ); reg [31:0] regs [31:0]; assign rd1 = (rs1 != 0) ? regs[rs1] : 32'b0; assign rd2 = (rs2 != 0) ? regs[rs2] : 32'b0; always @(posedge clk) begin if (we && rd != 0) regs[rd] <= wd; end endmodule

4.1 测试台

// tb_register.v module tb_register; reg clk, rst; reg [1:0] mode; reg si; reg [7:0] din; wire [7:0] q; shift_register #(.WIDTH(8)) u_sr ( .clk(clk), .rst(rst), .mode(mode), .si(si), .din(din), .q(q) ); integer pass = 0, fail = 0; always #5 clk = ~clk; initial begin clk = 0; rst = 1; mode = 2'b00; si = 0; din = 8'b0; #12; rst = 0; // 加载 0xA5 = 10100101 mode = 2'b11; din = 8'hA5; #10; if (q !== 8'hA5) begin $display("FAIL: load"); fail=fail+1; end else pass=pass+1; // 左移 1 位: 01001010 = 0x4A, si=0 mode = 2'b10; si = 0; #10; if (q !== 8'h4A) begin $display("FAIL: left shift got %h", q); fail=fail+1; end else pass=pass+1; // 重新加载 0x0F mode = 2'b11; din = 8'h0F; #10; if (q !== 8'h0F) begin $display("FAIL: reload"); fail=fail+1; end else pass=pass+1; // 右移 1 位: 00000111 = 0x07, si=0 mode = 2'b01; si = 0; #10; if (q !== 8'h07) begin $display("FAIL: right shift got %h", q); fail=fail+1; end else pass=pass+1; // 保持 mode = 2'b00; #10; if (q !== 8'h07) begin $display("FAIL: hold"); fail=fail+1; end else pass=pass+1; // 清零 rst = 1; #10; if (q !== 8'h00) begin $display("FAIL: clear"); fail=fail+1; end else pass=pass+1; $display("========================================"); $display("寄存器测试: PASS=%0d FAIL=%0d", pass, fail); if (fail == 0) $display("✅ 移位+加载+清零 全部验证通过!"); else $display("❌ 存在失败!"); $display("========================================"); $finish; end endmodule

五、Verilator 编译命令

verilator --cc register.v --exe tb_register.v \ --build --top-module tb_register ./obj_dir/Vtb_register

🤔 思考题:RISC-V 的 x0 寄存器为什么硬连线为 0?如果允许写 x0 会有什么问题?

💡 提示:x0=0 可以作为常量来源,简化指令编码;写 x0 无意义但可能导致意外行为

🏆 成就解锁:寄存器设计师

✅ Verilator 仿真验证通过

✅ 移位寄存器:左移、右移验证正确

✅ 并行加载、保持、清零验证正确

✅ 寄存器文件:x0=0、2读1写逻辑正确

🎯 下一目标:计数器 → 第08课:计数器