ZYNQ有專用的DDR Controller接口,,如果外部硬件連接了DDR器件,,于是在ZYNQ Processing System中正確配置了相應(yīng)的信號(hào)和參數(shù)后,DDR就可以成為ZYNQ的內(nèi)存,,在SDK中可以直接使用memcpy,、memset以及類似的函數(shù)對(duì)于Memory空間進(jìn)行操作。
Step1:查看ZYBO的原理圖,,找到相應(yīng)的配置,。ZYBO原理圖中與DDR相關(guān)的部分如下圖所示。
于是得到兩個(gè)信息,,第一個(gè)所使用的芯片是MT41J128M16JT-125,,第二個(gè)是兩片DDR3顆粒是通過(guò)位拼接完成的,也就是數(shù)據(jù)位寬為32bit,。
Step2:在Block Design中對(duì)DDR部分的參數(shù)進(jìn)行配置,。
Step3:完成Block Design設(shè)計(jì),產(chǎn)生Bitstream,,導(dǎo)入SDK,。
Step4:在SDK中編寫(xiě)Memory測(cè)試代碼。
#include <stdio.h>
#include <stdlib.h>
#include "platform.h"
#include "xil_printf.h"
#include "xil_types.h"
#include "xil_io.h"
int main()
{
u32 test_src[100];
int i;
int readback;
init_platform();
u32 *result = (u32*) malloc(sizeof(u32) * 100);
if (result) {
memset(result, 0, sizeof(u32) * 100);
} else {
return 0;
}
for(i=0;i<99;i++)
{
test_src[i]=i;
}
memcpy(result,test_src,100 * sizeof(u32));
for(i=0;i<100;i++)
{
readback = Xil_In32(result+i);
其中特別需要學(xué)習(xí)的就是malloc與memcpy的使用方法,。
貳
ZYNQ 中 MIO/EMIO GPIO 的使用
參考工程見(jiàn)“ZYBO_Memory_GPIO_Interrupt_demo.xpr”,。
MIO是PS端的外部引腳,共有54個(gè),;EMIO是PL端的外部引腳,,共有64個(gè)。ZYNQ支持通過(guò)配置將PS的控制器信號(hào)通過(guò)EMIO輸出,,例如PS自帶的UART Controller,,如果正常選擇引腳只能選擇MIO引腳輸出,但是通過(guò)設(shè)置可以選擇連接到EMIO引腳,。同時(shí)EMIO引腳也可以作為PS端的擴(kuò)展引腳,,即經(jīng)過(guò)擴(kuò)展PS一共可以控制118個(gè)引腳。
該例程演示將4個(gè)EMIO設(shè)置為PS的擴(kuò)展引腳,,這4個(gè)EMIO連接著LED,。于是,與“將用戶邏輯設(shè)計(jì)封裝成IP”中的實(shí)驗(yàn)相比,,同樣是控制外部4個(gè)LED,,就不需要另外設(shè)計(jì)一個(gè)邏輯模塊,并封裝成IP作為PS的外設(shè)了,,可以直接通過(guò)SDK的程序進(jìn)行控制,。
注意
用于擴(kuò)展GPIO的EMIO和用于擴(kuò)展外設(shè)的EMIO是完全獨(dú)立的,GPIO的EMIO共有64個(gè),,由2個(gè)bank組成,,如下圖所示。
2. EMIO的內(nèi)部排序按照EMIO54,、EMIO55... ... EMIO117,,以此類推。有了EMIO的編號(hào)之后就與內(nèi)部控制EMIO的寄存器一一對(duì)應(yīng),;而EMIO在外部與外部引腳的對(duì)應(yīng)關(guān)系又是可以通過(guò)管腳約束進(jìn)行更改的,。于是可以得出:不能通過(guò)EMIO的外部引腳的關(guān)系確定其內(nèi)部寄存器的地址。工具對(duì)于EMIO GPIO的連接關(guān)系是按照從EMIO54開(kāi)始依次向上排列,。
Step1:在Block Design中加入ZYNQ7 Processing System,,在ZYNQ7 Processing System配置中添加EMIO GPIO,如下圖所示,。通過(guò)設(shè)置EMIO GPIO Width來(lái)選擇擴(kuò)展EMIO GPIO的個(gè)數(shù),,此時(shí)就完成了與內(nèi)部寄存器之間的對(duì)應(yīng)關(guān)系,規(guī)則就是從EMIO54開(kāi)始向上排列,。
Step2:將ZYNQ的EMIO連接到外部引腳,。右擊生成的GPIO信號(hào),點(diǎn)擊Make External,。
Step3:約束EMIO與外部引腳Pad的對(duì)應(yīng)關(guān)系以及EMIO的電平標(biāo)準(zhǔn),。
方法有兩種:
第一種是通過(guò)XDC約束文件進(jìn)行約束,需要先將Block Design生成HDL Wrapper,,這樣才能知道其引腳名稱,。
set_property IOSTANDARD LVCMOS33 [get_ports {gpio_0_tri_io[3]}]]
set_property IOSTANDARD LVCMOS33 [get_ports {gpio_0_tri_io[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpio_0_tri_io[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {gpio_0_tri_io[0]}]
set_property PACKAGE_PIN D18 [get_ports {gpio_0_tri_io[3]}]
set_property PACKAGE_PIN G14 [get_ports {gpio_0_tri_io[2]}]
set_property PACKAGE_PIN M15 [get_ports {gpio_0_tri_io[1]}]
set_property PACKAGE_PIN M14 [get_ports {gpio_0_tri_io[0]}]
第二種方法就是Open Elaborated Design,在GUI中設(shè)置電平和引腳,。
Step4:完成Block Design的綜合,、實(shí)現(xiàn)、生成Bitstream并導(dǎo)入SDK,。
Step5:SDK中完成代碼的編寫(xiě),,EMIO的代碼編寫(xiě)需要包含的庫(kù)文件是"xgpiops.h"。
#include "xgpiops.h"
static XGpioPs emio;
#define EMIO_54 54
#define EMIO_55 55
#define EMIO_56 56
#define EMIO_57 57
int main()
{
//定義GPIOPS型指針,,用于初始化時(shí)綁定硬件
XGpioPs_Config *ConfigPtrPS;
init_platform();
//初始化GPIOPS,,將ConfigPtrPS與硬件綁定
ConfigPtrPS = XGpioPs_LookupConfig(0);
XGpioPs_CfgInitialize(&emio, ConfigPtrPS,
ConfigPtrPS->BaseAddr);
//設(shè)置EMIO的方向,并使能EMIO
XGpioPs_SetDirectionPin(&emio, EMIO_54, 1);
XGpioPs_SetOutputEnablePin(&emio, EMIO_54, 1);
XGpioPs_SetDirectionPin(&emio, EMIO_55, 1);
XGpioPs_SetOutputEnablePin(&emio, EMIO_55, 1);
XGpioPs_SetDirectionPin(&emio, EMIO_56, 1);
XGpioPs_SetOutputEnablePin(&emio, EMIO_56, 1);
XGpioPs_SetDirectionPin(&emio, EMIO_57, 1);
XGpioPs_SetOutputEnablePin(&emio, EMIO_57, 1);
while(1)
{
// 向EMIO寫(xiě)入數(shù)據(jù),,即驅(qū)動(dòng)EMIO引腳
XGpioPs_WritePin(&emio, EMIO_54, 0x0);
XGpioPs_WritePin(&emio, EMIO_55, 0x0);
XGpioPs_WritePin(&emio, EMIO_56, 0x0);
XGpioPs_WritePin(&emio, EMIO_57, 0x0);
usleep(200000);
XGpioPs_WritePin(&emio, EMIO_54, 0x1);
XGpioPs_WritePin(&emio, EMIO_55, 0x1);
XGpioPs_WritePin(&emio, EMIO_56, 0x1);
XGpioPs_WritePin(&emio, EMIO_57, 0x1);
usleep(200000);
}
cleanup_platform();
return 0;
}
Step6:如果需要將EMIO作為輸入端口,,只需要將IO的方向設(shè)置為input。對(duì)于IO,,作為輸出的時(shí)候需要Enable,,但是作為輸入是永遠(yuǎn)使能的,不需要額外的Enable,。具體代碼如下圖所示,。
補(bǔ)充說(shuō)明:
MIO和EMIO都屬于PS的GPIO,,用于指示的變量類型為XGpioPs;而使用AXI_GPIO外設(shè)的GPIO,,由于是屬于PL的,,所以指示這些IO的變量類型為XGpio。
MIO和EMIO的控制對(duì)于SDK是完全相同的,,其地址偏移量也是排在一起的,,MIO從0排到53,EMIO接著從54開(kāi)始,。示例代碼中顯示的是EMIO作為輸出和MIO作為輸入,,只需要將引腳編號(hào)的宏定義改為需要的MIO或者EMIO編號(hào)即可使用。
在硬件配置時(shí)MIO的配置方法與EMIO有所不同,,EMIO的配置如“Step1”所示,。而MIO由于不像EMIO,外部管腳是確定的,,所以可以在ZYNQ7 Processing System配置時(shí)同時(shí)完成屬性以及電平的設(shè)置,,如下圖所示。
叁
ZYNQ 中 Interrupt 使用
參考工程見(jiàn)“ZYBO_Memory_GPIO_Interrupt_demo.xpr”,。
ZYNQ中的中斷管理是通過(guò)Generic Interrupt Controller(GIC)完成的,。
任何的中斷功能都需要兩步,第一步是配置相應(yīng)的中斷,,第二步是設(shè)置中斷觸發(fā)之后的服務(wù)函數(shù),。
配置相應(yīng)中斷分以下幾個(gè)步驟:
使能相應(yīng)的功能,例如GPIO中斷需要首先使能和配置GPIO,;Timer中斷需要首先使能和配置Timer,;
2. 初始化并配置使能GIC,還要使能異常處理,。第1步中的操作對(duì)于每個(gè)中斷源來(lái)說(shuō)都不相同,,但是這一步的配置對(duì)于不同中斷源而言是類似的。不同之處在于有一個(gè)參數(shù):中斷ID,,即例子中的52是變化的,,52是GPIO的中斷號(hào),其他中端需要使用不同的ID,。該值可以在UG585中斷的相關(guān)章節(jié)查詢到,,如下圖所示。
另一個(gè)區(qū)別就是XScuGic_Connect時(shí)的服務(wù)子函數(shù)不同,。
3. 編寫(xiě)中斷服務(wù)函數(shù),,需要注意的是進(jìn)入服務(wù)函數(shù)后首先需要禁止中斷,保證在處理中斷時(shí)不會(huì)再次因觸發(fā)中斷而程序跳轉(zhuǎn),;另外就是需要清除中斷標(biāo)志位,,否則會(huì)不斷觸發(fā)中斷,。
ZYNQ CPU內(nèi)部任何有定時(shí)器,在Vivado的ZYNQ配置中無(wú)需任何操作就可以在SDK中直接使用,。與GPIO中斷類似,,Timer的中斷也包含相同的幾步操作,下面給出各個(gè)階段的代碼片段,,完整代碼請(qǐng)點(diǎn)擊“閱讀原文”,,登錄論壇獲取,。
初始化Timer,。
2. 初始化GIC,設(shè)置中斷服務(wù)函數(shù)入口,。
3. 配置Timer工作模式,,導(dǎo)入計(jì)數(shù)初值,使能中斷,。
4. 編寫(xiě)中斷服務(wù)函數(shù),,進(jìn)入中斷后首先Disable中斷,清楚中斷標(biāo)志位,;然后進(jìn)行中斷處理,;退出中斷服務(wù)函數(shù)前重新使能中斷。