DMA IP PS侧c / python代码笔记

DMA IP PS

实现DMA传输的三要素:

  1. TX_BUFFE&RX_BUFFER
  2. TX&RX channel
  3. TX&RX interrupts

一: Vivado SDK 上的c语言
对DMA ip核的调用其main()主要可以分为如下结构

main()
	DMA_Intr_Init()		//初始化DMA
		XAXIDma_Lookup Config()		//查找DMA设备
		XAXIDma_CfgIntialize()		//初始化DMA设备
	Init_Intr_System()		//初始化中断
		XScuGic_Lookup Config()
		XScuGic_CfgConfig()
	Setup_Intr_Exception()
		Xil_ExceptionInit()		//使能硬件中断
		Xil_ExceptionRegister Handler()		//中断注册函数
	DMA_setup_IntrSystem()		//设置DMA中断
		XScuGic_SetPriority Trigger Type()
		XScuGic_Connect()		//连接中断源
		XScuGic_Enable()
	DMA_Intr_Enable()		//axi_dma使能中断
		XAxiDma_IntrDisable()
		XAxiDma_IntrEnable()		//根据Xilinx的sample给出的先禁用再使能

Xilinx dma_test_bsp_xaxidma_example_simple_intr:
xilinx给出的官方用例(simple Intr模式)主要包含以下部分:

  1. main()函数
  2. CheckData()函数
  3. TxIntrHandler & RxIntrHandler 函数
  4. SetupIntrSystem及DisableIntrSystem函数

具体如下:
1.Include files及变量定义(分完全代码)

#ifndef
DDR_BASE_ADDR

#warning CHECK
FOR THE VALID DDR ADDRESS IN XPARAMETERS.H, \

        DEFAULT SET TO 0x01000000

#define
MEM_BASE_ADDR       0x01000000

#else

#define MEM_BASE_ADDR       (DDR_BASE_ADDR + 0x1000000)

#endif

 

#ifdef XPAR_INTC_0_DEVICE_ID

#define RX_INTR_ID      XPAR_INTC_0_AXIDMA_0_S2MM_INTROUT_VEC_ID

#define TX_INTR_ID      XPAR_INTC_0_AXIDMA_0_MM2S_INTROUT_VEC_ID

#else

#define
RX_INTR_ID      XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID

#define
TX_INTR_ID      XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID

#endif

 

#define
TX_BUFFER_BASE      (MEM_BASE_ADDR + 0x00100000)

#define
RX_BUFFER_BASE      (MEM_BASE_ADDR + 0x00300000)/*TX buffer与RX buffer的基地址分别定义为MEM_BASE_ADDR加上0x00100000和0x00300000的偏移量*/

#define RX_BUFFER_HIGH      (MEM_BASE_ADDR + 0x004FFFFF)

在这里我们通过查看xparameters.h文件可以得到DDR的内存地址范围,我们定义的MEM_BASE_ADDR必须处在这一范围之内,笔者查看到的范围如下:

/*
Definitions for peripheral PS7_DDR_0 */

#define
XPAR_PS7_DDR_0_S_AXI_BASEADDR 0x00100000

#define
XPAR_PS7_DDR_0_S_AXI_HIGHADDR 0x3FFFFFFF

2.main()

int main(void)

{

    int Status;

    XAxiDma_Config *Config;

    int Tries = NUMBER_OF_TRANSFERS;

    int Index;

    u8 *TxBufferPtr;

    u8 *RxBufferPtr;

    u8 Value;


    TxBufferPtr = (u8 *)TX_BUFFER_BASE ;

    RxBufferPtr = (u8 *)RX_BUFFER_BASE;
    

    xil_printf("\r\n--- Entering main() --- \r\n");


    Config = XAxiDma_LookupConfig(DMA_DEV_ID);

    if (!Config) {

        xil_printf("No config found for %d\r\n", DMA_DEV_ID);


        return XST_FAILURE;

    }


    /* Initialize DMA engine */

    Status = XAxiDma_CfgInitialize(&AxiDma,Config);

 

    if (Status != XST_SUCCESS) {

        xil_printf("Initialization failed %d\r\n", Status);

        return XST_FAILURE;

    }

 

    if(XAxiDma_HasSg(&AxiDma)){

        xil_printf("Device configured as SG mode \r\n");

        return XST_FAILURE;

    }

 

    /* Set up Interrupt system  */

    Status = SetupIntrSystem(&Intc,&AxiDma, TX_INTR_ID, RX_INTR_ID);

    if (Status != XST_SUCCESS) {

 

        xil_printf("Failed intr setup\r\n");

        return XST_FAILURE;

    }

 

    /* Disable all interrupts before setup */

 

    XAxiDma_IntrDisable(&AxiDma,XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE);

 

    XAxiDma_IntrDisable(&AxiDma,XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA);

 

    /* Enable all interrupts */

    XAxiDma_IntrEnable(&AxiDma,XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE);

 

 

    XAxiDma_IntrEnable(&AxiDma,XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA);

 

    /* Initialize flags before start transfer test  */

    TxDone = 0;

    RxDone = 0;

    Error = 0;

 

    Value = TEST_START_VALUE;

 

    for(Index = 0; Index < MAX_PKT_LEN; Index ++) {

            TxBufferPtr[Index] = Value;

 

            Value = (Value + 1) & 0xFF;

    }

 

    /* Flush the SrcBuffer before the DMA transfer, in case
the Data Cache is enabled*/

    Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, MAX_PKT_LEN);

