Trabalhe com cartão SD via interface SPI. Implementação de VHDL

Oi Habr! No trabalho, tive a tarefa de avaliar a possibilidade de implementar o armazenamento de dados em um cartão SD ao conectá-lo a um FPGA. O uso do SPI foi assumido como uma interface de interação, pois é mais fácil de implementar. Eu gostaria de compartilhar a experiência adquirida.



Como o espaço na placa de circuito impresso é sempre limitado, a consideração dos cartões SD será realizada no exemplo de cartões no formato microSD.

Conteúdo


1. Especificação de leitura
1.1 Informações gerais
1.2 Inicialização
1.3 Informações de apagamento
1.4 Informações de leitura
1.4.1 Lendo um bloco de dados
1.4.2 Lendo muitos blocos de dados
1.5 Gravando informações
1.5.1 Escrevendo um bloco de dados
1.5.2 Escrevendo muitos blocos de dados
2. Implementando o algoritmo no hardware
2.1 Componente da camada física
2.2 Componente do nível de comando
2.3 Componente de comunicação com o mundo exterior
3. Verificação no hardware
4. Resultados

1. Especificações de leitura


1.1 Geral


Uma leitura rápida da especificação indica as seguintes opções de cartão SD:

  • a transferência de dados para o cartão SD é realizada em uma linha;
  • a leitura de dados de um cartão SD é realizada em uma linha;
  • no modo SPI, a energia pode ser apenas + 3,3V;
  • freqüência do relógio no modo de inicialização na faixa de 100 a 400 kHz;
  • freqüência do relógio após a inicialização - 25 MHz.

Isso implica imediatamente o ponto da largura de banda do pico teórico: 25 MHz * 1 bit = 25 Mb / s, que é um pouco pequeno. O segundo menos o uso de cartões SD, potência + 3.3V. Na placa de circuito impresso na qual foi planejado instalar o cartão SD, não existe essa tensão.

Os cartões de formato microSD podem ser divididos em 3 categorias por capacidade:

  • SDSC Capacidade do cartão de até 2 GB, inclusive. O endereço dentro do cartão é byte.
  • SDHC. A capacidade do cartão é superior a 2 GB e até 32 GB, inclusive. O endereço dentro do cartão indica um bloco de 512 bytes.
  • SDXC. A capacidade do cartão é superior a 32 GB e até 128 TB, inclusive. O endereço dentro do cartão indica um bloco de 512 bytes.

A vista geral do mapa é mostrada na figura abaixo.


Número de contatoNomeUm tipoDescrição
1 1RSV-Não usado
2CSentradaSeleção de chips
3DIentradaLinha de dados do dispositivo mestre (MOSI)
4VddNutriçãoTensão de alimentação
5SCLKentradaSinal de relógio
6VssNutriçãoTerra
7FAZResultadoLinha de dados para dispositivo mestre (MISO)
8RSV-Não usado

A conexão é realizada de acordo com o diagrama abaixo:


A classificação do resistor deve ser de 50 kOhm

Depois de ligar a alimentação, o cartão SD está no modo SDIO. Para alternar para o modo SPI, você deve executar a inicialização. O protocolo para trabalhar com o cartão envolve o uso de um esquema de controle para a correta transferência de dados e comandos na forma de um algoritmo CRC. Ao operar no modo SPI, a verificação CRC é desativada por padrão. Portanto, o primeiro comando enviado ao cartão para alternar o cartão para o modo SPI deve conter o valor CRC correto.

Os comandos transmitidos pela interface SPI têm tamanho de 48 bits. Formato de comando:

  • O bit 47 (mais à esquerda) sempre contém o valor 0.
  • O bit 46 sempre contém o valor 1.
  • Os bits 45..40 contêm o índice de comando.
  • Os bits 39..8 contêm os argumentos do comando.
  • Os bits 7..1 contêm CRC dos bits anteriores.
  • O bit 0 sempre contém o valor 1.

Os bits 47 e 46 dão ao cartão a capacidade de rastrear inequivocamente o início de uma transação, já que o barramento MOSI em repouso é igual a um.



Os comandos usados ​​ao trabalhar com o cartão SD receberão respostas como R1, R3, R7.


