SDRAM控制器的实现与优化,包括配置切换、时序控制、突发传输等功能的详细说明。
sdram配置切换
1 | trait SDRAMConfig { |
1 | parameter SDRAM_2BYTE_NUM = 1; |
只有在使用sdram_axi时,才可以突发访问
协议
设置寄存器
写入BurstLength和CASLatency,完全是硬件自动写入,代码没管
突发读时序
16位写入,原始实例化了1个dsramhelper。burstLength设置成为了,这里就突发的读入了两个16bit。因为无论是lb、lh、lw都是读入32bit
1 | // 步骤 2:测试16位写入和读取 |
突发写时序
尤其注意传col的时候也是传bank的。所以sdram支持4个bank的开放,active时基于其中一个bank,read/write时选择一个bank。取巧的做法是锁存row地址而不是锁存row中的数据,等到read/write时使用bank索引锁存的row,bank,col一起访存
16位写入,原始实例化了1个dsramhelper。burstLength很早就设置成1了,为的是32写入时可以突发,虽然后面的col写入需要放弃,但col该加还得加。
1 | // 步骤 2:测试16位写入和读取 |
32位写入,突发的写入两个16bit
row hit
acitve的时候激活,如果rowhit了就不用active了,但是目前没实现row的寄存器(4bank*512col),所以只好禁止row hit了
以下图是因为row hit了所以没有active状态传入row和way和bank,就读错了
1 | // Open row hit |
位扩展
还需要修改sdram控制器,最不想做的事情
col在一次传输中都是32bit对齐的,是0,2,4这样的数,因为在sdram的视角下,即使对col_1写16bit也是对col_0写32bit。只不过在第二次传输是将strb和wen置为有效
1 | // Address bits |
位扩展后32位读取不需要突发了,因此不用到state_write1,但state_write0仍需要检测突发以应对64位或更高位的突发访问。此时要精确控制ack_q->ram_ack_i(axi_core)-> ram_ack_i(axi_pmem) -> push_i(u_response) 的拍数。多一拍的话会多压入一个data,当axi_bvalid/axi_rvalid->resp_accept_w->pop_i(u_response)时会弹出提前压入栈的数据。
字扩展
先来算一下一共能代表多少字节。
原先2**25 = 32MB
地址空间位数 | 地址位数 | way | row | bank | col | 字节偏移 |
---|---|---|---|---|---|---|
25 | 24 | - | 13 | 2 | 9 | 1 |
在最不常用的地方扩展,在地址最前面加上一bit的way,2**27 = 128MB
地址空间位数 | 地址位数 | way | row | bank | col | 字节偏移 |
---|---|---|---|---|---|---|
27 | 25 | 1 | 13 | 2 | 9 | 2 |
但是ysyxsoc分配给sdram了2**29 = 512MB空间
chisel
vec
1 | val sdram = VecInit(Seq(sdram0.io, sdram1.io)) |
Exception in thread “main” java.lang.IllegalArgumentException: requirement failed: can’t create Vec with heterogeneous types class ysyx.SDRAMHelper0$$anon$3 and class ysyx.SDRAMHelper1$$anon$4
问题出在使用 VecInit 创建 Vec 时,传入的 Seq 包含了不同类型的元素:sdram0.io 和 sdram1.io,分别是 SDRAMHelper0 和 SDRAMHelper1 的 IO 类型。即使这两个IO内部元素相同也不算同一个类型。
解决方法:
统一使用SDRAMHelperIO
1 | val io = IO(new SDRAMHelperIO) |
(实例化的模板都是SDRAMHelper,verilator找不到SDRAMHelper_${instanceId})
1 | class SDRAMHelper(instanceId: Int) |
类型转换
1 | val rdata = Reg(Vec(SDRAMNum,UInt(16.W))) |
Exception in thread “main” chisel3.package$ChiselException: Connection between sink (sdramChisel.dqout: Wire[UInt<32>]) and source (sdramChisel.rdata: Reg[UInt<16>[2]]) failed @: Sink (UInt<32>) and Source (UInt<16>[2]) have different types.
asTypeOf 并不能直接将 Vec[UInt] 转换为 UInt,因为它们在结构上不兼容。如果需要将整个 Vec 转换为一个宽度为 32 位的 UInt,可以使用 asUInt。相反,UInt转换成vec就可以asTypeOf。
解决方法:
1 | val rdata = Reg(Vec(SDRAMNum,UInt(16.W))) |
for循环
1 | for (i <- 0 until SDRAMNum) { |
1 | sdram.foreach { sdram => |
logic
看似command是wire,但是却综合出了Reg
1 | val command = |
automatic:
在 SystemVerilog 中,automatic 表示这个变量是 自动变量,即每次进入 always 块时重新创建,而不是在模块级别共享。
logic 是 SystemVerilog 中的新数据类型,类似于 Verilog 中的 reg
跨时钟域的always中,就会有这样的在新时钟域赋值的wire
运算优先级大于移位
要使用
1 | (1.U << BurstLength).asUInt -1.U |
make ARCH=riscv32e-ysyxsoc run ALL=printf | tee sdram
make ARCH=riscv32e-ysyxsoc run ALL=printf | tee >(grep “sdram” >> sdram)
sdram在最高地址开辟栈时,写入sp报错,因为做了字和位扩展会增大4被sdram空间