DMAC (direct memory access controller)


DMA

DMA(Direct memory access) 特性允許在CPU參與的情況下外設訪問DDR。如常見的ARM SOCCPU core通過AXI master,經常NOC(Network on a chipe)路由到DDR AXI SLAVE接口,實現DDR訪問。在SOC沒有DMA特性下,如果DDR需要和外設之間搬移數據,只能通過CPU 指令,這樣會耗費大量的CPU時間。如果有DMACPU配置好source addressdestination address,還有搬移數據的長度,DMAC(DMA controller)會在CPU不參與下搬移數據,CPU可以做其他工作,在完成后有中斷通知到CPU,進行相應的處理。有些DMAC還支持link listt模式,一次搬移多個不連續的DDR請求。

通常情況下SOC下的負載IPSD/SDIO/eMMC controller[chapter 26], USB Controller[chapter 31]都自帶專有的DMAC。其他相對簡單的IP如串口、SPI共用內部的通用DMAC[chapter 19](general purpose direct memory access)DMACCPU訪問DDR大概架構如下。其中CPUDMAC都有AXI master訪問DDR,同時CPU可以通過APB總線配置DMAC相關寄存器。

簡單CPUDMAC訪問DDR架構圖



通過圖1可以看到CPU訪問DDRDMAC有一點不同時,路徑上經過CacheCache是為了加速DDR訪問的一種緩存機制。因為有cache存在,CPUDMAC看到的同一個DDR地址空間可能不一致。有兩種情況導致不一致,第一種是CPU寫,DDR讀。第二種是DDR寫, CPU讀。第一種的不一致例子如DDR 0x80000000地址處值為0CPU往該地址寫入值為2,因為cacheWrite-back特性同時Cache可用,2只存在Cache中不會flushDDR上,所以DMACDDR看到的值還是0。第二種不一致和類似。對於這種SOC需要CPU相關指令clean cache , invalid cacheARM V7架構有一個Accelerator Coherency Port (ACP)接口,DMACCPU都通過Cache訪問DDR達到緩存一致的效果,避免CPU操作cacheACP位於圖2中虛線箭頭,為了區別ACP請求和普通DDR請求,經過ACP口的地址可以和普通的DDR地址不一致,NOC通過地址路由到相關的接口。這個地址的偏移ARM平台可以通過設備數的dma-ranges指定。如stm32mp151設備數的相關配置

 

mlahb {

compatible = "simple-bus";

#address-cells = <1>;

#size-cells = <1>;

dma-ranges = <0x00000000 0x38000000 0x10000>,

<0x10000000 0x10000000 0x60000>,

<0x30000000 0x30000000 0x60000>;

}

 

帶有ACP接口的DMAC

因為ARM PTE頁表policy可以設置成Strongly-ordered(no-cache)特性,CPU訪問繞過Cache,因此就沒有數據不一致的問題。圖3中虛線為CPU繞過Cache直接訪問DDR,不存在數據不一致問題。

3 stronger-order policyDMACCPU訪問路徑

IOMMU

從上面的介紹可以看到,CPUDMAC會同時訪問內存,如果DMAC軟件BUG造成越界寫DDR,從CPU角度很難發現問題。對於不支持link list模式的DMAC搬移大量的數據時,可能由於系統內存碎片導致申請不到一大段連續內存,導致請求失敗。鑒於這些原因在DMACDDR之間引入IOMMUARM也實現的IOMMU功能叫做SMMU。圖IOMMUDMAC架構圖。

帶有IOMMUDMAC架構圖

IOMMUCPU側的MMU類似機制,對於DDR請求會根據頁表做相應的映射,再請求到頁表對應DDR的物理地址。所以設定好DMAC IOMMU頁表,可以固定DDR請求的物理地址,避免訪問非法DDR物理地址。對於非法的范圍,出發相應的異常中斷。

Linux DMA架構

