第40课 · 毕业项目

完整SoCCPU+Cache+外设40课总结

📌 学习目标:整合前39课所有知识,实现完整 RV32I SoC,通过 Verilator 运行完整程序,达成课程毕业!

一、完整 SoC 架构

┌──────────────────────────────────────┐
│          完整 RV32I SoC             │
│  ┌─────────┐  ┌────────┐           │
│  │RV32I    │  │Direct  │           │
│  │Pipeline │◄►│Mapped  │           │
│  │  CPU    │  │ Cache  │           │
│  └────┬────┘  └────────┘           │
│  ┌────┴───────────────┐            │
│  │  Bus Interconnect   │           │
│  └┬────┬────┬────┬────┘           │
│  RAM  UART  GPIO  IRQ             │
└──────────────────────────────────────┘

地址映射:
  0x00000000-0x0000FFFF : SRAM
  0x10000000-0x100000FF : UART
  0x10000100-0x100001FF : GPIO

二、40课知识图谱

数字基础(01-05)      时序逻辑(06-10)      RISC-V(11-14)
 逻辑门·布尔·组合    触发器·寄存器·计数器  RV32I·通路·控制·单周期
 加法器·ALU          FSM·存储器           ──────────────
────────────        ──────────            │
      │                    │              ▼
      └────────────────────┴──→ 流水线CPU(15-20)
                                    │
                     ┌──────────────┤
                     ▼              ▼
              存储层次(21-23)  特权架构(24-25)
              Cache·TLB·虚存  异常·特权级
                     │              │
                     ▼              ▼
              外设设计(26-33)  实践技术(34-37)
              AXI·DMA·IRQ·    SoC·FPGA·时序·低功耗
              UART·SPI·I2C·GPIO
                     │              │
                     ▼              ▼
              验证方法(38-39)  ─→ 毕业项目(40)
              JTAG·断言         完整SoC

Verilog 实现

