概述
- 之前的文章分享了如何在裸跑的情况下实现AXI_DMA回环测试(有需要的可以翻看之前的文章),也分享过如何通过mmap函数映射的方式操作片内外设。接下来就在之前两篇文章的基础上聊一下在linux下如何实现axi_dma数据读写。
框图
-
实现框图如下所示
-
block design框图如下所示
-
Address map分配如下所示
代码实现(废话不多说直接上干货)
- axi_dma.h
#ifndef _AXI_DMA__H
#define _AXI_DMA__H
/***************************** Include Files *********************************/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/mman.h>
/************************** Constant Definitions *****************************/
#define MM2S_CR_OFFSET ((0x00000000) >> 2) /**< Channel control */
#define MM2S_SR_OFFSET ((0x00000004) >> 2) /**< Status */
#define MM2S_CDESC_OFFSET ((0x00000008) >> 2) /**< Current descriptor pointer */
#define MM2S_CDESC_MSB_OFFSET ((0x0000000C) >> 2) /**< Current descriptor pointer */
#define MM2S_TDESC_OFFSET ((0x00000010) >> 2) /**< Tail descriptor pointer */
#define MM2S_TDESC_MSB_OFFSET ((0x00000014) >> 2) /**< Tail descriptor pointer */
#define MM2S_SRCADDR_OFFSET ((0x00000018) >> 2) /**< Simple mode source address pointer */
#define MM2S_SRCADDR_MSB_OFFSET ((0x0000001C) >> 2) /**< Simple mode source address pointer */
#define MM2S_DESTADDR_OFFSET ((0x00000018) >> 2) /**< Simple mode destination address pointer */
#define MM2S_DESTADDR_MSB_OFFSET ((0x0000001C) >> 2) /**< Simple mode destination address pointer */
#define MM2S_BUFFLEN_OFFSET ((0x00000028) >> 2) /**< Tail descriptor pointer */
#define MM2S_SGCTL_OFFSET ((0x0000002c) >> 2) /**< SG Control Register */
#define S2MM_CR_OFFSET ((0x00000000 + 0x30) >> 2) /**< Channel control */
#define S2MM_SR_OFFSET ((0x00000004 + 0x30) >> 2) /**< Status */
#define S2MM_CDESC_OFFSET ((0x00000008 + 0x30) >> 2) /**< Current descriptor pointer */
#define S2MM_CDESC_MSB_OFFSET ((0x0000000C + 0x30) >> 2) /**< Current descriptor pointer */
#define S2MM_TDESC_OFFSET ((0x00000010 + 0x30) >> 2) /**< Tail descriptor pointer */
#define S2MM_TDESC_MSB_OFFSET ((0x00000014 + 0x30) >> 2) /**< Tail descriptor pointer */
#define S2MM_SRCADDR_OFFSET ((0x00000018 + 0x30) >> 2) /**< Simple mode source address pointer */
#define S2MM_SRCADDR_MSB_OFFSET ((0x0000001C + 0x30) >> 2) /**< Simple mode source address pointer */
#define S2MM_DESTADDR_OFFSET ((0x00000018 + 0x30) >> 2) /**< Simple mode destination address pointer */
#define S2MM_DESTADDR_MSB_OFFSET ((0x0000001C + 0x30) >> 2) /**< Simple mode destination address pointer */
#define S2MM_BUFFLEN_OFFSET ((0x00000028 + 0x30) >> 2) /**< Tail descriptor pointer */
#define XAXIDMA_PHY_ADDR 0x80000000
#define SRC_PHY_ADDR 0x10000000
#define DES_PHY_ADDR 0x20000000
/**************************** Type Definitions *******************************/
typedef struct
{
off_t phy_addr;
unsigned int *v_base_addr;
unsigned int *src;
unsigned int *des;
unsigned int rd_len;
unsigned int wt_len;
}xdma_handle;
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
/************************** Variable Definitions *****************************/
void xdma_test();
unsigned int copy_from_pl(xdma_handle *xdma);
unsigned int copy_to_pl(xdma_handle *xdma);
void axi_dma_init();
#endif
- axi_dma.c
#include "axi_dma.h"
xdma_handle xdma_hd;
unsigned int axi_dma_write(unsigned int* dma_base_addr, int reg_offset, unsigned int value)
{
dma_base_addr[reg_offset] = value;
return 1;
}
unsigned int axi_dma_read(unsigned int* dma_base_addr, int reg_offset)
{
return dma_base_addr[reg_offset];
}
void axi_dma_reset(unsigned int* dma_base_addr)
{
/*<! reset the channels */
axi_dma_write(dma_base_addr, MM2S_CR_OFFSET, 4);
axi_dma_write(dma_base_addr, S2MM_CR_OFFSET, 4);
/*<! halt the channels */
axi_dma_write(dma_base_addr, MM2S_CR_OFFSET, 0);
axi_dma_write(dma_base_addr, S2MM_CR_OFFSET, 0);
}
void axi_dma_init()
{
int dh = open("/dev/mem", O_RDWR | O_SYNC);
xdma_handle *xdma = &xdma_hd;
xdma->v_base_addr= mmap(NULL,
65535,
PROT_READ|PROT_WRITE,
MAP_SHARED,
dh,
XAXIDMA_PHY_ADDR);
xdma->src = mmap(NULL,
65535,
PROT_READ|PROT_WRITE,
MAP_SHARED,
dh,
SRC_PHY_ADDR);
xdma->des = mmap(NULL,
65535,
PROT_READ|PROT_WRITE,
MAP_SHARED,
dh,
DES_PHY_ADDR);
xdma->rd_len = 512;
xdma->wt_len = 512;
//
//
// printf(" - Source address : 0x%x \r\n", xdma->src);
// printf(" - Destination address: 0x%x\r\n",xdma->des);
//
//
axi_dma_reset(xdma->v_base_addr);
/*<! 1 = Run - Start DMA operations ,Mask interrupts*/
axi_dma_write(xdma->v_base_addr, MM2S_CR_OFFSET, 0xf001);
axi_dma_write(xdma->v_base_addr, S2MM_CR_OFFSET, 0xf001);
}
unsigned int copy_to_pl(xdma_handle *xdma)
{
unsigned int status;
/*<! set source addresses */
axi_dma_write(xdma->v_base_addr, MM2S_SRCADDR_OFFSET, SRC_PHY_ADDR);
/*<! set trans len*/
axi_dma_write(xdma->v_base_addr, MM2S_BUFFLEN_OFFSET, xdma->wt_len);
do{
status = axi_dma_read(xdma->v_base_addr, MM2S_SR_OFFSET);
}while(!(status & 0x02));
return status;
}
unsigned int copy_from_pl(xdma_handle *xdma)
{
unsigned int status;
/*<! set destination addresses */
axi_dma_write(xdma->v_base_addr, S2MM_SRCADDR_OFFSET, DES_PHY_ADDR);
/*<! set trans len*/
axi_dma_write(xdma->v_base_addr, S2MM_BUFFLEN_OFFSET, xdma->rd_len);
do{
status = axi_dma_read(xdma->v_base_addr, S2MM_SR_OFFSET);
}while(!(status & 0x02));
return status;
}
void source_buff_init()
{
int *dat = (int *)xdma_hd.src, i;
for(i=0; i<512/4; i++,dat++){
*dat = i;
// printf("%x\r\n", *dat);
}
}
int check_data()
{
int *dat = (int *)xdma_hd.des, i;
for(i=0; i<512/4; i++,dat++){
if(*dat != i) return 0;
}
return 1;
}
void xdma_test()
{
printf("========================\r\n");
printf(" Start XDMA test\r\n");
printf("========================\r\n");
axi_dma_init();
source_buff_init();
copy_to_pl(&xdma_hd);
copy_from_pl(&xdma_hd);
if(check_data() == 1){
printf("XDMA transfer successful\r\n");
}
else{
printf("XDMA transfer faile!\r\n");
}
}