<strike id="kiyse"></strike>
  • <tr id="kiyse"></tr>
  • <strike id="kiyse"></strike><samp id="kiyse"><tbody id="kiyse"></tbody></samp>
    <strike id="kiyse"><s id="kiyse"></s></strike>
    <tr id="kiyse"></tr>
    <noframes id="kiyse"><code id="kiyse"></code></noframes>
    <th id="kiyse"></th>
    <samp id="kiyse"></samp>
  • <th id="kiyse"><s id="kiyse"></s></th>
  • 基于PCIE接口的高速大容量數據采集——軟件篇

     2022-5-7     作者:Emtronix         

      高速大容量數據采集方案是一個可實現是連續采集數據率超過10MB/s的應用方案,方案的硬件由支持PCIE高速接口的Linux工控主板(ESM7100、ESM8100等)和基于FPGA的擴展模塊組成,軟件則包括符合Linux DMA Engine架構的DMA Controller驅動、DMA Client驅動及應用程序三個部分。方案的總體介紹請參考《基于PCIE接口的高速大容量數據采集—總體方案》一文。本文將重點介紹針對Xilinx的< DMA/Bridge Subsystem for PCI Express v4.1> FPGA IP核的特性,實現Linux DMA Engine架構驅動程序,并為應用程序提供簡潔方便的API函數,來控制數據采集過程并適時處理已在系統內存中的實時采集數據流。


      目前Xilinx公司為其IP核DMA/Bridge Subsystem for PCI Express v4.1,僅提供基于x86體系的驅動,而沒有在Linux DMA Engine架構上做工作。而事實上,DMA Engine架構已成為ARM嵌入式Linux平臺的DMA應用的事實標準(de facto),因此本方案針對高速大容量數據采集的需求,構建了DMA Engine架構的驅動程序,包括通用DMA Controller驅動和面向應用的DMA Client驅動,應用程序通過標準的字符型設備節點,操作DMA Client驅動,從而實現所需的數據采集。圖1是從軟件開發角度來看的總體功能框圖。


    基于PCIE接口的高速大容量數據采集.png

    圖1 方案總體功能框圖


      Linux DMA Engine架構的基本思路是把不同的DMA控制器封裝為統一的API接口,供面向應用的DMA Client調用。在本方案中,DMA Client驅動一方面通過DMA Engine的API函數操作PCIE端點的DMA功能,另一方面還要通過PCIE接口控制前端的數據采集邏輯。DMA Client驅動對上層的用戶應用程序,開放了一組標準的字符設備(char dev)API,供User App操作,在本方案中字符設備為“/dev/pcie0”。為了減少數據的搬動,采集數據緩沖區通常以ping-pong buffer的結構配置在Linux的保留存儲器(reserved memory)中,且用戶應用程序可通過mmap獲得ping-pong buffer指針,直接處理DMA傳上來的數據。Ping-pong buffer讓應用程序和DMA交替操作數據buffer,如應用程序處理buffer 0#的數據時,DMA把新數據傳送到buffer 1#,然后按固定時間周期ping-pong切換buffer,從而實現連續的實時數據采集和處理。字符設備“/dev/pcie0”僅傳遞采集硬件的控制與狀態信息。


    DMA Engine API


      DMA Engine架構為不同的DMA模式提供不同的API函數,其中最主要的是單次DMA和周期DMA兩種,其API函數分別為:


    struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(
               struct dma_chan *chan, struct scatterlist *sgl,
               unsigned int sg_len, enum dma_data_direction direction,
               unsigned long flags);
     
    struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic(
               struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
               size_t period_len, enum dma_data_direction direction);


      DMA Controller驅動要求DMA支持Scatter-gather結構的非連續數據Buffer,但在本方案的應用中,對單次DMA情形,采用單個Buffer是最常見的應用方式,這時可采用DMAEngine的簡化函數:


    struct dma_async_tx_descriptor *dmaengine_prep_slave_singl(
               struct dma_chan *chan, dma_addr_t buf, size_t len, 
               enum dma_data_direction direction, unsigned long flags);


      Cyclic DMA模式,是把多個DMA Buffer通過其描述符(dma descriptor)表連接成環狀,當一個buffer的DMA傳送結束后,驅動程序的中斷線程將自動啟動面向下一個描述符的DMA Buffer。由DMA descriptor表描述的邏輯流程如圖2所示:


    基于PCIE接口的高速大容量數據采集.png

    圖2 Cyclic DMA邏輯流程


      本方案的DMA Controller驅動實現了上述兩種DMA傳輸方式,即單次DMA傳輸和周期DMA傳輸。DMA Controller驅動本質上講,是一種通用的DMA服務器,如何使用DMA的傳輸功能,實現具體的數據傳輸任務,則是由DMA Client來決定的。Linux把DMA服務與具體應用分成兩個部分,有利于DMA Controller驅動面向不同的應用場景。


    DMA Client驅動


      DMA Client驅動是一個面向應用的驅動,如圖1所示,它需要與User Space的上層應用程序配合運行,來完成所需的數據采集與處理。


      單次DMA的操作如下所示。


    /* prepare a single buffer dma */
    desc = dmaengine_prep_slave_single(dchan, edev->dma_phys,
                  edev->total_len, DMA_DEV_TO_MEM, 
                  DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
    if (!desc) {
        dev_err(edev->dev, "dmaengine_prep_slave_single(..) failed\n");
        ret = -ENODEV;
        goto error_out;
    }
     
    /* setup dtaker hardware */
    eta750_dtaker_setup(edev);
     
    /* put callback, and submit dma */
    desc->callback = dma_callback;
    desc->callback_param = edev;
    edev->cookie = dmaengine_submit(desc);
    ret = dma_submit_error(edev->cookie);
    if (ret) {
    dev_err(edev->dev, "DMA submit failed %d\n", ret);
    goto error_submit;
    }
     
    /* init complete, and fire */
    reinit_completion(&edev->xdma_chan_complete);
    dma_async_issue_pending(dchan);
     
    /* simulate input data */
    eta750_dtaker_run(edev);
     
    /* wait dma complete */
    count = wait_for_completion_timeout(&edev->xdma_chan_complete, msecs_to_jiffies(DMA_TIMEOUT));
    if (count == 0) {
    dev_err(edev->dev, "wait_for_completion_timeout timeout\n");
    ret = -ETIMEDOUT;
    eta750_dtaker_end(edev);
    goto error_submit;
    }
     
    /* error processing */
    eta750_dtaker_error_pro(edev);
     
    /* stop front-end daq unit */
    count = eta750_dtaker_end(edev);
     
    /* dump data */
    eta750_dtaker_dump_data(edev);
    return edev->total_len;
     
    error_submit:
    dmaengine_terminate_all(dchan);
     
    error_out: 
    return ret;


      只有周期DMA方式才能實現連續數據采集,在DMA Client中采用雙DMA Buffer的乒乓結構來實現連續采集,應用程序處理0# Buffer數據時,DMA傳輸數據至1# Buffer,傳輸結束時,進行切換,應用程序處理1# Buffer數據,DMA傳輸新數據至0# Buffer。周期DMA需要指定每個buffer的長度period_len,同時需指定由2個buffer構成的ping-pong buffer的總長度total_len。其DMA流程如下所示。


    /* prepare cyclic buffer dma */
    desc = dmaengine_prep_dma_cyclic(dchan, edev->dma_phys, edev->total_len, 
    edev->period_len, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
    if (!desc) {
    dev_err(edev->dev, "%s: prep dma cyclic failed!\n", __func__);
    ret = -EINVAL;
    goto error_out;
    }
     
    /* in cyclic mode */
    edev->cyclic = true;
     
    /* setup dtaker hardware */
    eta750_dtaker_setup(edev);
     
    /* put callback, and submit dma */
    desc->callback = dma_callback;
    desc->callback_param = edev;
    edev->cookie = dmaengine_submit(desc);
    ret = dma_submit_error(edev->cookie);
    if (ret) {
    dev_err(edev->dev, "cyclic dma submit failed %d\n", ret);
    goto error_submit;
    }
     
    /* init complete, and fire */
    reinit_completion(&edev->xdma_chan_complete);
    dma_async_issue_pending(dchan);
    edev->running = true; 
     
    /* simulate input data */
    eta750_dtaker_run(edev);
    edev->data_seed += ((edev->period_len / sizeof(u16)) * edev->data_incr);
     
    while(!kthread_should_stop()) {
    /* wait dma complete */
    count = wait_for_completion_timeout(&edev->xdma_chan_complete, msecs_to_jiffies(DMA_TIMEOUT));
    if (count == 0) {
    dev_err(edev->dev, "wait_for_completion timeout, transfer %d\n",
    edev->transfer_count);
    ret = -ETIMEDOUT;
    break;
    }
     
    /* data processing */
    eta750_dtaker_error_pro(edev);
    edev->transfer_count++;
    reinit_completion(&edev->xdma_chan_complete);
     
    /* fill more data */
    eta750_dtaker_run(edev);
    edev->data_seed += ((edev->period_len / sizeof(u16)) * edev->data_incr);
    }
     
    /* stop front-end daq unit */
    count = eta750_dtaker_end(edev);
    edev->running = false; 
     
    error_submit:
    dmaengine_terminate_all(dchan);
    edev->cyclic = false;
    dev_info(edev->dev, "%s: dma stopped, cyclic %d, running %d\n", __func__,
    edev->cyclic, edev->running);
     
    error_out:
    return ret;


      從上面代碼可見,傳送過程是一個無限循環,DMA Controller驅動會自動進行ping-pong buffer的切換。并通過回調函數通知上層應用程序,新數據已準備就緒。應用程序可通過命令來終止采集傳輸過程。


    DTaker前端采集邏輯


      前端數據采集單元DTaker,包括在FPGA芯片XC7A50T-2CSG325中,主要是實現對AD芯片AD7606C的操作,并把數據寫入FPGA中的FIFO緩沖區中。從圖3可看到作為User Logic的DTaker與FIFO的相互關系。


    基于PCIE接口的高速大容量數據采集.png

    圖3 DTaker功能框圖


      從programming角度看,DTaker就是一組寄存器。代碼通過操作實現:(1)啟動AD轉換;(2)讀取AD數據,并按照一定格式把packed的數據寫入FIFO。具體說,DTaker是由8個32-bit寄存器及相關數字邏輯構成的可編程控制單元,寄存器的定義如下。


    Offset寄存器名稱功能簡述
    0x00CONTROL控制寄存器
    0x04STATUS狀態寄存器
    0x08PKG_SIZE采集數據長度寄存器
    0x0CDAQ_CFG前端采集控制寄存器
    0x10EVENT_CFG前端采集觸發事件配置寄存器
    0x14-未定義
    0x18CPU_DAT2CPU仿真測試數據
    0x1CCPU_DAT3CPU仿真測試數據


      各個寄存器功能定義如下:


      DTaker控制寄存器 CONTROL各bit定義如下:

      D[3 : 0] = LEDOUT[3 : 0]

      D4 = EVENT_IRQEN,置1使能DTAKER觸發中斷,事件=某種觸發條件

      D5 = OVERFLOW_IRQEN,置1使能FIFO寫溢出中斷

      D6 = TIMEOUT_IRQEN,置1使能C2H接口超時中斷,暫不用

      D7 = DMAEN,置1使能AXIS transfer

      D8 = DAQEN,= 0:選擇CPU仿真數據;= 1: 選擇前端AD數據

      D9 = FIFO_RESET,置1清FIFO,軟件清零控制位

      D[31 : 10] = 暫時未用


      狀態寄存器STATUS,各bit狀態標志RO/W1C

      D[1 : 0] = KEY_IN[1:0]

      D2 = EVENT_FLAG,事件標志,W1C

      D3 = OVERFLOW_ERR,FIFO寫溢出錯誤標志,W1C

      D4 = EVENT_IRQ,事件中斷標志,W1C

      D5 = OVERFLOW_IRQ,FIFO寫溢出錯誤中斷標志,W1C

      D6 = TIMEOUT_IRQ,C2H接口超時中斷標志,W1C

      D7 = DEBUG_BIT,調試用,根據情況臨時定義具體內容

      D[17 : 8] = 10’b0000000000,暫時未用

      D18 = FIFO_FULL,=1: FIFO 已滿

      D19 = FIFO_EMPTY,=1: FIFO 已空

      D[22 : 20] = EVENT_CODE[2 : 0],事件編碼信息

      D23 = 1’b0,暫時未用

      D[31 : 24] = Version Info, RO


      DMA傳送長度寄存器PKG_SIZE,字節為單位,需16字節整倍數。每完成一次transfer,PKG_SIZE -= 8字節,直至PKG_SIZE = 0;PKG_SIZE = 8時,表示后續是最后一次transfer,TLAST = 1。


      數據采集配置寄存器DAQ_CFG,用于定義采集的通道、采樣率等參數。

      DAQ_CFG.D0 = RUN,置1啟動前端硬件數據采集。


      觸發事件配置寄存器EVENT_CFG,用于定義采集單元的觸發事件條件,如觸發通道、電平、觸發沿,觸發采集長度等參數。

      

      仿真數據寄存器CPU_DAT2。


      仿真數據寄存器CPU_DAT3,與CPU_DAT2構成一次64-bit的raw_data。由fifo_writer狀態機寫入XPM_FIFO_ASYNC。


    應用程序


      從應用程序的角度看,程序的基本架構與《精簡ISA總線實現高速大容量數據采集》一文的描述是一樣的,其要點包括:

      1、使用預先劃定的Linux保留存儲區域作為采集數據buffer,應用程序通過mmap獲得buffer指針。

      2、打開設備文件(節點名稱:“/dev/pcie0”),通過write函數設置啟動數據采集過程,在接收線程通過poll函數等待驅動通知數據就緒信息。

      3、Write函數寫入pcie_dma_info數據結構,定義如下:


    struct pcie_dma_info {
        dma_addr_t phys;        /* dma buffer start addr, */
                                 /* = 0: internal buffer for testing */
        unsigned long size;     /* total buffer size in byte */
        int period;             /* = 1: single dma, > 1: cyclic dma */
        int count;              /* only for cyclic dma, = 0: free run, */
                                /* > 0: number of period buffer after */
                                 /* event happened */
        u32  daq_config;        /* data acquisition config register in */
                                 /* dtaker IP */
        u32  event_config;      /* event config register in dtaker IP */
    };


      4、由數據就緒信息觸發對采集數據buffer的相應處理。


      對本方案有實際應用需求的客戶,可與英創公司技術支持聯系,以了解進一步的技術細節,進行實際的評估應用。

    91精品啪在线观看国产18| 国产精品igao视频| 一本久久伊人热热精品中文| 国内精品免费在线观看| 国产精品白嫩美女在线观看| 亚洲性日韩精品一区二区三区| 久久亚洲精品成人av无码网站| 女同久久另类99精品国产 | 亚洲色精品vr一区二区三区| 国产成人精品日本亚洲直接| 国产精品va久久久久久久| 久久久久久精品久久久| 国产午夜精品一区二区三区漫画| 最新露脸国产精品视频| 国产免费阿v精品视频网址 | 9i9精品国产免费久久| 国产精品亚洲片在线va| 中文字幕一区二区三区日韩精品 | 久久久久久精品久久久| 国产精品久久永久免费| 精品无码人妻夜人多侵犯18 | 精品国产av一二三四区| 香蕉国产精品频视| 五月天婷亚洲天综合网精品偷| 国产SUV精品一区二区88| 亚洲AV日韩精品久久久久久| 日韩A∨精品日韩在线观看| 国产精品小视频免费无限app | 国产精品亚洲一区二区三区久久 | 国产精品亚洲专区无码不卡| 人妻少妇精品无码专区动漫 | 狼人无码精华AV午夜精品| 亚洲午夜国产精品| 国产精品看高国产精品不卡| 国产精品一区二区久久沈樵| fulidown国产精品合集| 亚洲精品视频在线播放| 亚洲国产成人一区二区精品区| 99精品在线播放| 四虎成人国产精品视频| 久9视频这里只有精品8|