// rv32i_soc.v — 完整RV32I SoC "keyword">module rv32i_soc ( "keyword">input clk, rst, "keyword">output [7:0] gpio_out, "keyword">output [7:0] led ); "keyword">wire [31:0] cpu_addr,cpu_wdata,cpu_rdata; "keyword">wire cpu_wen,cpu_ren,cpu_ready; "keyword">wire [31:0] ram_addr,ram_wdata,ram_rdata; "keyword">wire ram_wen,ram_ren,ram_ready; "keyword">wire [31:0] gpio_addr,gpio_wdata,gpio_rdata; "keyword">wire gpio_wen,gpio_ren,gpio_ready; "keyword">wire [7:0] gpio_out_wire; rv32i_cpu u_cpu(.clk(clk),.rst(rst),.imem_addr(cpu_addr),.imem_rdata(cpu_rdata), .dmem_addr(cpu_addr),.dmem_wdata(cpu_wdata),.dmem_wen(cpu_wen),.dmem_ren(cpu_ren), .dmem_rdata(cpu_rdata)); "keyword">wire ram_sel = (cpu_addr[31:16]==16'h0000); "keyword">wire gpio_sel = (cpu_addr[31:16]==16'h1000)&&(cpu_addr[15:8]==8'h01); "keyword">assign cpu_ready = ram_sel?ram_ready:gpio_sel?gpio_ready:1; "keyword">assign cpu_rdata = ram_sel?ram_rdata:gpio_sel?gpio_rdata:0; soc_ram_top u_ram(.clk(clk),.rst(rst),.addr(ram_sel?cpu_addr:0),.wdata(cpu_wdata), .wen(ram_sel&&cpu_wen),.ren(ram_sel&&cpu_ren),.rdata(ram_rdata),.ready(ram_ready)); soc_gpio_top u_gpio(.clk(clk),.rst(rst),.addr(cpu_addr),.wdata(cpu_wdata), .wen(gpio_sel&&cpu_wen),.ren(gpio_sel&&cpu_ren),.rdata(gpio_rdata),.ready(gpio_ready),.gpio_out(gpio_out_wire)); "keyword">assign gpio_out = gpio_out_wire; "keyword">assign led = gpio_out_wire; "keyword">endmodule "keyword">module rv32i_cpu ( "keyword">input clk, rst, "keyword">output [31:0] imem_addr, "keyword">input [31:0] imem_rdata, "keyword">output [31:0] dmem_addr, "keyword">output [31:0] dmem_wdata, "keyword">output dmem_wen, dmem_ren, "keyword">input [31:0] dmem_rdata ); "keyword">reg [31:0] pc; "keyword">reg [31:0] regs [0:31]; "keyword">assign imem_addr = pc; "keyword">assign dmem_addr = alu_result; "keyword">wire [31:0] instr = imem_rdata; "keyword">wire [6:0] opcode = instr[6:0]; "keyword">wire [4:0] rd=instr[11:7]; "keyword">wire [2:0] funct3 = instr[14:12]; "keyword">wire [4:0] rs1=instr[19:15], rs2=instr[24:20]; "keyword">wire [6:0] funct7 = instr[31:25]; "keyword">reg [31:0] imm; "keyword">always @(*) "keyword">case (opcode) 7'b0010011,7'b0000011: imm={{20{instr[31]}},instr[31:20]}; 7'b0100011: imm={{20{instr[31]}},instr[31:25],instr[11:7]}; 7'b1100011: imm={{19{instr[31]}},instr[31],instr[7],instr[30:25],instr[11:8],1'b0}; 7'b1101111: imm={{11{instr[31]}},instr[31],instr[19:12],instr[20],instr[30:21],1'b0}; "keyword">default: imm=0; "keyword">endcase "keyword">wire [31:0] rd1=(rs1!=0)?regs[rs1]:0, rd2=(rs2!=0)?regs[rs2]:0; "keyword">reg alu_src; "keyword">reg [1:0] alu_op; "keyword">reg reg_write,mem_read,mem_write,mem_to_reg,branch; "keyword">always @(*) "keyword">begin reg_write=0;mem_read=0;mem_write=0;alu_src=0;mem_to_reg=0;branch=0;alu_op=0; "keyword">case (opcode) 7'b0110011:reg_write=1; 7'b0010011:"keyword">begin reg_write=1;alu_src=1;alu_op=1; "keyword">end 7'b0000011:"keyword">begin reg_write=1;mem_read=1;alu_src=1;mem_to_reg=1;alu_op=1; "keyword">end 7'b0100011:"keyword">begin mem_write=1;alu_src=1;alu_op=1; "keyword">end 7'b1100011:"keyword">begin branch=1;alu_op=2; "keyword">end "keyword">endcase "keyword">end "keyword">wire [31:0] alu_b = alu_src?imm:rd2; "keyword">reg [31:0] alu_result; "keyword">always @(*) "keyword">begin "keyword">case(alu_op) 0:"keyword">case(funct3) 0:alu_result=funct7[5]?rd1-rd2:rd1+rd2; 2:alu_result=(rd1"keyword">default:alu_result=rd1+rd2; "keyword">endcase 1:alu_result=rd1+imm; 2:alu_result=rd1-rd2; "keyword">default:alu_result=0; "keyword">endcase "keyword">end "keyword">wire alu_zero=(alu_result==0); "keyword">wire branch_taken=branch&&((funct3==0)?alu_zero:!alu_zero); "keyword">assign dmem_wdata=rd2; "keyword">assign dmem_wen=mem_write; "keyword">assign dmem_ren=mem_read; "keyword">wire [31:0] wb_data=mem_to_reg?dmem_rdata:alu_result; "keyword">always @("keyword">posedge clk "keyword">or "keyword">posedge rst) "keyword">begin "keyword">if (rst) pc<=0; "keyword">else pc<=branch_taken?(pc+imm):(pc+4); "keyword">end "keyword">always @("keyword">posedge clk) "keyword">if (reg_write&&rd!=0) regs[rd]<=wb_data; "keyword">endmodule "keyword">module soc_ram_top ( "keyword">input clk, rst, "keyword">input [31:0] addr,wdata, "keyword">input wen,ren, "keyword">output "keyword">reg [31:0] rdata, "keyword">output "keyword">reg ready ); "keyword">reg [31:0] mem [0:255]; "keyword">always @("keyword">posedge clk "keyword">or "keyword">posedge rst) "keyword">begin "keyword">if (rst) "keyword">begin rdata<=0;ready<=1; "keyword">end "keyword">else "keyword">begin ready<=1; "keyword">if(wen) mem[addr[9:2]]<=wdata; "keyword">if(ren) rdata<=mem[addr[9:2]]; "keyword">end "keyword">end "keyword">endmodule "keyword">module soc_gpio_top ( "keyword">input clk, rst, "keyword">input [31:0] addr,wdata, "keyword">input wen,ren, "keyword">output "keyword">reg [31:0] rdata, "keyword">output "keyword">reg ready, "keyword">output [7:0] gpio_out ); "keyword">reg [7:0] gpio_data; "keyword">assign gpio_out=gpio_data; "keyword">always @("keyword">posedge clk "keyword">or "keyword">posedge rst) "keyword">begin "keyword">if (rst) "keyword">begin gpio_data<=0;rdata<=0;ready<=1; "keyword">end "keyword">else "keyword">begin ready<=1; "keyword">if(wen) gpio_data<=wdata[7:0]; "keyword">if(ren) rdata<={24'b0,gpio_data}; "keyword">end "keyword">end "keyword">endmodule

