1 cva6总览
Ariane处理器是一个6级流水线、顺序、单发射、64bit处理器,它完整的实现了非特权手册中的IMAFDC指令集,以及特权手册中的MSU特权等级,以支持类Unix操作系统。上图是论文中给出的微架构图,但在实际阅读源码的过程中,发现与论文还是有很多出入的,详细描述见后面章节。
1.1功能描述
Feature | Description |
ISA | RISCV64 GC |
order | in-order |
issue width | 1 |
pipelined | 6 (实际大于6) |
mode | MSU |
frequency | 1.7GHZ(22nm) |
peak efficiency | 40 Gop/sW |
core interface | 1组axi64,1组协处理器接口,1组trace接口 |
virtual memory | sv39 |
FPU | - |
icache latency | 1 |
dcache latency | 3 |
1.2 设计目标
主要设计目标是减少关键路径长度,同时保持每周期指令数(IPC)的损失在适度范围内。目标逻辑深度低于30个NAND门当量(GEs)
1.3 顶层接口
信号名 | 位宽 | 描述 |
clk_i | 1 | 时钟 |
rstn_i | 1 | 复位 |
boot_addr_i | VLEN | boot地址 |
hart_id_i | XLEN | hart id |
irq_i | 2 | m和s中断 |
ipi_i | 1 | 处理器之间的中断 |
time_irq_i | 1 | 计时器中断 |
debug_req_i | 1 | 调试中断 |
rvfi_o. | trace相关的所有输出信号,详见后面章节 | |
cvxif_req_o. | 协处理器相关的所有输出信号 详见后面章节 | |
cvxif_resp_i. | 协处理器相关的所有输入信号详见后面章节 | |
noc_req_o | axi64_mst端口的所有输出信号 | |
noc_resp_i | axi64_mst端口的所有输入信号 |
1.4 流水线划分
-
2 Frontend(前段)
2.1 Frontend流水线划分
如下图,在Frontend部分,分为4级流水:fetch0(取指0)、fetch1(取指1)、align/minidecode/prediction(指令对齐&微解码&分支预测)、ints_buffer(指令缓存)。pc_gen(pc生成)在fetch0阶段。
为了减小读取icahce的延迟,将取指分为两级流水,这导致了预测和取指没有实现背靠背,在预测结果是跳转时,将产生1个周期的惩罚。但由于支持压缩指令和ints_buffer的存在,仍然可以为后续流水提供连续指令。
此外,为了将惩罚减小到1个周期,得到的分支预测地址会直接用于当拍的取指,这带来了前段部较长的组合逻辑:align->minidecode->prediction->pc_gen(对齐/预解码 和 预测是并行的)。
如下图:红色标识表示各级流水.
2.2 PC生成
如下图,pc_gen生成fecth0的请求地址。
优先级顺序 | 描述 |
1 boot_pc | 复位释放后的起始地址 |
2 bp_pc | 预测跳转时,来自BTB/RTS的地址。 |
3 debug_pc | 进入debug模式时的地址,地址为0x800 |
4 flush_pc | 由于一些特殊的指令比如同步内存和I/O的fence指令会导致流水线刷新,从提交的指令地址 + 4 重新取指。 |
5 ex_pc | 异常和中断导致的跳转,跳转地址依据特权等级以及mtvec、stvec等计算。 |
6 eret_pc | 遇到从环境调用中返回指令mret、sret、dret,则下个取指地址分别为CSR寄存器mepc、sepc、dpc中记录的数值。 |
7 misperdict_pc | 分支预测错误,从执行阶段计算的地址开始取指。 |
8 replay_pc | ints_buffer满时,按分支预测阶段(第三级流水)的地址重新取指。 (为防止过长的反压组合逻辑) |
9 default | {pc[31:2],2’b0}+4 |
2.3 取指
CVA6采用 SV39的虚拟存储器系统。CVA6采用VIPT(vittually-indexed.physically-tagged)的icache和TLB设计,将icahe和TLB加入流水线,来减少访存延迟。
当icache命中时 两级取指操作如下:
-
- 取指0阶段的操作: 读4个cacheline和4个tag,
- 取指1阶段的操作,读TLB(当拍返回)并比较tag,选择cacheline(组合逻辑较长)
CVA6可以集成开源的OpenPtion,来拓展L2cache。对于L1 cache,采用哈弗结构,分别有icache和dcache,但两cache使用同一个读写访问接口访问总线。此外可选集成的L2cache是冯诺依曼结构。结构参考下图。
这里只对icache描述下,关于TLB和PTW后面在看。
2.3.1 icache特性
四路组相连
cacheline_size 128bit
cache_size 16KB
随机替换
fetch latency 1个周期
2.3.2 icache接口
2.3.3 icahce状态机
重新写了一下状态机,代码如下:
2.3.4 一些细节
- 对cache初始化的flush操作。
- 对于non-cacheable区域的pc地址,如果是推测的,则暂停取指流水。
- 内部实现了对icache的控制逻辑(将cache line置为无效等操作),但在接口上(adapter模块中)tie住了,没有实现。
2.4 指令对齐
由于支持C指令,所以取指一次可能包含多条指令。该模块输入32/64的指令数据,输出2/4个vld信号和2/4条指令,给到后面的预解码模块。
当分支预测是跳转时,要flush掉存在该模块的半条指令。
2.4.1接口
2.4.2 代码梳理
对其部分代码梳理了一下,具体如下:
2.5 指令预解码
两个预解码模块(scan模块),判断两条指令是否是分支指令,是哪种分支指令。
2.5.1接口
2.6 分支预测
同时对两条指令进行动态分支预测
2.6.1 方向预测
2.6.1.1 方向预测特性
- 基于pc值的一部分的两bit饱和计数器(尽管模块名字叫BHT,实际没有)
- 128个表项 ,使用pc[7:2]寻址
- 两个读端口,采用interleaving的方法,同时读取pc[1]==1和pc[1]==0。
- 对pc[1]==1的预测结果打一拍暂存。因为pc[1]==1的指令可能在下一拍才能完整收到。
- ex阶段执行update操作,因为分支指令是顺序执行,所以ex后的结果不是推测的。不用考虑恢复操作。当update和读操作同时发生时,读到的是旧值。
- 不支持flush操作(tie0了)。
- PHT缺失时默认不跳帧
2.6.1.2 BHT模块接口
2.6.2 地址预测
2.6.2.1 BTB特性
- 基于pc值的一部分的直接映射BTB(Branch Target Buffer)
- 32个表项,,使用pc[5:2]寻址
- 两个读端口,采用interleaving的方法,同时读取pc[1]==1和pc[1]==0。
- 对pc[1]==1的预测结果打一拍暂存。因为pc[1]==1的指令可能在下一拍才能完整收到。
- ex阶段执行update操作,因为分支指令是顺序执行,所以ex后的结果不是推测的。不用考虑恢复操作。当update和读操作同时发生时,读到的是旧值。
- 不支持flush操作(tie0了)。
- 调试模式下不更新BTB
- 对于jump指令,直接在当前周期计算出pc值,而不是用BTB的值
- BTB缺失时,貌似有bug(缺失时,方向预测仍可能有效,此时的预测地址是0)
2.6.2.2 BTB接口
2.6.2.3 RAS特性
- 深度是2
- 对于return指令,ras miss时,按不跳转执行
- 对于call指令,ras full时,挤掉最旧的值
- 与下游指令缓存握手成功时,才更新RAS(consumed信号)
2.6.2.4 RAS接口
2.7 指令缓存
用两个fifo实现的双端口写,单端口读的指令缓存模块。
2.7.1 instr_queue模块接口
与上游交互的端口
与下游交互的端口
2.7.1 instr_queue一些细节
对于分支预测指令所携带的预测地址,使用单独的fifo模块。
replay机制:当fifo满时,按当前要写入fifo的pc重新取指。因为每次写入的指令数不确定,不太好通过阈值的方法做反压。