Zynq. Transfer data antara modul prosesor dan logika yang dapat diprogram

Seperti yang dijanjikan di artikel sebelumnya ( Apa itu Zynq? Tinjauan singkat ), mari kita bicara tentang mentransfer data antara modul prosesor dan logika yang dapat diprogram. Artikel sebelumnya menyebutkan empat metode transfer data, artikel ini akan membahas dua metode yang telah menemukan aplikasi yang lebih besar. Detail di bawah potongan. Hati-hati, banyak gambar!



Kandungan



1 Informasi Umum

2 Mentransfer data dalam mode PIO

2.1 Perangkat Keras

2.2 Perangkat Lunak

2.3 Hasil

3 Mentransfer data di DMA

3.1 Perangkat Keras

3.2 Perangkat Lunak

3.3 Hasil

4 Kesimpulan

5 Sumber yang digunakan



1. Umum



Secara umum, transfer data antara modul prosesor dan logika yang dapat diprogram dimungkinkan dalam dua mode:



  • PIO , menggunakan port GP.
  • DMA , port HP digunakan.


2 Transfer data dalam mode PIO



Dalam mode PIO, modul prosesor beroperasi dengan logika yang dapat diprogram sebagai satu set register. Untuk menulis atau membaca sejumlah data, Anda memerlukan partisipasi konstan dari modul prosesor. Dalam mode PIO, semua transaksi dimulai oleh modul prosesor. Menghubungkan logika yang dapat diprogram melibatkan penggunaan port GP, di mana Master adalah modul prosesor, Slave adalah logika yang dapat diprogram. Struktur proyek saat menggunakan PIO









2.1 Perangkat Keras



  1. Kami membuat proyek untuk Zybo di Vivado, jenis chip xc7z010clg400-1.
  2. Buat desain blok. Dalam Flow Navigator => Buat Desain Blok => nama "ProcessingSystem" => OK.
  3. Menggunakan tombol "+" di lapangan atau pintasan keyboard Ctrl + I, tambahkan inti prosesor.



  4. Mari hubungkan pin yang diperlukan dengan mengklik tombol Run Block Automation => 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



Sekarang Anda perlu menulis aplikasi yang berjalan pada modul prosesor yang akan membaca data dari logika yang dapat diprogram dan menulis data ke logika yang dapat diprogram. Anda perlu memulai lingkungan pengembangan Vitis dan membuat aplikasi menggunakan template Hello World, contohnya ditunjukkan di artikel sebelumnya [1].



Alamat kernel yang dibuat untuk akses dari modul prosesor dapat dilihat di Vivado. Dalam Flow Navigator => Buka Desain Blok => Tab Editor Alamat. Dalam kasus ini, alamatnya adalah 0x43C0_0000. Sebuah register terletak di alamat ini, di mana atribut disimpan, dalam status apa sakelar tersebut. Karenanya, di alamat 0x43C0_0004 ada register yang terhubung ke LED.



Di Vitis, buka file helloworld.c dan isi:



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;
} 
      
      





Di mana fungsi Xil_In32 digunakan untuk membaca 4 byte data dari logika terprogram, dan Xil_Out32, masing-masing, untuk menulis 4 byte data ke logika yang dapat diprogram.



2.3 Hasil



Bangun aplikasi, buat file firmware dan unggah ke papan. Dijelaskan di artikel sebelumnya [1].



Kami mulai, lihat di monitor 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

      
      





Semuanya bekerja dengan benar.



Jadi, untuk mengakses logika yang dapat diprogram dalam mode PIO, perlu untuk mengimplementasikan salah satu antarmuka komunikasi dengan modul prosesor dalam logika yang dapat diprogram, di mana modul prosesor adalah inisiatornya. Antarmuka ini hanya diwakili oleh port GP.



Mari kita lihat seberapa cepat permintaan ke logika terprogram diproses melalui port GP. Untuk melakukan ini, dalam aplikasi yang berjalan pada modul prosesor, tambahkan beberapa entri berturut-turut ke register dalam logika terprogram, dan ukur waktu antara transaksi dalam logika terprogram menggunakan sinyal bus yang ditarik ke debugger.



Saat bus Axi-Lite berjalan pada 100 MHz, jeda di antara permintaan rata-rata adalah 23 siklus clock. Mari kita ubah frekuensi bus menjadi 200 MHz. Jeda antar permintaan menjadi rata-rata 33 siklus.