LinuxDMA的架構如下圖5..對於內存申請,IOMMU映射,Cache的操作通過inlclude/linux/dma-mapping.h相關接口。include/linux/dmaengine.h為使用通用DMAC相關接口。對於自帶DMACIP,只需要關注include/linux/dma-mapping.h相關接口。

5 linux DMA架構

通用的DMAC會通過DMA Engine導出統一的API。通用的處理流程如下。具體可參考dmates.c

 

Allocate a DMA slave channel

Set slave and controller specific parameters

Get a descriptor for transaction

Submit the transaction

Issue pending requests and wait for callback notification

 

其中申請給DMA使用的內存有兩種類型,一種Consistent DMA mappings ,另外一種是Streaming DMA mappings Consistent DMA mappings也稱作coherent DMA mapping,也就是驅動先申請一段stronger order內存,繞過cache達到數據一致性。Streaming DMA mappings使用的DDR normal policycache的內存,所以需要clean/invalid cache操作達到數據一致性。

Consistent DMA mapping API

這類接口適合驅動初始化時候分配好,后續的DMA相關操作固定使用申請的內存。申請接口為dma_alloc_coherent,釋放接口為dma_free_coherent



static inline void *dma_alloc_coherent(struct device *dev, size_t size,

dma_addr_t *dma_handle, gfp_t gfp)

返回值為CPU 范圍的地址, dma_handleDMAC操作地址。主要的流程如下

dma_alloc_coherent ->dma_mmap_attrs->dev->dma_ops->alloc

 

dma_alloc_coherent

->dma_mmap_attrs

-->dev->dma_ops->alloc

 



其中dev->dma_ops是在platform bus綁定platform device 解析其device tree設置的,相應的流程如下

 

driver_probe_device

->platform_dma_configure

->of_dma_configure

--->of_dma_get_range //dma-ranges map

--->of_dma_is_coherent // coherent

--->of_iommu_configure_device // get iommu_ops

--->arch_setup_dma_ops

---->arm_setup_iommu_dma_ops

----->arm_iommu_create_mapping

----->arm_get_iommu_dma_map_ops // iommu_coherent_ops

 

其中arm_get_iommu_dma_map_ops會根據device tree中時候配置coherent,決定返回值。這里說的coherent也就是這個DMAC支持ACP,對於帶有cacheDDR也不需要操作cache。這里分析不帶coherent配置相關接口,也就是iommu_ops。繼續上述的dev->dma_ops->alloc接口arm_iommu_alloc_attrs

 

arm_iommu_alloc_attrs

->__arm_iommu_alloc_attrs

-->__iommu_alloc_buffer

-->__iommu_create_mapping //創建DMA address DDR地址映射,配置IOMMU 頁表

-->dma_common_pages_remap //通過vmap接口創建stronger order內存映射

 

dma_common_pages_remap中的pte頁表的配置通過__get_dma_pgprot。可以看到pte的配置是L_PTE_MT_UNCACHED

 

static inline pgprot_t __get_dma_pgprot(unsigned long attrs, pgprot_t prot)

{

prot = (attrs & DMA_ATTR_WRITE_COMBINE) ?

pgprot_writecombine(prot) :

pgprot_dmacoherent(prot);

return prot;

}

 

#define pgprot_dmacoherent(prot) \

__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED | L_PTE_XN)

#endif

 

 

Streaming DMA mapping API

Streaming DMA mapping適用於內存已分配好,操作相應的cacheIOMMU mapDMAC使用。例如用戶態發送TCP/IP數據,數據包是協議棧通過alloc_skb分配的normal policy的帶cache的內存,再發起DMA操作前需要操作cache

dma_map_single 對內存分配操作cache,再進行IOMMU map操作返回DMA address

static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,

size_t size, enum dma_data_direction dir, unsigned long attrs)

 

dma_map_single_attrs

->dev->dma_ops->alloc //Consistent DMA mapping probe driver設定的

arm_iommu_map_page

-->__dma_page_cpu_to_dev //通過dir確定clean/invalid cache

-->arm_coherent_iommu_map_page //構建IOMMU映射,返回DMA address

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM