Zynq。プロセッサモジュールとプログラム可能なロジック間のデータ転送

前の記事(Zynqとは何ですか?簡単な概要)で約束した ように、プロセッサモジュールとプログラム可能なロジックの間でデータを転送することについて話しましょう前回の記事では、データを転送する4つの方法について説明しましたが、この記事では、より大きな用途が見つかった2つの方法について説明します。カットの下の詳細。注意して、たくさんの写真!



コンテンツ



1一般情報

2PIOモードでのデータ転送

2.1ハードウェア

2.2ソフトウェア

2.3結果

3DMAでのデータ転送

3.1ハードウェア

3.2ソフトウェア

3.3結果

4結論

5使用したソース



1一般



一般に、プロセッサモジュールとプログラム可能なロジック間のデータ転送は、次の2つのモードで可能です。



  • PIO、GPポートが使用されます。
  • DMA、HPポートが使用されます。


2PIOモードでのデータ転送



PIOモードでは、プロセッサモジュールはプログラム可能なロジックをレジスタのセットとして動作します。一定量のデータを読み書きするには、プロセッサモジュールが常に参加している必要があります。PIOモードでは、すべてのトランザクションはプロセッサモジュールによって開始されます。プログラム可能なロジックの接続には、GPポートの使用が含まれます。ここで、マスターはプロセッサモジュールであり、スレーブはプログラム可能なロジックです。 PIO使用時のプロジェクト構造









