學(xué)習(xí)內(nèi)容
本文首先進(jìn)行自定義IP的AXI總線IP的設(shè)計(jì),然后在SDK下編寫(xiě)代碼進(jìn)行DDR的讀寫(xiě)數(shù)據(jù)的測(cè)試。
開(kāi)發(fā)環(huán)境
vivado 18.3&SDKPYNQ-Z2開(kāi)發(fā)板
系統(tǒng)框圖
首先對(duì)本次工程進(jìn)行簡(jiǎn)要說(shuō)明:本次工程使用AXI-Full接口的IP進(jìn)行DDR的讀寫(xiě)測(cè)試。在我們的DDR讀寫(xiě)IP中,我們把讀寫(xiě)完成和讀寫(xiě)錯(cuò)誤信號(hào)關(guān)聯(lián)到PL端的LED上,用于指示DDR讀寫(xiě)IP的讀寫(xiě)運(yùn)行狀態(tài)。然后使用PL部分消抖處理后的按鍵進(jìn)行啟動(dòng)AXI總線工作,控制數(shù)據(jù)寫(xiě)入。通過(guò)AXI互聯(lián)模塊連接到AXI_HP0端口,由PS端口進(jìn)行數(shù)據(jù)的讀取操作,并通過(guò)串口進(jìn)行讀寫(xiě)數(shù)據(jù)的監(jiān)控。
自定義IP設(shè)計(jì)
首先打開(kāi)Vivado軟件,在Tasks這里選擇New IP lacation
點(diǎn)擊next,對(duì)IP的信息進(jìn)行設(shè)置,這里我們使用默認(rèn)配置即可。設(shè)置好我們IP要保存的位置。
點(diǎn)擊Tools中的創(chuàng)建和封裝新的IP選項(xiàng),
點(diǎn)擊NEXT ,選擇我們的封裝類型。因?yàn)檫@里我們是直接進(jìn)行打開(kāi)IP設(shè)計(jì)的界面,前兩個(gè)選項(xiàng)是可以在我們的vivado當(dāng)前工程下面進(jìn)行封裝設(shè)計(jì),這里我們只進(jìn)行了IP設(shè)計(jì)沒(méi)有建立工程,所以前兩個(gè)選項(xiàng)是無(wú)法選中的。我們也可以通過(guò)工程界面,進(jìn)入點(diǎn)擊Tools中的創(chuàng)建和封裝新的IP選項(xiàng)。
這里是用DDR讀寫(xiě)IP來(lái)做主機(jī),控制數(shù)據(jù)寫(xiě)入,PS作為從機(jī)進(jìn)行讀取IP中寫(xiě)入的數(shù)據(jù)。
可以直接選中進(jìn)行編輯IP,用戶可以根據(jù)自己的設(shè)計(jì)進(jìn)行修改編輯IP的功能,這里沒(méi)有對(duì)IP進(jìn)行修改處理,所以可以直接保存選擇第一個(gè)添加到IP庫(kù)中即可。
若修改相應(yīng)的邏輯功能打開(kāi)IP,在對(duì)應(yīng)位置編輯添加代碼即可。
添加完成綜合后對(duì)IP進(jìn)行重新打包。DDR讀寫(xiě)IP設(shè)計(jì)完成,創(chuàng)建的 IP 核將通過(guò) AXI4 Master 端口向 Slave 端指定的 4K 存儲(chǔ)空間中連續(xù)寫(xiě)入 1024 個(gè)數(shù)據(jù), 寫(xiě)入的數(shù)值從 1 累加到 1024, 每個(gè)數(shù)據(jù)占 32bit。然后進(jìn)行硬件平臺(tái)的構(gòu)建。
硬件平臺(tái)構(gòu)建
首先,添加ZYNQ7 IP核,以及添加已經(jīng)完成設(shè)計(jì)的ddr讀寫(xiě)IP核。
添加用戶自定義IP
用戶自定義的IP可通過(guò)以下步驟完成添加。點(diǎn)擊Settings,
在project settings選擇IP,依次點(diǎn)擊,在IP庫(kù)那里點(diǎn)擊加號(hào),把對(duì)應(yīng)的IP目錄文件夾添加后,點(diǎn)擊OK或者Apply即可完成添加,在IP庫(kù)中就可以找到用戶設(shè)計(jì)的IP。
完成IP和ZYNQ7 IP的導(dǎo)入后,如下圖:
雙擊打開(kāi)zynq刪除多余的接口,這里只需要保留uart,并打開(kāi)Slave HP0端口、時(shí)鐘、復(fù)位端口。
配置完,選擇自動(dòng)連接接口,完成部分連接設(shè)計(jì)。
整體設(shè)計(jì)圖如下,
添加按鍵消抖IP
由于ddr讀寫(xiě)IP的axi_init_axi_txn接入的是按鍵,這里按鍵按下會(huì)產(chǎn)生抖動(dòng),axi_init_axi_txn與好多讀寫(xiě)信號(hào)關(guān)聯(lián),如果不添加消抖IP,在按鍵按下的時(shí),產(chǎn)生的毛刺會(huì)進(jìn)行影響后續(xù)的操作,從而導(dǎo)致讀寫(xiě)操作的錯(cuò)誤,也就是讀寫(xiě)操作的指示燈會(huì)亮起。
系統(tǒng)復(fù)位后, 狀態(tài)機(jī)處于初始狀態(tài),在該狀態(tài)下等待外部輸入的啟動(dòng)傳輸脈沖 init_txn_pulse。一旦檢測(cè)到 init_txn_pulse 為高電平,狀態(tài)機(jī)跳轉(zhuǎn)到 INIT_WRITE 狀態(tài)。在 INIT_WRITE 狀態(tài)下, 狀態(tài)機(jī)拉高 start_single_burst_write 信號(hào), 來(lái)不斷地啟動(dòng) AXI4 Master 接口對(duì)Slave 端大小為 4KB 的存儲(chǔ)空間進(jìn)行突發(fā)寫(xiě)操作。寫(xiě)操作完成后, write_done 信號(hào)會(huì)拉高,狀態(tài)機(jī)進(jìn)入INIT_READ 狀態(tài)。在 INIT_READ 狀態(tài)下, 狀態(tài)機(jī)拉高 start_single_burst_read 信號(hào), 不斷地啟動(dòng) AXI4 Master 接口對(duì) Slave端同一存儲(chǔ)空間進(jìn)行突發(fā)讀操作, 同時(shí)將讀出的數(shù)據(jù)與寫(xiě)入的數(shù)據(jù)進(jìn)行對(duì)比。讀操作完成后, read_done 信號(hào)拉高,狀態(tài)機(jī)進(jìn)入 INIT_COMPARE 狀態(tài)。在 INIT_COMPARE 狀態(tài)下, 判斷 AXI4 接口在讀寫(xiě)過(guò)程中的是否發(fā)生錯(cuò)誤, 并將錯(cuò)誤狀態(tài)賦值給ERROR 信號(hào), 然后將 compare_done 信號(hào)拉高,表示一次讀寫(xiě)測(cè)試完成。最后跳轉(zhuǎn)到 IDLE 狀態(tài),等待下一次讀寫(xiě)操作的啟動(dòng)信號(hào)。這里的消抖模塊直接添加之前寫(xiě)過(guò)的按鍵消抖模塊即可,這里給出我的設(shè)計(jì):
module key_filter( Clk, //50M時(shí)鐘輸入 Rst_n, //模塊復(fù)位 key_in, //按鍵輸入 key_flag, //按鍵標(biāo)志信號(hào) key_state //按鍵狀態(tài)信號(hào) ); input Clk; input Rst_n; input key_in; output reg key_flag; output reg key_state; localparam IDEL= 4'b0001, FILTER0= 4'b0010, DOWN= 4'b0100, FILTER1 = 4'b1000; reg [3:0]state; reg [19:0]cnt; reg en_cnt;//使能計(jì)數(shù)寄存器 //對(duì)外部輸入的異步信號(hào)進(jìn)行同步處理 reg key_in_sa,key_in_sb; always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin key_in_sa <= 1'b0; key_in_sb <= 1'b0; end else begin key_in_sa <= key_in; key_in_sb <= key_in_sa; end reg key_tmpa,key_tmpb; wire pedge,nedge; reg cnt_full;//計(jì)數(shù)滿標(biāo)志信號(hào) //使用D觸發(fā)器存儲(chǔ)兩個(gè)相鄰時(shí)鐘上升沿時(shí)外部輸入信號(hào)(已經(jīng)同步到系統(tǒng)時(shí)鐘域中)的電平狀態(tài) always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin key_tmpa <= 1'b0; key_tmpb <= 1'b0; end else begin key_tmpa <= key_in_sb; key_tmpb <= key_tmpa; end //產(chǎn)生跳變沿信號(hào) assign nedge = !key_tmpa & key_tmpb; assign pedge = key_tmpa & (!key_tmpb); always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin en_cnt <= 1'b0; state <= IDEL; key_flag <= 1'b0; key_state <= 1'b1; end else begin case(state) IDEL : begin key_flag <= 1'b0; if(nedge)begin state <= FILTER0; en_cnt <= 1'b1; end else state <= IDEL; end FILTER0: if(cnt_full)begin key_flag <= 1'b1; key_state <= 1'b0; en_cnt <= 1'b0; state <= DOWN; end else if(pedge)begin state <= IDEL; en_cnt <= 1'b0; end else state <= FILTER0; DOWN: begin key_flag <= 1'b0; if(pedge)begin state <= FILTER1; en_cnt <= 1'b1; end else state <= DOWN; end FILTER1: if(cnt_full)begin key_flag <= 1'b1; key_state <= 1'b1; state <= IDEL; en_cnt <= 1'b0; end else if(nedge)begin en_cnt <= 1'b0; state <= DOWN; end else state <= FILTER1; default: begin state <= IDEL; en_cnt <= 1'b0; key_flag <= 1'b0; key_state <= 1'b1; end endcase end always@(posedge Clk or negedge Rst_n) if(!Rst_n) cnt <= 20'd0; else if(en_cnt) cnt <= cnt + 1'b1; else cnt <= 20'd0; always@(posedge Clk or negedge Rst_n) if(!Rst_n) cnt_full <= 1'b0; else if(cnt == 20'd999_999) cnt_full <= 1'b1; else cnt_full <= 1'b0; endmodule
添加完模塊后系統(tǒng)設(shè)計(jì)如下:注:axi_init_axi_txn是上升沿有效,這里為了保證系統(tǒng)上電后是初始默認(rèn)隨機(jī)狀態(tài),要確保按鍵未按下給啟動(dòng)脈沖時(shí),是低電平。因?yàn)镻YNQZ2開(kāi)發(fā)板按鍵默認(rèn)電位是低,按下為高,這里不用進(jìn)行處理,若按鍵按下后為低,默認(rèn)拉高,這里可以對(duì)按鍵進(jìn)行添加非邏輯的IP進(jìn)行取反。(使用 utility vector logic IP完成配置)
雙擊DDR讀寫(xiě)的IP核進(jìn)行配置,這里沒(méi)有用到user的接口所以都設(shè)置為0,如圖:
對(duì)于Base address,我們可以查看下接口的地址范圍,避免數(shù)據(jù)操作超過(guò)可操作范圍,這里我們就取中間值0X10000000。
然后我們進(jìn)行g(shù)enerate output product 然后生成HDL封裝。接著就對(duì)應(yīng)引腳進(jìn)行引腳約束即可(PYNQ的粉色開(kāi)發(fā)板可以直接引用這個(gè)約束):
##LEDs set_property -dict { PACKAGE_PIN R14 IOSTANDARD LVCMOS33 } [get_ports { m0_axi_error_0 }]; #IO_L6N_T0_VREF_34 Sch=led[0] set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { m0_axi_txn_done_0}]; #IO_L6P_T0_34 Sch=led[1] ##Buttons set_property -dict { PACKAGE_PIN D19 IOSTANDARD LVCMOS33 } [get_ports { key }]; #IO_L4P_T0_35 Sch=btn[0]
完成約束后進(jìn)行綜合布局布線,等待生成bit流文件。
bit文件生成后在FILE處,點(diǎn)擊導(dǎo)出硬件資源(包含bit流文件),接著launch SDK。
SDK軟件部分
打開(kāi)SDK后,新建application project。在main.c中輸入以下代碼:
#include "stdio.h" #include "xil_cache.h" #include "xil_printf.h" #include "xil_io.h" int main(){ int i; char chardata; Xil_DCacheDisable(); printf("AXI4-FULL RW TEST~ "); while(1){ scanf("%c",chardata); if(chardata="y"){ printf("start "); for(i=0;i<4096;i=i+4){ printf("%d is %d ",i,(int)(Xil_In32(0x10000000+i))); } } } return 0; }
代碼簡(jiǎn)要說(shuō)明
這里使用的IP我們?cè)O(shè)定成不需要進(jìn)行緩存的,所以在main函數(shù)中調(diào)用Xil_DCacheDisable();。使用Xil_In32(),對(duì)DDR對(duì)應(yīng)位置的數(shù)據(jù)進(jìn)行讀取。參數(shù)只需要傳遞所要讀取的地址即可。因?yàn)橐淮螌?xiě)入的數(shù)據(jù)是32位的,每個(gè)地址的數(shù)據(jù)位寬是8位,所以在for循環(huán)中使用了i=i+4。
在串口中使用printf("%d is %d ",i,(int)(Xil_In32(0x10000000+i)));對(duì)相應(yīng)地址的數(shù)據(jù)進(jìn)行讀取顯示。
運(yùn)行效果
當(dāng)按鍵未按下時(shí),也就是未進(jìn)行寫(xiě)入操作直接讀取數(shù)據(jù),DDR中的數(shù)據(jù)是默認(rèn)的隨機(jī)狀態(tài),如下所示:
當(dāng)按鍵按下后,IP完成數(shù)據(jù)寫(xiě)入操作,數(shù)據(jù)是從1-1024自增的
如何波形進(jìn)行debug?
這里我們選中要進(jìn)行DEBUG的數(shù)據(jù)信號(hào)右擊選中debug
然后點(diǎn)擊自動(dòng)連接,完成debug功能搭建
綜合后會(huì)多出ILA的IP進(jìn)行波形分析幫助DEBUG。
然后打開(kāi)硬件設(shè)備,如下圖在ila界面即可看到我們debug的波形數(shù)據(jù)了。
添加觸發(fā)條件,標(biāo)號(hào)2是單次觸發(fā),標(biāo)號(hào)1是一直運(yùn)行debug抓取波形。
審核編輯:劉清
-
DDR
+關(guān)注
關(guān)注
11文章
712瀏覽量
65340 -
AXI總線
+關(guān)注
關(guān)注
0文章
66瀏覽量
14261 -
SDK
+關(guān)注
關(guān)注
3文章
1036瀏覽量
45940 -
AXI
+關(guān)注
關(guān)注
1文章
127瀏覽量
16631
原文標(biāo)題:如何波形進(jìn)行debug?
文章出處:【微信號(hào):zhuyandz,微信公眾號(hào):FPGA之家】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論