Uma resposta do tipo R1, tamanho 8 bits. Resposta tipo R3, tamanho 40 bits. Resposta tipo R7, tamanho 40 bits. Todos os comandos e suas respostas são detalhados na Especificação Simplificada da Camada Física.









1.2 Inicialização


Algoritmo de Inicialização:

  1. Depois de ligar a alimentação, aguarde pelo menos 1 ms.
  2. Gere no mínimo 74 comutações de clock para o cartão. As linhas CS e MOSI devem estar no estado da unidade lógica.
  3. Gere o comando CMD0. O comando CMD0 redefine o cartão SD.
  4. CMD0. R1. , R1 16 , 3. R1 , 0x01 ( ), 3, 5.
  5. CMD8. .
  6. CMD8. R7. , , , 7, , , 17, 13.
  7. CMD55. CMD55 , .
  8. CMD55. R1. 0x01 ( ), 7, 9.
  9. ACMD41. ACMD41 , SDSC ( 0x00000000) .
  10. ACMD41. R1. , , 11. , 0x00 ( ) 14, 7.
  11. CMD1. CMD1 ACMD41.
  12. CMD1. R1. , , 13, , 0x00 ( ), 14, 11.
  13. . , . .
  14. CMD16. , 512 .
  15. CMD16. R1. , 0x00 ( ) 16, 13.
  16. . . .
  17. CMD55. CMD55 , .
  18. CMD55. R1. 0x01 ( ), 17, 19.
  19. ACMD41. ACMD41. ACMD41 , SDHC ( 0x40000000) .
  20. ACMD41. R1. , , 13. , 0x00 ( ) 21, 17.
  21. CMD58. .
  22. Aguardando uma resposta ao comando CMD58. A resposta é uma resposta do tipo R3. Se um bit for definido na resposta de que o cartão funciona com endereços de bloco de 512 bytes, vá para a etapa 16, caso contrário, para a etapa 14.

Após a conclusão da inicialização, o cartão pode ser operado em blocos de 512 bytes com uma frequência de clock de 25 MHz. Esta é a versão completa do algoritmo de inicialização, que abrange todos os tipos de cartões. No meu caso, ao usar um cartão de 16 GB, o algoritmo de inicialização consistiu nas etapas de 1 a 6, 17 a 22, 16.

Algoritmo Gráfico

1.3 Apagando informações


Os cartões de fator de forma Micro SD suportam comandos de exclusão. Após o comando apagar, o valor dos endereços de exclusão especificados será preenchido com o valor 0xFF ou 0x00, dependendo do cartão.

Algoritmo de apagamento de informações

  1. CMD32. .
  2. CMD32. R1. 0x00 ( - ), 3, 4.
  3. . , .
  4. CMD33. . , CMD32.
  5. CMD33. R1. 0x00 ( - ), 3, 6
  6. Enviando o comando CMD38. O comando para apagar informações dos blocos selecionados. Quaisquer 4 bytes devem ser enviados como argumento, exceto para os valores 0x00000001, 0x00000002.
  7. Aguardando uma resposta ao comando CMD38. A resposta é uma resposta do tipo R1b. Esta é uma versão estendida da resposta quando o cartão gera uma resposta R1 e, em seguida, desenha a linha MISO para zero, indicando que o chip está ocupado. É necessário aguardar até que o valor da unidade apareça na linha MISO. Se a resposta não for 0x00 (houve algum erro ao executar o comando), vá para a etapa 3, caso contrário, para a etapa 8
  8. Conclusão do algoritmo de apagamento.

Algoritmo Gráfico

1.4 Lendo informações


A leitura de informações de um cartão SD via SPI é possível de duas maneiras.

1.4.1 Lendo um único bloco de dados


Ao ler um bloco de dados, o dispositivo Mestre gera um comando para o cartão SD ler um bloco de dados, aguarda uma resposta de que o comando foi processado e aguarda um pacote com dados do cartão. Depois de receber um pacote com dados do cartão, a transação de leitura termina.


Visão geral de uma transação que lê um bloco de dados.

Inicialmente, essa opção foi implementada, mas a velocidade resultante foi muito alterada (as comparações de velocidade serão mais baixas).

1.4.2 Lendo vários blocos de dados