2.1ハードウェア



  1. チップタイプxc7z010clg400-1のVivadoでZyboのプロジェクトを作成します。
  2. ブロックデザインを作成します。フローナビゲーター=>ブロックデザインの作成=>名前 "ProcessingSystem" => OK。
  3. フィールドの「+」ボタンまたはCtrl + Iキーボードショートカットを使用して、プロセッサコアを追加します。



  4. [ブロック自動化の実行]ボタン=> [OK]をクリックして、必要なピンを接続しましょう。
  5. . Zynq7 Processing System => Import XPS Setting => => OK => OK.
  6. , . Tools => Create and Package New IP => Next => Create a new AXI4 peripheral => Next => , «PIO_registers» => Next => (4 ), , Lite => Next => Add IP to the repository => Finish.



  7. , IP . , Flow Navigator => IP Catalog.



  8. . Ctrl + I => PIO_registers.



  9. , . PIO_registers => Edit in IP Packager => OK. Vivado .
  10. PIO_registers_v1_0.vhd :



    iSwitches	: in	std_logic_vector( 3 downto 0);
    oLeds		: out	std_logic_vector( 3 downto 0);
    ...
    iSwitches	=> iSwitches,
    oLeds		=> oLeds,
    
          
          





  11. PIO_registers_v1_0_S_AXI.vhd :



    iSwitches	: in	std_logic_vector( 3 downto 0);
    oLeds		: out	std_logic_vector( 3 downto 0);
    
          
          





  12. :



    signal	SwitchesReg	: std_logic_vector(31 downto 0);
    ...
    process (SwitchesReg, slv_reg1, slv_reg2, slv_reg3, axi_araddr, S_AXI_ARESETN, slv_reg_rden)
    variable loc_addr :std_logic_vector(OPT_MEM_ADDR_BITS downto 0);
    begin
        -- Address decoding for reading registers
        loc_addr := axi_araddr(ADDR_LSB + OPT_MEM_ADDR_BITS downto ADDR_LSB);
        case loc_addr is
          when b"00" =>
            reg_data_out <= SwitchesReg;
          when b"01" =>
            reg_data_out <= slv_reg1;
          when b"10" =>
            reg_data_out <= slv_reg2;
          when b"11" =>
            reg_data_out <= slv_reg3;
          when others =>
            reg_data_out  <= (others => '0');
        end case;
    end process;
    
    process (S_AXI_ACLK) begin
    	if (rising_edge(S_AXI_ACLK)) then
    		if (S_AXI_ARESETN = '0') then
    			SwitchesReg <= (others => '0');
    		else
    			SwitchesReg( 3 downto 0) <= iSwitches;
    		end if;
    	end if;
    end process;
    	
    process (S_AXI_ACLK) begin
    	if (rising_edge(S_AXI_ACLK)) then
    		if (S_AXI_ARESETN = '0') then
    			oLeds <= (others => '0');
    		else
    			oLeds <= slv_reg1( 3 downto 0);
    		end if;
    	end if;
    end process;
    
          
          





  13. vhd , Package IP – PIO_registers. . Compatibility Life Cycle Production. File Groups => Merge changes from File Group Wizard. Customization Parameters => Merge changes from Customization Parameters Wizard. Review and Package => Re-Package IP => Yes. Vivado .
  14. Block Design Report IP Status, Upgrade Selected => OK => Skip => OK.



  15. . Run Connection Automation => OK.



  16. block design’a. , => Make External.



  17. iSwitches_0 => iSwitches. oLeds_0 => oLeds.



  18. => Tools => Validate Design => Ok.
  19. File => Save Block Design.
  20. block design , Flow Navigator => Project Manager.
  21. , block design’a. ProcessingSystem.bd, => View Instantiation Template.



  22. vhd top- block design. File => Add Sources => Add or create design sources => Next => Create File => => OK => Finish => OK => Yes.
  23. :



    entity PioTransfer is
    port	(	DDR_addr		: inout std_logic_vector(14 downto 0 );
    		DDR_ba			: inout std_logic_vector( 2 downto 0 );
    		DDR_cas_n		: inout std_logic;
    		DDR_ck_n		: inout std_logic;
    		DDR_ck_p		: inout std_logic;
    		DDR_cke			: inout std_logic;
    		DDR_cs_n		: inout std_logic;
    		DDR_dm			: inout std_logic_vector( 3 downto 0 );
    		DDR_dq			: inout std_logic_vector(31 downto 0 );
    		DDR_dqs_n		: inout std_logic_vector( 3 downto 0 );
    		DDR_dqs_p		: inout std_logic_vector( 3 downto 0 );
    		DDR_odt			: inout std_logic;
    		DDR_ras_n		: inout std_logic;
    		DDR_reset_n		: inout std_logic;
    		DDR_we_n		: inout std_logic;
    		FIXED_IO_ddr_vrn	: inout std_logic;
    		FIXED_IO_ddr_vrp	: inout std_logic;
    		FIXED_IO_mio		: inout std_logic_vector( 53 downto 0 );
    		FIXED_IO_ps_clk		: inout std_logic;
    		FIXED_IO_ps_porb	: inout std_logic;
    		FIXED_IO_ps_srstb	: inout std_logic;
    		-- Control
    		iSwitches		: in	std_logic_vector( 3 downto 0 );
    		oLeds			: out	std_logic_vector( 3 downto 0 ) );
    end PioTransfer;
    
    architecture Behavioral of PioTransfer is
    
    begin
    
    PS : entity WORK.ProcessingSystem
    port map	(	DDR_addr		=> DDR_addr,
    			DDR_ba			=> DDR_ba,
    			DDR_cas_n		=> DDR_cas_n,
    			DDR_ck_n		=> DDR_ck_n,
    			DDR_ck_p		=> DDR_ck_p,
    			DDR_cke			=> DDR_cke,
    			DDR_cs_n		=> DDR_cs_n,
    			DDR_dm			=> DDR_dm,
    			DDR_dq			=> DDR_dq,
    			DDR_dqs_n		=> DDR_dqs_n,
    			DDR_dqs_p		=> DDR_dqs_p,
    			DDR_odt			=> DDR_odt,
    			DDR_ras_n		=> DDR_ras_n,
    			DDR_reset_n		=> DDR_reset_n,
    			DDR_we_n		=> DDR_we_n,
    			FIXED_IO_ddr_vrn	=> FIXED_IO_ddr_vrn,
    			FIXED_IO_ddr_vrp	=> FIXED_IO_ddr_vrp,
    			FIXED_IO_mio		=> FIXED_IO_mio,
    			FIXED_IO_ps_clk		=> FIXED_IO_ps_clk,
    			FIXED_IO_ps_porb	=> FIXED_IO_ps_porb,
    			FIXED_IO_ps_srstb	=> FIXED_IO_ps_srstb,
    			-- Control
    			iSwitches		=> iSwitches,
    			oLeds			=> oLeds );
    end Behavioral;
          
          





  24. . File => Add sources => Add or create constrains => Next => Create File => => OK => Finish.



  25. :



    #Switches
    set_property PACKAGE_PIN G15 [get_ports {iSwitches[0]}]
    set_property PACKAGE_PIN P15 [get_ports {iSwitches[1]}]
    set_property PACKAGE_PIN W13 [get_ports {iSwitches[2]}]
    set_property PACKAGE_PIN T16 [get_ports {iSwitches[3]}]
    set_property IOSTANDARD LVCMOS33 [get_ports {iSwitches[*]}]
    
    #LEDs
    #IO_L23P_T3_35
    set_property PACKAGE_PIN M14 [get_ports {oLeds[0]}]
    set_property PACKAGE_PIN M15 [get_ports {oLeds[1]}]
    set_property PACKAGE_PIN G14 [get_ports {oLeds[2]}]
    set_property PACKAGE_PIN D18 [get_ports {oLeds[3]}]
    set_property IOSTANDARD LVCMOS33 [get_ports {oLeds[*]}] 
          
          





  26. . Flow Navigator => Generate Bitstream => OK. , , .
  27. . File => Export => Export Hardware => => OK. .xsa





