LabVIEW FPGA PCIe開發(fā)講解-7.6節(jié):PCIe Memory Socket CLIP 講解(讀寫FPGA前面板控

? ? ? ??前面7.4節(jié)我們給用戶講解了PCIe下的FIFO DMA高速傳輸通道的用法,F(xiàn)IFO顧名思義就是在保證數(shù)據(jù)不丟失的情況下,高速傳輸,特別適合那些數(shù)據(jù)采集、圖像采集、波形回放等應用;本節(jié)我們給用戶介紹另外一種相對慢速的通道,只要不斷電可以一直保存數(shù)據(jù)的存儲器,也稱之為Memory。
?????? ?這里我們說的Memory并不是LabVIEW FPGA芯片里面的存儲器,而是Xillybus官方PCIe IP核里面封裝的Memory通道,上位機可以直接通過PCIe總線把上位機要進行讀寫的地址和數(shù)據(jù)通過Memory通道下發(fā)給FPGA,非常適合用來傳輸同時具備地址和數(shù)據(jù)信息的內(nèi)容。
?????? ?相對于只有一個數(shù)據(jù)屬性的FIFO而言,Memory通道具備兩個屬性參數(shù),一個是地址概念,一個是數(shù)據(jù)部分。簡而言之,就是下位機FPGA里面如果我們模擬一個一維數(shù)組或者一個Memory,上位機可以直接調(diào)用我們封裝的好的DLL驅(qū)動,讀寫下位機的數(shù)組或者Memory里面指定位置的元素,地址就相當于索引,這樣做的好處是,用戶可以提前將下位機里面用到的參數(shù)放在數(shù)組或者Memory里面進行自動更新維護,上位機可以隨時對下位機的數(shù)組或者Memory進行指定位置的讀寫。
?????? ?舉1個常見的例子:比如上位機要通過PCIe控制FPGA板子上的LED燈、繼電器、數(shù)字IO輸出引腳,還想著讀取FPGA板子外接的按鍵、開關、輸入信號這些狀態(tài)信號。一般有兩個實現(xiàn)方式。
?????? ?第一種方式是,將下位機所有的數(shù)據(jù)拼接之后通過PCIe DMA FIFO發(fā)送到上位機,或者把上位機拼接后的數(shù)據(jù)通過FIFO下發(fā)給FPGA再逆向解析出來,這種思路很常見,對應前面我們講解的串口、網(wǎng)口、USB里面其實都有所提到。優(yōu)點是框架統(tǒng)一,不局限于何種通信接口,缺點是用戶需要對數(shù)據(jù)包里面的數(shù)據(jù)組織方式有所了解。
?????? ?第二種方式是,利用本節(jié)我們所要講解的Memory通道實現(xiàn)地址和數(shù)據(jù)的下發(fā)。比如我們可以先把下位機FPGA里面要控制的LED燈、繼電器、DO或者AO這些引腳或者參數(shù)統(tǒng)一按照先后順序放到一個數(shù)組或者Memory里面(元素類型可以是U8、U16、U32),這個數(shù)組或者Memory我們稱之為“下行數(shù)組”,意為上位機可以直接通過PCIe總線對這個數(shù)組或者Memory里面的元素進行寫操作;然后把按鍵、開關、DI或者AI等信息按照先后順序放到另外一個數(shù)組或者Memory里面,這個數(shù)組稱之為“上行數(shù)組”,意為上位機可以直接通過PCIe總線讀取這個數(shù)組或者Memory里面指定位置的元素。優(yōu)點是用戶不需要像前面FIFO那樣對接收的所有數(shù)據(jù)包進行解析了,直接將各個變量當成元素存放到數(shù)組或者Memory里面去,上位機想讀寫控制下位機FPGA時,直接通過下發(fā)地址就可以實現(xiàn)對指定位置的元素進行讀取或者更新了;缺點是Memory的傳輸速度沒有FIFO快,但是對于一般的指令或者參數(shù)等狀態(tài)信息是完全足夠的,另外就是下位機FPGA里面要始終維護對數(shù)組或者Memory里面的數(shù)據(jù)更新。
?????? ?上面描述的這個過程,我們在后續(xù)實驗69里面再詳細的給用戶講解怎么編程實現(xiàn)。
?????? ?衍生:通過上面的講解,用戶是不是有一種感覺:那就是可以直接將下位機FPGA前面板上的控件以及感興趣的局部或者全局變量按照順序放到一維數(shù)組或者Memory里面去,然后上位機只要知道每個控件或者參數(shù)所在數(shù)組或者Memory的索引位置,就可以通過我們封裝的PCIe Memory通道Socket CLIP下發(fā)地址來讀取或者更新下位機FPGA的控件數(shù)值了。等于是變相的實現(xiàn)了類似NI FPGA那樣的在線前面板仿真、調(diào)試、運行、觀察。
?
?????? ?本節(jié)重點給用戶講解的是我們封裝好的LabVIEW FPGA下的PCIe Memory CLIP組件功能和用法。這個PCIe Memory CLIP也是我們My FPGA軟件工具包的核心組件之一,我們花費了近1個月的時間,將Xillybus官方提供的Memory代碼進行移植、修改和適配,并進行了大量的測試驗證,最終才把底層的精髓部分PCIe Memory IP封裝到LabVIEW FPGA CLIP里面來,并且給用戶提供了極其簡單的四線握手Memory接口,所有關于PCIe Memory協(xié)議本身的通信都封裝到底層去了,用戶不需要了解什么是PCIe Memory,可以直接使用LabVIEW FPGA來調(diào)用這個PCIe Memory來讀寫下位機FPGA里面的控件或者IO端口。
?????? ?下面我們重點給用戶介紹一下我們封裝好的2上2下的FPGA PCIe Memory Socket CLIP,首先在LabVIEW項目瀏覽器下的“我的電腦”上右擊,選擇新建“終端和設備”,如圖7-58所示。注意:這個后綴為8CMs的FPGA終端是完全包含后綴為8Chs的FPGA終端項目的。因此,用戶將8Chs終端項目下的所有例程拖拽到8CMs下面重新編譯下載都是可以的。

? ? ? ? 然后在彈出來的終端設備新建對話框里面,找到ARTIX7-100T對應的X4模式下包含2通道Memory和8通道FIFO的B版本FPGA終端,如圖7-59所示。

? ? ? ? 點擊“確定”按鈕后,同時包含PCIe Memory和DMA FIFO通信的Socket CLIP組件會自動添加到FPGA終端里面來,如圖7-60所示。

? ? ? ? 展開這個CLIP之后,用戶可以看到一共有67個EIO節(jié)點,主要分為兩個部分:
?????? ?前面50個是8對DMA FIFO通道的,也就是16個通道,每個通道都是一個標準的四線握手制FIFO,相當于3根線,所以,所有通道的握手信號就有16×3=48個,再加上1個100MHz同步時鐘和反應PCIe IP工作狀態(tài)的的4個LED燈,一共就是48+1+1=50個EIO節(jié)點。這部分我們在前面的7.4節(jié)已經(jīng)做過詳細的講解了。
?????? ?后面17個EIO節(jié)點是我們封裝的2對PCIe Memory讀寫通道(位寬分別是16位和32位),也就是4個通道,每個通道都有4跟信號線,2根是地址線,2根是數(shù)據(jù)線,因此,所有Memory的握手信號就是:4×4=16個,再加上1個125MHz同步時鐘,一共就是16+1=17個EIO節(jié)點。如圖7-61所示。

? ? ? ? 默認情況下,我們給這2上2下共計4個PCIe Memory通道分配的驅(qū)動名稱和帶寬,如下面的圖7-62所示。這個表里面每個Memory通道的傳輸帶寬也可以自由分配,并不局限于我們的設置那樣,今后,如果用戶需要修改這些參數(shù)可以聯(lián)系我們神電測控。

? ? ? ? 下面我們給大家逐一講解一下B版本的LabVIEW FPGA PCIe Memory CLIP里面每個信號端口的含義和注意事項。通過虛線將17個CLIP端口劃分成3類:分別Memory端口同步125M時鐘、16位和32位的上行(FPGA-->Host)Memory讀取通道以及下行(Host-->FPGA)寫入通道。其中,Host可以直觀的理解成上位機PC。
?????? ?1)Clock PCIE_LV_MEM_Clock_125M
---------------------------------------------------------------------------------------------------------------------------
?????? ?2)pcie_lv_clip_from_host_16_rdaddr_update_mem0
?????? ?3)pcie_lv_clip_from_host_16_rdaddr_mem0
?????? ?4)pcie_lv_clip_from_host_16_rden_mem0
?????? ?5)pcie_lv_clip_to_host_16_rddata_mem0
??????
?????? ?6)pcie_lv_clip_from_host_16_wraddr_update_mem0
?????? ?7)pcie_lv_clip_from_host_16_wraddr_mem0
?????? ?8)pcie_lv_clip_from_host_16_wren_mem0
?????? ?9)pcie_lv_clip_from_host_16_wrdata_mem0
----------------------------------------------------------------------------------------------------------------------?????? ?????? ?10)pcie_lv_clip_from_host_32_rdaddr_update_mem0
?????? ?11)pcie_lv_clip_from_host_32_rdaddr_mem0
?????? ?12)pcie_lv_clip_from_host_32_rden_mem0
?????? ?13)pcie_lv_clip_to_host_32_rddata_mem0
??????
?????? ?14)pcie_lv_clip_from_host_32_wraddr_update_mem0
?????? ?15)pcie_lv_clip_from_host_32_wraddr_mem0
?????? ?16)pcie_lv_clip_from_host_32_wren_mem0
?????? ?17)pcie_lv_clip_from_host_32_wrdata_mem0
---------------------------------------------------------------------------------------------------------------------------
?????? ?備注:2~9與10~19對應的信號端口含義都是一樣的,只是端口的位寬不一樣,用法完全一樣,前8個是16位位寬的Memory讀寫通道,后面8個是32位位寬的Memory讀寫通道。用戶可以根據(jù)實際應用中所需要的控件類型選擇合適位寬的Memory通道。下面我們給用戶詳細講解一下Memory每個端口的含義。
?????? ?1)Clock PCIE_LV_MEM_Clock_125M
?????? ?FPGA PCIe Memory通道讀寫時鐘,默認是125MHz,這個時鐘是路由出來給用戶自己寫下位機FPGA程序用的Memory同步讀寫時鐘。眾所周知,Xilinx官方給的PCIe IP核配置頁面里面,時鐘一般是125MHz或者250MHz,對于Memory而言,用的就是這個125MHz時鐘,對于前面7.4節(jié)里面的DMA FIFO,用的是帶隔離的100MHz時鐘,切記,不要弄混了。也就是說,如果下位機FPGA想要把上位機PC下發(fā)給FPGA的地址和數(shù)據(jù)讀出來或者將FPGA數(shù)據(jù)通過Memory通道返回給上位機PC,必須要使用這個125MHz時鐘源,才能保持Memory同步握手。為此,我們將這個125MHz時鐘直接封裝到我們的LabVIEW FPGA PCIe Socket CLIP里面來了,時鐘信號名為“Clock PCIE_LV_MEM_Clock_125M”。
?????? ?如何使用外部CLIP路由進來的時鐘,跟前面我們封裝的千兆以太網(wǎng)、USB時鐘一樣,用戶可以直接在FPGA定時循環(huán)左邊創(chuàng)建一個時鐘源常量,然后在下拉列表里面選擇,如圖7-63所示;或者直接雙擊FPGA定時循環(huán),在彈出來的時鐘配置頁面里面選擇“PCIE Data\ Clock PCIE_LV_MEM_Clock_125M”,如圖7-64所示。


