Zynq. Transfert de données entre le module processeur et la logique programmable

Comme promis dans l'article précédent ( Qu'est-ce que Zynq? Un bref aperçu ), parlons du transfert de données entre le module processeur et la logique programmable. L'article précédent mentionnait quatre façons de transférer des données, l'article abordera deux méthodes qui ont trouvé une plus grande application. Détails sous la coupe. Attention, beaucoup de photos!



Contenu



1 Généralités

2 Transfert de données en mode PIO

2.1 Matériel

2.2 Logiciel

2.3 RĂ©sultats

3 Transfert de données en mode DMA

3.1 Matériel

3.2 Logiciel

3.3 RĂ©sultats

4 Conclusion

5 Sources utilisées



1. Général



En général, le transfert de données entre le module processeur et la logique programmable est possible dans deux modes:



  • PIO , le port GP est utilisĂ©.
  • DMA , le port HP est utilisĂ©.


2 Transfert de données en mode PIO



En mode PIO, le module processeur fonctionne avec une logique programmable comme un ensemble de registres. Pour Ă©crire ou lire une certaine quantitĂ© de donnĂ©es, vous avez besoin de la participation constante du module processeur. En mode PIO, toutes les transactions sont initiĂ©es par le module processeur. La connexion de la logique programmable implique l'utilisation du port GP, oĂč Master est un module de processeur, Slave est une logique programmable. Structure du projet lors de l'utilisation de PIO









2.1 Matériel



  1. Nous créons un projet pour Zybo dans Vivado, type de puce xc7z010clg400-1.
  2. Créez une conception de bloc. Dans le navigateur de flux => Créer une conception de bloc => le nom "ProcessingSystem" => OK.
  3. En utilisant le bouton «+» sur le terrain ou les raccourcis clavier Ctrl + I, ajoutez le cƓur du processeur.



  4. Connectons les broches nécessaires en cliquant sur le bouton 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



Vous devez maintenant écrire une application qui s'exécute sur le module processeur qui lira les données de la logique programmable et écrira des données dans la logique programmable. Vous devez démarrer l'environnement de développement Vitis et créer une application à l'aide du modÚle Hello World, un exemple en est présenté dans l'article précédent [1].



L'adresse du noyau crĂ©Ă© pour l'accĂšs depuis le module processeur peut ĂȘtre visualisĂ©e dans Vivado. Dans Flow Navigator => Ouvrir la conception de bloc => Onglet Editeur d'adresses. Dans ce cas, l'adresse est 0x43C0_0000. Un registre est situĂ© Ă  cette adresse, dans lequel l'attribut est stockĂ©, dans quel Ă©tat se trouvent les commutateurs. En consĂ©quence, Ă  l'adresse 0x43C0_0004, il y a un registre qui est connectĂ© aux LED.



Dans Vitis, ouvrez le fichier helloworld.c et remplissez:



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





OĂč la fonction Xil_In32 est utilisĂ©e pour lire 4 octets de donnĂ©es Ă  partir de la logique programmable, et Xil_Out32, respectivement, pour Ă©crire 4 octets de donnĂ©es dans la logique programmable.



2.3 RĂ©sultats



Construisez l'application, créez un fichier de micrologiciel et téléchargez-le sur la carte. Décrit dans l'article précédent [1].



Lancez, regardez dans le moniteur 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

      
      





Tout fonctionne correctement.



Ainsi, pour accĂ©der Ă  la logique programmable en mode PIO, il est nĂ©cessaire de mettre en Ɠuvre l'une des interfaces de communication avec le module processeur dans la logique programmable, oĂč le module processeur est l'initiateur. Cette interface est reprĂ©sentĂ©e uniquement par le port GP.



Voyons Ă  quelle vitesse les requĂȘtes Ă  la logique programmable sont traitĂ©es via le port GP. Pour ce faire, dans une application fonctionnant sur un module processeur, ajoutez plusieurs entrĂ©es d'affilĂ©e au registre en logique programmable et mesurez le temps entre les transactions en logique programmable Ă  l'aide des signaux de bus tirĂ©s dans le dĂ©bogueur.



Lorsque le bus Axi-Lite fonctionne Ă  100 MHz, la pause entre les requĂȘtes est de 23 cycles d'horloge en moyenne. Modifions la frĂ©quence du bus Ă  200 MHz. La pause entre les demandes devient 33 cycles en moyenne.



