天天看點

嘗試與了解vivado下的zynq7使用流程(通過)Vivado+Zedboard之流水燈例程

第一部分:前言

因為剛拿到zedboard這塊闆子,是以一直在嘗試先跑上一個例程。這篇部落格https://blog.csdn.net/weixin_42639919/article/details/81130581的整個流程相當詳細,而且在vivado2018.2上嘗試後确實是可以正常運作的。但有幾個點想補充一下,也是在嘗試的過程中碰到的問題。

第二部分:嘗試

2.1 vivado2018在界面上相對于之前的版本有些不一樣,但基本的選項還是一樣的。

2.2 在IP diagram連線的時候,注意按照步驟中,連接配接GPIO至led引腳,這一點要注意,不要選擇預設的選項

2.3 之前的很多教程裡,都沒有提到hardware manager這一過程,都是直接讓看裝置管理器裡的端口配置。但是很遺憾,我的電腦無論如何都顯示不出有探測到zedboard裝置,還是之前做項目時添加的幾個藍牙com。是以檢測有沒有連上還是要看hardware manager。

嘗試與了解vivado下的zynq7使用流程(通過)Vivado+Zedboard之流水燈例程

然後,别人的部落格也說了,如果闆子的status是closed,要右鍵,然後open

2.4 uart to usb是可以在裝置管理器的端口中被發現的,而且一般會提示你安裝相應的驅動,如果沒有相應提示,建議換一條資料線試試。

第三部分:補充與了解

做這麼個例程,說白了還是想要借助來了解整個zynq7 ip的使用過程和流程。但我也才剛開始看,很多了解和補充可能不到位,僅供參考,之後也就慢慢學,慢慢完善咯。

參考的資料有《vivado從此開始》、《Xilinx Zynq-7000嵌入式系統設計與實作基于ARM Cortex-A9雙核處理器和Vivado的設計方法》。對應的資源下載下傳https://download.csdn.net/download/iatkotw1998/10840227。抱歉收了一個積分。

3.1 了解VIVADO和SDK的關系:一個最直覺上的感受便是VIVADO負責硬體部分的設計,然後導入至SDK中進行軟體部分的設定。這和以前使用的系列單片機是不一樣的,當然,這也歸功于zynq架構的設計理念。其次,很多之前的教程都還是基于像是PlanAhead、XPS或者ISE,近些年VIVADO的教程才開始多了起來,但是VIVADO是一個大綜合,把幾乎所有的流程功能內建到一個架構下,不用四處切換工具。

3.2 國内的原創稿比例還是比較少,像如果直接搜vivado+zedboard之流水燈例程,幾乎全是一樣的,轉過來轉過去,流程多,了解少。反正也都是在入門,之後可能會寫幾篇有關自己對zynq和vivado開發工具的了解和學習筆記。這一篇還是回到結合例子了解流程的話題上來。

