AXI DMA 设计分析

发布时间 2023-04-24 17:33:13作者: luckylan

AXI DMA 架构

SBIU

SBIU:Slave Bus Interface Unit。从机总线接口模块:通过外部 AHB/APB4 主机访问 DW_axi_dmac 的内部寄存器的读写控制逻辑。从机总线接口可以通过 DMAX_SLVIF_MODE 参数进行配置。

  • DMAX_SLVIF_MODE:用于从机接口的协议。AHB (0),APB4 (2)
  • 根据子系统要求,可以选择AHB/APB4 从机接口。这些模块仅支持用于数据传输的 little-endian 方案。
  • 从机总线接口可以配置为在与 DW_axi_dmac 内核时钟(dmac_core_cllock) 不同的时钟上运行,可以使用 coreConsultant 中的 DMAX_SLVIF_CLOCK_MODE 参数进行配置。

    • 如果 DMAX_SLVIF_CLOCK_MODE 参数设置为 1,则从机总线接口模块根据用于该接口的协议在 hclk/pclk 上运行,与DW_axi_dmac 内核时钟不同。

    • DW_axi_dmac 设计中的Handshake Interface synchronization module负责这种情况下的时钟域交叉。

    • DMAX_SLVIF_CLOCK_MODE:选择从接口时钟(AHB/APB4)和dma核时钟之间的关系。

    • 0:从接口时钟与dma核时钟同步。1:从接口时钟与dma核时钟异步。 

 

  • AHB 或 APB4 从机总线接口可用于对公共寄存器和通道寄存器空间执行读或写操作。在执行读或写传输时,从机总线接口可能会提供从机接口访问错误,并且读/写传输可能由于以下任何原因而失败:
    • 解码访问错误
    • 写入只读寄存器访问错误
    • 只读到只写寄存器错误
    • 写保持错误
    • 未定义地址访问错误
  • 当从机总线接口配置为 AHB 时,通过驱动 ERROR(2'h01) 在 HRESP 信号上提供错误响应。当从机总线接口配置为 APB4 时,通过置位 PSLVERR 信号提供错误响应。

MBIU

  • 主机接口实现 AXI 总线上的数据传输。AXI 协议可以是 AXI3 或 AXI4,可在 coreConsultant 中进行配置。DW_axi_dmac 最多支持两个主机接口,它们独立运行以在外设和存储器之间传输数据。两个主机接口必须使用相同的 AXI 协议。主机接口的地址和数据总线宽度可在 coreConsultant 中配置,但两个主机接口使用相同的配置。
  • 主机接口实现 AXI 协议中指定的所有五个通道:
    • 写地址
    • 写数据
    • 写响应
    • 读地址
    • 读数据
  • 主机接口实现了不同的 FIFO,用于临时存储通过 DW_axi_dmac 中不同通道在外部存储器或外设之间传输的数据。
  • 主机接口可以配置为在与 DW_axi_dmac 内核时钟 (dmac_core_clock) 不同的时钟上运行,这可以通过在coreConsultant 中设置 DMAX_MSTIFN_CLOCK_MODE 参数来配置。如果 DMAX_MSTIFN_CLOCK_MODE 设置为 1,则主机接口模块在 aclk_mN上运行。在这种情况下,DW_axi_dmac 设计负责时钟域交叉。
  • 主机接口实现逻辑在大端和小端格式之间进行转换。big-endian 格式使用的 endian 方案是 Byte Invariant (BE-8) 方法,它是 AXI 协议支持的 big-endian 格式。
  • DW_axi_dmac 能够使用 little-endian 方案进行 LLI 获取和 LLI 回写,而不管该特定主机接口上用于数据传输的 endian 格式。

 

 

 

HSIF_SYNC

  • Handshake Interface synchronization module,握手同步控制状态机。
  • ST_WAIT4_REQ: 2'b00;它是握手同步控制状态机的默认状态。对应的通道正在等待握手请求。如果状态机接收到Handshake Request (DMA_REQ或DMA_SINGLE),则状态机跳转到ST_WAIT4_ACK_REQDSRT。
always @(*) begin : HS_SYNC_CTRL_FSM_PROC
    nxt_hs_sync_ctrl_state = hs_sync_ctrl_state_r;
    case (hs_sync_ctrl_state_r)
    ST_WAIT4_REQ: begin  //默认状态
        if (hs_req_tog_a == 1'b1) begin  //如果检测到DMA握手请求,跳转到ST_WAIT4_ACK_REQDSRT状态
          nxt_hs_sync_ctrl_state = ST_WAIT4_ACK_REQDSRT;
       end else begin
          nxt_hs_sync_ctrl_state = ST_WAIT4_REQ;
        end
      end
  • ST_WAIT4_ACK_REQDSRT:2'b01;在这种状态下,状态机在等待DMA握手请求DMA_REQ去断言的同时等待DMA握手确认DMA_ACK。一旦收到DMA握手确认DMA_ACK和DMA握手请求DMA_REQ去断言,状态机转换到状态ST_WAIT4_ACK_DSRT。
      ST_WAIT4_ACK_REQDSRT: begin
        if ((hs_ack_active == 1'b1) && (hs_req_tog_a == 1'b1)) begin  //等待DMA_ACK断言的同时,检测到dma_req或dma_single去断言。
          nxt_hs_sync_ctrl_state = ST_WAIT4_ACK_DSRT;
        end else begin   
        nxt_hs_sync_ctrl_state = ST_WAIT4_ACK_REQDSRT;
        end  
      end
  • ST_WAIT4_ACK_DSRT: 2'b11;它是状态机等待DMA握手确认DMA_ACK的解除断言的状态。
    • 如果DMA握手请求已经激活,并且现在我们检测到DMA握手确认DMA_ACK撤销断言,那么状态机将传输到状态ST_WAIT4_ACK_REQDSRT。
    • 如果DMA握手请求未激活,并且如果我们检测到DMA握手确认DMA_ACK撤销断言,那么状态机将传输到状态ST_WAIT4_REQ。
    ST_WAIT4_ACK_DSRT: begin
        if ((hs_req_active == 1'b1) && (hs_ack_tog_a == 1'b1)) begin  //DMA握手请求激活,并且检测到dma_ack 撤销断言
          nxt_hs_sync_ctrl_state = ST_WAIT4_ACK_REQDSRT;
      end else if ((hs_req_active == 1'b0) && (hs_ack_tog_a == 1'b1)) begin //DMA握手请求未激活,检测到dma_ack 撤销断言
      nxt_hs_sync_ctrl_state = ST_WAIT4_REQ;     
    end else begin 
      nxt_hs_sync_ctrl_state = ST_WAIT4_ACK_DSRT;
    end
end
  • 默认情况
    default : begin
        nxt_hs_sync_ctrl_state = ST_WAIT4_REQ;
      end  
    endcase
  end

  • next state到current state的跳转
  // Handshake Synchronization Control State Machine State Register
  always @(posedge hs_clk or negedge hs_rstn) begin : HS_SYNC_CTRL_FSM_REG_PROC
    if (hs_rstn == 1'b0) begin
      hs_sync_ctrl_state_r   <= ST_WAIT4_REQ;
    end else begin
      if (ch_en_sync == 1'b0) begin
        hs_sync_ctrl_state_r <= ST_WAIT4_REQ;
      end else begin    
        hs_sync_ctrl_state_r <= nxt_hs_sync_ctrl_state;
      end  
    end
  end
  • dma_req 和dma_single信号同步

    • 只有通道使能的情况下才会接收通道握手请求,hs_req_tog_a用于检测握手请求的跳变
  assign dma_hs = {dma_single_i, dma_req_i}; // Create bus of dma_req and dma_single
// DMA Handshake request bus Register always @(posedge hs_clk or negedge hs_rstn) begin : DMA_HS_REG_PROC if (hs_rstn == 1'b0) begin dma_hs_r <= 2'b00;   end else begin if (ch_en_sync == 1'b0) begin dma_hs_r <= 2'b00; end else begin dma_hs_r <= dma_hs; end end end // Detect DMA Handshake Request toggle. assign dma_hs_req_tog_a = (dma_hs ^ dma_hs_r) & ({2{ch_en_sync}}); assign hs_req_tog_a = (|dma_hs_req_tog_a);

// 当 DMA 握手确认DMA_ACK断言时,握手确认有效信号hs_ack_active在状态 ST_WAIT4_ACK_REQDSRT 中断言。
// 当 DMA 握手确认无效时,握手确认有效信号在状态 ST_WAIT4_ACK_DSRT 中解除断言

 always @(*) begin : HS_ACK_ACTIVE_PROC
    if ((hs_sync_ctrl_state_r == ST_WAIT4_ACK_REQDSRT) && (hs_ack_tog_a == 1'b1)) begin
  hs_ack_active = 1'b1;
    end else if ((hs_sync_ctrl_state_r == ST_WAIT4_ACK_DSRT) && (hs_ack_tog_a == 1'b1)) begin  
      hs_ack_active = 1'b0;
  end else begin  
      hs_ack_active = hs_ack_active_r;
    end  
  end
// The Handshake Acknowledgement active signal register
  always @(posedge hs_clk or negedge hs_rstn) begin : HS_ACK_ACTIVE_REG_PROC
  if (hs_rstn == 1'b0) begin
      hs_ack_active_r   <= 1'b0;
    end else begin
      if (ch_en_sync == 1'b0) begin
        hs_ack_active_r <= 1'b0;
     end else begin    
        hs_ack_active_r <= hs_ack_active;
      end  
    end
  end

 

// DMA握手请求限定符生成是基于DMA握手请求toggle和握手同步控制状态机

assign dma_hs_syncreq_a = (hs_req_tog_a &&    //在DMA握手信号dma_req/dma_single断言时

(  (hs_sync_ctrl_state_r  == ST_WAIT4_REQ) ||    //在DMA握手信号dma_req/dma_single解除断言,同时确保DMA握手确认dma_ack已经被断言。

((hs_sync_ctrl_state_r == ST_WAIT4_ACK_REQDSRT) && hs_ack_active)

)

) ||

(hs_ack_tog_a && (hs_sync_ctrl_state_r  == ST_WAIT4_ACK_DSRT) && hs_req_active) ||

//当外设不是流控制器并且在突发传输区域-有//机会dma_req可能晚到

late_hs_req_a;

 

当断言DMA握手请求时:在ST_WAIT4_ACK_DSRT状态断言握手请求active信号。当DMA握手请求去断言时,握手请求active信号在ST_WAIT4_ACK_REQDSRT状态下去断言。

 

 

1.1.1.1.2 dma_ack 信号同步

 

 

 

 

当断言DMA握手确认时,在ST_WAIT4_ACK_REQDSRT状态断言握手确认活动信号。当DMA握手确认被解除断言时,握手确认活动信号在ST WAIT4_ACK_DSRT状态下被解除断言.

 

 

assign dma_hs_ack_vector = {dma_finish_i, dma_ack_i};

寄存DMA握手确认总线基于同步DMA握手确认样本控制信号hs_ack_tog_a。

 

 

 

 

 

 

  • 握手输入的控制:基于CHx_CFG.SRC_PER和CHx CFG.DST_PER字段,分别对源信号和目标信号进行处理,执行握手接口信号到通道的映射。 
  • 由于DMAX_NUM_HS_IF=64 ,硬件握手接口个数为64,DMAX_NUM_CHANNELS=32,通道数为32.
  • SRC_PER和DST_PER字段的位宽为硬件握手接口个数64 对2取对数:SRC_PER/DST_PER.width= ceil(log2(DMAC_NUM_HS_IF))
  • SRC_PER/DST_PER 默认值为0,理论上当某个通道想要选择某个硬件握手接口接收的传输请求时,该通道的SRC_PER/DST_PER需要配置为对应硬件握手接口的接口号。如果不配置,则默认值为0,则下述等式满足以下条件:
{ dst_per_i[6*REQ_DST_I + 5],dst_per_i[6*REQ_DST_I + 4],
 dst_per_i[6*REQ_DST_I + 3],dst_per_i[6*REQ_DST_I + 2],
  dst_per_i[6*REQ_DST_I + 1],dst_per_i[6*REQ_DST_I]}==0
  • 即REQ_DST_J=0,即来自dma_single_sync[SINGLE_SRC_J]= dma_single_sync[0]的硬件握手接口请求信号。
  • 不同通道可以配置不同的值,从而选择不同的硬件握手接口请求信号。dst_per_i[6*REQ_DST_I + :6]:为每一个通道配置一个硬件握手接口对应的目标外设
always @(*) begin : DMA_REQ_DST_PROC
  dma_req_dst_c = {`DMAX_NUM_CHANNELS{1'b0}};
  for(REQ_DST_I=0; REQ_DST_I < `DMAX_NUM_CHANNELS; REQ_DST_I = REQ_DST_I + 1)
    for(REQ_DST_J=0; REQ_DST_J < `DMAX_NUM_HS_IF; REQ_DST_J = REQ_DST_J + 1)
      dma_req_dst_c[REQ_DST_I] = (dma_req_dst_c[REQ_DST_I] || ( dma_req_sync[REQ_DST_J]
                        &&({dst_per_i[6*REQ_DST_I + 5],dst_per_i[6*REQ_DST_I + 4],                           dst_per_i[6*REQ_DST_I + 3],dst_per_i[6*REQ_DST_I + 2],                        dst_per_i[6*REQ_DST_I + 1],dst_per_i[6*REQ_DST_I]}=REQ_DST_J)                       )); end
  • 不同通道可以配置不同的值,从而选择不同的硬件握手接口请求信号。src_per_i[6*REQ_SRC_I + :6]:为每一个通道配置一个硬件握手接口对应的目标外设
always @(*) begin : DMA_REQ_SRC_PROC
  dma_req_src_c = {`DMAX_NUM_CHANNELS{1'b0}};
  for(REQ_SRC_I=0; REQ_SRC_I < `DMAX_NUM_CHANNELS; REQ_SRC_I = REQ_SRC_I + 1)
    for(REQ_SRC_J=0; REQ_SRC_J < `DMAX_NUM_HS_IF; REQ_SRC_J = REQ_SRC_J + 1)
      dma_req_src_c[REQ_SRC_I] = (dma_req_src_c[REQ_SRC_I] || ( dma_req_sync[REQ_SRC_J]
                        &&({src_per_i[6*REQ_SRC_I + 5],src_per_i[6*REQ_SRC_I + 4],
                            src_per_i[6*REQ_SRC_I + 3],src_per_i[6*REQ_SRC_I + 2],
                              src_per_i[6*REQ_SRC_I + 1],src_per_i[6*REQ_SRC_I]}=REQ_SRC_J)
                      ));
end

将硬件握手接口请求信号映射到通道后,通过和通道使能信号相与,从而选择未被禁用的通道响应DMA请求。

  • dma ack 信号生成逻辑
    • 确认信号的生成(dma_ack):
    •  如果dma_ack是活动的,而dma_req是不活动的,那么dma ack被驱动为不活动的。
    • 如果dma_ack处于活动状态,则保持它处于活动状态
    • 如果dma_ack未激活,则在传输结束时trans_cmplt_dst_hs_c[DMA_ACK_DST_I]=1驱动dma_ack=1,考虑握手接口的极性并生成它。
always @( posedge dmac_core_clk_ng or negedge dmac_core_rstn) begin : S_DMA_ACK_SRC_PROC
    if(!dmac_core_rstn)
      dma_ack_src_r <= {`audss_DMAX_NUM_HS_IF{1'b0}};
    else if(dmac_sft_rst_i)
      dma_ack_src_r <= {`audss_DMAX_NUM_HS_IF{1'b0}};
    else begin
    for(DMA_ACK_SRC_I=0; DMA_ACK_SRC_I < `audss_DMAX_NUM_HS_IF; DMA_ACK_SRC_I = DMA_ACK_SRC_I + 1) begin
          if(!ch_en_int_src_c[DMA_ACK_SRC_I])
           dma_ack_src_r[DMA_ACK_SRC_I] <= 1'b0;
          else if ((!(dma_req_sync[DMA_ACK_SRC_I] | dma_single_sync[DMA_ACK_SRC_I])) && dma_ack_src_r[DMA_ACK_SRC_I])//如果dma_ack是活动的,而dma_req是不活动的,那么dma ack被驱动为不活动的。
          dma_ack_src_r[DMA_ACK_SRC_I] <= 1'b0;
          else if(trans_cmplt_src_hs_c[DMA_ACK_SRC_I])//如果dma_ack未激活,则在传输结束时trans_cmplt_dst_hs_c[DMA_ACK_DST_I]=1驱动dma_ack=1
              dma_ack_src_r[DMA_ACK_SRC_I] <= 1'b1;
      end
    end
  end

dma_ack信号选择和寄存

  assign dma_ack_c                = dma_ack_src_r | dma_ack_dst_r ;
  assign dma_ack_i                =dma_ack_c ;      
 always @(posedge dmac_core_clk or negedge dmac_core_rstn) begin : DMA_ACK_REG_PROC//在core时钟域下生成
    if (dmac_core_rstn == 1'b0) begin
      dma_ack_r   <= 1'b0;
    end else begin
      if (ch_en_i == 1'b0) begin  
        dma_ack_r <= 1'b0;
      end else begin  
        dma_ack_r <= dma_ack_i;
      end  
    end  
  end          

dma_ack是在core时钟域下生成的,需要两级同步到hs_clk域下(对应apb时钟域),同步后的信号为dma_hs_ack_sync,然后对dma_hs_ack_sync打一拍得到dma_hs_ack_sync_dly用于生成dma_hs_ack_sync的边沿检测信号dma_hs_ack_sync_a。