DMA数据发送模块实现

发布时间 2023-07-05 16:18:17作者: 李白的白

DMA数据发送模块实现

发送模块的数据接口

  • 发送模块使用AXI Stream接口与DMA控制器通信,AXI Stream是一种简单的点对点数据流协议,主要包含以下三个信号:
    • valid:表示发送方是否有有效的数据。
    • ready:表示接收方是否准备好接收数据。
    • data:表示数据本身,可以是任意位宽。
  • 另外,AXI Stream接口还有一个可选的信号:
    • last:表示当前的数据是否是一帧数据的最后一个字节,用于标识数据边界。
  • 发送方是FPGA(PL),接收方是DMA控制器(PS),因此valid和last信号由FPGA控制,ready信号由DMA控制器控制。
  • 只有当valid和ready信号同时为高时,才表示一次有效的数据传输。

image

graph TD A[发送读开始命令] --> B[主控模块跳转到读状态并拉高value信号] B --> C{PS端是否ready?} C --是--> D[产生FIFO读使能信号并从FIFO中读取数据] D --> E{是否发送完所有数据?} E --是--> F[拉高last信号并跳回idle状态] E --否--> D C --否--> B
  • FIFO的读使能信号(buffer_rd_en)
    • 需要用value和ready的与运算来产生,因为只有当两个信号都为高时,数据才能正常发送出去
    • 这里假设FIFO用的是fast forward模式,即读使能来了之后,数据马上就能出来,没有延时
  • last信号的产生
    • 需要用一个寄存器(register)来记录FIFO内部的数据量,在每次read start为高时更新这个值
    • 需要用一个计数器(counter)来记录已经发送的数据量,在每次FIFO读使能为高时加1,在value为0时清零
    • 当计数器的值等于寄存器的值减1时,就把last信号拉高
1. 当read start信号为高时,跳转到read状态,拉高value信号,表示数据有效
2. 当value和ready信号同时为高时,产生FIFO的读使能信号,从FIFO里读出数据,并用一个计数器记录读出的数据个数
3. 当计数器的值等于FIFO内部的实时数据量(data count)时,拉高last信号,表示最后一个数据
4. 当last信号为高时,产生read finish信号,表示读取数据完成,跳转回idle状态,清零计数器
 例子
- 假设FIFO里有1000个数据,data count为1000
- 发出read start信号,主控模块跳转到read状态,拉高value信号
- PS端给出ready信号,FIFO产生读使能信号,从FIFO里读出第一个数据,并把计数器加一
- 重复上述过程,直到计数器达到999时,拉高last信号,表示最后一个数据
- PS端收到last信号后,给出read finish信号,主控模块跳转回idle状态,清零计数器

代码清单:

stream_tx

module  stream_tx(
        // system siganls
        input                   sclk                    ,       
        input                   s_rst_n                 ,       
        //
        output  wire    [63:0]  s_axis_s2mm_tdata       ,       
        output  wire    [ 7:0]  s_axis_s2mm_tkeep       ,       
        output  wire            s_axis_s2mm_tvalid      ,       
        input                   s_axis_s2mm_tready      ,       
        output  reg             s_axis_s2mm_tlast       ,      
        // 
        output  wire            buffer_rd_en            ,       
        input           [63:0]  buffer_rd_data          ,
        input           [ 5:0]  state                   ,
        input           [12:0]  buffer_data_count       ,
        output  wire            read_finish                    
);

//========================================================================\
// =========== Define Parameter and Internal signals =========== 
//========================================================================/

reg     [12:0]                  tx_cnt                          ;       

//=============================================================================
//**************    Main Code   **************
//=============================================================================
assign  s_axis_s2mm_tvalid      =       state[2];
assign  s_axis_s2mm_tkeep       =       8'hff;
assign  buffer_rd_en            =       s_axis_s2mm_tvalid & s_axis_s2mm_tready;
assign  s_axis_s2mm_tdata       =       buffer_rd_data;
assign  read_finish             =       s_axis_s2mm_tlast;

always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                tx_cnt  <=      'd0;
        else if(s_axis_s2mm_tvalid == 1'b0)
                tx_cnt  <=      'd0;    
        else if(buffer_rd_en == 1'b1)
                tx_cnt  <=      tx_cnt + 1'b1;
end

always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                s_axis_s2mm_tlast       <=      1'b0;
        else if(s_axis_s2mm_tvalid == 1'b1 && tx_cnt == (data_count-2))
                s_axis_s2mm_tlast       <=      1'b1;
        else
                s_axis_s2mm_tlast       <=      1'b0;
end

endmodule