Ao ler vários blocos de dados, o dispositivo Mestre gera um comando para o cartão SD ler vários blocos de dados, aguarda uma resposta de que o comando foi processado e espera um pacote de dados do cartão. Depois de enviar um pacote de dados, o cartão envia o próximo pacote de dados. Isso continuará até que um comando seja recebido do dispositivo Master para concluir a leitura.


Visão geral de uma transação que lê muitos blocos de dados.


Estrutura do pacote de dados

Onde:

  • Token de dados O comando read usa o valor 0xFE.
  • Bloco de dados. Contém dados lidos no cartão.
  • CRC Contenha a soma de verificação dos campos anteriores.

Se ocorreu um erro durante a leitura, o cartão em vez do Data Token retornará um Error Token. O tamanho do token de erro é de 1 byte. Os bits 7..5 contêm um valor zero, os bits 4..0 codificam o tipo de erro.

Algoritmo para leitura de vários blocos de dados:

  1. Enviando o comando CMD18. O comando informa ao cartão que vários blocos serão lidos.
  2. Aguardando uma resposta ao comando CMD18. A resposta é uma resposta do tipo R1. Se a resposta não for 0x00 (houve algum erro ao executar o comando), vá para a etapa 3, caso contrário, para a etapa 4.
  3. Status de erro. A leitura falhou, saindo do algoritmo de leitura.
  4. À espera de um token do cartão. Se um Cartão de Erro for recebido do cartão, vá para a etapa 3, caso contrário, vá para a etapa 5.
  5. Receba de um cartão de bloco de dados com 512 bytes de tamanho.
  6. Receba do tamanho do campo CRC do cartão de 2 bytes.
  7. . , 8, 4.
  8. CMD12. . , Data Packet, .
  9. CMD12. R1b. R1, MISO , . MISO , . 0x00 ( - ), 3, 10.
  10. .

Há uma ligeira nuance no algoritmo de leitura. O sinal de seleção de chip (CS) deve ser definido como zero lógico antes de gerar o comando CMD18 e definido como lógico um após receber uma resposta ao comando CMD12.

Algoritmo Gráfico

1.5 Gravando informações


A gravação de informações no cartão SD através da interface SPI é possível em duas versões.

1.5.1 Escrevendo um único bloco de dados


Ao gravar um bloco de dados, o dispositivo Mestre gera um comando para o cartão SD gravar um bloco de dados, aguarda uma resposta do cartão que o comando foi processado, transmite um pacote com dados para gravação no cartão, espera uma resposta do cartão que os dados foram gravados, conclui a transação.


Visão geral de uma transação de gravação de um bloco de dados.

Como na leitura, um registro de um bloco de dados foi originalmente implementado. Os resultados da velocidade foram insatisfatórios.

1.5.2 Escrevendo vários blocos de dados


Ao gravar vários blocos de dados, o dispositivo Mestre gera um comando para gravar vários blocos de dados, aguarda uma resposta do cartão que o comando foi processado, transmite um pacote com dados para gravação no cartão, espera uma resposta do cartão que os dados são gravados. Depois de receber uma resposta principal, o dispositivo transmite um pacote com os seguintes dados para gravar no cartão. Isso continuará até que o dispositivo mestre envie um token de parada de dados.


Visão geral de uma transação de gravação para vários blocos de dados.

A estrutura do pacote de dados é semelhante à estrutura do pacote de dados ao ler dados.

Formato:

  • Token de dados Para o comando write, o valor 0xFC é usado.
  • Bloco de dados. Contém dados gravados no cartão.
  • CRC Contenha a soma de verificação dos campos anteriores.

O Stop Tran Token usado para concluir o comando write tem 1 byte de tamanho e é igual a 0xFD.

Após o último bit do Pacote de Dados ter sido inserido no cartão, o cartão responde ao próximo relógio com o status do registro de dados - Resposta de Dados. A resposta de dados tem um tamanho de 1 byte, os bits 7..5 podem ser qualquer, o bit 4 é sempre igual a zero, o bit 0 é sempre igual a um, os bits 3..1 codificam o status do registro de dados. Depois que o cartão retornou o pacote de dados, o cartão desenha a linha MISO para zero, indicando que o cartão está ocupado. Depois que o nível da unidade lógica estiver na linha MISO, você poderá transferir o próximo pacote de dados para o cartão.