2.2



次に、プログラム可能なロジックからデータを読み取り、プログラム可能なロジックにデータを書き込むプロセッサモジュール上で実行されるアプリケーションを作成する必要があります。 Vitis開発環境を開始し、Hello Worldテンプレートを使用してアプリケーションを作成する必要があります。この例は、前の記事[1]に示されています。



プロセッサモジュールからアクセスするために作成されたカーネルのアドレスは、Vivadoで表示できます。 Flow Navigator => Open Block Design => AddressEditorタブで。この場合、アドレスは0x43C0_0000です。レジスタはこのアドレスにあり、そこに属性が格納されており、スイッチはどの状態にあります。したがって、アドレス0x43C0_0004には、LEDに接続されているレジスタがあります。



Vitisで、helloworld.cファイルを開き、次のように入力します。



int main()
{
	init_platform();

	u32 Status = 0x00;
	u32 Command = 0x00;

	xil_printf("Hello World\n\r");

	while (1)
	{

		Status = Xil_In32(0x43C00000);

		xil_printf("Status %x\n\r", Status);

		if (Status == 0x01 || Status == 0x02 || Status == 0x04 || Status == 0x08)
		{
			Command = 0x01;
		}
		else if (Status == 0x03 || Status == 0x5 || Status == 0x06 || Status == 0x9 || Status == 0xA || Status == 0x0C)
		{
			Command = 0x03;
		}
		else if (Status == 0x7 || Status ==  0x0B || Status == 0x0D || Status == 0x0E)
		{
			Command = 0x7;
		}
		else if (Status == 0x0F)
		{
			Command = 0x0F;
		}
		else
		{
			Command = 0x00;
		}

		xil_printf("Command %x\n\r", Command);

		Xil_Out32(0x43C00004, Command);

		usleep(1000000);
	}

	cleanup_platform();
	return 0;
} 
      
      





ここで、Xil_In32関数を使用してプログラム可能なロジックから4バイトのデータを読み取り、Xil_Out32を使用して4バイトのデータをプログラム可能なロジックに書き込みます。



2.3結果



アプリケーションをアセンブルし、ファームウェアファイルを作成して、ボードにアップロードします。前回の記事[1]で説明されています。



起動し、com-portモニターを確認します。



Xilinx First Stage Boot Loader 
Release 2019.2	Dec  9 2020-15:16:52
Silicon Version 3.1
Boot mode is QSPI
SUCCESSFUL_HANDOFF
FSBL Status = 0x1
Hello World
Status 0
Command 0
Status 8
Command 1
Status C
Command 3
Status D
Command 7
Status F
Command F

      
      





すべてが正しく機能します。



したがって、PIOモードでプログラマブルロジックにアクセスするには、プロセッサモジュールがイニシエータであるプログラマブルロジックのプロセッサモジュールとの通信インターフェイスの1つを実装する必要があります。このインターフェイスは、GPポートによってのみ表されます。



プログラム可能なロジックへの要求がGPポートを介してどれだけ迅速に処理されるかを見てみましょう。これを行うには、プロセッサモジュールで実行されているアプリケーションで、プログラム可能なロジックのレジスタに連続して複数のエントリを追加し、デバッガにプルされたバス信号を使用してプログラム可能なロジックのトランザクション間の時間を測定します。



Axi-Liteバスが100MHzで実行されている場合、要求間の一時停止は平均23クロックサイクルです。バス周波数を200MHzに変えてみましょう。リクエスト間の一時停止は平均33サイクルになります。



合計4バイトのデータが100MHzで23クロック送信されます。速度は次のとおりです。32/(23 * 10ns)= 139 130434ビット/秒≈135869Kbps≈132Mbps≈16MB/秒。