#ifdef __aarch64__

    Xil_DCacheFlushRange((UINTPTR)RxBufferPtr,MAX_PKT_LEN);

#endif

 

    /* Send a packet */

    for(Index = 0; Index < Tries; Index ++) {

 

        Status = XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR)RxBufferPtr,MAX_PKT_LEN,XAXIDMA_DEVICE_TO_DMA);

 

        if (Status != XST_SUCCESS) {

            return XST_FAILURE;

        }

 

        Status = XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) TxBufferPtr,MAX_PKT_LEN,XAXIDMA_DMA_TO_DEVICE);

 

        if (Status != XST_SUCCESS) {

            return XST_FAILURE;

        }


        /*Wait TX done and RX done*/

        while (!TxDone && !RxDone &&!Error) {

                /* NOP */

        }

 

        if (Error) {

            xil_printf("Failed test transmit%s done, "

            "receive%s done\r\n", TxDone? "":" not",

                            RxDone? "":" not");


            goto Done;


        }

 

        /*Test finished, check data*/

        Status = CheckData(MAX_PKT_LEN, 0xC);

        if (Status != XST_SUCCESS) {

            xil_printf("Data check failed\r\n");

            goto Done;

        }

    }



    xil_printf("Successfully ran AXI DMA interrupt Example\r\n");


    /* Disable TX and RX Ring interrupts and return success */

 
    DisableIntrSystem(&Intc, TX_INTR_ID,RX_INTR_ID);


Done:

    xil_printf("--- Exiting main() --- \r\n");


    return XST_SUCCESS;

}

3.TxIntrHandler & RxIntrHandler 函数(二者结构功能类似,这里以TX为例)

static void TxIntrHandler(void *Callback)

{

    u32 IrqStatus;

    int TimeOut;

    XAxiDma *AxiDmaInst = (XAxiDma *)Callback;


    /* Read pending interrupts */

    IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst,XAXIDMA_DMA_TO_DEVICE);		//取出当前中断

 
    /* Acknowledge pending interrupts */


    XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus,XAXIDMA_DMA_TO_DEVICE);		//确认当前中断被接收


    /* If no interrupt is asserted, we do not do anything*/

    if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {


        return;

    }

 

    /** If error interrupt is asserted, raise error flag, reset the hardware to recover from the error, and return with no further processing.*/

    if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {


        Error = 1;		//设置错误标记


        /*Reset should never fail for transmit channel*/

        XAxiDma_Reset(AxiDmaInst);		//复位DMA

 

        TimeOut = RESET_TIMEOUT_COUNTER;

 
//看门狗控制复位完成等待//
        while (TimeOut) {

            if (XAxiDma_ResetIsDone(AxiDmaInst)) {

                break;

            }


            TimeOut -= 1;

        }

 
        return;

    }


    /*If Completion interrupt is asserted, then set the TxDone flag*/

    if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {

        TxDone = 1;		//中断类型为传输完成,表示tx传输结束

    }

}

二:PYNQ里的python.lib.dma

from pynq import DefaultIP
import warnings

MAX_C_SG_LENGTH_WIDTH = 26		#应大于vivado pl侧定义的width of buffer length register


class _DMAChannel:
    """Drives a single channel of the Xilinx AXI DMA

    This driver is designed to be used in conjunction with the
    `pynq.allocate()` method of memory allocation. The channel has
    main functions `transfer` and `wait` which start and wait for
    the transfer to finish respectively. If interrupts are enabled
    there is also a `wait_async` coroutine.

    This class should not be constructed directly, instead used
    through the AxiDMA class.

    """
    def __init__(self, mmio, offset, size, flush_before, interrupt=None):
        self._mmio = mmio
        self._offset = offset
        self._interrupt = interrupt
        self._flush_before = flush_before
        self._size = size
        self._active_buffer = None
        self._first_transfer = True
        self.start()

    @property
    def running(self):
        """True if the DMA engine is currently running

        """
        return self._mmio.read(self._offset + 4) & 0x01 == 0x00
