天天看點

基于 PYNQ 的 AXI 總線主從控制編寫(ddr3的讀寫)

菜鳥一枚,最近也學習了關于 AXI 總線的相關知識。基于 PYNQ 編寫了一個簡單的 AXI 主從控制(牽涉到 DDR3 的讀寫)。

設計目的

設計出以下一個通過 AXI 總線連接配接的簡單片上系統。包含以下部分:

PL 端邏輯

PL 端實作四個子產品包括:

  • 寄存器堆

    寄存器堆主要是對指令等控制資訊進行片上存儲。具體的指令将由 PS 端寫入。對于寄存器堆來說,其相當于 slave,而 PS 端的 jupyter 伺服器相當于 master。使用的總線是 AXI Lite 總線(棕色的大箭頭)。

  • 控制端

    控制端主要做的是将寄存器堆送來的指令進行譯碼,進而從寄存器堆中取得各種配置資訊,産生控制信号(主要是對 DRAM 的訪存操作,相當于是 SDRAM 控制器)。

寄存器堆與控制端要進行連線。

  • 記憶體讀處理(read)

    該子產品主要負責處理對DRAM的讀請求與接收到資料的處理。主要負責實作 AXI FULL 總線的相關讀邏輯。内部有一個讀請求的 FIFO 隊列。

  • 記憶體寫處理(write)

    該子產品主要負責處理對DRAM的寫請求與寫資料的處理。主要負責實作 AXI FULL 總線的相關寫邏輯。内部有一個寫資料請求的FIFO隊列。

控制端與讀寫請求處理子產品(DRAM)是 master 與 slave 的關系。即控制端通過産生相應的訪存請求信号,進而令讀寫請求子產品進行訪存的相應操作。簡而言之,讀寫請求子產品其實就是 AXI 總線的訪存接口,其内部實作了 AXI 總線相應的握手機制,我們可以通過這兩個子產品在片上進行 ddr 的讀寫控制。故讀寫請求子產品要挂在 AXI 總線上(綠色大箭頭)。

下圖中紅色的框即為上面所說的 PL 端邏輯子產品的系統框圖。我們可以用 HDL 把子產品實作好之後封裝成 IP 核,寫好輸入輸出端口,就可以挂在 AXI_Interconect 和 AXI register slice IP 核上并且連接配接到 ZYNQ 硬核上。進而編寫 PS 端程式。

基于 PYNQ 的 AXI 總線主從控制編寫(ddr3的讀寫)

可能有人要問,為什麼綠色的箭頭有5條,而棕色有2條?其實這就是 AXI 總線的内部機制,下圖記錄了 AXI 讀寫的時序圖。可以很清楚地看到讀資料時,隻需要兩個通道即可,而寫資料則需要三條通道,是以說對于訪存請求處理的兩個子產品共需要5條導線。而寄存器堆其實也是需要3條線,隻是圖中省去了,因為在 pynq 裡我們可以很友善的對寄存器位址進行判斷(4的倍數)。

基于 PYNQ 的 AXI 總線主從控制編寫(ddr3的讀寫)

PS 端程式設計

接下來是軟體方面的設計。我們知道,為了發揮 zynq 系列的優勢,我們将通訊密集型的應用放在 CPU 上——即指令相關的控制資訊的寫入或預先對 ddr 資料的寫入。

由于本篇博文是基于 pynq 的,故不牽涉到 sdk 程式設計(其實兩者原理相同)。

  • 對 DDR 的讀寫

    由于我們使用的是 pynq,其中已經封裝好了我們需要用到的相應 API,隻需調用即可!在這一步裡我們主要調用以下 API:

from pynq import Xlnk
xlnk.cma_array(shape=(),dtype=np.float16)
           

該函數傳回一個 numpy 數組,相當于在 DRAM 裡申請了一段大小為 shape 的空間,然後可以自由地對這一段空間進行讀寫操作。

  • 對寄存器堆的寫入

    我們在 jupyter 伺服器下可以很友善的對寄存器堆進行讀寫!隻需提前定義好相應的寄存器位址即可。在這一步裡我們主要調用以下 API:

from pynq import MMIO
ctrl = MMIO(0x40000000, 0x1000)# 開辟一塊區域可供使用者讀寫,主要是對寄存器(slave)的讀寫
ctrl.write(reg_addr, data) # 寫寄存器
ctrl.read(reg_addr) # 讀寄存器
           

我們封裝一個 FPGA 類,将上面的操作完整寫下來:

from pynq import Xlnk
from pynq import MMIO

# 寄存器位址
RESET_REG           = 0
PHY_ADDR_REG        = 4
START_REG           = 8
DONE_REG            = 12

class FPGA(object):
    initdone = False
    memalloc = False
    xlnk = None
    ctrl = None
    mem = None
    im_wh_q = None
    bb_list = list()
    cmdidx = 0
    
    def __init__(self, xlnk):
        self.xlnk = xlnk
        self.ctrl = MMIO(0x40000000, 0x1000) # 開辟一塊區域可供使用者讀寫,主要是對寄存器(slave)的讀寫
        self.im_wh_q = deque()
        try:
            self.mem = xlnk.cma_array(shape=(0x800000,4), dtype=np.float16) #配置設定buff記憶體區域,傳回的是numpy數組
            self.memalloc = True           
        except:
            print("CMA array allocation failed (64MB)")
            print("Please call xlnk.xlnk_reset() or Restart notebook")
            xlnk.xlnk_reset()   
        
    def configure(self, weight_file, padding_file):
        if (self.memalloc == False):
            return False
        
        self.ctrl.write(PHY_ADDR_REG, self.mem.physical_address)  # 将記憶體偏移(起始位址)寫入寄存器中
        self.ctrl.write(RESET_REG, 1)
        self.ctrl.write(RESET_REG, 0)
        rd = self.ctrl.read(PHY_ADDR_REG) # 讀寄存器,看是否成功寫入資訊
        if (rd != self.mem.physical_address):
            xlnk.xlnk_reset()
            print("Error: Memory mapped IO failed")
            return False
        
        if (os.path.isfile(weight_file)):  # 打開一個本地的檔案
            W = np.load(weight_file)
            woff = 0
			# 寫權重到DRAM裡面去
            for i in range(len(W)):
                self.mem[woff:(woff + W[i].shape[0]),:] = W[i]
                woff += W[i].shape[0]
        else:
            print("Error: Weight file not found")
            self.xlnk.xlnk_reset()
            return False
        # 寫padding項到DRAM裡面去
        if (os.path.isfile(padding_file)):
            W = np.load(padding_file)
            membase = 7 << 20  # 寫pading項的起始位址,這個位址是如何得到的??? 7340032
			# 11100000000000000000000
            self.mem[membase: (membase | W.shape[0]), :] = W
        else:
            print("Error: Padding file not found")
            self.xlnk.xlnk_reset()
            return False
        
        membase = 4 << 20  # 4194304
		# 10000000000000000000000
        self.mem[membase:(membase | 106496), 3] = np.zeros((106496), dtype=np.float16) 
           

未完待更。。。。