合計4バイトのデータが200MHzで33クロック送信されます。速度は32 /(33 * 5ns)= 193939393bps≈189393Kbps≈184Mbps≈23Mbpsです。

したがって、23 MB / sの速度を達成できますが、プロセッサモジュールが常に関与します。



プロジェクト: github.com/Finnetrib/PioTransfer



3DMAモードでのデータ転送



DMAモードでのデータ転送は、プロセッサモジュールがデータ交換パラメータを設定し、交換に直接参加しないことを意味します。したがって、2つの目標が達成されます。それは、プロセッサモジュールの負荷を軽減することと、データ処理速度を向上させることです。この代償は、ハードウェアの複雑さです。



Zynqでは、DMA機能を実装する複数のipコアを使用できます。この記事では、コアAXIDMA [2]について説明します。



AXI DMAには、MM2SとS2MMの2つのチャネルがあります。チャネルMM2S(ストリームにメモリマップ)は、プロセッサモジュールからプログラム可能なロジックにデータを転送するために使用されます。 S2MM(Stream to memory-mapped)チャネルは、プログラム可能なロジックからプロセッサモジュールにデータを転送するために使用されます。チャネルは互いに独立して機能します。



AXIDMAには2つの使用例があります。



  • 直接登録モード
  • スキャッター/ギャザーモード


ダイレクトレジスタモードでは、1セットのレジスタを使用します。これにより、1つのバッファをプログラム可能なロジックからプロセッサモジュールに、またはその逆に転送できます。たとえば、プログラム可能なロジックからプロセッサモジュールにデータバッファを転送するには、アドレスフィールドとバッファサイズフィールドに入力して、DMAを開始する必要があります。その結果、DMAはプロセッサユニットの1つのバッファを満たし、停止します。



スキャッター/ギャザーモードは、記述子のリストを使用します。DMAは、記述子に記述されているバッファーを処理し、次の記述子に記述されているバッファーの処理に進みます。



3.1ハードウェア





DMAを使用する場合のプロジェクト構造