3.3 zynq這個ip的使用流程與傳統的FPGA在VIVADO的設計流程很不一樣,我們甚至可以在使用zynq架構的闆子時,一句HDL代碼都不用寫,直接上ip integrator部分就行。我們知道,zynq架構有PS和PL兩個重要的組成部分。二者之間的互聯通過AXI(AMBA協定中的的一種規範)總線。還有AXI4-Lite功能和AXI4-Stream功能,前者專用與和原件内的控制寄存器進行通信,後者則用于連接配接希望交換資料的元件,如果不是很清楚的話可以先看一看這個部落格進行學習(https://www.cnblogs.com/milinker/p/6474706.html)。說到這裡,先上來這個例程的整體框圖,圖檔轉自部落格http://blog.chinaaet.com/cuter521/p/35978,非常具有原創性的一個部落客。

嘗試與了解vivado下的zynq7使用流程(通過)Vivado+Zedboard之流水燈例程

圖1

​​​​

可以看到是調用了PL中的GPIO IP的,ARM GPIO的調用的方式有兩種,其一直接對Cortex-A9處理器内GPIO子產品的寄存器進行直接讀寫操作;其二是調用SDK工具提供的應用程式接口函數API。當然這隻不過是兩種不同的風格的代碼,之後會結合代碼具體分析,但更重要的是了解這種結構的GPIO調用的基層形式。我們首先需要了解一些有關的基礎知識,這些在資料中的zynq書中有詳細介紹,這裡隻簡要提一下:

  1. zynq架構肯定是有引腳的,但是分為直接所屬PS部分的MIO和布線連接配接到PL部分的EMIO。MIO一共隻有54個,那麼如果我想要更多的引腳怎麼辦,很自然的利用PL部分呀,可以通過EMIO将引腳數擴充到192個。而且因為MIO是54個,是以分成了兩個MIO電壓組,bank0[15:0],bank1[53:16],通過配置界面可以獨立的額配置設定引腳的工作電壓,這就解釋了下圖的很多部分
    嘗試與了解vivado下的zynq7使用流程(通過)Vivado+Zedboard之流水燈例程
    圖2
  2. 但是到這裡就又出現一個問題了,到底是MIO還是EMIO呢?答案是,都不是。圖1中寫的是AXI GPIO,一開始我也一直以為是EMIO。但是仔細看了看例子和參考書籍上使用EMIO的方式,因為EMIO是PS也即ZYNQ IP本身就有的,根本不會需要新添加一個EMIO IP,況且也沒有。是以AXI_GPIO是GPIO的IP CORE,調用的時候占用的是AXI總線位址空間,綜合後需要消耗PL的邏輯資源,這和EMIO的簡單連線是有差別的。而且之後SDK中控制時調用的頭檔案都不一樣。(參考以下部落格:https://blog.csdn.net/cllovexyh/article/details/79304378;https://blog.csdn.net/xzyiverson/article/details/19934837;https://blog.csdn.net/u014485485/article/details/78141594;https://blog.csdn.net/lg2lh/article/details/49499587)有機會我會嘗試用MIO或者EMIO跑一個流水燈試試。

3.4 在添加IP的流程,首先需要ZYNQ和AXI GPIO兩個部分的IP,ZYNQ這個之後還會接着圍繞其為中心細講,這裡先接着上面來學習AXI GPIO部分,其實xilinx是有對其進行詳細介紹的https://www.xilinx.com/support/documentation/ip_documentation/axi_gpio/v2_0/pg144-axi-gpio.pdf。在參考書的GPIO中雖然沒有對AXI GPIO的介紹,但是有專門的兩章對定制簡單的AXI-Lite IP進行了介紹,定制封裝後的子產品長這樣

嘗試與了解vivado下的zynq7使用流程(通過)Vivado+Zedboard之流水燈例程

圖3

是不是和AXI GPIO子產品巨像,是以這其實就是借助AXI總線利用PL程式設計實作了一個GPIO的定制功能IP。再看前面給出的官方PDF,特征(features)中提到支援1到32位的GPIO引腳,支援單一或雙GPIO通道,還支援中斷響應。主要的信号描述如下

嘗試與了解vivado下的zynq7使用流程(通過)Vivado+Zedboard之流水燈例程

随後,在Designing with the core中的Operation和Programming Sequence以及Design Flow Steps中相當詳細的給出了使用方法,這裡便不再贅述。

3.5 按照步驟Run Connection Automation後,看整個結構圖,GPIO的輸出連接配接着外設LED,這其實有一個問題,管腳限制什麼時候添加的?我們在Source目錄下其實并不能看到相應的限制檔案。

嘗試與了解vivado下的zynq7使用流程(通過)Vivado+Zedboard之流水燈例程

但是打開I/O prot的确是能夠看到已經完成了相應的引腳配置設定的

嘗試與了解vivado下的zynq7使用流程(通過)Vivado+Zedboard之流水燈例程

是以說Run Connection Automation這個過程自動的完成了引腳配置設定的過程,而且确實是生成了限制檔案的,隻不過路徑卻不再和以前一樣了而已,如下圖的路徑可以找到對應的實體限制。

嘗試與了解vivado下的zynq7使用流程(通過)Vivado+Zedboard之流水燈例程

這其實與我們正常的FPGA設計流程中有關引腳限制的步驟有一些不一樣,需要注意。

3.6 AXI GPIO的輸入S_AXI,s_axi_aclk,s_axi_aresetn都直接間接的通過另一個子產品,AXI SmartConnect

嘗試與了解vivado下的zynq7使用流程(通過)Vivado+Zedboard之流水燈例程

有關這個子產品,在zynq參考書的AMBA AXI4互聯結構中有詳細的提到。首先我們要明白,有關AMBA協定規範,APB、AHB、AXI是其中的三種規範。而AXI是指Advanced Extensive Interface,即進階可擴充接口,用于高性能的互聯。AXI協定指定的不是總線,而是一一對應的接口,當有多個外設需要互動資料時,就需加入AXI Interconnect子產品,smartconnect相當于是進階版本的interconnect。AXI Interconnect的作用是将一個或多個AXI主裝置連接配接到一個或多個AXI從裝置的一種交換機制。AXI Interconnect IP核最多支援16個主裝置和16個從裝置,如果需要更多的接口可以在設計中加入多個IP核。下圖有助于我們了解主裝置、從裝置、AXI總線和AXI Interconnect core的關系,注意圖中的握手信号及資料信号的寬度。(部分結論轉自部落格http://www.eefocus.com/tastier/blog/13-04/293148_06eee.html)

嘗試與了解vivado下的zynq7使用流程(通過)Vivado+Zedboard之流水燈例程

Zynq中的AXI接口總共有9個,三類:AXI_ACP、AXI_HP(4個)、AXI_GP(4個),AXI_GP為通用接口,使用頻率很高,分為兩個主接口和兩個從接口,當需要連接配接更多外設時,在這個接口接上AXI_Interconnect子產品即可。下圖可以直覺的看到M_AXI_GP0與AXI_Smartconnect的連接配接:

嘗試與了解vivado下的zynq7使用流程(通過)Vivado+Zedboard之流水燈例程

當然其實最原汁原味最詳細的介紹還是得參考官方給的pdf文檔https://www.xilinx.com/support/documentation/ip_documentation/smartconnect/v1_0/pg247-smartconnect.pdf(介紹相當詳細)。之後會考慮專門寫一篇對這個子產品以及其他相關子產品的解讀。這次仍先點到為止。

3.7 那麼差不多就到了最後一個了Processor System Reset,PDF(https://www.xilinx.com/support/documentation/ip_documentation/proc_sys_reset/v5_0/pg164-proc-sys-reset.pdf),開篇的Introduction裡就很明确的指出The Xilinx LogiCORE™ IP Processor System Reset Module core provides customized resets for an entire processor system。其在連線中的表現也确實如此,控制所有裝置的reset。

嘗試與了解vivado下的zynq7使用流程(通過)Vivado+Zedboard之流水燈例程

其中FCLK_RESETO_N是一個zynq提供的複位信号,需要注意的是zynq的寄存器具有“寫保護”,如果直接将資料寫入FCLK——RESET0_N的對應寄存器寫資料之前應該把“寫保護”模式關閉,然後再把資料寫入FCLK_RESET0_N的寄存器中,這樣才能有效實作想要的功能。最後不要忘記再将“寫保護”模式開啟。(參考部落格http://blog.sina.com.cn/s/blog_16bd70da40102ybc8.html)

3.8 最後我們來分析sdk中的代碼,其實可以說雖然在整個結構中有那麼多的子產品,但SDK中需要自己寫的隻有xgpio部分,前面提到的部落格中已經給出了,這裡隻再對其中的幾個部分進行補充,整體的功能其實很好了解。

/*
 * zj.c
 *
 *  Created on: 2018年12月9日
 *      Author: Tiputer
 */

#include "xparameters.h"        /* Peripheral parameters  */

#include "xgpio.h"              /* GPIO data struct and APIs,這個頭檔案專門針對GPIO IP的*/

#include "xil_printf.h"

#include "xil_cache.h"

#define GPIO_BITWIDTH   8       /* This is the width of the GPIO */

#define GPIO_DEVICE_ID   0//device id

#define LED_DELAY       10000000/* times delay*/

#define LED_MAX_BLINK   0x1     /* Number of times the LED Blinks */

#define LED_CHANNEL     1       /* GPIO channel,可以回憶一下GPIO IP的結構隻有兩個channel*/

#define printf xil_printf   /* A smaller footprint printf */

XGpio Gpio; /* The Instance of the GPIO Driver */

XGpio GpioOutput; /* The driver instance for GPIO Device configured as O/P */



int GpioMarquee (u16 DeviceId, u32 GpioWidth)

{

    volatile int Delay;
    
    /*volatile 是個關鍵字。在一個變量前加上這個關鍵字,表示的含義是告訴編譯器在編譯的時候不要優                
    化掉這個變量,因為一般的編譯器都有優化選項,某些優化過程就會把一些變量優化掉。這個在嵌入式系 
    統中很重要,比如說你要在某個PROT不停的讀取資料,而且這個PORT的資料時實時更新的,那麼你就要在 
    你的變量前面加上volatile ,否則編譯器很有可能就隻讀取一遍,以後都不讀取仍然使用上一個值 例如 
    int ValueRead; ValueRead = PORTB 這樣的話重複讀就會被優化掉,要volatile int ValueRead; 
    ValueRead = PORTB 這樣就OK了*/

    u32 LedBit;

    u32 LedLoop;

    int Status;

    /*

     * Initialize the GPIO driver so that it's ready to use,

     * specify the device ID that is generated in xparameters.h

     */

     Status = XGpio_Initialize(&GpioOutput, DeviceId);

    if (Status != XST_SUCCESS)

    {

        return XST_FAILURE;

     }

    //Set the direction for all signals to be outputs

     XGpio_SetDataDirection(&GpioOutput, LED_CHANNEL, 0x0);

    // Set the GPIO outputs to low

     XGpio_DiscreteWrite(&GpioOutput, LED_CHANNEL, 0x0);



    for (LedBit = 0x0; LedBit < GpioWidth; LedBit++)

    {

        for (LedLoop = 0; LedLoop < LED_MAX_BLINK; LedLoop++)

        {

            //Set the GPIO Output to High

            XGpio_DiscreteWrite(&GpioOutput, LED_CHANNEL,1 << LedBit);

            //Wait a small amount of time so the LED is visible

            for (Delay = 0; Delay < LED_DELAY;Delay++);

            //Clear the GPIO Output

            //XGpio_DiscreteClear(&GpioOutput, LED_CHANNEL,1 << LedBit);
            //XGpio_DiscreteWrite(&GpioOutput, LED_CHANNEL,0); //這一句和上面一句的效果一樣

            // Wait a small amount of time so the LED is visible

            for (Delay = 0; Delay < LED_DELAY; Delay++);

          }

     }

    return XST_SUCCESS;

}

int main(void)

{//Application start

    /* loop forever*/

    int cnt=0;

    while(1)

    {

        u32 status;

        status = GpioMarquee (GPIO_DEVICE_ID,GPIO_BITWIDTH);

        if (status == 0)

        {

            printf("%d:SUCESS!.\r\n",cnt++);

            if(cnt>=1000)

                cnt=0;

        }

        else

            printf("FAILED.\r\n");

    }

    return XST_SUCCESS;

}
           

嗯,本文到這裡就結束了,可以說是一個簡單的入門吧,雖然知道了很多東西的作用,但還是有很多步驟不清楚,在之後的學習中會繼續總結,歡迎指正,互相學習。