? ? ? ? 2)pcie_lv_clip_from_host_16_rdaddr_update_mem0
?????? ?3)pcie_lv_clip_from_host_16_rdaddr_mem0
?????? ?4)pcie_lv_clip_from_host_16_rden_mem0
?????? ?5)pcie_lv_clip_to_host_16_rddata_mem0???
?????? ?上面這4個是16位位寬的PCIe Memory讀取通道的四線握手信號,其中,地址信號和數(shù)據(jù)信號各占用2個。為了讓用戶更加形象的理解和掌握這些握手信號的用法,我們先給出一個FPGA PCIe Memory讀取程序框圖,如圖7-65所示。畢竟LabVIEW框圖看起來顯而易見,一圖勝千言!

? ? ? ? 如果“pcie_lv_clip_from_host_16_rdaddr_update_mem0”信號拉高,說明上位機下發(fā)的地址也就是數(shù)組或者Memory的索引值已經(jīng)到達了FPGA,此時,可以將這個地址值“pcie_lv_clip_from_host_16_rdaddr_mem0”讀出來經(jīng)過條件判斷后放到移位寄存器里面,如圖7-66所示,相當于將地址進行鎖存保持,這部分代碼其實也可以用帶條件的反饋節(jié)點來實現(xiàn),如圖7-67所示。