記述子のリストがプログラム可能なロジックに格納されている場合のバリアントについて考えてみましょう。DMAブロックには、プロセッサユニットのGPポートに接続する制御ポートがあります。プロセッサRAMへのアクセスに使用されるHPポートもあります。記述子リストは記述子メモリに保存されます。記述子メモリには、DMAとプロセッサユニットの両方からアクセスできます。プロセッサモジュールが記述子を入力し、DMAが記述子を読み取ります。



  1. ブロックデザインを作成します。フローナビゲーター=>ブロックデザインの作成=>名前 "ProcessingSystem" => OK。
  2. フィールドの「+」ボタンまたはCtrl + Iキーボードショートカットを使用して、プロセッサコアを追加します。
  3. [ブロック自動化の実行]ボタン=> [OK]をクリックして、必要なピンを接続しましょう。
  4. . Zynq7 Processing System => Import XPS Setting => => OK => OK
  5. AXI Direct Memory Access, AXI BRAM Controller, Block Memory Generator.



  6. AXI Direct Memory Access, . «Enable Scatter Gather Engine» , . «Enable Control / Status Stream» AXI Ethernet, . «With of Buffer Length Register» , . 20, 2^20 = 1 048 576 . «Address With» . 32 . «Enable Read Channel» «Enable Write Channel» . «Enable Single AXI4 Data interface» , . «OK» .



  7. AXI BRAM Controller. «Number of BRAM Interfaces» 1. «OK» .



  8. AXI BRAM Controller.
  9. Block Memory Generator. «Memory Type» «True Dual Port RAM». «OK» .



  10. . «Run Connection Automation» => axi_bram_ctrl_0 BRAM_PORTA => axi_bram_ctrl_1 BRAM_PORTA => OK.



  11. . «Run Connection Automation» => axi_bram_ctrl_0 S_AXI => Master Interface /processing_system7_0/M_AXI_GP0 => OK. , .



  12. DMA. «Run Connection Automation» => axi_bram_ctrl_1 S_AXI => Master Interface /axi_dma_0/M_AXI_SG => OK. , DMA .



  13. DMA . «Run Connection Automation» => axi_dma_0 S_AXI_LITE => OK.



  14. – HP . Zynq7 Processing System => PS-PL Configuration => HP Slave AXI Interface => S AXI HP0 Interface.





    Interrupts => Fabric Interrupts => PL-PS Interrupts Ports => Fabric Interrupts => IRQ_F2P => OK.



  15. DMA . «Run Connection Automation» => processing_system7_0 S_AXI_HP0 => Master Interface /axi_dma_0/M_AXI => OK.



  16. DMA . Concat + Ctrl + I.
  17. mm2s_introut DMA, . mm2s_introut In0 Concat. , , .



  18. s2mm_introut, In1 Concat.
  19. dout Concat IRQ_F2P Zynq7 Processing System.
  20. DMA . DMA . Block Design, . Create Port Ctrl + K. , => OK.



  21. FCLK_CLK0 Zynq7 Processing System.
  22. . peripheral_reset Processor System Reset => => Make External.
  23. , , .



  24. DMA. S_AXIS_S2MM AXI Direct Memory Access => => Make External.
  25. , , .



  26. DMA. M_AXIS_MM2S AXI Direct Memory Access => => Make External.
  27. , , .



  28. S_AXIS_S2MM M_AXIS_MM2S AXI Direct Memory Access. «Run Connection Automation» => m_axi_mm2s_aclk m_axi_s2mm_aclk => OK
  29. , DMA . . Address Editor => processing_system7_0 / Data / axi_bram_ctrl_0 => Offset Address 0x4000_0000 => Range 32K. axi_dma_0 / Data_SG / axi_bram_ctrl_1 => Offset Address 0x4000_0000 => Range 32K.



  30. Tools => Validate Design => OK. :



  31. File => Save Block Design.
  32. block design , Flow Navigator => Project Manager.
  33. , block design’a. ProcessingSystem.bd, => View Instantiation Template.
  34. vhd top- block design. File => Add Sources => Add or create design sources => Next => Create File => => OK => Finish => OK => Yes.





  35. :

    entity DmaTransfer is
    port	(	DDR_addr		: inout std_logic_vector(14 downto 0);
    		DDR_ba			: inout std_logic_vector( 2 downto 0);
    		DDR_cas_n		: inout std_logic;
    		DDR_ck_n		: inout std_logic;
    		DDR_ck_p		: inout std_logic;
    		DDR_cke			: inout std_logic;
    		DDR_cs_n		: inout std_logic;
    		DDR_dm			: inout std_logic_vector( 3 downto 0);
    		DDR_dq			: inout std_logic_vector(31 downto 0);
    		DDR_dqs_n		: inout std_logic_vector( 3 downto 0);
    		DDR_dqs_p		: inout std_logic_vector( 3 downto 0);
    		DDR_odt			: inout std_logic;
    		DDR_ras_n		: inout std_logic;
    		DDR_reset_n		: inout std_logic;
    		DDR_we_n		: inout std_logic;
    		FIXED_IO_ddr_vrn	: inout std_logic;
    		FIXED_IO_ddr_vrp	: inout std_logic;
    		FIXED_IO_mio		: inout std_logic_vector(53 downto 0);
    		FIXED_IO_ps_clk		: inout std_logic;
    		FIXED_IO_ps_porb	: inout std_logic;
    		FIXED_IO_ps_srstb	: inout std_logic );
    end DmaTransfer;
    
    architecture Behavioral of DmaTransfer is
    
    	signal	RxData			: std_logic_vector(31 downto 0);
    	signal	RxKeep			: std_logic_vector( 3 downto 0);
    	signal	RxLast			: std_logic;
    	signal	RxValid			: std_logic;
    	signal	RxReady			: std_logic;
    	signal	TxData			: std_logic_vector(31 downto 0);
    	signal	TxKeep			: std_logic_vector( 3 downto 0);
    	signal	TxLast			: std_logic;
    	signal	TxValid			: std_logic;
    	signal	TxReady			: std_logic;
    	signal	clk			: std_logic;
    	signal	rst			: std_logic;
    	signal	FifoDataW		: std_logic_vector(36 downto 0);
    	signal	FifoWrite		: std_logic;
    	signal	FifoRead		: std_logic;
    	signal	FifoDataR		: std_logic_vector(36 downto 0);
    	signal	FifoEmpty		: std_logic;
    	signal	FifoFull		: std_logic;
    
    begin
    
    	PS : entity WORK.ProcessingSystem
    	port map	(	DDR_addr		=> DDR_addr,
    				DDR_ba			=> DDR_ba,
    				DDR_cas_n		=> DDR_cas_n,
    				DDR_ck_n		=> DDR_ck_n,
    				DDR_ck_p		=> DDR_ck_p,
    				DDR_cke			=> DDR_cke,
    				DDR_cs_n		=> DDR_cs_n,
    				DDR_dm			=> DDR_dm,
    				DDR_dq			=> DDR_dq,
    				DDR_dqs_n		=> DDR_dqs_n,
    				DDR_dqs_p		=> DDR_dqs_p,
    				DDR_odt			=> DDR_odt,
    				DDR_ras_n		=> DDR_ras_n,
    				DDR_reset_n		=> DDR_reset_n,
    				DDR_we_n		=> DDR_we_n,
    				FIXED_IO_ddr_vrn	=> FIXED_IO_ddr_vrn,
    				FIXED_IO_ddr_vrp	=> FIXED_IO_ddr_vrp,
    				FIXED_IO_mio		=> FIXED_IO_mio,
    				FIXED_IO_ps_clk		=> FIXED_IO_ps_clk,
    				FIXED_IO_ps_porb	=> FIXED_IO_ps_porb,
    				FIXED_IO_ps_srstb	=> FIXED_IO_ps_srstb,
    				-- Dma Channel
    				iDmaRx_tdata		=> RxData,
    				iDmaRx_tkeep		=> RxKeep,
    				iDmaRx_tlast		=> RxLast,
    				iDmaRx_tready		=> RxReady,
    				iDmaRx_tvalid		=> RxValid,
    				oDmaTx_tdata		=> TxData,
    				oDmaTx_tkeep		=> TxKeep,
    				oDmaTx_tlast		=> TxLast,
    				oDmaTx_tready		=> TxReady,
    				oDmaTx_tvalid		=> TxValid,
    				-- System
    				oZynqClk		=> clk,
    				oZynqRst(0)		=> rst );
    	
    	FifoDataW(31 downto  0) <= not TxData;
    	FifoDataW(35 downto 32) <= TxKeep;
    	FifoDataW(	    36) <= TxLast;
    	
    	FifoWrite <= TxValid and not FifoFull;
    	
    	TxReady <= not FifoFull;
    	
    	EchFifo : entity WORK.SyncFifoBram37x1024
    	port map	(	clk		=> clk,
    				srst		=> rst,
    				din		=> FifoDataW,
    				wr_en		=> FifoWrite,
    				rd_en		=> FifoRead,
    				dout		=> FifoDataR,
    				full		=> open,
    				empty		=> FifoEmpty,
    				prog_full	=> FifoFull );
    
    	RxData <= FifoDataR(31 downto  0);
    	RxKeep <= FifoDataR(35 downto 32);
    	RxLast <= FifoDataR(36);
    	
    	RxValid <= not FifoEmpty;
    	
    	FifoRead <= RxReady;
    
    end Behavioral; 
          
          



  36. . Flow Navigator => Generate Bitstream => OK. , , .
  37. . File => Export => Export Hardware => => OK. .xsa