Au total, 4 octets de donnĂ©es sont transmis Ă  100 MHz pour 23 horloges. La vitesse est: 32 / (23 * 10ns) = 139130434 bps ≈ 135 869 kbps ≈ 132 Mbps ≈ 16 Mo / s.

Au total, 4 octets de donnĂ©es sont transmis Ă  200 MHz pour 33 horloges. La vitesse est de 32 / (33 * 5ns) = 193939393 bit / s ≈ 189 393 Kbps ≈ 184 Mbps ≈ 23 Mo / s.

Ainsi, vous pouvez atteindre une vitesse de 23 Mo / s, mais avec la participation constante du module processeur.



Projet: github.com/Finnetrib/PioTransfer



3 Transfert de données en mode DMA



Le transfert de données en mode DMA implique que le module processeur configure les paramÚtres d'échange de données et ne participe pas directement à l'échange. Ainsi, deux objectifs sont atteints: réduire la charge sur le module processeur et augmenter la vitesse de traitement des données. Le prix pour cela est la complication du matériel.



Dans Zynq, il est possible d'utiliser plusieurs ip-cores qui implémentent des fonctions DMA. Cet article se penchera sur le noyau AXI DMA [2].



AXI DMA dispose de deux canaux MM2S et S2MM. Le canal MM2S (mémoire mappée au flux) est utilisé pour transférer les données du module processeur vers la logique programmable. Le canal S2MM (Stream to memory-mapped) est utilisé pour transférer des données de la logique programmable vers le module processeur. Les canaux fonctionnent indépendamment les uns des autres.



AXI DMA a deux cas d'utilisation:



  • Mode d'enregistrement direct
  • Mode Scatter / Gather


Le mode de registre direct utilise un ensemble de registres, ce qui permet Ă  un tampon d'ĂȘtre transfĂ©rĂ© de la logique programmable au module processeur et vice versa. Par exemple, pour transfĂ©rer un tampon de donnĂ©es de la logique programmable vers un module de processeur, vous devez remplir les champs d'adresse et de taille de tampon et dĂ©marrer DMA. En consĂ©quence, DMA remplira un tampon dans l'unitĂ© de processeur et s'arrĂȘtera.



Le mode Scatter / Gather utilise une liste de descripteurs. DMA traite le tampon décrit dans le descripteur et procÚde au traitement du tampon décrit dans le descripteur suivant.



3.1 Matériel





Structure du projet lors de l'utilisation de DMA



Considérons une variante lorsque la liste des descripteurs est stockée en logique programmable. Le bloc DMA a un port de contrÎle qui se connecte au port GP de l'unité de processeur. Il existe également un port HP utilisé pour accéder à la RAM du processeur. La liste des descripteurs est stockée dans la mémoire des descripteurs. La mémoire de descripteur est accessible à la fois depuis le DMA et depuis l'unité de processeur. Le module processeur remplit les descripteurs, le DMA lit les descripteurs.



  1. Créez une conception de bloc. Dans le navigateur de flux => Créer une conception de bloc => le nom "ProcessingSystem" => OK.
  2. En utilisant le bouton «+» sur le terrain ou les raccourcis clavier Ctrl + I, ajoutez le cƓur du processeur.
  3. Connectons les broches nécessaires en cliquant sur le bouton 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



Vous devez maintenant écrire une application qui s'exécute sur le module processeur. Vous devez démarrer l'environnement de développement Vitis et créer une application à l'aide du modÚle Hello World, un exemple en est présenté dans l'article précédent.



Le format des descripteurs pour Axi DMA est dĂ©crit dans le document du noyau [2]. Le descripteur a une taille de 52 octets, cependant, l'adresse Ă  laquelle se trouve le descripteur doit ĂȘtre alignĂ©e sur 64 octets.



En bref sur le format du descripteur:



  • NXTDESC - adresse du descripteur suivant;
  • NXTDESC_MSB - 32 bits de haut de la prochaine adresse de descripteur;
  • BUFFER_ADDRESS - adresse du tampon;
  • BUFFER_ADDRESS_MSB - 32 bits de haut de l'adresse du tampon;
  • RÉSERVÉ - non utilisĂ©;
  • RÉSERVÉ - non utilisĂ©;
  • CONTROL - dĂ©finit la taille du tampon, les signes du dĂ©but et de la fin du paquet;
  • STATUS - montre combien d'octets ont Ă©tĂ© reçus / transmis, traitĂ©s / non traitĂ©s;
  • APP0 - utilisĂ© pour travailler avec le canal Control / Status Stream;
  • APP1 - utilisĂ© pour fonctionner avec le canal Control / Status Stream;
  • APP2 - utilisĂ© pour travailler avec le canal Control / Status Stream;
  • APP3 - utilisĂ© pour fonctionner avec le canal "Control / Status Stream";
  • APP4 - utilisĂ© pour travailler avec le canal "Control / Status Stream".


