Zynq. Transferência de dados entre o módulo do processador e a lógica programável

Conforme prometido no artigo anterior ( O que é Zynq? Uma breve visão geral ), vamos falar sobre a transferência de dados entre o módulo do processador e a lógica programável. O artigo anterior mencionou quatro maneiras de transferir dados, o artigo discutirá dois métodos que encontraram maior aplicação. Detalhes sob o corte. Cuidado, muitas fotos!



Conteúdo



1 Informações gerais

2 Transferindo dados em modo PIO

2.1 Hardware

2.2 Software

2.3 Resultados

3 Transferindo dados em DMA

3.1 Hardware

3.2 Software

3.3 Resultados

4 Conclusão

5 Fontes usadas



1. Geral



Em geral, a transferência de dados entre o módulo processador e a lógica programável é possível em dois modos:



  • PIO , a porta GP é usada.
  • DMA , porta HP é usada.


2 Transferência de dados no modo PIO



No modo PIO, o módulo processador opera com lógica programável como um conjunto de registros. Para escrever ou ler uma determinada quantidade de dados, é necessária a participação constante do módulo processador. No modo PIO, todas as transações são iniciadas pelo módulo processador. A conexão de lógica programável envolve o uso da porta GP, onde Master é um módulo processador, Slave é uma lógica programável. Estrutura do projeto ao usar PIO









2.1 Hardware



  1. Criamos um projeto para Zybo no Vivado, chip tipo xc7z010clg400-1.
  2. Crie o desenho do bloco. No Flow Navigator => Create Block Design => o nome "ProcessingSystem" => OK.
  3. Usando o botão "+" no campo ou os atalhos de teclado Ctrl + I, adicione o núcleo do processador.



  4. Conecte os pinos necessários clicando no botão 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



Agora você precisa escrever um aplicativo que seja executado no módulo do processador que irá ler dados da lógica programável e gravar dados na lógica programável. É necessário iniciar o ambiente de desenvolvimento Vitis e criar uma aplicação usando o template Hello World, um exemplo disso é mostrado no artigo anterior [1].



O endereço do kernel criado para acesso a partir do módulo processador pode ser visualizado no Vivado. No Flow Navigator => Open Block Design => Address Editor Tab. Nesse caso, o endereço é 0x43C0_0000. Um registro está localizado neste endereço, no qual o atributo é armazenado, em que estado as chaves estão. Consequentemente, no endereço 0x43C0_0004 existe um registro que está conectado aos LEDs.



No Vitis, abra o arquivo helloworld.c e preencha:



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





Onde a função Xil_In32 é usada para ler 4 bytes de dados da lógica programável, e Xil_Out32, respectivamente, para escrever 4 bytes de dados para a lógica programável.



2.3 Resultados



Montamos o aplicativo, criamos um arquivo de firmware e fazemos o upload para a placa. Descrito no artigo anterior [1].



Inicie, olhe no monitor da porta de comunicação:



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

      
      





Tudo funciona corretamente.



Assim, para acessar a lógica programável no modo PIO, é necessário implementar uma das interfaces de comunicação com o módulo processador na lógica programável, onde o módulo processador é o iniciador. Esta interface é representada apenas pela porta GP.



Vamos ver com que rapidez as solicitações para lógica programável são processadas por meio da porta GP. Para fazer isso, em um aplicativo executado em um módulo processador, adicione várias entradas em uma linha ao registro na lógica programável e meça o tempo entre as transações na lógica programável usando os sinais de barramento puxados para o depurador.



Quando o barramento Axi-Lite está funcionando a 100 MHz, a pausa entre as solicitações é de 23 ciclos de clock em média. Vamos mudar a frequência do barramento para 200 MHz. A pausa entre as solicitações chega a 33 ciclos em média.



No total, 4 bytes de dados são transmitidos a 100 MHz por 23 ciclos de clock. A velocidade é: 32 / (23 * 10ns) = 139 130 434 bps ≈ 135 869 kbps ≈ 132 Mbps ≈ 16 MB / s.

No total, 4 bytes de dados são transmitidos a 200 MHz para 33 relógios. A velocidade é 32 / (33 * 5ns) = 193 939 393 bit / s ≈ 189 393 Kbps ≈ 184 Mbps ≈ 23 MB / s.

Assim, você pode atingir uma velocidade de 23 MB / s, mas com a participação constante do módulo processador.



Projeto: github.com/Finnetrib/PioTransfer



3 Transferência de dados em modo DMA



A transferência de dados no modo DMA implica que o módulo processador ajusta os parâmetros de troca de dados e não participa diretamente da troca. Assim, dois objetivos são alcançados: reduzir a carga no módulo processador e aumentar a velocidade de processamento dos dados. O preço disso é a complicação do hardware.



No Zynq, é possível usar vários ip-cores que implementam funções DMA. Este artigo examinará o núcleo AXI DMA [2].



AXI DMA tem dois canais MM2S e S2MM. O canal MM2S (mapeado em memória para fluxo) é usado para transferir dados do módulo do processador para a lógica programável. O canal S2MM (fluxo para mapeamento de memória) é usado para transferir dados da lógica programável para o módulo do processador. Os canais funcionam independentemente um do outro.



AXI DMA tem dois casos de uso:



  • Modo de registro direto
  • Modo Scatter / Gather


O modo de registro direto usa um conjunto de registros, o que permite que um buffer seja transferido da lógica programável para o módulo do processador e vice-versa. Por exemplo, para transferir um buffer de dados da lógica programável para um módulo processador, você precisa preencher os campos de endereço e tamanho do buffer e iniciar o DMA. Como resultado, o DMA preencherá um buffer na unidade do processador e parará.