3.2



次に、プロセッサモジュールで実行されるアプリケーションを作成する必要があります。Vitis開発環境を開始し、Hello Worldテンプレートを使用してアプリケーションを作成する必要があります。この例は、前の記事に示されています。



Axi DMAの記述子の形式は、カーネルドキュメント[2]に記載されています。記述子のサイズは52バイトですが、記述子が配置されているアドレスは64バイトに揃える必要があります。



記述子の形式について簡単に説明します。



  • NXTDESC-次の記述子のアドレス。
  • NXTDESC_MSB-次の記述子アドレスの上位32ビット。
  • BUFFER_ADDRESS-バッファアドレス;
  • BUFFER_ADDRESS_MSB-バッファアドレスの上位32ビット。
  • 予約済み-使用されていません。
  • 予約済み-使用されていません。
  • CONTROL-バッファサイズ、パケットの開始と終了の符号を設定します。
  • STATUS-受信/送信、処理済み/未処理のバイト数を示します。
  • APP0-「コントロール/ステータスストリーム」チャネルを操作するために使用されます。
  • APP1-「コントロール/ステータスストリーム」チャネルを操作するために使用されます。
  • APP2-コントロール/ステータスストリームチャネルを操作するために使用されます。
  • APP3-「コントロール/ステータスストリーム」チャネルを操作するために使用されます。
  • APP4-コントロール/ステータスストリームチャネルを操作するために使用されます。


