【技術分享】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 上板最終效果


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



              返回列表
              成年日本片黄网站色大全免费 - 视频 - 在线播放 - 影视资讯 - 绝爱网