Introdução à NandFlash
A NandFlash é uma tecnologia de memória flash que permite alta densidade de armazenamento, atinginod capacidades como 256 MB, com velocidades rápidas de escrita e apagagem. Sua aplicação exige interfaces de sistema especializadas para gerenciamento eficiente.
Comparação entre NandFlash e NorFlash
| Característica | NorFlash | NandFlash |
|---|---|---|
| Capacidade | 1 MB a 32 MB | 16 MB a 511 MB |
| XIP (Execução no local) | Sim | Não |
| Velocidade de apagagem | Lenta (cerca de 5 segudnos) | Rápida (cerca de 3 ms) |
| Velocidade de escrita | Lenta | Rápida |
| Velocidade de leitura | Rápida | Rápida |
| Confiabilidade | Alta, com baixa taxa de inversão de bits | Moderada, requer mecanismos de correção de erros |
| Ciclos de apagagem | 10.000 a 100.000 | 100.000 a 1.000.000 |
| Vida útil | Inferior à NandFlash | Superior ao NorFlash |
| Interface | Semelhante à RAM | Interface de E/S |
| Método de acesso | Aleatório | Sequencial |
| Facilidade de uso | Simples | Complexa |
| Aplicação principal | Armazenamento de código e dados críticos | Armazenamento de dados gerais |
| Custo | Elevado | Reduzido |
Pinagem Comum da NandFlash
- I/O0 a I/O7: Linhas de entrada/saída de dados.
- CE# (Chip Enable): Sinal de habilitação do chip; mantém o dispositivo em modo standby quando inativo.
- WE# (Write Enable): Controla a gravação de dados, endereços ou comandos.
- RE# (Read Enable): Ativa o buffer de saída de dados.
- CLE (Command Latch Enable): Quando alto, comandos são capturados na borda de subida de WE#.
- ALE (Address Latch Enable): Quando alto, endereços são capturados na borda de subida de WE#.
- R/B# (Ready/Busy): Indica status do dispositivo; baixo quando ocupado, requer resistor de pull-up.
Estrutura Física da NandFlash
Usando o chip K9F2G08U0B como referência:
- 1 Página = (2K + 64) bytes
- 1 Bloco = 64 Páginas = (128K + 4K) bytes
- 1 Dispositivo = 2048 Blocos = 128K Páginas = (256M + 8M) bytes
A memória inclui áreas adicionais para coreção de erros (ECC), conhecidas como OOB (Out Of Band) em sistemas Linux, essenciais para garantir a integridade dos dados.
Sequência de Endereçamento
Os endereços são divididos em: A0-A11 para endereçamento dentro da página (incluindo dados e ECC), e A12-A28 para endereçamento de páginas e blocos.
Características Especiais em Relação à RAM
| Aspecto | RAM | NandFlash |
|---|---|---|
| Terminologia de operações | Leitura/Escrita | Leitura/Programação |
| Unidade mínima de leitura/escrita | Byte | Página |
| Unidade mínima de apagagem | Byte | Bloco |
| Significado do apagagem | Apaga dados ou escreve zeros | Apaga todo o bloco para 0xFF |
| Pré-requisito para escrita | Escrita direta | Apagar antes de escrever |
Exemplos de Código para Operações
Função para Apagar um Bloco
/* Apaga um bloco especificado
Parâmetros:
bloco: número do bloco a ser apagado
Retorno:
0 para falha, 1 para sucesso */
unsigned char apagarBloco(unsigned int bloco) {
unsigned int endereco_bloco = bloco << 6;
selecionarChip();
enviarComando(0x60);
enviarEndereco(endereco_bloco & 0xFF);
enviarEndereco((endereco_bloco >> 8) & 0xFF);
enviarEndereco((endereco_bloco >> 16) & 0x01);
enviarComando(0xD0);
aguardarPronto();
enviarComando(0x70);
aguardarPronto();
unsigned char status = lerDado();
deselecionarChip();
return (status & 0x1) ? 0 : 1;
}
Função para Ler uma Página
/* Lê dados de uma página
Parâmetros:
buffer: ponteiro para armazenar dados lidos
endereco_origem: endereço de leitura no flash
Retorno:
0 para falha, 1 para sucesso */
static unsigned char lerPagina(unsigned char *buffer, unsigned int endereco_origem) {
if (endereco_origem & MASCARA_BLOCO) return 0;
selecionarChip();
enviarComando(0x00);
enviarEnderecoCompleto(endereco_origem);
enviarComando(0x30);
aguardarPronto();
for (int i = 0; i < TAMANHO_SETOR; i++) {
*buffer++ = lerDado();
}
deselecionarChip();
return 1;
}
Função para Escrever uma Página
/* Escreve dados em uma página
Parâmetros:
buffer: ponteiro com dados a serem escritos
endereco_destino: endereço de escrita no flash
Retorno:
0 para falha, 1 para sucesso */
static unsigned char escreverPagina(unsigned char *buffer, unsigned int endereco_destino) {
if (endereco_destino & MASCARA_BLOCO) return 0;
selecionarChip();
enviarComando(0x80);
enviarEnderecoCompleto(endereco_destino);
for (int i = 0; i < TAMANHO_PAGINA; i++) {
enviarDado(*buffer++);
}
enviarComando(0x10);
aguardarPronto();
enviarComando(0x70);
aguardarPronto();
unsigned char status = lerDado();
deselecionarChip();
return (status & 0x1) ? 0 : 1;
}
Função de Depuração para Imprimir Conteúdo
/* Imprime o conteúdo de uma página específica (para depuração) */
void imprimirPagina() {
unsigned char bloco, pagina;
unsigned char *destino = (unsigned char *)ENDERECO_MEMORIA;
printf("\nLeitura de página NandFlash.\n");
printf("Informe o bloco: ");
bloco = getchar() - '0';
printf("Informe a página: ");
pagina = getchar() - '0';
unsigned int endereco = ((bloco << 6) + pagina) << 11;
if (lerPagina(destino, endereco)) {
printf("Dados lidos (bloco %d, página %d):\n", bloco, pagina);
for (int i = 0; i < TAMANHO_PAGINA; i++) {
if (i % 16 == 0) printf("\n%4x: ", i);
printf("%02x ", *destino++);
}
} else {
printf("Erro na leitura.\n");
}
}
Função para Enviar Endereço
/* Envia endereço completo ao dispositivo */
static void enviarEnderecoCompleto(unsigned int endereco) {
unsigned int coluna = endereco & MASCARA_BLOCO;
unsigned int pagina = endereco / TAMANHO_SETOR;
enviarEndereco(coluna & 0xFF);
pequenaEspera();
enviarEndereco((coluna >> 8) & 0x0F);
pequenaEspera();
enviarEndereco(pagina & 0xFF);
pequenaEspera();
enviarEndereco((pagina >> 8) & 0xFF);
pequenaEspera();
enviarEndereco((pagina >> 16) & 0x01);
pequenaEspera();
}
Nota: As chamadas de espera (por exemplo, pequenaEspera()) são críticas para sincronização, especialmente em frequências de clock elevadas como 400 MHz, para evitar erros de endereçamento.