プロセッサモジュールからアクセスするためのプログラム可能なロジックのアドレスは、Vivadoで表示できます。Flow Navigator => Open Block Design => AddressEditorタブで。この場合、DMAアドレスは0x4040_0000です。記述子のメモリ領域の先頭のアドレスは0x4000_0000です。



  1. Vitisで、helloworld.cファイルを開き、次のライブラリを含めます



    #include <xil_io.h>
    #include "sleep.h"
    #include "xil_cache.h"
    #include "xil_mem.h"
    
          
          



  2. , 64 . , 32 32 768 / 64 = 512 . 256 256 .



    #define DESC_COUNT 256
    ...
     /** Descriptors for receive */
    struct SGDesc RxDesc[DESC_COUNT];
    
    /** Descriptors for transmit */
    struct SGDesc TxDesc[DESC_COUNT];
    
          
          



  3. , , .



    /** Flush Cache */
    Xil_DCacheFlush();
    
    /** Disable Cache */
    Xil_DCacheDisable();
    
          
          





  4. , .



    for (u16 desc = 0; desc < DESC_COUNT; desc++)
    {
    	for (u32 i = 0; i < BUFFER_SIZE; i++)
    	{
    		TxBuffer[desc][i] = desc + i;
    	}
    }
    
          
          



  5. .



    for (u16 i = 0; i < DESC_COUNT; i++)
    {
    	TxDesc[i].NXTDESC = &TxDesc[i];
    	TxDesc[i].NXTDESC_MSB = 0x0;
    	TxDesc[i].BUFFER_ADDRESS = &TxBuffer[i][0];
    	TxDesc[i].BUFFER_ADDRESS_MSB = 0x0;
    	TxDesc[i].RESERVED0 = 0x0;
    	TxDesc[i].RESERVED1 = 0x0;
    	TxDesc[i].CONTROL = 0xC000000 + sizeof(TxBuffer[i]);
    	TxDesc[i].STATUS = 0x0;
    	TxDesc[i].APP0 = 0x0;
    	TxDesc[i].APP1 = 0x0;
    	TxDesc[i].APP2 = 0x0;
    	TxDesc[i].APP3 = 0x0;
    	TxDesc[i].APP4 = 0x0;
    }
    
          
          



  6. , .



    DescAddr = 0x40000000;
    for (u16 i = 0; i < DESC_COUNT; i++)
    {
    	Xil_MemCpy(DescAddr, &TxDesc[i], sizeof(TxDesc[i]));
    	DescAddr += 0x40;
    }
    
          
          



  7. .

    /** Write pointer to next pointer */
    DescAddr = 0x40000000;
    for (u16 i = 0; i < DESC_COUNT - 1; i++)
    {
    	Xil_Out32(DescAddr, DescAddr + 0x40);
    	DescAddr += 0x40;
    }
    
    /** Write pointer for last descriptor */
    Xil_Out32(DescAddr, DescAddr);
    
          
          



  8. .



    /** Fill descriptor to receive */
    for (u16 i = 0; i < DESC_COUNT; i++)
    {
    	RxDesc[i].NXTDESC = &RxDesc[i];
    	RxDesc[i].NXTDESC_MSB = 0x0;
    	RxDesc[i].BUFFER_ADDRESS = &RxBuffer[i][0];
    	RxDesc[i].BUFFER_ADDRESS_MSB = 0x0;
    	RxDesc[i].RESERVED0 = 0x0;
    	RxDesc[i].RESERVED1 = 0x0;
    	RxDesc[i].CONTROL = sizeof(RxBuffer[i]);
    	RxDesc[i].STATUS = 0x0;
    	RxDesc[i].APP0 = 0x0;
    	RxDesc[i].APP1 = 0x0;
    	RxDesc[i].APP2 = 0x0;
    	RxDesc[i].APP3 = 0x0;
    	RxDesc[i].APP4 = 0x0;
    }
    
    /** Copy receive descriptor for memory of descriptors */
    DescAddr = 0x40000000 + 0x4000;
    for (u16 i = 0; i < DESC_COUNT; i++)
    {
    	Xil_MemCpy(DescAddr, &RxDesc[i], sizeof(RxDesc[i]));
    	DescAddr += 0x40;
    }
    
    /** Write pointer to next pointer */
    DescAddr = 0x40000000 + 0x4000;
    for (u16 i = 0; i < DESC_COUNT - 1; i++)
    {
    	Xil_Out32(DescAddr, DescAddr + 0x40);
    	DescAddr += 0x40;
    }
    
    /** Write pointer for last descriptor */
    Xil_Out32(DescAddr, DescAddr); 
    
          
          



  9. DMA . DMA .



    /** Reset DMA and setup */
    /** MM2S */
    Xil_Out32(0x40400000, 0x0001dfe6);
    Xil_Out32(0x40400000, 0x0001dfe2);
    
    /** S2MM */
    Xil_Out32(0x40400030, 0x0001dfe6);
    Xil_Out32(0x40400030, 0x0001dfe2);
    
    /** PL => PS */
    Xil_Out32(0x4040003c, 0x00000000);
    Xil_Out32(0x40400038, 0x40004000);
    Xil_Out32(0x40400030, 0x0001dfe3);
    Xil_Out32(0x40400044, 0x00000000);
    Xil_Out32(0x40400040, 0x40007FC0);
    
    /** PS => PL */
    Xil_Out32(0x4040000C, 0x00000000);
    Xil_Out32(0x40400008, 0x40000000);
    Xil_Out32(0x40400000, 0x0001dfe3);
    Xil_Out32(0x40400014, 0x00000000);
    Xil_Out32(0x40400010, 0x40003FC0); 
    
          
          



  10. , . , , .



    /** Wait ready in last descriptor */
    while (1)
    {
    	status = Xil_In32(0x40003FDC);
    	if ((status & 0x80000000) == 0x80000000)
    	{
    		break;
    	}
    	else
    	{
    		countWait++;
    		usleep(100);
    	}
    }
    
    xil_printf("Time %x \n\r", countWait);
    
          
          