? ? ? ? 當FPGA接收到上位機下發(fā)的地址后,用戶就可以將這個地址當成數(shù)組或者Memory的索引值了,等到“pcie_lv_clip_from_host_16_rden_mem0”信號有效時,將FPGA里面的數(shù)組或者Memory元素按照地址索引出來賦給“pcie_lv_clip_to_host_16_rddata_mem0”這個端口,如圖7-68所示。此時,上位機就收到了一個FPGA發(fā)上來的數(shù)據(jù),這樣就實現(xiàn)了上位機訪問讀取下位機FPGA里面的指定位置的數(shù)組或者Memory元素了。

? ? ? ? 要想真正理解Memory讀取的工作機理,需要同時理解上位機和下位機FPGA的Memory運行機制:其實,上位機對下位機里面的Memory進行讀取的時候,上位機首先會調(diào)用lseek函數(shù)改變讀的文件指針位置,當lseek函數(shù)執(zhí)行完成后,下位機FPGA的Memory address兩根信號(pcie_lv_clip_from_host_16_rdaddr_update_mem0、pcie_lv_clip_from_host_16_rdaddr_mem0)線就有反應了;然后上位機再調(diào)用read函數(shù)讀取文件里面的數(shù)據(jù),此時,下位機FPGA發(fā)送回來的Memory元素就被上位機讀取出來了。這部分涉及上位機驅(qū)動程序,我們在接下來的7.7節(jié)里面再給用戶做詳細的講解。
?????? ?下面我們再看看FPGA PCIe Memory寫入通道都有哪些信號端口以及是如何工作的。
?????? ?6)pcie_lv_clip_from_host_16_wraddr_update_mem0
?????? ?7)pcie_lv_clip_from_host_16_wraddr_mem0
?????? ?8)pcie_lv_clip_from_host_16_wren_mem0
?????? ?9)pcie_lv_clip_from_host_16_wrdata_mem0
?????? 跟讀通道一樣,寫通道也是由4個四線握手信號構(gòu)成,其中,地址信號和數(shù)據(jù)信號各占用2個。同理,為了方便表述和理解,我們先看一個FPGA PCIe Memory寫入程序框圖,如圖7-69所示。畢竟LabVIEW框圖看起來顯而易見,一圖勝千言!

