如何在Chisel3/Firrtl中将SRAM移至顶层层级?
解决方案:将Chisel3 RISCV核中的SRAM迁移至顶层作为黑盒
要把内部SRAM移到顶层并作为黑盒处理,不能依赖topWiring(它仅暴露信号而非迁移模块),正确的方式是手动拆分模块接口,将SRAM实例移至顶层,再通过端口映射连接核内部逻辑。以下是完整示例:
1. 定义SRAM黑盒模块
先写一个符合流片需求的SRAM黑盒,接口匹配你要使用的物理SRAM:
import chisel3._ import chisel3.experimental.{BlackBox, HasBlackBoxResource} // 流片用SRAM黑盒,接口可根据实际物理SRAM调整 class SramBlackBox(addrWidth: Int, dataWidth: Int) extends BlackBox with HasBlackBoxResource { val io = IO(new Bundle { val clk = Input(Clock()) val we = Input(Bool()) // 写使能 val addr = Input(UInt(addrWidth.W)) // 地址总线 val din = Input(UInt(dataWidth.W)) // 写数据 val dout = Output(UInt(dataWidth.W))// 读数据 }) // 若有预先编写的SRAM Verilog黑盒文件,可通过addResource引入 // addResource("/path/to/your/sram_blackbox.v") }
2. 修改顶层模块:实例化SRAM黑盒并暴露端口
顶层模块需要同时包含RISCV核和SRAM黑盒,并将SRAM的端口暴露到顶层外部(供后续综合工具绑定物理库):
class TopRiscvCore(addrWidth: Int = 12, dataWidth: Int = 32) extends Module { val io = IO(new Bundle { // RISCV核的外部接口(示例:指令访存接口) val instr_req = Output(Bool()) val instr_addr = Output(UInt(addrWidth.W)) val instr_rdata = Input(UInt(dataWidth.W)) // ... 补充你的核其他外部端口(如中断、调试接口等) // 暴露SRAM的所有端口到顶层 val sram_clk = Input(Clock()) val sram_we = Output(Bool()) val sram_addr = Output(UInt(addrWidth.W)) val sram_din = Output(UInt(dataWidth.W)) val sram_dout = Input(UInt(dataWidth.W)) }) // 实例化SRAM黑盒 val sram = Module(new SramBlackBox(addrWidth, dataWidth)) // 连接黑盒与顶层端口 sram.io.clk := io.sram_clk sram.io.we := io.sram_we sram.io.addr := io.sram_addr sram.io.din := io.sram_din io.sram_dout := sram.io.dout // 实例化RISCV核 val core = Module(new RiscvCore(addrWidth, dataWidth)) // 连接核的常规外部端口 io.instr_req := core.io.instr_req io.instr_addr := core.io.instr_addr core.io.instr_rdata := io.instr_rdata // ... 映射其他核端口 // 连接核的SRAM接口到顶层SRAM端口 io.sram_we := core.io.sram_we io.sram_addr := core.io.sram_addr io.sram_din := core.io.sram_din core.io.sram_dout := io.sram_dout }
3. 修改RISCV核:移除内部SRAM,添加SRAM接口
把核内部原本的SRAM实例删除,替换成对外的SRAM接口,让核的内存访问逻辑直接通过这些端口与顶层黑盒交互:
class RiscvCore(addrWidth: Int, dataWidth: Int) extends Module { val io = IO(new Bundle { // 核的常规外部端口 val instr_req = Output(Bool()) val instr_addr = Output(UInt(addrWidth.W)) val instr_rdata = Input(UInt(dataWidth.W)) // ... 补充其他端口 // 新增SRAM交互接口,用于连接顶层黑盒 val sram_we = Output(Bool()) val sram_addr = Output(UInt(addrWidth.W)) val sram_din = Output(UInt(dataWidth.W)) val sram_dout = Input(UInt(dataWidth.W)) }) // --- 核内部内存访问逻辑修改 --- // 原本的内部SRAM实例(删除): // val internalSram = Module(new SyncSram(addrWidth, dataWidth)) // internalSram.io.clk := clock // internalSram.io.we := memWriteEn // internalSram.io.addr := memAddr // internalSram.io.din := memWriteData // val memReadData = internalSram.io.dout // 替换为:直接通过顶层SRAM接口交互 val memWriteEn = Wire(Bool()) // 核内部生成的写使能信号 val memAddr = Wire(UInt(addrWidth.W)) // 核内部生成的内存地址 val memWriteData = Wire(UInt(dataWidth.W)) // 核内部写数据 // 连接到顶层SRAM接口 io.sram_we := memWriteEn io.sram_addr := memAddr io.sram_din := memWriteData val memReadData = io.sram_dout // 从顶层SRAM读取数据 // 后续核内部逻辑正常使用memReadData即可 }
关键注意事项
- 多SRAM场景:如果核内有多个SRAM(如指令SRAM和数据SRAM),只需在顶层实例化多个
SramBlackBox,并在核内添加多组对应接口即可。 - 黑盒保留:生成Verilog时,Chisel会自动将
SramBlackBox标记为黑盒,不会展开其内部逻辑,后续综合工具可直接绑定流片用的物理SRAM库。 - 时序约束:可在顶层模块的注释中添加SRAM的时序约束,或在综合脚本中单独配置。
内容的提问来源于stack exchange,提问作者DDK
相关产品推荐
相关产品推荐

