學習內容
本文首先進行自定義IP的AXI總線IP的設計,然后在SDK下編寫代碼進行DDR的讀寫數據的測試。
開發環境
vivado 18.3&SDKPYNQ-Z2開發板
系統框圖
首先對本次工程進行簡要說明:本次工程使用AXI-Full接口的IP進行DDR的讀寫測試。在我們的DDR讀寫IP中,我們把讀寫完成和讀寫錯誤信號關聯到PL端的LED上,用于指示DDR讀寫IP的讀寫運行狀態。然后使用PL部分消抖處理后的按鍵進行啟動AXI總線工作,控制數據寫入。通過AXI互聯模塊連接到AXI_HP0端口,由PS端口進行數據的讀取操作,并通過串口進行讀寫數據的監控。
自定義IP設計
首先打開Vivado軟件,在Tasks這里選擇New IP lacation
點擊next,對IP的信息進行設置,這里我們使用默認配置即可。設置好我們IP要保存的位置。
點擊Tools中的創建和封裝新的IP選項,
點擊NEXT ,選擇我們的封裝類型。因為這里我們是直接進行打開IP設計的界面,前兩個選項是可以在我們的vivado當前工程下面進行封裝設計,這里我們只進行了IP設計沒有建立工程,所以前兩個選項是無法選中的。我們也可以通過工程界面,進入點擊Tools中的創建和封裝新的IP選項。
這里是用DDR讀寫IP來做主機,控制數據寫入,PS作為從機進行讀取IP中寫入的數據。
可以直接選中進行編輯IP,用戶可以根據自己的設計進行修改編輯IP的功能,這里沒有對IP進行修改處理,所以可以直接保存選擇第一個添加到IP庫中即可。
若修改相應的邏輯功能打開IP,在對應位置編輯添加代碼即可。
添加完成綜合后對IP進行重新打包。DDR讀寫IP設計完成,創建的 IP 核將通過 AXI4 Master 端口向 Slave 端指定的 4K 存儲空間中連續寫入 1024 個數據, 寫入的數值從 1 累加到 1024, 每個數據占 32bit。然后進行硬件平臺的構建。
硬件平臺構建
首先,添加ZYNQ7 IP核,以及添加已經完成設計的ddr讀寫IP核。
添加用戶自定義IP
用戶自定義的IP可通過以下步驟完成添加。點擊Settings,
在project settings選擇IP,依次點擊,在IP庫那里點擊加號,把對應的IP目錄文件夾添加后,點擊OK或者Apply即可完成添加,在IP庫中就可以找到用戶設計的IP。
完成IP和ZYNQ7 IP的導入后,如下圖:
雙擊打開zynq刪除多余的接口,這里只需要保留uart,并打開Slave HP0端口、時鐘、復位端口。
配置完,選擇自動連接接口,完成部分連接設計。
整體設計圖如下,
添加按鍵消抖IP
由于ddr讀寫IP的axi_init_axi_txn接入的是按鍵,這里按鍵按下會產生抖動,axi_init_axi_txn與好多讀寫信號關聯,如果不添加消抖IP,在按鍵按下的時,產生的毛刺會進行影響后續的操作,從而導致讀寫操作的錯誤,也就是讀寫操作的指示燈會亮起。
系統復位后, 狀態機處于初始狀態,在該狀態下等待外部輸入的啟動傳輸脈沖 init_txn_pulse。一旦檢測到 init_txn_pulse 為高電平,狀態機跳轉到 INIT_WRITE 狀態。在 INIT_WRITE 狀態下, 狀態機拉高 start_single_burst_write 信號, 來不斷地啟動 AXI4 Master 接口對Slave 端大小為 4KB 的存儲空間進行突發寫操作。寫操作完成后, write_done 信號會拉高,狀態機進入INIT_READ 狀態。在 INIT_READ 狀態下, 狀態機拉高 start_single_burst_read 信號, 不斷地啟動 AXI4 Master 接口對 Slave端同一存儲空間進行突發讀操作, 同時將讀出的數據與寫入的數據進行對比。讀操作完成后, read_done 信號拉高,狀態機進入 INIT_COMPARE 狀態。在 INIT_COMPARE 狀態下, 判斷 AXI4 接口在讀寫過程中的是否發生錯誤, 并將錯誤狀態賦值給ERROR 信號, 然后將 compare_done 信號拉高,表示一次讀寫測試完成。最后跳轉到 IDLE 狀態,等待下一次讀寫操作的啟動信號。這里的消抖模塊直接添加之前寫過的按鍵消抖模塊即可,這里給出我的設計:
module key_filter( Clk, //50M時鐘輸入 Rst_n, //模塊復位 key_in, //按鍵輸入 key_flag, //按鍵標志信號 key_state //按鍵狀態信號 ); 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;//使能計數寄存器 //對外部輸入的異步信號進行同步處理 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;//計數滿標志信號 //使用D觸發器存儲兩個相鄰時鐘上升沿時外部輸入信號(已經同步到系統時鐘域中)的電平狀態 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 //產生跳變沿信號 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
添加完模塊后系統設計如下:注:axi_init_axi_txn是上升沿有效,這里為了保證系統上電后是初始默認隨機狀態,要確保按鍵未按下給啟動脈沖時,是低電平。因為PYNQZ2開發板按鍵默認電位是低,按下為高,這里不用進行處理,若按鍵按下后為低,默認拉高,這里可以對按鍵進行添加非邏輯的IP進行取反。(使用 utility vector logic IP完成配置)
雙擊DDR讀寫的IP核進行配置,這里沒有用到user的接口所以都設置為0,如圖:
對于Base address,我們可以查看下接口的地址范圍,避免數據操作超過可操作范圍,這里我們就取中間值0X10000000。
然后我們進行generate output product 然后生成HDL封裝。接著就對應引腳進行引腳約束即可(PYNQ的粉色開發板可以直接引用這個約束):
##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]
完成約束后進行綜合布局布線,等待生成bit流文件。
bit文件生成后在FILE處,點擊導出硬件資源(包含bit流文件),接著launch SDK。
SDK軟件部分
打開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; }
代碼簡要說明
這里使用的IP我們設定成不需要進行緩存的,所以在main函數中調用Xil_DCacheDisable();。使用Xil_In32(),對DDR對應位置的數據進行讀取。參數只需要傳遞所要讀取的地址即可。因為一次寫入的數據是32位的,每個地址的數據位寬是8位,所以在for循環中使用了i=i+4。
在串口中使用printf("%d is %d ",i,(int)(Xil_In32(0x10000000+i)));對相應地址的數據進行讀取顯示。
運行效果
當按鍵未按下時,也就是未進行寫入操作直接讀取數據,DDR中的數據是默認的隨機狀態,如下所示:
當按鍵按下后,IP完成數據寫入操作,數據是從1-1024自增的
如何波形進行debug?
這里我們選中要進行DEBUG的數據信號右擊選中debug
然后點擊自動連接,完成debug功能搭建
綜合后會多出ILA的IP進行波形分析幫助DEBUG。
然后打開硬件設備,如下圖在ila界面即可看到我們debug的波形數據了。
添加觸發條件,標號2是單次觸發,標號1是一直運行debug抓取波形。
審核編輯:劉清
-
DDR
+關注
關注
11文章
712瀏覽量
65340 -
AXI總線
+關注
關注
0文章
66瀏覽量
14261 -
SDK
+關注
關注
3文章
1036瀏覽量
45940 -
AXI
+關注
關注
1文章
127瀏覽量
16631
原文標題:如何波形進行debug?
文章出處:【微信號:zhuyandz,微信公眾號:FPGA之家】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論