O algoritmo para gravar vários blocos de dados:

  • Enviando o comando CMD25. O comando informa ao cartão que vários blocos serão gravados.
  • CMD25. R1. 0x00 ( - ), 3, 4.
  • . , .
  • .
  • 512 .
  • CRC 2 .
  • Data Response . , 3, 8.
  • . , 9, 4.
  • Stop Tran Token. , .
  • Aguardando uma resposta do cartão. O cartão no Stop Tran Token desenha a linha MISO para zero, indicando que o cartão está ocupado. É necessário aguardar na linha MISO o valor da unidade, que indicará o final do comando
  • A conclusão do algoritmo de gravação.

Há também uma pequena nuance no algoritmo de gravação. O sinal de seleção de chip (CS) deve ser definido como zero lógico antes de gerar o comando CMD25 e definido como lógico após receber uma resposta ao Stop Tran Token

Algoritmo Gráfico

2. Implementação do algoritmo em hardware


Como resultado da leitura da especificação, obtemos algumas características do algoritmo que precisam ser implementadas no hardware.

Possíveis modos de operação:

  • Formação inicial de frequências. As linhas CS e MOSI devem estar no estado da unidade lógica.
  • Transfira dados para um cartão SD.
  • Receba dados de um cartão SD.
  • Aguardando a conclusão da equipe. É usado quando, após gerar uma resposta, o cartão SD desenha a linha MISO para zero, a fim de aguardar o aparecimento de uma unidade.
  • Leia a resposta ao gravar dados.
  • Aguardando um token durante a leitura de dados.

Modos de relógio possíveis:

  • Sinal de relógio para inicializar.
  • Sinal de relógio para o trabalho.

Do meu ponto de vista, o algoritmo é idealmente implementado usando três componentes:

  • O componente da camada física, conectado diretamente ao cartão SD, gera sinais SCLK, CS, DI e lê com DO.
  • Um componente em nível de comando que prepara todos os dados para um componente na camada física.
  • Um componente de comunicação com o mundo externo que oculta todo o dispositivo interno e fornece uma interface para comandos (leitura, gravação, exclusão) e dados.

2.1 Componente da camada física


entity SDPhy is
	generic	(	gTCQ		: time := 2 ns );
	port	(	-- Control bus
			iPhyTxData	: in	std_logic_vector( 9 downto 0);
			iPhyMode	: in	std_logic_vector( 4 downto 0);
			iPhyTxWrite	: in	std_logic;
			oPhyTxReady	: out	std_logic; 
			-- Out Data
			oPhyRxData	: out	std_logic_vector( 7 downto 0); 
			oPhyRxWrite	: out	std_logic;
			oPhyCmdEnd	: out	std_logic;
			-- Spi
			oSdCS		: out	std_logic;
			oSdClk		: out	std_logic;
			oSdMosi		: out	std_logic;
			oSdMosiT	: out	std_logic;
			iSdMiso		: in	std_logic;
			-- system
			sclk		: in	std_logic;
			pclk		: in	std_logic;
			rst		: in	std_logic ); 
end SDPhy;

Onde:

  • iPhyTxData , iPhyMode , .
  • iPhyTxWrite , iPhyTxData iPhyMode .
  • oPhyTxReady , . FULL FIFO, .
  • oPhyRxData , SD-.
  • oPhyRxWrite , oPhyRxData .
  • oPhyCmdEnd , .
  • oSdCS (CS) SD-.
  • oSdClk SD-.
  • oSdMosi SD-.
  • oSdMosiT SD-.
  • iSdMiso SD-.
  • sclk SD- (50 ).
  • pclk , .
  • primeiro sinal de reset, nível um ativo.

Nos FPGAs, existem unidades especiais para trabalhar com um sinal de clock (PLL, MMCM), no entanto, é problemático receber um sinal de clock a menos de 5 MHz de sua saída. Como resultado, a camada física opera a uma frequência de 50 MHz. Juntamente com cada dado no sinal iPhyMode, é recebido um bit que indica com que frequência esses dados devem ser transferidos para o cartão SD (ou recebidos a partir dele). Dependendo do bit de velocidade, os sinais de ativação do relógio são gerados.

Dois autômatos são implementados no componente da camada física, para transferir dados para um cartão SD e para receber dados dele.