3.3



アプリケーションをアセンブルし、ファームウェアファイルを作成して、ボードにアップロードします。前回の記事[1]で説明されています。



起動し、com-portモニターを確認します。



Xilinx First Stage Boot Loader
Release 2019.2  Dec 16 2020-15:11:44
Silicon Version 3.1
Boot mode is QSPI
SUCCESSFUL_HANDOFF
FSBL Status = 0x1
Hello World
Time 10F

      
      





したがって、プロセッサモジュールとプログラム可能なロジックの間でデータを交換するには、プロセッサモジュールとの通信インターフェイスの1つをプログラム可能なロジックに実装する必要があります。ここで、イニシエータはプログラム可能なロジックです。このようなインターフェイスは、ポートGP、HP、ACPで表されます。前回の記事[1]では、それらはすべて考慮されていました。



データ転送速度を計算してみましょう:(256回* 102400バイト)/(271 *100μs)≈967321033バイト/s≈944649KB/s≈922MB/ s。

ビットレート7,738,568,264bps。

理論速度は32ビット* 250 MHz = 8,000,000,000ビット/秒です。



また、記述子をプログラマブルロジックメモリではなく、プロセッサモジュールに接続されたRAMに格納することもできます。この場合、M_AXI_SGポートはHPZynqポートに接続します。



データおよびプロセッサのRAM内の記述子へのDMAアクセスに異なるHPポートが使用される場合の最初のオプションを考えてみましょう。プログラム可能なロジックのファームウェアを変更して、次のスキームを取得しましょう。 異なるポート介したデータと記述子へのアクセスアプリケーションのソースコードは提供しません。唯一の違いは、記述子をプログラム可能なロジックメモリにコピーする必要がないことです。ただし、各記述子のアドレスが64バイトにアラインされているという条件を考慮する必要があります。













アプリケーションを起動した後、com-portモニターで、データバッファーをコピーするための実行時間が変更されていないことがわかります。これも271 *100μsです。



同じポートを使用してプロセッサのRAM内のDMAと記述子にアクセスする場合は、2番目のオプションを検討してください。プログラム可能なロジックのファームウェアを変更して、次のスキームを取得しましょう。 同じポートを介したデータと記述子へのアクセスアプリケーションのソースコードは、以前のバージョンと比較して変更されていません。 アプリケーションを起動した後、com-portモニターに、バッファーコピー操作の新しい実行時間が表示されます:398 *100μs。















その結果、処理速度は(256回* 102400バイト)/(398 *100μs)≈658653266バイト/s≈643216KB/s≈628MB/ sになります。

ビットレート5269226128ビット/秒。



プロジェクト: github.com/Finnetrib/DmaTransfer



4結論



この記事では、プロセッサモジュールとプログラマブルロジック間のデータ交換の2つの実装について説明しました。PIOモードは実装が簡単で、最大23 MB / sの速度を得ることができます。DMAモードはやや複雑ですが、速度も高く、最大628 MB / sです。



使用された5つのソース



  1. habr.com/ru/post/508292
  2. www.xilinx.com/support/documentation/ip_documentation/axi_dma/v7_1/pg021_axi_dma.pdf



All Articles