DMA硬件实现——读写实现
队伍编号:CICC2136
队伍名称:芯如止水
DMA,Direct Memory Access,直接内存访问,是一种不经过CPU而直接从内存存取数据的数据交换模式。在DMA模式下,CPU只需要向DMA控制器下达指令(配置DMA寄存器),传输数据由DMA来完成,数据传送完再把信息反馈给CPU,这样能够减少CPU的资源占有率。
这里在蜂鸟核中添加了DMA模块,使得通过DMA将数据直接从SRAM1中搬移至SRAM2。硬件实现主要分成三个部分:DMA寄存器的配置、读写模块和fifo。
之前已经有对DMA寄存器配置的分析讨论,这里主要分享对DMA读写模块和fifo的分析讨论。
1.DMA读写模块
当DMA单元的寄存器配置完成之后,读写模块开始工作。此时DMA作为主设备,而SRAM1和SRAM2作为从设备。
设置模块read_or_write,这个模块中我设置了一个计数器寄存器count。前16个计数周期(count<16)时从SRAM1中读取数据,后16个计数周期(15 为了保证数据的正确读取写入,当dma_icb_rsp_valid信号拉高时遇到时钟上升沿count信号才进行加一操作。 同时,还设置了一个与数据长度寄存器长度相同的已写数据长度寄存器length_count,与数据长度寄存器data_length的值不相等时,每写入一个数据就加一;length_count的值与数据长度寄存器data_length的值相等时,将完成信号done和的中断信号dma_irq拉高。 读写模块接受DMA的配置寄存器的信息,读地址寄存器和写地址寄存器初始状态为0,DMA作为主设备对外发出读写信号时,判断读或写与上面的count信号有关。当DMA读入数据时,读地址寄存器加4,写出数据时,写地址寄存器加4。 对于其它output信号,md_valid信号在上一部分寄存器全部配置完成、且rsp_valid不为1时拉高;cmd_read在已写数据长度寄存器还没有到达要写的数据长度、且计数count<=16时拉高,表示模式为读;cmd_addr根据cmd_read分别取在读或者在写的地址。这里没有使用到掩码的功能,因此cmd_wmask的4位全部置1;中断信号irq在输出计数length_count与data_length相等且均不为0时拉高。中断拉高时根据上一部分寄存器的配置,源地址寄存器、目的地址寄存器、长度寄存器回到初始状态0,状态寄存器回到初始状态111。 此外,由于这里一次进行16个数据的读写,需要一个fifo模块存放数据。这里例化了fifo。下一小部分具体阐述fifo的实现。 2.fifo 首先进行fifo大小、数组空间及指针的定义:由上一部分,读写模块一次进行16个32位数据的读写,因此这里设置宽32位,深度16。最多存放16个数据,为防止溢出指针大小均设置为5位。 读写指针和输入输出data_in、data_out的具体实现分别如下: 值得注意的是从dma从从设备中读出的数据实际写入FIFO时是写入数据,要写入从设备的数据实际是从FIFO中读出的数据。因此例化时,dma_icb_cmd_wdata对应FIFO模块中的data_r,dma_icb_rsp_rdata对应FIFO模块中的data_w。 3.波形分析 如下图为从SRAM1中读入数据部分的波形。 当cmd_valid拉高时,当前周期dma作为主设备会发送这次请求的具体信息,包括地址cmd_addr、读/写cmd_read,此时读操作没有wdata和wmask。cmd通道握手成功时从设备SRAM1接受了这次请求,rsp通道握手成功时从返回通道返回rsp_rdata。 可以看到设置的count寄存器正在对单次读取的数据个数进行计数;length_count则在读周期保持不变,在写周期随着写出数据个数而变化。根据第一部分硬件代码的分析,这里设置的是一轮读/写16个数据,因此,当count= 5‘h10 时,读完16个数据放入了FIFO,状态从读转为写。 而对写部分,当cmd_valid拉高时,此时的主设备dma发送这次请求的具体信息,包括地址cmd_addr、读/写cmd_read、写数据cmd_wdata、写掩码cmd_mask。cmd通道握手成功时,从设备SRAM2接受了这次请求。这里当前周期反馈信息,并且rsp通道握手成功。//din
always@(posedge clk)
if(w_en)
ram[wp_addr] <= data_w;
//dout
always @(posedge clk)
if(r_en)
data_r <= ram[rp_addr];
//wp
always@(posedge clk or negedge rst_n)
if(!rst_n)
wp_addr <= 0;
else if(w_en && wp_addr < 15)
wp_addr <= wp_addr + 1;
else if(w_en && wp_addr == 15)
wp_addr <= 0;
//rp
always@(posedge clk or negedge rst_n)
if(!rst_n)
rp_addr <= 0;
else if(r_en && rp_addr < 15)
rp_addr <= rp_addr + 1;
else if(r_en && rp_addr == 15)
rp_addr <= 0;