Código da máquina para transferência de dados: github .

  • O estado SDummy fornece modelagem de frequência inicial, 128 chaveamento.
  • O status do STxBits fornece transferência de dados para o cartão SD.

O código da máquina para receber dados: github .

  • O estado sRxBits fornece recepção de dados do cartão SD.
  • O estado sBusy garante que o cartão SD esteja pronto (o cartão libera a linha MISO para o nível da unidade).
  • O estado sResp implementa a leitura da resposta ao gravar dados.
  • O estado sToken implementa uma espera de token durante a leitura de dados.

2.2 Componente em nível de comando


entity SdCommand is
	generic	(	gTCQ		: time := 2 ns );
	port	(	-- Command from host
			oSdInitComp	: out	std_logic;
			oSdInitFail	: out	std_logic;
			iSdAddress	: in	std_logic_vector(31 downto 0);
			iSdStartErase	: in	std_logic;
			iSdStartRead	: in	std_logic;
			iSdStartWrite	: in	std_logic;
			oSdCmdFinish	: out	std_logic_vector( 1 downto 0);
			oSdhcPresent	: out	std_logic;
			-- Data
			oSdReadData	: out	std_logic;
			iSdDataR	: in	std_logic_vector(31 downto 0);
			oSdWriteData	: out	std_logic;
			oSdDataW	: out	std_logic_vector(32 downto 0);
			-- Spi
			oSdCS		: out	std_logic;
			oSdClk		: out	std_logic;
			oSdMosi		: out	std_logic;
			oSdMosiT	: out	std_logic;
			iSdMiso		: in	std_logic;
			-- system
			pclk		: in	std_logic;
			sclk		: in	std_logic;
			rst		: in	std_logic );

Onde:

  • oSdInitComp assina a conclusão da inicialização do cartão SD.
  • oSdInitFail sinal de falha na inicialização.
  • Endereço iSdAddress no cartão SD para executar o comando.
  • O iSdStartErase inicia a exclusão da execução do comando.
  • O iSdStartRead começa a ler a execução do comando.
  • iSdStartWrite inicia a execução do comando write.
  • oSdCmdFinish o status de conclusão da equipe. O bit zero é igual a um, o comando foi concluído com êxito. O primeiro bit é um, o comando foi concluído com um erro.
  • oSdhcPresent Flag de detecção de cartão SDHC / SDXC.
  • oSdReadData Leia os dados para gravar no cartão SD.
  • Dados iSdDataR para gravar no cartão SD.
  • oSdWriteData para gravar dados lidos no cartão SD.
  • Dados do SDDataW lidos a partir de um cartão SD.

Os demais sinais coincidem com os da camada física.

O componente possui 5 máquinas automáticas.

  • smSdInit ( github ) - inicialização do cartão SD.
  • smSdErase ( github ) - apaga dados de um cartão SD.
  • smSdRead ( github ) - lê dados de um cartão SD.
  • smSdWrite ( github ) - grava dados em um cartão SD.
  • smSdCommand ( github ) - com base nos recursos gerados, prepara os dados para a camada física de todas as máquinas anteriores.

2.3 Componente da comunicação com o mundo exterior


entity SdHost is
	generic	(	gTCQ		: time := 2 ns );
	port	(	-- Sd Host command
			iSdCommand	: in	std_logic_vector( 2 downto 0);
			iSdAddress	: in	std_logic_vector(31 downto 0);
			iSdStart	: in	std_logic;
			oSdStatus	: out	std_logic_vector( 1 downto 0);
			oSdInitFail	: out	std_logic;
			-- Write data to card
			iSdTxData	: in	std_logic_vector(31 downto 0);
			iSdTxValid	: in	std_logic;
			iSdTxLast	: in	std_logic;
			oSdTxReady	: out	std_logic;
			-- Read data from card
			oSdRxData	: out	std_logic_vector(31 downto 0);
			oSdRxValid	: out	std_logic;
			oSdRxLast	: out	std_logic;
			iSdRxReady	: in	std_logic;
			-- Spi
			oSdCS		: out	std_logic;
			oSdClk		: out	std_logic;
			oSdMosi		: out	std_logic;
			oSdMosiT	: out	std_logic;
			iSdMiso		: in	std_logic;
			-- system
			pclk		: in	std_logic;
			sclk		: in	std_logic;
			rst		: in	std_logic );

