FIRRTL中存储器部分更新的惯用实现及效率问题
FIRRTL中缓存部分写入的惯用实现与优化效率问题
在FIRRTL中编写寄存器文件的逻辑十分直观:创建以机器字为单元的存储器,对其执行读写操作即可。
但编写缓存时情况有所不同:缓存通常以缓存行作为存储单元,写入操作仅需要更新缓存行的部分内容,即缓存行中的单个元素。
请问FIRRTL中实现该操作的惯用方式是什么?(请勿指引参考Chisel编写的Rocket实现,Chisel代码可读性极差)
目前可想到至少两种实现方式:
- 方式1:将存储器的存储元素定义为Vector或Bundle类型,写入时直接选中存储器元素的对应成员操作,示例代码如下:
cmem mem1 : {x:SInt<64>,y:SInt<64>}[4] infer mport temp_x_mem1 = mem1[i].x, clock temp_x_mem1 <= foo
- 方式2:采用读-改-写流程实现,示例代码如下:
cmem mem1 : {x:SInt<64>,y:SInt<64>}[4] infer mport temp_x_mem1 = mem1[i], clock bar <= temp_x_mem1 bar.x <= foo infer mport temp_x_mem1_B = mem1[i], clock temp_x_mem1_B <= bar
当前场景为从其他格式生成FIRRTL代码,前期未针对该场景做规划,生成存储器逻辑时仅支持对整个存储器元素执行读写,不支持部分元素写入,因此方案1实现难度较高,方案2更易落地。核心疑问为:如果所有代码都在同一个模块内,FIRRTL优化器或后续的Verilog优化器能否将方案2优化至与方案1同等的运行效率?
回答
核心结论
同模块下结构合规的读-改-写实现,经过FIRRTL标准优化流程和下游Verilog综合工具处理后,完全可以达到和直接部分写入的方案1同等的运行效率,不存在额外的面积、时序开销。
具体说明
- FIRRTL优化器原生支持该模式的识别转换
FIRRTL存储器编译阶段内置了部分写入识别逻辑:只要检测到同一时钟域下,对同一存储器同一地址的读操作后紧跟写操作,且写数据中未修改的字段完全直通读回的对应位、没有额外逻辑篡改,就会自动将这组读-改-写序列映射为存储器的字段掩码写端口,不会生成冗余的读通路、临时寄存器开销,最终生成的硬件结构和手动写部分字段选中的方案1完全等价。
触发该优化仅需满足两个条件:- 读、写端口使用相同时钟
- 写操作中不需要更新的字段,直接连接读回数据的对应位,不插入额外组合逻辑或时序逻辑
- 代码生成的注意事项
给出的方案2示例存在一个语法问题:两个存储器端口不能使用相同命名,写端口需要单独定义标识符。另外要保证读回数据到写入数据的路径是纯组合逻辑,不要在中间插入寄存器,就能稳定触发优化。如果需要验证优化是否生效,可以检查FIRRTL编译导出的Verilog代码,只要对应存储器的写端口出现了按字段拆分的写使能信号,就说明转换已经完成。 - 实现选型建议
手写FIRRTL时直接选中字段写入的方案1是更原生的写法,但对于自动代码生成场景,读-改-写的方案2落地成本更低,且没有效率损失,不需要为了支持部分字段写入重构已有的全元素读写生成逻辑。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