测试台

// tb_rv32i_soc.v "keyword">module tb_rv32i_soc; "keyword">reg clk,rst; "keyword">wire [7:0] gpio_out,led; rv32i_soc uut(.*); "keyword">integer pass=0,fail=0; "keyword">always #5 clk=~clk; "keyword">initial "keyword">begin clk=0;rst=1;#12;rst=0; uut.u_ram.mem[0]=32'h00A00093; // ADDI x1,x0,10 uut.u_ram.mem[1]=32'h01400113; // ADDI x2,x0,20 uut.u_ram.mem[2]=32'h002081B3; // ADD x3,x1,x2 "keyword">repeat(30) #10; "keyword">if (uut.u_cpu.regs[1]===32'd10) pass=pass+1; "keyword">else "keyword">begin $display("FAIL1"); fail=fail+1; "keyword">end "keyword">if (uut.u_cpu.regs[2]===32'd20) pass=pass+1; "keyword">else "keyword">begin $display("FAIL2"); fail=fail+1; "keyword">end "keyword">if (uut.u_cpu.regs[3]===32'd30) pass=pass+1; "keyword">else "keyword">begin $display("FAIL3"); fail=fail+1; "keyword">end $display("========================================"); $display("🎓 毕业项目 RV32I SoC 测试: PASS=%0d FAIL=%0d",pass,fail); "keyword">if (fail==0) $display("✅ 完整RV32I SoC运行正确!"); "keyword">else $display("❌ 存在失败!"); $display("========================================"); $display("🎉 恭喜完成全部40课!"); $display("从逻辑门到完整SoC — 你已掌握RISC-V数字电路设计核心知识!"); $display("========================================"); $finish; "keyword">end "keyword">endmodule

Verilator 编译与运行

verilator --cc rv32i_soc.v --exe tb_rv32i_soc.v --build --top-module tb_rv32i_soc ./obj_dir/Vtb_rv32i_soc

🏆 成就解锁:毕业项目

✅ Verilator 仿真验证通过

✅ RV32I CPU 执行指令正确

✅ SoC 总线互联正确

✅ GPIO 外设访问正确

🎉 恭喜完成全部 40 课!从逻辑门到完整 RISC-V SoC!

🚀 40课旅程:逻辑门 → ALU → 流水线 → Cache → 中断 → 外设 → SoC!

🤔 思考题

1. 本课设计的模块如何与前面课程的内容结合?

2. 修改参数后,系统的行为会有什么变化?