整体流程:
一些基本概念:
1.p_bank和l_bank
2.rank和bank
3.DIMM和SIMM
4.DLL概念:
DDR控制器架构:
时钟频率对比:
(1)memory和phy/controller时钟频率一般是2:1;
(2)假设memory那边数据位宽是32bit,因此在仅仅考虑axi一个通道的情况下带宽匹配时总线带宽一般是800MHZ,但是这是只考虑写或者只考虑读,axi读写是并行的,因此总线带宽需要根据实际情况考虑。
axi-if模块
将axi的transaction转化为ddr的transaction。
(1)reorder模块返回信息到wresp fifo中;(2)将地址对齐,也就是axi的地址对齐成ddr的地址;(3)注意两边的带宽问题,也就是数据位宽的问题(4)内部是异步fifo,两侧的速率不匹配;(5)aw和ar fifo的内容给到command split模块
command split模块
(1)将axi的地址转化为bank address、行地址、列地址、write buffer ID、axi的len长度需要怎么把它转化成bl4还是bl8等,也就是真正的将axi数据类型转化为ddr数据类型;
(2)command split操作,执行的是对进行跨行操作的transaction,将其变成两个命令,这两个命令分别交给command reorder模块;(这种跨行操作称为page conflict,为reorder做准备);
(3)write data buffer实现的作用,也就是一个transaction的id假设是1,那么为这个transaction开辟一个buffer,这个buffer的id是2,那么会把这个buffer的id2和这个burst的transaction的id绑定在一起送到下级,以防止下级重排序的时候出现错误;
(4)对读写进行仲裁;
write data buffer模块
(1)存取写数据的buffer,buffer的数量由写操作能接受多少个命令来决定,一个命令对应一个buffer;
(2)数据位宽的转化;
(3)buffer的释放管理,假设一个buffer的数据被读取出去,就会将这个buffer的id存取到BID FIFO中,这个fifo是用于存取ID号,下次数据来的时候再次从BID FIFO中读取,同时需要注意的是这个ID同时需要备份到BID BACK FIFO中,这个FIFO吐出来的ID用于给上面command split模块。
(4)DFI接口在接收到上级模块的命令的时候(这个命令指的是写操作的命令,这个命令包含buffer的id号),DFI通过buffer的id去找相应的buffer,并且要去判断这个buffer中的数据够不够;
command reorder模块
page conflict:这里的page实际上就可以认为ddr中的row,page conflict实际上就是行的冲突;page conflict解决的方式是在发生冲突的两行之间插入更多的其他bank的行访问,也就是一个行访问完需要等precharge时间,用这个需要等待的precharge时间去访问别的bank;
reorder1:
(1)对于这个write queue(也就是多个buffer组成的queue),输出只能按照buffer1-buffer16的顺序输出,但是输入的话命令是可以任意进入的,进入buffer2、buffer3等均可以(buffer1不可以进入),这种操作也就是插队;另外需要注意的是,对于输出来说,buffer1的数据输出后,buffer2的数据就进入buffer1,buffer3的数据进入buffer2,依次类推,对于输入来说,假设buffer5此时有数据,新来的数据又需要进入到buffer5中,那么就会将buffer5的数据推后到buffer6中,buffer6的数据推到buffer7中,依次类推;
(2)上述操作只是如何进行插队的,那么如何选择新的内如插入到内部具体哪个位置呢?插入的准则就是将一个bank的不同row尽量中间插入别的bank的操作,假设一开始bank1 row10在第四个buffer中,那么新的命令来临就会将其推入到后面的buffer,继续来新的继续推,最终到达buffer9。这样的一波操作是一个group,如果page conflict还存在的话,那么可能还会分一个group,继续按照之前的进行操作;
(3)需要指出的是,实际过程中我们是希望严格的1-3-5-6-4-0-2-7这样的顺序,但是实际过程中可能很难去严格按照这样的顺序执行,因此只能这样初步的划一个组,通过这种方式以流水线的方式提升速率;
当然这只是其中的一种方式,也就是插队的调度机制,也可以采用用8个fifo,每个fifo中分别存取当前bank的操作,在输出的时候依次按照8个fifo的顺序这样调度,这也是其中一种方式
(4)这里的仲裁也需要注意,因为对于ddr来说从读到写和从写到读也对效率的有较大的影响,一般来说是将一组的内容全部执行完毕之后才会去发生读写切换,也就是假如要切换,需要等到写的一组切换完毕之后才会转到读的那一组去。另外,也可以去设置一些别的参数使得出现先进行两组写在进行一组读的这种方式,也就是根据读写带宽可以按照一定比例的方式进行切换(除了按照组的情况来区分也可以按照具体读写操作多少次来执行,也就是假设写多少个row然后在进行读操作,在进行读多少个row);
(5)除此以外还存在老化的因素,也就是由于存在插队的因素,因此可能存在某个row一直在内部没有出去的情况,当内部的参数达到一定程度以后,在切换的时候就会考虑到老化因素,因此会优先考虑这个row;
reorder2
(1)微调命令,比如B2r1,B3r3,B4r4,B2r2,B4r3,B3r1经过微调之后变为B2r1,B3r3,B4r4,B2r2,B3r1,B4r4,另外需要注意微调的时候禁止把读写混在一起;
(2)也可以在这个commond reorder模块中通过寄存器设置优先级,在排序的时候就会考虑优先级,优先级大的部分就提前;
(3)look ahead + auto precharge
look ahead就是b0 r3和b0 r1,这个时候b0 r3会往前看b0 r1,经过一段时间后,b0 r1往前了,此时b0 r3可以提前发送precharge命令,在经过一段时间后trp时间经历之后发送active操作,这种look ahead操作保证了在真正执行b0 r3的时候page conflict已经经历过precharge和active,这种方式提升了效率;
auto precharge指的是向后看的操作,也就是假设b0 r1出来的时候往后看发现了b0 r3,此时在b0 r1出来的时候就会发送一个precharge,auto precharge和look ahead是两个完全相反的过程,这种操作相当于在节约了一个周期,在总线上少占用一个周期;
DFI interface
这个模块主要实现对各个模块的命令进行调度:
(1)初始化(对ddr memory进行初始化,实际上有些phy中已经包含了一些初始化的操作,这种情况下就不需要这里的初始化模块了)
(2)command reorder2主要发送:读、写、active、precharge;command reorder拿到写数据的id,然后去write data buffer中的id拿到相应的数据
(3)refresh control主要发送:self refresh(自刷新)、auto refresh(保持电荷)、power down、speculative refresh control(为了解决刷新窗口的问题,正常刷新过程中最多可以等九个tREFI个时间间隔不需要刷新,但是在空闲的时候你可以去插入刷新保证不用刷新周期最大,而这个操作就是由speculative control控制)、ZQCS command control
注意,self refresh和power down都是为了低功耗而出现的,如果长时间没有操作的化就会发一个self refresh、power down操作使得其进入低功耗的模式
(4)dcu
为了debug
(5)除此以外还需要根据DFI协议产生相应的数据,另外需要注意需要提前根据命令计算出来一些东西,比如一次读八个数据,但是其中可能有一部分是无效的,这些情况都需要提前计算出来;
DFI时序:
write data/read data group时序:
dfi_ctrlupd_ack:确认MC-initiated updata的req,phy不需要对这个信号进行会议回应;当这个信号线被拉高,DFI总线只能在如下几种情况下:(1)总线保持idle状态(2)DFI总线在发起更新的一些操作;
如果phy相应了这个请求,dfi_ctrlupd_ack必须在dfi_ctrlupd_req拉低之前拉高,如果phy不响应这个信号,dfi_ctrlupd_ack必须保持为低直到dfi_ctrlupd_req拉低;
dfi_ctrlupd_req需要确保至少tctrlupd_mid个周期;
dfi_ctrlupd_req信号用来表示MC启动更新后以指示DFI将处于空闲状态一段时间,在这个时间段内phy可能执行一个updata操作;
dfi_ctrlupd_req信号必须拉高时间范围在tctrlupd_min~tctrlupd_max个周期;
dfi_ctrlupd_req用来请求phy产生一个updata的操作;
dfi_ctrlupd_req的行为是依靠dfi_ctrlupd_ack信号,(1)如果phy给了回应,那么只要dfi_ctrlupd_ack拉高dfi_ctrlupd_req也将会拉高,但是dfi_ctrlupd_req将会拉低如果超过tctrlupd_max范围。当dfi_ctrlupd_req拉高,dfi总线保持idel状态或者处于更新相关操作的状态;(2)如果phy没有回应,dfi_ctrlupd_req必须在tctrlupd_min~tctrlupd_max范围内拉低;
dfi_phyupd_ack用来表示DFI总线保持idle状态直到dfi_phyupd_req信号拉低;MC必须保持dfi_phyupd_ack为高在dfi_phyupd_req信号的tphyupd_resp个周期内,并且只要dfi_phyupd_req信号为高这个信号就要为高,dfi_phyupd_ack信号一旦检测到dfi_phyupd_req信号拉低,之后可以将dfi_phyupd_ack信号拉低,dfi_phyupd_req不能再dfi_phyupd_ack信号对上一个transaction进行拉低之前再拉高;
dfi_phyupd_ack拉高到dfi_phyupd_req拉低的时间差最多是tphyupd_type个周期;
dfi_phyupd_req信号表示的是phy需要DFI保持一个idle状态用于phy自身产生更新同时MC不要发送一些控制信息;
一旦dfi_phyupd_req信号拉高就必须保持直到dfi_phyupd_ack信号回应;
这个信号用于表示phy更新的时候dfi_phyupd_req需要用哪个具体的参数。
两组信号的区别:
一个phy可以响应也可以不响应,另一个是必须相应;
timing check模块
read data buffer模块
注意这里的read id fifo的作用,假设axi总线位宽是128bit,颗粒是32位的颗粒,发出的命令是BL8,也就是一共有4*64bit的数据,axi那边要的是128,因此ddr这边相当于一个周期提供64bit数据,总共两个周期,而不是需要四个周期,因此需要将这个2存到read id fifo中表示虽然有4*64个数据,但是我只要其中一部分,也就是2*64个数据