Les adresses en logique programmable pour l'accĂšs depuis le module processeur peuvent ĂȘtre visualisĂ©es dans Vivado. Dans Flow Navigator => Ouvrir la conception de bloc => Onglet Editeur d'adresses. Dans ce cas, l'adresse DMA est 0x4040_0000. L'adresse du dĂ©but de la zone mĂ©moire des descripteurs est 0x4000_0000.



  1. Dans Vitis, ouvrez le fichier helloworld.c et incluez les bibliothĂšques suivantes



    #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



Construisez l'application, créez un fichier de micrologiciel et téléchargez-le sur la carte. Décrit dans l'article précédent [1].



Lancez, regardez dans le moniteur 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

      
      





Ainsi, pour l'Ă©change de donnĂ©es entre le module processeur et la logique programmable, l'une des interfaces de communication avec le module processeur doit ĂȘtre implĂ©mentĂ©e dans la logique programmable, oĂč l'initiateur est la logique programmable. Ces interfaces sont reprĂ©sentĂ©es par les ports GP, HP, ACP. Dans l'article prĂ©cĂ©dent [1], ils ont tous Ă©tĂ© pris en compte.



Calculons le taux de transfert de donnĂ©es: (256 fois * 102400 octets) / (271 * 100 ÎŒs) ≈ 967321033 octets / s ≈ 944 649 Ko / s ≈ 922 Mo / s.

DĂ©bit binaire 7 738 568 264 bps.

La vitesse théorique est de 32 bits * 250 MHz = 8 000 000 000 bits / s.



De plus, il est possible de stocker des descripteurs non pas dans la mémoire logique programmable, mais dans la RAM connectée au module processeur. Dans ce cas, le port M_AXI_SG se connecte au port HP Zynq.



ConsidĂ©rons la premiĂšre option, lorsque diffĂ©rents ports HP sont utilisĂ©s pour l'accĂšs DMA aux donnĂ©es et aux descripteurs dans la RAM du processeur. Modifions le firmware dans la logique programmable pour obtenir le schĂ©ma suivant: AccĂšs aux donnĂ©es et descripteurs via diffĂ©rents ports Nous ne fournirons pas le code source de l'application. La seule diffĂ©rence est que les descripteurs n'ont pas besoin d'ĂȘtre copiĂ©s dans la mĂ©moire logique programmable. Cependant, il est nĂ©cessaire de prendre en compte la condition que l'adresse de chaque descripteur soit alignĂ©e sur 64 octets.













AprĂšs le lancement de l'application, nous verrons dans le moniteur du port com que le temps d'exĂ©cution de la copie du tampon de donnĂ©es n'a pas changĂ©, Ă©galement 271 * 100 ÎŒs.



ConsidĂ©rons la deuxiĂšme option, lorsque le mĂȘme port est utilisĂ© pour accĂ©der au DMA et aux descripteurs dans la RAM du processeur. Modifions le firmware dans la logique programmable pour obtenir le schĂ©ma suivant: AccĂšs aux donnĂ©es et descripteurs via le mĂȘme port Le code source de l'application n'a pas changĂ© par rapport Ă  la version prĂ©cĂ©dente. AprĂšs le lancement de l'application, dans le moniteur du port com, nous verrons la nouvelle heure pour l'opĂ©ration de copie du tampon: 398 * 100 ÎŒs.















En consĂ©quence, la vitesse de traitement sera: (256 fois * 102400 octets) / (398 * 100 ÎŒs) ≈ 658 653 266 octets / s ≈ 643216 Ko / s ≈ 628 Mo / s.

DĂ©bit binaire 5 269 226 128 bit / s.



Projet: github.com/Finnetrib/DmaTransfer



4. Conclusion



Dans cet article, nous avons examinĂ© deux implĂ©mentations de l'Ă©change de donnĂ©es entre le module processeur et la logique programmable. Le mode PIO est facile Ă  mettre en Ɠuvre et vous permet d'obtenir la vitesse jusqu'Ă  23 Mo / s, le mode DMA est un peu plus compliquĂ©, mais la vitesse est Ă©galement plus Ă©levĂ©e - jusqu'Ă  628 Mo / s.



5 Sources utilisées



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



All Articles