ZYNQ例程搭建 + PS-PL數據傳遞


實驗環境:Win10-64bit,Vivado + Xilinx SDK 2019.1,硬件平台非官方開發板,板上器件包含:ZYNQ7020,DDR3 SDRAM 4Gbit兩顆,RTL8211E千兆PHY芯片等。

主要任務:使用Xilinx的LwIP Echo例程工程,在開發板上部署TCP/IP服務器,上位機向板子發送字符串消息,板子完成消息回顯,並且將消息轉存到PL中的BRAM,我們自定義的Verilog Module可以讀出該段消息。

實驗中的部件:

 先整理一下ZYNQ器件在Vivado和Xilinx SDK環境中的開發流程:

注意:每次改動Block Design並綜合布線生成比特流后,必須將生成的.bit和.hdf導入到SDK工作目錄下,並且在HDF工程和BSP工程中設定好對應的文件。

比如這樣的目錄結構:(圖中只標出了關鍵文件)

在HDF工程中,需要設定好.hdf文件和.bit文件(保持文件路徑、名稱正確和文件版本更新)。

在BSP工程中,需要設定好程序固件需要用到的外設庫、庫與組件的基本配置。(在.mss頁面中使用Modify進行修改,或在Project Explorer中右鍵修改)

在C/C++ Application工程中,進行自定義應用的開發。

 

//********************************************************************************************************************************

//********************************************************************************************************************************

//********************************************************************************************************************************

一、設計Block Design。

 

 Block Design中的核心模塊有:PS7、Block RAM和BRAM Reader。其中PS7即ZYNQ7 Processing System,Block RAM是由Block Memory Generator例化的存儲單元,BRAM Reader是Verilog Module進行IP封裝打包產生的模塊。

 1、PS7參數配置。

 啟用ETH0外設(根據硬件連接,直接用MIO引腳),同時開啟其MDIO接口(同樣用MIO引腳),將速率調整為1000Mbps。

 開啟UART0外設,使用EMIO引腳(UART0的MIO引腳沒有在板子上引出)。

 開啟PS7的AXI GP0 Master Port。(PS7通過AXI GP接口對BRAM進行操作)

 ARM Core頻率配置為200MHz,DDR頻率配置為200MHz。

 PL Fabric Clock配置為100MHz。

 2、Block RAM。

 BRAM不能直接與PS7相連,需要用AXI BRAM Controller進行轉換。AXI讀寫接口規范、時序和BRAM的不兼容,所以用控制器進行轉接。

 BRAM Controller選用AXI4規范,位寬32位,不用糾錯碼(ECC)。它將驅動BRAM的Port.A。

 BRAM驅動方式選擇BRAM Controller模式,配置為True Dual Port RAM。RAM的存儲寬度為32bit,存儲深度/容量在Address Editor中(Addr Map環節)進行調整。

 3、BRAM Reader。

 這是一個Verilog Module打包成的模塊,它驅動BRAM的Port.B。它按照0~20的地址順序對Port.B進行讀操作。

 4、AXI互聯模塊(Smart Connect,相比與AXI InnerConnect它只面向存儲接口)。

 這個模塊相當於是AXI總線的開關矩陣,它決定了AXI Master將會驅動哪些AXI Slave,同時決定了AXI Slave的驅動源。

 5、ILA例化。

 ILA在Block Design中需要指定探頭端口(Probe)數目,而端口的數據位寬可以設定成自動調整。

 6、系統復位。

 PS7向可以芯片內其他部分發送全局復位信號,這樣可以使邏輯進入正常的初狀態。

 7、反相器(非門)。

 各個模塊復位信號的極性不同,在PS7復位輸出加反相器就可以得到高有效和低有效的復位信號(也可以在模塊的復位輸入端加反相器)。

 說明:Block Design編輯完成后,需要生成對應的HDL Wrapper,而后進行綜合、布線,這樣就生成了.bit文件和.hdf文件。這兩個文件進行導出,為SDK提供底層支持。

 

