Dispositivo: dsPIC33FJ128GP802
Eu tenho alguns arquivos * .s da seguinte maneira
.global _D1
.section .speex, code
_D1:
.pword 0x66C821, 0x1B0090, 0xD96C36, 0x9B60B0, 0xDD4E36, 0xBF4E53
.pword 0xD1098B, 0x719BD9, 0x873989, 0x003B69, 0x279035, 0xED4244
.pword 0xE1403C, 0x54D439, 0x826550, 0xC59627, 0xDD0432, 0x88FA29
Eu declarei o mesmo em um * .h
extern void D1(void);
Agora estou passando o D1 para uma função de leitura de tabela
nowPlaying.file1 = (unsigned long) D1;
function(nowPlaying.file1);
Meu problema é que, se o endereço de D1 estiver acima de 0X8000, a rotina não está correta. Tentei modelos de código grandes e pequenos, mas o resultado é o mesmo. Eu acho que isso é devido à limitação de 16 bits dos ponteiros. Existe algum método para acessar o endereço absoluto de D1 diretamente do código. Pode ser algo como função incorporada ou macros.
microcontroller
pic
microchip
compiler
Saneesh AT
fonte
fonte
D1
devem representar uma função ou uma matriz de dados?const short D1[]
.Respostas:
Os dados que você está descrevendo (uso completo de 24 bits da memória do programa para armazenar dados) não podem ser definidos e inicializados em C e não podem ser lidos diretamente via C; a única maneira de acessá-lo é encapsulando em uma função de montagem chamada C ou intrínseca.
Há realmente duas perguntas aqui:
como jogar bem com o compilador, o assembler e o vinculador, para que quando você defina seus dados de 24 bits em um arquivo de assembly como dados relocáveis com um nome simbólico
D1
, em vez de dados não nomeados em um endereço fixo, o compilador possa ver essa variável para determinar seu endereçocomo acessar os dados
A segunda pergunta (como acessar os dados) é respondida pelas partes 33EP no DS70613C e deve ser respondida pelas partes 33FJ no DS70204C (mas os exemplos no manual 33FJ usam apenas os baixos 16 bits). Aqui está um exemplo de trecho de código do manual de referência da 33EP que funciona para as peças da 33EP + deveria para a 33FJ (não tenho um dispositivo 33FJ facilmente disponível):
(nota: o código usa
int
, ao passo que seria melhor usaruint16_t
e#include <stdint.h>
)Você notará que o builtin funciona
__builtin_tblrdl()
e__builtin_tblrdh()
é usado para ler as palavras baixas e altas de 16 bits dos dados de um local de memória do programa e__builtin_tblpage() and __builtin_tbloffset()
pode ser usado para extrair a página e o deslocamento do endereço. Neste exemplo em particular, a matriz highWord é sempre 0 e a matriz lowWord corresponde aos dados prog_data definidos e inicializados em C.Observe que nenhum ponteiro é usado aqui! Embora seja possível usar variáveis normais marcadas com
const
, para que elas estejam localizadas pelo vinculador no espaço de programa somente leitura e para que você possa ler a memória usando técnicas padrão de ponteiro C, com o compilador gerenciando automaticamente os registros de paginação para você, você só pode armazenar dados de 16 bits. Você precisa acessar as funções internas TBLRDL e TBLRDH para obter todos os 24 bits de dados.Quanto a como jogar bem com o compilador / vinculador / etc, você precisa enganar o compilador e dizer que ele está apenas vendo dados de 16 bits. Aqui está um exemplo que funcionou para obter a variável D1 declarada em outro lugar:
Isso lê corretamente os valores de 24 bits e os armazena nos 24 bits inferiores de um uint32_t. A variável D1 externa declarada em C é uma variável fictícia que é usada apenas para obter o endereço inicial, aproveitando a maneira como o compilador / montador / vinculador trabalha em conjunto. As funções internas lidam com o restante do trabalho.
O que eu não sei é como obter automaticamente o tamanho dos dados, pois eles são definidos + inicializados na montagem.
fonte
Não o converta em sem assinatura por muito tempo e de volta. Pedindo problemas. Você está basicamente mentindo para o compilador. A declaração correta para nowPlaying.file1 é
E da mesma forma para function ():
e remova todas as previsões.
Ou, se o @PeterJ sugere, são dados, eles devem ser declarados como D1 externo curto [] nos dois lugares: e você realmente não precisa do montador; você poderia ter declarado tudo em C como const short D1 [] = {...}; O compilador deve colocá-lo no segmento de código, pois é const.
fonte
Parece que a resposta simples é escrever a sub-rotina no assembler. Se bem me lembro, o C30 não acessa a memória do programa como dados usando ponteiros de 24 bits. Na melhor das hipóteses, ele pode acessar a memória do programa através da janela PSV, mas só é possível ver os baixos 16 bits de cada palavra de memória de programa de 24 bits.
Se seria muito simples escrever uma rotina de montagem que possa ser chamada do C30 que retorne os 24 bits de dados, dado um endereço de memória de programa de 24 bits. No entanto, seus dados são uma coleção de valores de 24 bits ou realmente uma lista de bytes que são compactados 3 por palavra? Neste último caso, é ainda mais fácil. Escreva uma rotina de montagem que ofereça uma visão de endereços de bytes da memória do programa. O endereço ainda precisaria ter 24 bits, mas os valores dos dados agora são apenas 8 bits.
Ou apenas escreva toda a rotina no assembler. Se você estiver fazendo esse tipo de troca de bytes e empacotamento de memória de baixo nível, provavelmente é mais fácil. No assembler, você pode fazer o que deseja da maneira que a máquina deseja. Em C, você precisa descobrir quais encantamentos murmurar no compilador para que ele escreva o código da máquina para você. Às vezes, é mais fácil fazê-lo diretamente. A arquitetura dsPIC é particularmente fácil de escrever código de montagem, definitivamente mais fácil do que uma PIC 16.
fonte
__builtin_tblrdX()
funções. Eu concordo com você. Recebi um chute de seu fraseado "Em C, você precisa descobrir que encantamentos murmurar para o compilador". Ironicamente, no entanto, se você está realmente tentando reduzir o desempenho máximo, às vezes as__builtin()
funções são melhores, porque o compilador pode otimizar a maneira como gera código, enquanto ele precisa tratar as funções de montagem codificadas à mão como caixas pretas que não podem alterar .