Secara total, 4 byte data ditransmisikan pada 100 MHz selama 23 clock. Kecepatannya adalah: 32 / (23 * 10ns) = 139.130.434 bps ≈ 135.869 kbps ≈ 132 Mbps ≈ 16 MB / s.

Secara total, 4 byte data ditransmisikan pada 200 MHz selama 33 clock. Kecepatannya adalah 32 / (33 * 5ns) = 193.939393 bit / s ≈ 189.393 Kbps ≈ 184 Mbps ≈ 23 MB / s.

Dengan demikian, Anda dapat mencapai kecepatan 23 MB / s, tetapi dengan partisipasi konstan dari modul prosesor.



Proyek: github.com/Finnetrib/PioTransfer



3 Transfer data dalam mode DMA



Transfer data dalam mode DMA menyiratkan bahwa modul prosesor mengkonfigurasi parameter pertukaran data dan tidak berpartisipasi secara langsung dalam pertukaran. Dengan demikian, dua tujuan tercapai: mengurangi beban pada modul prosesor dan meningkatkan kecepatan pemrosesan data. Harga untuk ini adalah kerumitan perangkat keras.



Di Zynq, dimungkinkan untuk menggunakan beberapa ip-core yang mengimplementasikan fungsi DMA. Artikel ini akan membahas inti AXI DMA [2].



AXI DMA memiliki dua saluran MM2S dan S2MM. Channel MM2S (Memory-mapped to stream) digunakan untuk mentransfer data dari modul prosesor ke logika yang dapat diprogram. Saluran S2MM (Stream ke memory-mapped) digunakan untuk mentransfer data dari logika yang dapat diprogram ke modul prosesor. Saluran bekerja secara independen satu sama lain.



AXI DMA memiliki dua kasus penggunaan:



  • Mode Pendaftaran Langsung
  • Mode Scatter / Gather


Mode Register Langsung menggunakan satu set register, yang memungkinkan satu buffer ditransfer dari logika yang dapat diprogram ke modul prosesor dan sebaliknya. Misalnya, untuk mentransfer buffer data dari logika yang dapat diprogram ke modul prosesor, Anda perlu mengisi kolom alamat dan ukuran buffer dan memulai DMA. Akibatnya, DMA akan mengisi satu buffer di unit prosesor dan berhenti.



Mode Scatter / Gather menggunakan daftar deskriptor. DMA memproses buffer yang dijelaskan dalam deskriptor dan melanjutkan untuk memproses buffer yang dijelaskan di deskriptor berikutnya.



3.1 Perangkat Keras





Struktur proyek saat menggunakan DMA



Mari pertimbangkan opsi ketika daftar deskriptor disimpan dalam logika yang dapat diprogram. Blok DMA memiliki port kontrol yang terhubung ke port GP dari unit prosesor. Ada juga port HP yang digunakan untuk mengakses RAM prosesor. Daftar deskriptor disimpan dalam memori deskriptor. Memori deskriptor dapat diakses dari DMA dan dari unit prosesor. Modul prosesor mengisi deskriptor, DMA membacakan deskriptor.



  1. Buat desain blok. Dalam Flow Navigator => Buat Desain Blok => nama "ProcessingSystem" => OK.
  2. Menggunakan tombol "+" di lapangan atau pintasan keyboard Ctrl + I, tambahkan inti prosesor.
  3. Mari hubungkan pin yang diperlukan dengan mengklik tombol Run Block Automation => 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



Sekarang Anda perlu menulis aplikasi yang berjalan pada modul prosesor. Anda perlu memulai lingkungan pengembangan Vitis dan membuat aplikasi menggunakan template Hello World, contohnya ditunjukkan di artikel sebelumnya.



Format deskriptor untuk Axi DMA dijelaskan dalam dokumen kernel [2]. Deskriptor berukuran 52 byte, namun, alamat tempat deskriptor berada harus selaras 64 byte.



