Endereço absoluto de uma função no Microchip XC16


8

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.


Eu nunca usei a série dsPIC, mas por algum motivo você não pode usar uma matriz C const em vez do assembler? Eu acho que existem opções para colocar isso em locais específicos de código (se você precisar por algum outro motivo). Só de pensar que talvez alguma otimização do compilador reduza os ponteiros, porque não espera fazer referência aos dados nos locais mais altos de memória.
precisa

Sim, o manual do compilador diz que todos os ponteiros, incluindo os ponteiros de função, são de 16 bits. Estou procurando outro método para acessar o endereço de memória. Se usarmos a matriz const, não podemos usar o byte superior de uma palavra de 3 bytes no dsPIC. Outro motivo é que, o arquivo de montagem é gerado por um software de computador fornecido pelo microchip para compactar os dados de fala.
Saneesh em 02/02

Questão relacionada: electronics.stackexchange.com/questions/56058/... (embora isto é para PIC, dsPIC provavelmente funciona da mesma)

Os dados D1devem representar uma função ou uma matriz de dados?
O fóton

2
@Saneesh Então, responda minhas perguntas. É este código ou são dados? Se for código, o sistema não o suporta além do espaço de endereço de 16 bits; portanto, o que você está tentando fazer é impossível, não importa como você o expresse. Se forem dados, diga-o e tente abordá-lo como const short D1[].
user207421

Respostas:


4

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:

  1. 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ço

  2. como 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 usar uint16_te #include <stdint.h>)

int prog_data[10] __attribute__((space(prog))) =
  {0x0000, 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888, 0x9999};

unsigned int lowWord[10], highWord[10];
unsigned int tableOffset, loopCount;

int main(void){
    TBLPAG = __builtin_tblpage (prog_data);
    tableOffset = __builtin_tbloffset (prog_data);
    /* Read all 10 constants into the lowWord and highWord arrays */
    for (loopCount = 0; loopCount < 10; loopCount ++)
    {
        lowWord[loopCount] = __builtin_tblrdl (tableOffset);
        highWord[loopCount] = __builtin_tblrdh (tableOffset);
        tableOffset +=2;
    }
    while(1)
        ;
}

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:

#define D1_SIZE 18
extern uint16_t __attribute__((space(prog))) D1[D1_SIZE];

#define READ_DATA(dst, v, len) readData(dst, __builtin_tblpage(v), __builtin_tbloffset(v), len)
void readData(uint32_t *pdst, uint16_t page, uint16_t offset, uint16_t len)
{
    TBLPAG = page;
    while (len-- > 0)
    {
        uint16_t lo = __builtin_tblrdl (offset);
        uint16_t hi = __builtin_tblrdh (offset);
        *pdst++ = (((uint32_t)(hi)) << 16) | ((uint32_t)(lo));
        offset += 2;
    }
}

...

uint32_t d1copy[D1_SIZE];
READ_DATA(d1copy, D1, D1_SIZE);

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.


1

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 é

struct
{
    // other stuff ...
    void (*D1)(void);
    // other stuff ...
} nowPlaying;

E da mesma forma para function ():

extern void function(void (*file)(void));

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.


A menos que eu esteja lendo algo que D1 não é um ponteiro para uma função, são dados armazenados no espaço do código.
precisa

1
@PeterJ Então não deve ser declarado como vazio externo D1 (vazio), deve ser definido como externo curto D1 []. Nada disso me convence de que a pergunta não pertence ao SO.
precisa saber é o seguinte

O que o OP está falando não pode ser expresso em C, ele precisa ser encapsulado por uma função de montagem chamada C ou por uma intrínseca.
Jason S

@JasonS Não há evidências disso na pergunta, e o OP não conseguiu esclarecer.
user207421

Sim, existe, se você estiver familiarizado com as arquiteturas PIC33F / PIC33E.
Jason S #

0

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.


TBLRDL e TBLRDH são a chave aqui, se você usa montagem ou as __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 .
Jason S #
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.