二、SDK工程設定。

 1、在創建Application Project時,需要指定HDF和BSP,如圖:(這里我們選擇Vivado Launch SDK自動建立的HDF工程,選擇創建新的BSP工程)

 

 2、使用一個Xilinx例程:lwIP Echo Server。

 這個例程默認將板子地址設定為192.168.1.10(或IPv6地址FE80:0:0:0:20A:35FF:FE00:102),MAC地址設定為00:0a:35:00:01:02。例程中會建立一個lwIP服務器,在服務器端偵聽端口7收到的數據,並將其從這個端口轉發回去。

 3、更改BSP設定。

 選擇stdin和stdout為ps7_uart_0,這樣程序就能驅動UART0進行串口打印消息。

 

 根據實際需要,我們選擇取消DHCP功能。(實際環境中沒有給板子分配一個DHCP服務,所以取消以減小TCP連接前的物理層初始化時間)

 

 注意:更改BSP設定更改后需要進行Re-generate BSP Sources操作。從而更新.h和.c中的工作參數。

 

三、程序說明和實現。

 程序中應用LwIP協議搭建TCP服務器,建立連接后可以對客戶端發來的消息進行回顯,並將消息存到BRAM中。(回顯是例程自帶功能,存BRAM是我們自定義功能)

 程序中指定了回調函數,這個概念很迷,還不懂。只了解它的運行是和一個“母函數”相關聯的。

 1、消息回顯。

 在recv_callback回調函數中,有這樣的語句:

err = tcp_write(tpcb, p->payload, p->len, 1);

 這個語句將當前的payload寫到了發送區,關於TCP協議后面如何處理,這里暫且擱置不談。

 2、存BRAM並在PL端讀出。

 在上述的回調函數里面,對payload進行轉存:

//***    AXI Operation
flush_str_buf(top_buf, N_BUF);
update_buf(top_buf, (char*)p->payload, p->len);  // Load Received String to char String
N_PLD_BYTE = p->len;
N_LOAD = N_PLD_BYTE / 4;
N_REM = N_PLD_BYTE % 4;
axi_addr_offset = 0;
        
for(ite=0; ite<N_LOAD; ite+=1)
{
  for(u8 ite_ch_temp=0; ite_ch_temp<N_BYTE_PER_U32; ite_ch_temp+=1) // Get the 4 Byte
  {
    ch_temp[ite_ch_temp] = top_buf[ite*N_BYTE_PER_U32 + ite_ch_temp];
  }
            
  axi_data_temp = (u32)0;  // Clear
  axi_data_temp ^= ch_temp[0];
  axi_data_temp = axi_data_temp << 8;
  axi_data_temp ^= ch_temp[1];
  axi_data_temp = axi_data_temp << 8;
  axi_data_temp ^= ch_temp[2];
  axi_data_temp = axi_data_temp << 8;
  axi_data_temp ^= ch_temp[3];
  axi_addr = XPAR_BRAM_0_BASEADDR + axi_addr_offset;
  // Xil_Out32(axi_addr, axi_data_temp);
  XBram_Out32(axi_addr, axi_data_temp);
  axi_addr_offset = axi_addr_offset + 4;
}
        
u32 base_byte = ite << 2;
axi_data_temp = (u32)0;  // Clear
for(ite=0; ite<N_REM; ite+=1)
{
  axi_data_temp ^= top_buf[base_byte + ite];
  if(ite < (N_REM-1))
  {
    axi_data_temp = axi_data_temp << 8;
  }
}
axi_addr = XPAR_BRAM_0_BASEADDR + axi_addr_offset;
// Xil_Out32(axi_addr, axi_data_temp);
XBram_Out32(axi_addr, axi_data_temp);
axi_addr_offset = axi_addr_offset + 4;

上述程序完成了載荷的備份,並將載荷存入BRAM。

完成轉存函數是

XBram_Out32(axi_addr, axi_data_temp);

它也是xil_io.h中的寫內存函數:

#define XBram_Out32 Xil_Out32

Xil_Out32()函數定義如下:

/*****************************************************************************/
/**
*
* @brief    Performs an output operation for a memory location by writing the
*           32 bit Value to the the specified address.
*
* @param    Addr contains the address to perform the output operation
* @param    Value contains the 32 bit Value to be written at the specified
*           address.
*
* @return    None.
*
******************************************************************************/
static INLINE void Xil_Out32(UINTPTR Addr, u32 Value)
{
#ifndef ENABLE_SAFETY
    volatile u32 *LocalAddr = (volatile u32 *)Addr;
    *LocalAddr = Value;
#else
    XStl_RegUpdate(Addr, Value);
#endif
}

 

四、實驗結果。

 1、命令行發送信息:GOOD MORNING MY NEIGHBOR

 鍵入消息的下一行得到回顯結果。

 

 

 2、處理器內存中的BRAM的存儲單元:(消息以ASCII保存)

 

 

 3、bram_Reader讀到的值:(圖糊了)

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM