【技術分享】FPGA雜記之基礎篇二Demo案例

                日期:2020-12-11 作者:潤欣科技創研社 返回列表

                本文接續上一篇《FPGA雜記基礎篇》,繼續為大家分享IP例化和幾個基于FPGA芯片實現的Demo工程。



                IP例化


                IP即是一個封裝好的模塊,集成在相應的開發環境里面,以安路的TD軟件為例,不同系列的芯片集成了不同的IP模塊,可以通過軟件例化調用。


                以下是安路TD4.6.5集成的EF3L40CG332B的相關IP。



                1.1 PLL&RAM


                以例化PLL和RAM為例,實現兩個異步雙口 RAM。


                讀寫時鐘都設置 100Mhz, 兩個 RAM 為 RAMA 和RAMB, 深度為 1024,位寬為 8bit,寫入數據為 8bit,100Mhz 持續數據流, 當 RAMA被寫入 1024 字節數據后切換到寫 RAMB, RAMB 被寫入 1024 字節后切換 RAMA。以此循環類推。


                當 RAMA 被寫入 1024 字節時, 給讀時序提供一個啟動信號讀取 RAMA 的數據, 讀取完 RAMA 的 1024 字節數據時, 切換讀 RAMB 以此類推。


                這個工程的工程結構如下圖:



                首先EF3L40CG332B_DEV開發板提供了25Mhz的晶振時鐘輸入到EF3L40CG332B的時鐘管腳。



                想要得到100Mhz的讀寫速率,需要先用PLL得到倍頻時鐘。



                在tools目錄下點擊IP Generator進入IP core頁面,并選擇PLL,輸入時鐘填入板子晶振25Mhz。



                輸出時鐘填入所需要的100Mhz,并從C0輸出。



                設置完成后,生成的module聲明如下(完整模塊可參考代碼)



                再生成ram的IP模塊。


                在IP core中選擇RAM。



                Memory type選擇簡單雙口ram,memory size設置位寬8bit深度1024。


                設置完成后,生成的module聲明如下。(完整模塊可參考代碼)



                然后編寫頂層文件并且在頂層例化PLL和RAMA、RAMB。


                頂層文件中主要是對ram的輸入口進行時序操作,包括ramA、ramB的讀地址,寫地址、使能信號和輸入輸出數據,詳細代碼筆記中不再贅述,可以直接參考代碼。


                可綜合模塊編寫完成后,編寫仿真模塊并使用仿真軟件進行仿真。


                由于本次工程使用到了安路的IP庫,因此也需要在modelsim中添加相應的安路仿真庫,添加方法如下:


                首先在modelsim的安裝目錄下面編輯modelsim的初始化文件modelsim.ini,右鍵屬性后,將它的只讀屬性取消,然后用文本文件(本工程使用的是notepad++)編輯。



                在modelsim.ini的[library]列表下添加安路的仿真庫文件目錄,安路所有的仿真庫文件都在安路的編譯軟件TD安裝目錄下的sim文件夾中,此處將其所有的庫文件都復制進了modelsim的文件夾里,若不復制,也可直接輸入安路文件夾的路徑。



                保存后退出,打開modelsim并創建工程,編譯通過后,進入仿真步驟,在simulate狀態欄下選擇start simulate,如下圖:



                選擇后進入到如下頁面:



                選擇仿真的頂層并且關閉優化選項。


                同一個窗口打開libraries頁面并在search libraries欄右側選擇add,下拉列表選擇對應的ef3的庫文件。



                設置完成后點擊OK進入仿真即可。


                本次實驗中遇見問題和調試如下:


                01



                剛開始pll沒有輸出信號,因此打開了pll查看波形發現pll波形如下:



                發現是置位了reset信號導致的,查看代碼發現如下:



                復位信號直接連接到了pll的置位信號,由于復位信號是低電平有效而置位信號是高電平有效,因此導致了pll一直處于復位的情況。更改后,直接將pll復位信號置0,代碼和仿真結果正常,如下所示:




                02


                PLL問題解決后,觀測數據整體讀寫情況。



                初步觀測可以發現,rama讀使能信號只轉變了一次,而ramb的讀使能始終未能跳變,返回代碼查看發現:



                邏輯判斷時未將rama的寫使能信號置1的條件寫出來。


                ramb的寫使能同理:



                修改后,代碼如下:



                波形仿真如下:



                粗看基本符合rama、ramb交替讀寫的功能,觀測rama、ramb時序交替細節。



                在rama和ramb寫使能信號轉換時,發現空了一拍,再查看代碼,發現rama、ramb寫使能在條件判斷時,使用的判斷邏輯是不一樣的,導致ramb_wren的置位會在rama_wren置位后的下一拍進行。



                因此更改ramb_wren的判斷條件,使之與rama_wren的一樣,都以寫地址為條件判斷再仿真。



                可觀測到,時序正常。



                quick start & GPIO Demo


                本次demo實現功能如下:FPGA控制LED D1閃爍,MCU控制LED D2常亮。


                2.1 keil工程環境創建


                創建文件夾目錄如下:


                圖1


                其中板級支持包直接由原廠提供。


                先創建keil工程,打開keil,創建工程,保存在對應的MCU→project目錄下。



                器件選擇ARM cortex M3器件。



                工程建好后,添加必要的BSP包中的文件如下,創建好后的工程目錄如左欄:



                其中,startup組下的文件分別來自MCU\ELF2_BSP\Device\ELF2\Source和MCU\ELF2_BSP\Device\ELF2\Source\ARM目錄下;lib組的文件來自MCU\ELF2_BSP\Driver;log組文件保存在MCU\ELF2_BSP\Debug和MCU\ELF2_BSP\Debug\RTT目錄下。


                新建main.c文件并保存在圖1所示的總文件目錄瀏覽的MCUàsrc文件夾下并添加main.c到工程main組中。



                下面設置一些工程的環境,打開options for target對話框。



                切換到user欄,設置如下參數,這些參數會影響輸出keil工程的*.asm 和*.bin 文件,我們需要通過添加這兩條指令得到bin文件并最終提供給FPGA。



                添加的語句分別如下:


                fromelf -c -v -a --output=@P.asm Objects\%L

                fromelf --bin --output=@P.bin Objects\%L


                再切換到C/C++欄,設置頭文件路徑如下:



                也可以直接添加如下目錄


                ..\ELF2_BSP;..\ELF2_BSP\CMSIS\Core\Include;..\ELF2_BSP\Debug;..\ELF2_BSP\Debug\RTT;..\ELF2_BSP\Driver;..\ELF2_BSP\Driver\regmap;..\ELF2_BSP\Device\ELF2\Include;..\ELF2_BSP\Device\ELF2\Source\ARM


                其余設置如下圖:



                添加分散加載文件elf2_example.sct(elf2_example.sct文件具體代碼可參考工程)



                環境設置完畢后可以開始編寫工程代碼。


                2.2 C代碼編輯


                在main函數中編寫對GPIO的操作。



                先對GPIO初始化結構體賦值,再調用GPIO初始化函數,HAL_GPIO_WritePin函數對相應的GPIO進行高低賦值。


                本次使用C代碼對GPIO1_0的操作是置低,GPIO1_0具體含義會在下一節(1.3)進行說明。


                2.3 TD工程創建和代碼編輯


                打開TD4.6.5或其他版本創建新的工程。



                保存在總目錄的FPGA→project目錄下,并選擇對應的器件類型。



                添加或者編輯源文件,本次工程模塊聲明如下:



                其中hw_led是由FPGA邏輯控制的led,sw_led是由MCU代碼控制的led(即1.2中的gpio1_0)。hw_led的控制代碼如下,sw_led的控制代碼詳見1.2:



                然后例化MCU和PLL,PLL例化主要得到輸入到MCU的系統時鐘,例化過程略,這里貼上在頂層中調用的結果:



                輸出的200M的時鐘接到MCU的系統時鐘。


                例化MCU界面如下:



                如圖所示,MCU支持最大 32 個GPIO,其中低16位,即GPIO_L0~GPIO_L15是直接連接至pad的;而GPIOH0~GPIOH15則是通過FPGA連接至外部,因此,當使用這16個GPIO的時候,需要在FPGA工程的管教約束文件中指定具體連接至哪個腳。


                在例化MCU時,使用到哪個腳就可以打開對應的開關,例如本例中,打開了L0、L1和H0,PPM_CLK,其中PPM_CLK是FPGA Fabric 輸入時鐘,連接至FPGA的PLL輸出clk200;L0、L1連接至PAD,觀察原理圖。



                GPIO0和GPIO1連接的是調試口;最后H0連接至FPGA 中sw_led并通過管腳約束連接至LED D2。


                工程的管教約束文件如下:



                查開發板原理圖,D2連接至FPGA的16腳,且從原理圖可觀察,keil工程中對該GPIO的操作是置低,具體顯示是D2常亮。



                設置完畢后,完成結果聲明如下:



                并在頂層中調用:



                2.4 下載


                Keil和TD的工程都創建編寫完成后,編譯工程。其中,keil生成的工程bin文件需要與TD關聯并通過TD下載至芯片或開發板中。


                關聯的步驟如下:


                在HDL2Bit Flow欄右鍵選擇properties。



                在generate bitstream的第六項instruct ram中選擇keil工程生成的bin文件的目錄(此時keil工程已經編譯通過),并保存。



                保存后,雙擊generate bitstream編譯TD工程,假如在選擇路徑前已經編譯過TD工程了,需要右鍵選擇rerun重新編譯(注意:假如修改了keil的C文件而TD的HDL文件沒有變化,建議也rerun后再將文件下載至開發板)



                下載:



                板子現象如圖:


                D1持續閃爍,D2常亮:



                FPGA串口通信


                本Demo案例基于安路的EF2M45LG48_MINI_DEV2開發板,通過測試板的uart口和PC機的uart口連接來形成一個閉環回路,即PC機發送數據至FPGA測試板,FPGA接收并返回相同的數據。實驗結果通過PC機的串口調試助手調試查看。


                3.1 UART協議


                UART 是一種通用串行數據總線,用于異步通信,將數據在串行通信和并行通信間的傳輸轉換。通俗的講就是把多比特的數據轉化為單比特的數據(tx端),或者把單比特的數據轉化為多比特的數據(rx端)。工作原理是將數據的每個 bit 一位接一位地傳輸。


                rx,接收端,位寬為 1 比特, pc 機通過串口往 FPGA 發 8 比特數據時,FPGA 通過串口線 rx 一位一位地接收,從最低位到最高位依次接收,最后在 FPGA 里面位拼接成8 比特數據。


                tx,發送端,位寬為 1 比特, FPGA 通過串口往 pc 機發 8 比特數據時, FPGA 把 8 比特數據通過 tx 線一位一位的傳給 pc 機,從最低位到最高位依次發送,最后上位機通過串口助手把這一位一位的數據位拼接成 8 比特數據。


                注意點:


                1、串行數據的發送和接收都是從低位到高位。


                2、在不發送或者不接收數據的情況下, rx 和 tx 處于空閑狀態,此時 rx 和 tx 線都保持【高電平】,如果有數據傳遞,首先會有一個起始位0,然后是 8 比特的數據位,接著有 1 比特的停止位(高電平),如果停止位以后不再發數據,將進入空閑狀態,否則又將數據線拉低(進入起始位狀態)。


                3、波特率計算:uart傳輸有不同的波特率,使用HDL語言描述時,通常使用計數器來實現不同波特率的數據傳播。計數器的計數值與具體波特率有關,以常見的115200為例,假設系統時鐘是25Mhz,則傳輸1bit所需要的時鐘周期為25 * 1000 *1000 /115200 = 217個,因此計數器計數值即216(從0開始計數)。


                3.2 模塊總框架


                模塊的總體框架如下:



                top層除了時鐘和復位信號的輸入,還有輸入信號rx和輸出信號tx,分別來自PC機和輸出到PC機,形成閉環。子模塊中,Rx信號再作為uart_rx模塊的輸入,經過uart_rx模塊的處理,轉換成八位并行數據o_data輸出;對于uart_tx模塊,主要將輸入的i_data并行信號轉換成串行數據再輸出到PC機。


                3.3 代碼實現


                1. Rx端



                2.Tx端



                3.4 頂層



                3.5 仿真



                本次仿真使用到了task語句,task語句通常在當仿真時需要給輸入變量特定的輸入值時使用,例如本次仿真對rx端進行賦值。


                3.6 仿真結果及問題排查


                Rx端:


                整體波形如圖:



                查看細節如下:



                當rx=1時,輸出的o_data并行數據在o_flag = 1(即表示傳輸結束)時也為1,結果正常。


                Tx端:


                整體波形如下:



                上圖很明顯可以看出tx端傳輸有問題,當tx發送起始位(即拉低)后,沒有將數據輸出。觀測其他信號波形,基本正常。可見問題大概率出現在tx賦值部分,一開始以為是發送數據位的條件判斷有問題,檢查代碼,數據傳輸時的判斷條件如下:



                查看波形發現該條件可以被滿足。



                后來查看起始位的發送條件時發現了錯誤:



                起始位發送要與tx_en同步。假設條件使用tx_en判斷,則會比tx_en慢一拍。


                另外,不能使用tx_en == 1'b1作為發送起始位的判斷條件,因為tx_en 在數據發送時一直為1,這樣tx端會恒為0,修改后代碼如下:



                再觀察波形正常。




                3.7 上板最終效果


                代碼下載進開發板后,在串口調試助手中可以正常收發數據,如下:



                返回列表
                色三级床上片电影完整版 - 视频 - 在线播放 - 影视资讯 - 夫妻片