? ? ? ? 如果“pcie_lv_clip_from_host_16_wraddr_update_mem0”信號拉高,說明上位機下發(fā)的地址也就是數(shù)組或者Memory的索引值已經(jīng)到達了FPGA,此時,可以將這個地址值“pcie_lv_clip_from_host_16_wrdata_mem0”讀出來經(jīng)過條件判斷后放到移位寄存器里面,如圖7-70所示,相當于將地址進行鎖存保持,這部分代碼其實也可以用帶條件的反饋節(jié)點來實現(xiàn),如圖7-71所示。


? ? ? ? 當FPGA接收到上位機下發(fā)的地址后,用戶就可以將這個地址當成數(shù)組或者Memory的索引值了,等到“pcie_lv_clip_from_host_16_wren_mem0”信號有效時,就可以將端口“pcie_lv_clip_from_host_16_wrdata_mem0”里面的數(shù)據(jù)讀出來然后賦給FPGA本地的數(shù)組或者Memory,按照給定的地址進行覆蓋,如圖7-72所示。此時,上位機就實現(xiàn)了對FPGA里面的數(shù)組或者Memory進行寫入更新了。

? ? ? ? 要想真正理解Memory寫入的工作機理,需要同時結(jié)合上位機和下位機FPGA的Memory運行機制:上位機對下位機里面的Memory進行數(shù)據(jù)寫入之前,上位機首先會調(diào)用lseek函數(shù)改變讀的文件指針位置,當lseek函數(shù)執(zhí)行完成后,下位機FPGA的Memory address兩根信號(pcie_lv_clip_from_host_16_wraddr_update_mem0、pcie_lv_clip_from_host_16_wrdata_mem0)線就有反應了;然后上位機再調(diào)用write函數(shù)把數(shù)據(jù)寫入到文件里面去,此時,下位機FPGA就接收到了下發(fā)下來的數(shù)據(jù),最后用戶利用鎖存的地址和這個數(shù)據(jù)做自己想做的任何事,比如數(shù)組更新。這部分涉及上位機驅(qū)動程序,上位機我們在接下來的7.7節(jié)里面再給用戶做詳細的講解。
?????? ?10)pcie_lv_clip_from_host_32_rdaddr_update_mem0
?????? ?11)pcie_lv_clip_from_host_32_rdaddr_mem0
?????? ?12)pcie_lv_clip_from_host_32_rden_mem0
?????? ?13)pcie_lv_clip_to_host_32_rddata_mem0
??????
?????? ?14)pcie_lv_clip_from_host_32_wraddr_update_mem0
?????? ?15)pcie_lv_clip_from_host_32_wraddr_mem0
?????? ?16)pcie_lv_clip_from_host_32_wren_mem0
?????? ?17)pcie_lv_clip_from_host_32_wrdata_mem0
?????? ?余下的8個信號端口,跟前面16位位寬的Memory類似,只是封裝的時候,我們將位寬設置成32位,方便用戶針對不同的項目應用時,可以靈活應對。
?????? ?注意:Xillybus提供的PCIe Memory IP核跟前面的DMA FIFO是一樣的,內(nèi)部都是小端格式,也就是說,低字節(jié)在前,高字節(jié)在后,所以,要么在下位機FPGA里面提前做翻轉(zhuǎn),要么在上位機做大小端變換!比如,如果用戶在FPGA里面接收或者發(fā)送一個32位位寬的數(shù)據(jù),那么需要借助“拆分數(shù)字”和“交換字節(jié)”兩個函數(shù)來完成大小端格式的轉(zhuǎn)換,如圖7-73所示。這里,我們給出了3種不同數(shù)據(jù)類型的大小端格式轉(zhuǎn)換,這個功能將在后續(xù)的實驗部分會用到,大家可以留心一下。
?????? ?提醒1:其實在學完后面的實驗之后,很多用戶發(fā)現(xiàn),對于用到DMA FIFO相關的實驗里面(實驗54~實驗67),我們都是在下位機FPGA里面做了大小端格式轉(zhuǎn)換,這樣做的好處是,上位機就不需要再轉(zhuǎn)換了,因為DMA FIFO傳輸?shù)臄?shù)據(jù)量有時候非常大,上位機做大小端格式轉(zhuǎn)換的話會增加不必要的額外時間,消耗更多的CPU資源,所以我們直接在下位機FPGA里面處理好,上位機就輕松了!
?????? ?提醒2:而針對本節(jié)的Memory,我們在后續(xù)的實驗68里面,采用的是截然相反的方法,也就是下位機FPGA里面對于16位或者32位位寬的Memory數(shù)據(jù)不做任何轉(zhuǎn)換,而是在上位機提前將要下發(fā)的數(shù)據(jù)或者讀取的數(shù)據(jù)進行大小端格式轉(zhuǎn)換,這是因為Memory一次只傳輸一個數(shù)據(jù)(U16或者U32),并且傳輸速度慢,所以不會消耗上位機太多資源,反而節(jié)省了下位機FPGA的邏輯資源。

? ? ? ?可以看出,雖然PCIe協(xié)議本身很復雜,但是對于用戶來說,只要理解了FIFO和Memory的四線握手編程,就可以搞定所有線程之間的數(shù)據(jù)傳輸問題了,這也就是為什么我們在書中每個實驗里面都反復強調(diào)四線握手和并轉(zhuǎn)串以及串轉(zhuǎn)并的重要性。