#通过查阅pg021 IP AXI DMA v7.0手册得知,self._offset+4对应寄存器为MM2S_DMASR,其bit0在为0时,DMA cahnnel 处于running状态
    @property
    def idle(self):
        """True if the DMA engine is idle

        `transfer` can only be called when the DMA is idle

        """
        return self._mmio.read(self._offset + 4) & 0x02 == 0x02
#通过查阅pg021 IP AXI DMA v7.0手册得知,self._offset+4对应寄存器为MM2S_DMASR,其bit1在为1时,DMA cahnnel 处于Idle状态
    def start(self):
        """Start the DMA engine if stopped

        """
        if self._interrupt:
            self._mmio.write(self._offset, 0x1001)	#向寄存器MM2S_DMACR赋值,0x1001,bit0为1时DMA = RUN,bits31-24赋值为1使能Interrupt Delay Time Out
        else:
            self._mmio.write(self._offset, 0x0001)	#为启用interrupt则正常开启DMA
        while not self.running:
            pass
        self._first_transfer = True

    def stop(self):
        """Stops the DMA channel and aborts the current transfer

        """
        self._mmio.write(self._offset, 0x0000)	#DMA Stop
        while self.running:
            pass

    def _clear_interrupt(self):
        self._mmio.write(self._offset + 4, 0x1000)

    def transfer(self, array):
        """Transfer memory with the DMA

        Transfer must only be called when the channel is idle.

        Parameters
        ----------
        array : ContiguousArray
            An xlnk allocated array to be transferred

        """
        if array.nbytes > self._size:
            raise ValueError('Transferred array is {} bytes, which exceeds '
                             'the maximum DMA buffer size {}.'.format(
                              array.nbytes, self._size))
        if not self.running:
            raise RuntimeError('DMA channel not started')
        if not self.idle and not self._first_transfer:
            raise RuntimeError('DMA channel not idle')
        if self._flush_before:
            array.flush()
        self._mmio.write(self._offset + 0x18,array.physical_address)	#Direct Register Mode MM2S_SA,Source Address
        self._mmio.write(self._offset + 0x28, array.nbytes)	#MM2S transfer length(Bytes)
        self._active_buffer = array
        self._first_transfer = False

    def wait(self):
        """Wait for the transfer to complete

        """
        if not self.running:
            raise RuntimeError('DMA channel not started')
        while not self.idle:
            pass
        if not self._flush_before:
            self._active_buffer.invalidate()

    async def wait_async(self):
        """Wait for the transfer to complete

        """
        if not self.running:
            raise RuntimeError('DMA channel not started')
        while not self.idle:
            await self._interrupt.wait()
        self._clear_interrupt()
        if not self._flush_before:
            self._active_buffer.invalidate()


class DMA(DefaultIP):
def __init__(self, description, *args, **kwargs):
        """Create an instance of the DMA Driver

        Parameters
        ----------
        description : dict
            The entry in the IP dict describing the DMA engine

        """
        if type(description) is not dict or args or kwargs:
            raise RuntimeError('You appear to want the old DMA driver which '
                               'has been deprecated and moved to '
                               'pynq.lib.deprecated')
        super().__init__(description=description)

        if 'parameters' in description and \
                'c_sg_length_width' in description['parameters']:
            self.buffer_max_size = \
                1 << int(description['parameters']['c_sg_length_width'])
        else:
            self.buffer_max_size = 1 << MAX_C_SG_LENGTH_WIDTH
            message = 'Failed to find parameter c_sg_length_width; ' \
                      'users should really use *.hwh files for overlays.'
            warnings.warn(message, UserWarning)

        if 'mm2s_introut' in description['interrupts']:
            self.sendchannel = _DMAChannel(self.mmio, 0x0,
                                           self.buffer_max_size,
                                           True, self.mm2s_introut)	#基地址00h配置MM2S
        else:
            self.sendchannel = _DMAChannel(self.mmio, 0x0,
                                           self.buffer_max_size,
                                           True)

        if 's2mm_introut' in description['interrupts']:
            self.recvchannel = _DMAChannel(self.mmio, 0x30,
                                           self.buffer_max_size,
                                           False, self.s2mm_introut)	#基地址30h配置S2MM
        else:
            self.recvchannel = _DMAChannel(self.mmio, 0x30,
                                           self.buffer_max_size,
                                           False)

    bindto = ['xilinx.com:ip:axi_dma:7.1']

Fin

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值