Onde:

  • Código de comando iSdCommand para executar.
  • iSdAddress é o endereço para executar o comando.
  • O iSdStart inicia a execução do comando.
  • Status de conclusão da equipe oSdStatus. O bit zero é igual a um - o comando está concluído. O primeiro bit é um - o comando foi concluído com um erro.
  • oSdInitFail sinal de falha na inicialização.
  • iSdTxData. Interface Axi-Stream para gravar dados em um cartão SD. Porta com dados.
  • iSdTxValid. Interface Axi-Stream para gravar dados em um cartão SD. Porta com um sinal de gravação.
  • iSdTxLast. Interface Axi-Stream para gravar dados em um cartão SD. Porta com um sinal da última dw nos dados.
  • oSdTxReady. Interface Axi-Stream para gravar dados em um cartão SD. Porta com um sinal de prontidão para receber dados.
  • oSdRxData. Interface Axi-Stream para leitura de dados de um cartão SD. Porta com dados.
  • oSdRxValid. Interface Axi-Stream para leitura de dados de um cartão SD. Porta com um sinal de gravação.
  • oSdRxLast. Interface Axi-Stream para leitura de dados de um cartão SD. Porta com um sinal da última dw nos dados.
  • iSdRxReady. Interface Axi-Stream para leitura de dados de um cartão SD. Porta com um sinal de prontidão para receber dados.

Os demais sinais coincidem com os da camada física.

O componente implementa uma máquina smSdControl ( github ).

  • Estado de grupo. Aguardando a inicialização e o comando serem concluídos.
  • O estado de sWaitCmd. Verificando o tipo de comando.
  • sReadCmd. FIFO, , SD- .
  • sWriteCmd. , FIFO SD-, .
  • sEraseCmd. .
  • sWaitEnd. .
  • sFinish. , .

3.


O algoritmo é escrito, verificado no simulador. É necessário verificar agora no ferro. Do que estava disponível, surgiu a placa Zybo da Digilent, que possui terminais FPGA gratuitos em um banco com tensão de + 3,3V, ao qual você pode conectar facilmente um dispositivo externo. Sim, e o tipo de FPGA usado é o Zynq-7000, o que significa que há um núcleo do processador. Você pode escrever um teste em C, o que simplificará a tarefa de teste. Portanto, conectamos o algoritmo implementado ao núcleo do processador através da porta GP (é possível uma operação de 4 bytes, semelhante ao PIO ). Não nos incomodamos com interrupções; implementamos uma pesquisa de cronômetro. Ao trabalhar no módulo do processador, o algoritmo de gravação de dados será o seguinte:









  • Defina o endereço no cartão SD.
  • Defina o código de comando 2.
  • Grave dados em um buffer localizado na lógica programável.
  • Execute o comando
  • Aguarde até que o comando seja concluído.
  • Redefina o status de conclusão da equipe.

Teste implementado:

for (SectorAddress = 0; SectorAddress < 1048576; SectorAddress ++)
{
	if ((SectorAddress % 1024) == 0)
	{
		xil_printf("Data write to %d sector \n\r", SectorAddress);
	}

	/** Set address */
	Xil_Out32(0x43c00008, SectorAddress);

	/** Set command */
	Xil_Out32(0x43c00004, 2);

	/** Write data to PL */
	for (int32_t i = 0; i < 1024; i++)
	{
		Xil_Out32(0x43c00014, cntrData);
		cntrData++;
	}

	/** Start */
	Xil_Out32(0x43c00000, 1);

	/** Wait end of operation */
	for (;;)
	{
		status = Xil_In32(0x43c0000c);
		if (status == 0x01 || status == 0x03)
		{
			if (status == 0x03)
			{
				xil_printf("Error in write \n\r");
			}
			break;
		}
		else
		{
			cntrDuration++;
			usleep(100);
		}
	}

	/** Duration operation */
	durationWrite += cntrDuration;

	if (cntrDuration > MaxWrite )
	{
		MaxWrite = cntrDuration;
	}

	cntrDuration = 0x00;

	/** Clear start */
	Xil_Out32(0x43c00000, 0);

	SectorAddress += 7;
}

