虽然这部分的内容是存储器,但是因为蜂鸟的原书将BIU总线放到了存储器架构部分的后面,考虑到两者关系还是比较密切的,这里就把存储器架构和总线协议放在一起,个人认为这样更便于理解
蜂鸟E203的处理器中没有配备缓存,处理器外则分别配备了ITCM和DTCM用于存储指令和数据
虽然在常见的PC的CPU大都配备了缓存,甚至有一二三级不同缓存用来提高内核执行效率,但实际上大部分的低功耗中低性能处理器并没有配备缓存,主要原因在于以下几点:
缓存无法保证实时性
缓存利用软件程序的时间局部性和空间局部性,将存储器数据动态映射到容量很小的缓存中,这样减小了访问存储器的平均延迟。但是正因如此,访问缓存存在着巨大的不确定性,最直观的,一旦缓存不命中就会导致取指或访存时间增加,这就导致实时性不好
针对这一点,大多低功耗中低性能处理器都使用延迟确定的ITCM和DTCM
嵌入式设备软件规模较小
在嵌入式领域的软件代码一般很小,所需的数据段也很小,所以使用片上SRAM、片内FLASH或者ITCM/DTCM便可以满足其需求,因此缓存的优点就被大大影响,不能说一点用都没有,只能说映射了个寂寞
缓存面积功耗大
缓存会消耗大量的芯片面积和功耗,从AMD的5900x就可以看出来,32MB的三级缓存面积都快比核心本体大了
在低功耗的中低性能处理器中不可能费尽心思引入一个徒增功耗还没什么用的东西
正因如此,即使是ARM的高端Cortex-M7内核也只是配备了一个16KB的I-Cache(指令缓存)和一个16KB的D-Cache(数据缓存),整体的缓存只有一级——补充一下,这玩意是六级流水线、双发射、乱序执行的大号处理器,全力开跑甚至需要散热片
蜂鸟E203采用的是哈佛架构,主要特点如下:
搞嵌入式的不可能没见过哈佛架构,想必各位已经品鉴的足够多了。然而现在的哈佛架构正在逐渐和冯诺依曼架构合体,具体表现如下:
所以说现代处理器中,低功耗处理器正在通过上层封装向冯氏架构靠拢;而高性能处理器则正在通过硬件设计向佛氏架构靠拢
RISC-V架构对于存储器也进行了一定简化
仅支持小端格式
小端格式是现在CPU的主流应用,RISC-V架构也就仅支持小端格式,这就简化了硬件设计
没有地址自增自减
RISC-V取消了存储器读写指令的地址自增自减模式,可以简化地址的生成逻辑
没有一次读/写多个数据的指令
RISC-V没有定义能够从存储器中一次读/写多个数据的指令,虽然减少了单条指令的访存效率,但是大大简化了硬件的实现
专用存储器读写指令
使用Load进行存储器读
使用Store进行存储器写
RISC-V还定义了7条存储器读指令和存储器写指令用来进行字节、半字、单字的读写操作
松散存储器模型
RISC-V使用松散存储器模型,对不同地址访存的顺序不做要求,使用Fence和Fence.I指令来强行界定访存的顺序
扩展指令
RISC-V定义了一种扩展指令子集,用于支持多线程状态下访问存储器的原子操作或同步操作
包括Atomic Memory Operation(AMO)指令、Load-Reserved指令、Store-Conditional指令
蜂鸟E203的实现则基于RISC-V的规定设计了四个模块:
地址生成单元AGU
为读写指令和“A”扩展指令生成存储器访问地址
LSU
存储器访问的控制模块
指令紧耦合存储器ITCM
作为存储器子系统的指令存储部件,但是也能用于存储数据而被读写指令访问
数据紧耦合存储器DTCM
作为存储器子系统的数据存储部件
以上DTCM和ITCM并联到LSU的地址判断和数据对齐控制器之间,AGU则旁挂在LSU上,LSU通过BIU与ICB总线相连
因为AGU需要将第一个寄存器索引的源操作数和符号位扩展的立即数相加来得到读写指令最终的方寸地址,所以需要用到加法器
因此蜂鸟E203中将AGU和ALU放在一起,共用ALU的加法器来节省面积
其代码保存在/rtl/e203/core/e203_exu_alu.v、e203_exu_dpath.v、e203_exu_lsuagu.v文件中,很明显和ALU放在了一起
其中e203_exu_alu.v文件中实现了AGU所用的加法器和多路选择器的硬件结构
e203_exu_dpath.v文件则实现了AGU的数据处理代码,其中主模块就是ALU的共享数据通路,复用了ALU处例化的加法器
RISC-V架构对于地址非对齐的读写指令可以使用硬件支持也可以使用软件通过异常服务程序的方式采用软件支持,而蜂鸟E203采用软件支持的解决方案,ALU对生成出的访存地址进行判断,如果地址非对齐,则产生异常标志,通过ALU传送给交付模块,交付模块则据此产生异常,相关代码在e203_exu_lsuagu.v文件中
此外,e203_exu_lsuagu.v文件中还实现了AGU的控制和选择代码,但是不包括加法器部分;生成地址部分的HDL代码也在这个文件中。文件最后还例化了ICB接口用于挂载到BIU
详细内容可以参考源代码,这里不再赘述
这是蜂鸟E203处理器核存储器子系统的主要控制单元,源代码保存在/rtl/e203/core/e203_lsu.v和e203_lsu_ctrl.v中,它们的架构和代码实现与BIU模块很相似,具体情况可以参考下面的总线协议部分
LSU有2组输入ICB总线接口,分别接入AGU和EAI协处理器的接口;3组输出ICB接口,分别分发给BIU、DTCM和ITCM;LSU还配备了一个写回接口。2组输入总线通过一个ICB汇合模块合成一组ICB总线,采用优先级仲裁的逻辑,EAI总线具有更高的优先级,经过汇合之后的ICB总线通过命令通道的地址进行判断,通过其访问的地址区间产生分发信息,随后接入的ICB分发模块会将其分发给不同存储器组件的ICB接口
如果EAI需要访问存储器,就需要在LSU这里进行配置
特别地,LSU使用的ICB汇合和ICB分发模块的FIFO深度均为1,也就是LSU默认只支持1个滞外传输
最终的返回数据需要经过尺寸对齐后经过LSU写回接口写回。对于可能出现的存储器访问错误,LSU预留了一条反馈通道信号线用于返回标志信号,异常标志会通过LSU的写回接口传送给内核的交付模块,交付模块会据此产生异常
蜂鸟E200系列处理器使用专用总线分别访问ITCM和DTCM,ITCM位宽为64位,DTCM位宽为32位
ITCM也有一组输入ICB总线接口来自LSU的访问,所以ITCM所在的地址区间同样能够通过LSU被读写指令访问到用于存储数据;另外有一组64位宽ICB总线接入到IFU,有一组32位宽的外部直接访问接口用于SoC中的外设能够直接访问ITCM。三组输入的ICB总线会经过一个ICB汇合模块汇成一组ICB总线,模块依旧采用优先级仲裁,IFU总线优先级>LSU优先级>外部直接访问接口。操作的来源信息会被寄存,SRAM返回的数据会根据寄存结果分发回三个总线之一
对于硬件实现,ITCM采用一块数据宽度为64位的单口SRAM组成,可以通过config.v中的宏定义配置ITCM大小和基地址
DTCM则拥有2组输入ICB总线接口,分别来自LSU和外部直接访问接口,具体实现和ITCM大同小异
ITCM的实现代码为e203_itcm_ctrl.v,DTCM的实现代码为e203_dtcm_ctrl.v
特别地,可以根据片上资源情况选择使用ITCM和DTCM,控制例化的代码位于e203_itcm_ram.v和e203_dtcm_ram.v,如下所示
`ifdef E203_HAS_DTCM //{
module e203_dtcm_ram(
input sd,
input ds,
input ls,
input cs,
input we,
input [`E203_DTCM_RAM_AW-1:0] addr,
input [`E203_DTCM_RAM_MW-1:0] wem,
input [`E203_DTCM_RAM_DW-1:0] din,
output [`E203_DTCM_RAM_DW-1:0] dout,
input rst_n,
input clk
);
sirv_gnrl_ram #(
.FORCE_X2ZERO(1),//Always force X to zeros
.DP(`E203_DTCM_RAM_DP),
.DW(`E203_DTCM_RAM_DW),
.MW(`E203_DTCM_RAM_MW),
.AW(`E203_DTCM_RAM_AW)
) u_e203_dtcm_gnrl_ram(
.sd (sd ),
.ds (ds ),
.ls (ls ),
.rst_n (rst_n ),
.clk (clk ),
.cs (cs ),
.we (we ),
.addr(addr),
.din (din ),
.wem (wem ),
.dout(dout)
);
endmodule
`endif//}
`ifdef E203_HAS_ITCM //{
module e203_itcm_ram(
input sd,
input ds,
input ls,
input cs,
input we,
input [`E203_ITCM_RAM_AW-1:0] addr,
input [`E203_ITCM_RAM_MW-1:0] wem,
input [`E203_ITCM_RAM_DW-1:0] din,
output [`E203_ITCM_RAM_DW-1:0] dout,
input rst_n,
input clk
);
sirv_gnrl_ram #(
`ifndef E203_HAS_ECC//{
.FORCE_X2ZERO(0),
`endif//}
.DP(`E203_ITCM_RAM_DP),
.DW(`E203_ITCM_RAM_DW),
.MW(`E203_ITCM_RAM_MW),
.AW(`E203_ITCM_RAM_AW)
) u_e203_itcm_gnrl_ram(
.sd (sd ),
.ds (ds ),
.ls (ls ),
.rst_n (rst_n ),
.clk (clk ),
.cs (cs ),
.we (we ),
.addr(addr),
.din (din ),
.wem (wem ),
.dout(dout)
);
endmodule
`endif//}
蜂鸟203默认支持“A”扩展指令子集
详细内容可以查看LSU部分代码
蜂鸟E203通过在派遣控制模块、交付模块、分支处理模块等部分加入Fence指令处理的代码实现了对Fence和Fence.I指令的处理
这些内容大都在源码部分用注释写明,不再赘述
蜂鸟E200系列部分内核配备了ECC算法来对SRAM进行保护,但蜂鸟E203并没有配备
蜂鸟E203并没有使用AMBA协议中规定的任何一种总线
这就导致它的总线部署和修改相当恶心
但是好在这玩意的时序和AHB总线比较相似,虽然不能在vivado中一键生成,但是封装一个IP并不是要人命的问题
虽然原书和相关教程中总结比较了各个总线的优缺点,但个人认为采用自定义的总线协议对于一个以应用为目的的处理器而言实属逆天。最重要的是这个破总线给笔者的比赛和队员们的心理造成了极大损伤!
不管怎么吐槽,以学习为目的看待这个自定义总线协议还是可取的(况且这玩意好歹能算是国产总线协议,战狼狂喜)
该总线协议和AHB和AXI协议比较类似,最主要的特性如下(括号内注明和哪个协议类似):
总线时序不能说和AXI时序一模一样,只能说差不多得了,所以在这里不多介绍,想了解详细信息可以查看官方书籍和官网的时序图
位于rtl/e203/core/e203_biu.v
BIU主要负责接收来自IFU和LSU的存储器访问请求,使用标准的ICB接口向内核、外设分发,通过判断指令访问的地址区间来访问以下五种外设接口:快速IO接口、私有外设几口、系统存储接口、CLIENT接口、PLIC接口
BIU有2组输入接口,分别来自IFU和LSU单元,通过一个ICB汇合模块合成一组ICB总线,采用优先级仲裁,LSU具有更高的优先级
为了截断外界和处理器内核之间的时序路径,在汇合的ICB总线处额外插入了一组乒乓缓存
ICB汇合、分发两个模块的FIFO深度默认配置为1,它默认只支持一个滞外交易,用于减少面积开销
此外,如果IFU访问到设备区间,会直接通过反馈通道返回错误标志
特别地,LSU和BIU的结构很相似,除了接口方向、数目不同外,本质上都是用于转发总线数据的“交换机”
详细内容参考代码即可
E200系列处理器配套SoC中,总线分为两组:附属于BIU的系统存储总线和通过BIU与内核耦合的私有外设总线
系统存储总线用于访问SoC中的ROM、OTP、FLASH只读区间等外设存储器
私有外设总线则用于外设寄存器映射和访问
其中所有存在时序兼容的重要路径都会插入乒乓缓存来砍断前后的时序路径;任何需要跨越异步时钟域或整数倍分频时钟域都会插入异步FIFO或流水线级数