? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??ZYNQ 、AXI協議、PS與PL內部通信?
三種AXI總線分別為:
AXI4:(For high-performance memory-mapped requirements.)主要面向高性能地址映射通信的需求,是面向地址映射的接口,允許最大256輪的數據突發傳輸;
AXI4-Lite:(For simple, low-throughput memory-mapped communication )是一個輕量級的地址映射單次傳輸接口,占用很少的邏輯單元。
AXI4-Stream:(For high-speed streaming data.)面向高速流數據傳輸;去掉了地址項,允許無限制的數據突發傳輸規模。
AXI4總線和AXI4-Lite總線具有相同的組成部分:
(1)讀地址通道,包含ARVALID, ARADDR, ARREADY信號;
(2)讀數據通道,包含RVALID, RDATA, RREADY, RRESP信號;
(3)寫地址通道,包含AWVALID,AWADDR, AWREADY信號;
(4)寫數據通道,包含WVALID, WDATA,WSTRB, WREADY信號;
(5)寫應答通道,包含BVALID, BRESP, BREADY信號;
(6)系統通道,包含:ACLK,ARESETN信號。
AXI4-Stream總線的組成:
(1)ACLK信號:總線時鐘,上升沿有效;
(2)ARESETN信號:總線復位,低電平有效
(3)TREADY信號:從機告訴主機做好傳輸準備;
(4)TDATA信號:數據,可選寬度32,64,128,256bit
(5)TSTRB信號:每一bit對應TDATA的一個有效字節,寬度為TDATA/8
(6)TLAST信號:主機告訴從機該次傳輸為突發傳輸的結尾;
(7)TVALID信號:主機告訴從機數據本次傳輸有效;
(8)TUSER信號 :用戶定義信號,寬度為128bit。
三種AXI接口分別是:
AXI-GP接口(4個):是通用的AXI接口,包括兩個32位主設備接口和兩個32位從設備接口,用過改接口可以訪問PS中的片內外設。
AXI-HP接口(4個):是高性能/帶寬的標準的接口,PL模塊作為主設備連接(從下圖中箭頭可以看出)。主要用于PL訪問PS上的存儲器(DDR和On-Chip RAM)
AXI-ACP接口(1個):是ARM多核架構下定義的一種接口,中文翻譯為加速器一致性端口,用來管理DMA之類的不帶緩存的AXI外設,PS端是Slave接口。
我們可以雙擊查看ZYNQ的IP核的內部配置,就能發現上述的三種接口,圖中已用紅色方框標記出來,我們可以清楚的看出接口連接與總線的走向:
AXI協議之握手協議
AXI4所采用的是一種READY,VALID握手通信機制,簡單來說主從雙方進行數據通信前,有一個握手的過程。傳輸源產生VLAID信號來指明何時數據或控制信息有效。而目地源產生READY信號來指明已經準備好接受數據或控制信息。傳輸發生在VALID和READY信號同時為高的時候。VALID和READY信號的出現有三種關系:VALID先變高READY后變高,READY先變高VALID后變高,VALID和READY信號同時變高。無論什么情況信息傳輸立馬發生在VALID和READY信號同時為高且ACLK上升沿,時序圖如下:
需要強調的是,AXI的五個通道,每個通道都有握手機制,接下來我們就來分析一下AXI-Lite的源碼來更深入的了解AXI機制。
突發式讀
當地址出現在地址總線后,傳輸的數據將出現在讀數據通道上。設備保持VALID為低直到讀數據有效。為了表明一次突發式讀寫的完成,設備用RLAST信號來表示最后一個被傳輸的數據。
突發式寫
這一過程的開始時,主機發送地址和控制信息到寫地址通道中,然后主機發送每一個寫數據到寫數據通道中。當主機發送最后一個數據時,WLAST信號就變為高。當設備接收完所有數據之后他將一個寫響應發送回主機來表明寫事務完成。
PS與PL內部通信(用戶自定義IP)
??先要自定義一個AXI-Lite的IP,新建工程之后,選擇,菜單欄->Tools->Creat and Package IP:
選擇Next>>選擇Create AXI4 Peripheral>>然后Next默認,選擇Next>>注意這里接口類型選擇Lite,選擇Next>>選擇Edit IP,點擊Finish:
此后,Vivado會新建一個工程,專門編輯該IP,通過該工程,我們就可以看到Vivado為我們生成的AXI-Lite的操作源碼:
AXI-Lite 源碼分析
當打開頂層文件的時,是一堆AXI的信號。
PS向PL寫數據(PS作為Master,PL作為Slave)
先來看一段WDATA相關的代碼:
always?@(?posedge?S_AXI_ACLK?) begin ??if?(?S_AXI_ARESETN?==?1'b0?) ????begin ??????slv_reg0?<=?0; ??????slv_reg1?<=?0; ??????slv_reg2?<=?0; ??????slv_reg3?<=?0; ????end ??else?begin ????if?(slv_reg_wren) ??????begin ????????case?(?axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]?) ??????????2'h0: ????????????for?(?byte_index?=?0;?byte_index?<=?(C_S_AXI_DATA_WIDTH/8)-1;?byte_index?=?byte_index+1?) ??????????????if?(?S_AXI_WSTRB[byte_index]?==?1?)?begin ????????????????//?Respective?byte?enables?are?asserted?as?per?write?strobes ????????????????//?Slave?register?0 ????????????????slv_reg0[(byte_index*8)?+:?8]?<=?S_AXI_WDATA[(byte_index*8)?+:?8]; ??????????????end?? ??????????2'h1: ????????????for?(?byte_index?=?0;?byte_index?<=?(C_S_AXI_DATA_WIDTH/8)-1;?byte_index?=?byte_index+1?) ??????????????if?(?S_AXI_WSTRB[byte_index]?==?1?)?begin ????????????????//?Respective?byte?enables?are?asserted?as?per?write?strobes ????????????????//?Slave?register?1 ????????????????slv_reg1[(byte_index*8)?+:?8]?<=?S_AXI_WDATA[(byte_index*8)?+:?8]; ??????????????end?? ??????????2'h2: ????????????for?(?byte_index?=?0;?byte_index?<=?(C_S_AXI_DATA_WIDTH/8)-1;?byte_index?=?byte_index+1?) ??????????????if?(?S_AXI_WSTRB[byte_index]?==?1?)?begin ????????????????//?Respective?byte?enables?are?asserted?as?per?write?strobes ????????????????//?Slave?register?2 ????????????????slv_reg2[(byte_index*8)?+:?8]?<=?S_AXI_WDATA[(byte_index*8)?+:?8]; ??????????????end?? ??????????2'h3: ????????????for?(?byte_index?=?0;?byte_index?<=?(C_S_AXI_DATA_WIDTH/8)-1;?byte_index?=?byte_index+1?) ??????????????if?(?S_AXI_WSTRB[byte_index]?==?1?)?begin ????????????????//?Respective?byte?enables?are?asserted?as?per?write?strobes ????????????????//?Slave?register?3 ????????????????slv_reg3[(byte_index*8)?+:?8]?<=?S_AXI_WDATA[(byte_index*8)?+:?8]; ??????????????end?? ??????????default?:?begin ??????????????????????slv_reg0?<=?slv_reg0; ??????????????????????slv_reg1?<=?slv_reg1; ??????????????????????slv_reg2?<=?slv_reg2; ??????????????????????slv_reg3?<=?slv_reg3; ????????????????????end ????????endcase ??????end ??end這段程序的作用是,當PS那邊向AXI4-Lite總線寫數據時,PS這邊負責將數據接收到寄存器slv_reg。而slv_reg寄存器有0~3共4個。至于賦值給哪一個由axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]決定,根據宏定義其實就是由axi_awaddr[3:2] (寫地址中不僅包含地址,而且包含了控制位,這里的[3:2]就是控制位)決定賦值給哪個slv_reg。
PS讀取PL數據(PS作為Master,PL作為Slave)
我們繼續來看有關RADTA讀數據代碼:
//?Output?register?or?memory?read?data always?@(?posedge?S_AXI_ACLK?) begin ??if?(?S_AXI_ARESETN?==?1'b0?) ????begin ??????axi_rdata??<=?0; ????end ??else ????begin???? ??????//?When?there?is?a?valid?read?address?(S_AXI_ARVALID)?with ??????//?acceptance?of?read?address?by?the?slave?(axi_arready), ??????//?output?the?read?dada ??????if?(slv_reg_rden) ????????begin ??????????axi_rdata?<=?reg_data_out;?????//?register?read?data ????????end??? ????end end//當PS讀取數據時,程序會把reg_data_out復制給axi_rdata(RADTA讀數據)。 always?@(*) begin ??????//?Address?decoding?for?reading?registers ??????case?(?axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]?) ????????2'h0???:?reg_data_out?<=?slv_reg0; ????????2'h1???:?reg_data_out?<=?slv_reg1; ????????2'h2???:?reg_data_out?<=?slv_reg2; ????????2'h3???:?reg_data_out?<=?slv_reg3; ????????default?:?reg_data_out?<=?0; ??????endcase
? ? ZYNQ嵌入式設計時,用戶在PL中自定義的IP相當于PS(ARM的外設)掛在AXI總線上,他們之間是通過AXI總線進行數據傳輸。創建好哦IP后,文件自動的生成,用戶只需要做簡單的修改,寄存器賦值就可以。
?
評論
查看更多