Secara singkat tentang format deskriptor:



  • NXTDESC - alamat deskriptor berikutnya;
  • NXTDESC_MSB - 32 bit tinggi dari alamat deskriptor berikutnya;
  • BUFFER_ADDRESS - alamat penyangga;
  • BUFFER_ADDRESS_MSB - 32 bit tinggi dari alamat buffer;
  • RESERVED - tidak digunakan;
  • RESERVED - tidak digunakan;
  • CONTROL - mengatur ukuran buffer, tanda awal dan akhir paket;
  • STATUS - menunjukkan berapa banyak byte yang diterima / dikirim, diproses / tidak diproses;
  • APP0 - digunakan untuk bekerja dengan saluran "Kontrol / Aliran Status";
  • APP1 - digunakan untuk bekerja dengan saluran Control / Status Stream;
  • APP2 - digunakan untuk bekerja dengan saluran Control / Status Stream;
  • APP3 - digunakan untuk bekerja dengan saluran Control / Status Stream;
  • APP4 - digunakan untuk bekerja dengan saluran "Kontrol / Aliran Status".


Alamat dalam logika yang dapat diprogram untuk akses dari modul prosesor dapat dilihat di Vivado. Dalam Flow Navigator => Buka Desain Blok => Tab Editor Alamat. Dalam hal ini, alamat DMA adalah 0x4040_0000. Alamat awal area memori untuk deskriptor adalah 0x4000_0000.



  1. Di Vitis, buka file helloworld.c dan sertakan pustaka berikut



    #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



Bangun aplikasi, buat file firmware dan unggah ke papan. Dijelaskan di artikel sebelumnya [1].



Kami mulai, lihat di monitor 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

      
      





Jadi, untuk pertukaran data antara modul prosesor dan logika yang dapat diprogram, salah satu antarmuka komunikasi dengan modul prosesor harus diimplementasikan dalam logika yang dapat diprogram, di mana pemrakarsanya adalah logika yang dapat diprogram. Antarmuka seperti itu diwakili oleh port GP, HP, ACP. Dalam artikel sebelumnya [1] mereka semua dianggap.



Mari kita hitung kecepatan transfer data: (256 kali * 102400 byte) / (271 * 100 μs) ≈ 967 321 033 byte / s ≈ 944 649 KB / s ≈ 922 MB / s.

Kecepatan bit 7.738.568.264 bps.

Kecepatan teoritis adalah 32 bit * 250 MHz = 8.000.000.000 bit / s.



Juga, dimungkinkan untuk menyimpan deskriptor tidak dalam memori logika yang dapat diprogram, tetapi dalam memori akses acak yang terhubung ke modul prosesor. Dalam kasus ini, port M_AXI_SG terhubung ke port HP Zynq.



Mari pertimbangkan opsi pertama, ketika port HP yang berbeda digunakan untuk akses DMA ke data dan ke deskriptor di RAM prosesor. Mari kita ubah firmware dalam logika yang dapat diprogram sehingga kita mendapatkan skema berikut: Akses ke data dan deskriptor melalui port yang berbeda Kami tidak akan memberikan kode sumber aplikasi. Satu-satunya perbedaan adalah deskriptor tidak perlu disalin ke memori logika yang dapat diprogram. Namun, perlu diperhatikan kondisi bahwa alamat setiap deskriptor selaras 64-byte.













Setelah memulai aplikasi, di monitor com-port, kita akan melihat bahwa waktu eksekusi untuk menyalin buffer data tidak berubah, juga 271 * 100 μs.



Mari pertimbangkan opsi kedua, ketika port yang sama digunakan untuk mengakses DMA dan deskriptor di RAM prosesor. Mari kita ubah firmware dalam logika terprogram untuk mendapatkan skema berikut: Akses ke data dan deskriptor melalui port yang sama Kode sumber aplikasi tidak berubah dibandingkan dengan versi sebelumnya. Setelah meluncurkan aplikasi, di monitor com-port kita akan melihat waktu baru untuk operasi salinan buffer: 398 * 100 μs.















Hasilnya, kecepatan pemrosesan akan menjadi: (256 kali * 102400 byte) / (398 * 100 μs) ≈ 658653266 byte / s ≈ 643 216 KB / s s 628 MB / s.

Kecepatan bit 5.269.226128 bps.



Proyek: github.com/Finnetrib/DmaTransfer



4. Kesimpulan



Pada artikel ini, kami meninjau dua implementasi pertukaran data antara modul prosesor dan logika yang dapat diprogram. Mode PIO mudah diimplementasikan dan memungkinkan Anda mendapatkan kecepatan hingga 23 MB / s, mode DMA agak lebih rumit, tetapi kecepatannya juga lebih tinggi - hingga 628 MB / s.



5 Sumber yang digunakan



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



All Articles