O modo Scatter / Gather usa uma lista de descritores. O DMA processa o buffer descrito no descritor e prossegue para o processamento do buffer descrito no próximo descritor.



3.1 Hardware





Estrutura do projeto ao usar DMA



Vamos considerar uma variante quando a lista de descritores é armazenada em lógica programável. O bloco DMA possui uma porta de controle que se conecta à porta GP da unidade do processador. Há também uma porta HP usada para acessar a RAM do processador. A lista de descritores é armazenada na memória do descritor. A memória do descritor pode ser acessada tanto do DMA quanto da unidade do processador. O módulo processador preenche os descritores, o DMA lê os descritores.



  1. Crie o desenho do bloco. No Flow Navigator => Create Block Design => o nome "ProcessingSystem" => OK.
  2. Usando o botão "+" no campo ou os atalhos de teclado Ctrl + I, adicione o núcleo do processador.
  3. Conecte os pinos necessários clicando no botão 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



Agora você precisa escrever um aplicativo que execute no módulo do processador. Você precisa iniciar o ambiente de desenvolvimento Vitis e criar um aplicativo usando o modelo Hello World, um exemplo disso é mostrado no artigo anterior.



O formato dos descritores para o Axi DMA é descrito no documento do kernel [2]. O descritor tem 52 bytes de tamanho; no entanto, o endereço no qual o descritor está localizado deve ter 64 bytes alinhados.



Resumidamente sobre o formato do descritor:



  • NXTDESC - endereço do próximo descritor;
  • NXTDESC_MSB - 32 bits altos do próximo endereço do descritor;
  • BUFFER_ADDRESS - endereço do buffer;
  • BUFFER_ADDRESS_MSB - 32 bits altos do endereço do buffer;
  • RESERVADO - não usado;
  • RESERVADO - não usado;
  • CONTROLE - define o tamanho do buffer, sinais de início e fim do pacote;
  • STATUS - mostra quantos bytes foram recebidos / transmitidos, processados ​​/ não processados;
  • APP0 - usado para trabalhar com o canal Control / Status Stream;
  • APP1 - usado para trabalhar com o canal Control / Status Stream;
  • APP2 - usado para trabalhar com o canal Control / Status Stream;
  • APP3 - usado para trabalhar com o canal Control / Status Stream;
  • APP4 - usado para trabalhar com o canal Control / Status Stream.


Os endereços em lógica programável para acesso a partir do módulo processador podem ser visualizados no Vivado. No Flow Navigator => Open Block Design => Address Editor Tab. Nesse caso, o endereço DMA é 0x4040_0000. O endereço do início da área de memória para descritores é 0x4000_0000.



  1. No Vitis, abra o arquivo helloworld.c e inclua as seguintes bibliotecas



    #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



Montamos o aplicativo, criamos um arquivo de firmware e fazemos o upload para a placa. Descrito no artigo anterior [1].



Inicie, olhe no monitor da porta de comunicação:



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

      
      





Assim, para a troca de dados entre o módulo processador e a lógica programável, uma das interfaces de comunicação com o módulo processador deve ser implementada na lógica programável, onde o iniciador é a lógica programável. Essas interfaces são representadas pelas portas GP, HP, ACP. No artigo anterior [1], todos foram considerados.



Vamos calcular a taxa de transferência de dados: (256 vezes * 102400 bytes) / (271 * 100 μs) ≈ 967 321 033 bytes / s ≈ 944 649 KB / s ≈ 922 MB / s.

Taxa de bits 7.738.568.264 bps.

A velocidade teórica é de 32 bits * 250 MHz = 8.000.000.000 bits / s.



Além disso, é possível armazenar descritores não na memória lógica programável, mas na memória de acesso aleatório conectada ao módulo processador. Nesse caso, a porta M_AXI_SG se conecta à porta HP Zynq.



Considere a primeira opção, quando diferentes portas HP são usadas para acesso DMA aos dados e aos descritores na RAM do processador. Vamos modificar o firmware na lógica programável para que tenhamos o seguinte esquema: Acesso aos dados e descritores por diferentes portas Não forneceremos o código fonte da aplicação. A única diferença é que os descritores não precisam ser copiados para a memória lógica programável. Entretanto, é necessário levar em consideração a condição de que o endereço de cada descritor esteja alinhado com 64 bytes.













Depois de iniciar a aplicação, no monitor da porta de comunicação, veremos que o tempo de execução para copiar o buffer de dados não mudou, também 271 * 100 μs.



Considere a segunda opção, quando a mesma porta é usada para acessar o DMA e os descritores na RAM do processador. Vamos modificar o firmware na lógica programável para obter o seguinte esquema: Acesso aos dados e descritores pela mesma porta O código-fonte da aplicação não mudou em relação à versão anterior. Após iniciar a aplicação, no monitor da porta de comunicação veremos o novo tempo de execução da operação de cópia do buffer: 398 * 100 µs.















Como resultado, a velocidade de processamento será: (256 vezes * 102400 bytes) / (398 * 100 μs) ≈ 658 653 266 bytes / s ≈ 643 216 KB / s ≈ 628 MB / s.

Taxa de bits 5 269 226 128 bits / s.



Projeto: github.com/Finnetrib/DmaTransfer



4. Conclusão



Neste artigo, revisamos duas implementações de troca de dados entre o módulo processador e a lógica programável. O modo PIO é fácil de implementar e permite que você obtenha uma velocidade de até 23 MB / s, o modo DMA é um pouco mais complicado, mas a velocidade também é maior - até 628 MB / s.



5 fontes usadas



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



All Articles