Pergunta

Em um sistema embarcado, nós temos um programa de configuração que nos permite ler dados arbitrários através de uma interface de linha de comando para fins de diagnóstico.Para a maioria dos dados, isso funciona bem, usamos memcpy() para copiar os dados no endereço solicitado e enviar de volta através de uma conexão serial.

No entanto, para 16-bit de registradores de hardware, memcpy() causa alguns problemas.Se eu tentar acessar um de 16 bits, hardware registrar-se utilizar dois de 8 bits de acessos, o byte de ordem elevada não ler corretamente.

Alguém já encontrou esse problema?Eu sou um 'alto nível' (C#/Java/Python/Ruby) cara que está se aproximando o hardware e este é território estrangeiro.

Qual é a melhor maneira para lidar com isso?Eu vejo algumas informações, especificamente, é um pouco confuso [para mim] post aqui.O autor deste post tem exatamente o mesmo problema que eu faço, mas eu odeio a implementar uma solução sem entender o que eu estou fazendo.

Qualquer luz que você pode derramado sobre esta questão é muito apreciada.Obrigado!

Foi útil?

Solução

Além de o que Eddie disse, você normalmente precisa usar um ponteiro volátil para ler um registro de hardware (assumindo um registro mapeado de memória, o que não é o caso de todos os sistemas, mas parece que é verdade para o seu). Algo como:

// using types from stdint.h to ensure particular size values
// most systems that access hardware registers will have typedefs
// for something similar (for 16-bit values might be uint16_t, INT16U,
// or something)

uint16_t volatile* pReg = (int16_t volatile*) 0x1234abcd;  // whatever the reg address is

uint16_t val = *pReg;  // read the 16-bit wide register

Aqui está uma série de artigos de Dan Saks que devem fornecer praticamente tudo o que você precisa saber para poder usar efetivamente registros mapeados de memória em C/C ++:

Outras dicas

Cada registro neste hardware é exposto como uma matriz de dois bytes, o primeiro elemento é alinhado em um limite de dois bytes (seu endereço é par). Memcpy () executa um ciclo e copia um byte em cada iteração, por isso copia desses registros dessa maneira (todos os loops desenrolados, Char é um byte):

*((char*)target) = *((char*)register);// evenly aligned - address is always even
*((char*)target + 1) = *((char*)register + 1);//oddly aligned - address is always odd

No entanto, a segunda linha funciona incorretamente por alguns motivos específicos de hardware. Se você copiar dois bytes de cada vez, em vez de um de cada vez, isso será feito dessa maneira (curto int é dois bytes):

*((short int*)target) = *((short*)register;// evenly aligned

Aqui você copia dois bytes em uma operação e o primeiro byte está alinhado uniformemente. Como não há cópia separada de um endereço estranhamente alinhado, ele funciona.

O memcpy modificado verifica se os endereços estão alinhados venosos e cópias em bytes de reboque, se forem.

Se você precisar de acesso aos registros de hardware de um tamanho específico, você terá duas opções:

  • Entenda como seu compilador C gera código para que você possa usar o tipo inteiro apropriado para acessar a memória, ou
  • Incorpore alguma montagem para fazer o acesso com o byte correto ou o tamanho da palavra.

A leitura de registros de hardware pode ter efeitos colaterais, dependendo do registro e de sua função, é claro, por isso é importante acessar registros de hardware com o acesso adequado para que você possa ler o registro inteiro de uma só vez.

Normalmente, é suficiente usar um tipo inteiro do mesmo tamanho que o seu registro. Sobre a maioria Compiladores, um curto é de 16 bits.

void wordcpy(short *dest, const short *src, size_t bytecount)
{
    int i;
    for (i = 0;  i < bytecount/2;  ++i)
        *dest++ = *src++;
}

Acho que todos os detalhes estão contidos naquele tópico que você postou, então tentarei dividi -lo um pouco;

Especificamente;

If you access a 16-bit hardware register using two 8-bit
accesses, the high-order byte doesn't read correctly (it
always read as 0xFF for me). This is fair enough since
TI's docs state that 16-bit hardware registers must be
read and written using 16-bit-wide instructions, and
normally would be, unless you're using memcpy() to
read them.

Portanto, o problema aqui é que os registros de hardware relatam apenas o valor correto se seus valores forem lidos em uma única leitura de 16 bits. Isso seria equivalente a fazer;

uint16 value = *(regAddress);

Isso lê do endereço no registro de valor usando uma leitura de 16 bytes. Por outro lado, você tem memcpy, que está copiando dados um byte de cada vez. Algo como;

while (n--)
{
  *(uint8*)pDest++ = *(uint8*)pSource++;
}

Portanto, isso faz com que os registros sejam lidos de 8 bits (1 byte) por vez, resultando nos valores inválidos.

A solução publicada nesse encadeamento é usar uma versão do memcpy que copiará os dados usando leituras de 16 bits, onde quer que a origem e o destino estejam alinhados.

O que você precisa saber?Você já encontrou um post separado para explicá-la.Aparentemente, a CPU documentação requer 16-bit de registradores de hardware são acessados com 16 bits lê e escreve, mas a sua implementação de memcpy usa 8 bits lê/escreve.Portanto, eles não funcionam juntos.

A solução é simplesmente não usar a função memcpy para aceder a este registo.Em vez disso, escrever a sua própria rotina que copia os valores de 16 bits.

Não tenho certeza exatamente qual é a pergunta - acho que essa postagem tem a solução certa. Como você afirmou, o problema é que a rotina Memcpy () padrão lê um byte por vez, o que não funciona corretamente para registros de hardware mapeados de memória. Essa é uma limitação do processador - simplesmente não há como obter um valor válido lendo um byte no momento.

A solução sugerida é escrever seu próprio memcpy (), que funciona apenas em endereços alinhados a palavras e lê palavras de 16 bits por vez. Isso é bastante direto - o link fornece uma versão CA e de montagem. O único Gotcha é garantir que você sempre faça as cópias de 16 bits a partir de um endereço valioso. Você pode fazer isso de duas maneiras: use comandos de ligação ou pragmas para garantir que as coisas estejam alinhadas ou adicione um caso especial para o byte extra na frente de um buffer desalinhado.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top