Para a pergunta de por que o limite externo de 1024 é usado no loop.O número de blocos é definido como 8. O tamanho de um bloco é de 512 bytes. O tamanho total de 8 blocos de dados é 8 * 512 bytes = 4096 bytes. O barramento entre o módulo do processador e a lógica programável tem 4 bytes de tamanho. Acontece que, para enviar 4096 bytes de 4 bytes do módulo do processador para a lógica programável, é necessário executar operações de gravação 4096/4 = 1024.

Ao trabalhar no módulo do processador, o algoritmo de leitura de dados será o seguinte:

  • Defina o endereço no cartão SD.
  • Defina o código de comando 1.
  • Execute o comando
  • Aguarde até que o comando seja concluído.
  • Redefina o status de conclusão da equipe.
  • Leia os dados do buffer na lógica programável.

Teste implementado:

for (SectorAddress = 0; SectorAddress < 1048576; SectorAddress++)
{
	if ((SectorAddress % 1024) == 0)
	{
		xil_printf("Data read from %d sector \n\r", SectorAddress);
	}

	/** Set address */
	Xil_Out32(0x43c00008, SectorAddress);

	/** Set command */
	Xil_Out32(0x43c00004, 1);

	/** Start */
	Xil_Out32(0x43c00000, 1);

	/** Wait end of operation */
	for (;;)
	{
		status = Xil_In32(0x43c0000c);
		if (status == 0x01 || status == 0x03)
		{
			 if (status == 0x03)
			{
				xil_printf("Error in read \n\r");
			}
			break;
		}
		else
		{
			cntrDuration++;
			usleep(100);
		}
	}

	 /** Duration operation */
	 durationRead += cntrDuration;

	 if (cntrDuration > MaxRead )
	 {
		 MaxRead = cntrDuration;
	 }

	cntrDuration = 0x00;

	/** Clear start */
	Xil_Out32(0x43c00000, 0);

	/** Read data from PL */
	for (int32_t i = 0; i < 1024; i++)
	{
		DataR = Xil_In32(0x43c0001c);
		if (DataR != cntrData)
		{
			xil_printf("Data corrupt! \n\r");
		}
		DataR = Xil_In32(0x43c00020);
		cntrData++;
	}

	SectorAddress += 7;
}

Ao trabalhar no módulo do processador, o algoritmo de eliminação de dados será o seguinte:

  • Defina o endereço no cartão SD.
  • Defina o código de comando 4.
  • Execute o comando
  • Aguarde até que o comando seja concluído.
  • Redefina o status de conclusão da equipe.

Teste implementado:

for (SectorAddress = 0; SectorAddress < 1048576; SectorAddress++)
{
	if ((SectorAddress % 1024) == 0)
	{
		xil_printf("Data erase from %d sector \n\r", SectorAddress);
	}

	/** Set address */
	Xil_Out32(0x43c00008, SectorAddress);

	/** Set command */
	Xil_Out32(0x43c00004, 4);

	/** Start */
	Xil_Out32(0x43c00000, 1);

	/** Wait end of operation */
	for (;;)
	{
		status = Xil_In32(0x43c0000c);
		if (status == 0x01 || status == 0x03)
		{
			if (status == 0x03)
			{
				xil_printf("Error in write! \n\r");
			}
			break;
		}
		else
		{
			cntrDuration++;
			usleep(100);
		}
	}

	/** Duration operation */
	durationErase += cntrDuration;

	if (cntrDuration > MaxErase )
	{
		MaxErase = cntrDuration;
	}

	cntrDuration = 0x00;

	/** Clear start */
	Xil_Out32(0x43c00000, 0);

	SectorAddress += 7;
}

Teste completamente no github.

4. Resultados


Quantidade de dadosLendoRegistroApagar
1 bloco (512 bytes)4.7 Mbps1,36 Mbps0,58 Mbps
8 blocos (4096 bytes)15,4 Mbps6.38 Mbps4,66 Mbps
16 blocos (8192 bytes)18,82 Mbps11,26 Mbps9,79 Mbps

Um cartão de 16 GB foi usado. Durante o teste, 2 GB de dados foram registrados, 2 GB de dados foram lidos e 2 GB de dados foram apagados.

As conclusões são decepcionantes. Ao usar o FPGA, não faz sentido usar o cartão SD no modo SPI, exceto no caso em que é muito necessário armazenar grandes quantidades de dados sem apresentar requisitos de velocidade.

All Articles