Indirizzo assoluto di una funzione in Microchip XC16


8

Dispositivo: dsPIC33FJ128GP802

Ho alcuni file * .s come segue

.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

Ho dichiarato lo stesso in un * .h

extern void D1(void);

Ora sto passando il D1 a una funzione di lettura della tabella

nowPlaying.file1 = (unsigned long) D1;
function(nowPlaying.file1);

Il mio problema è che, se l'indirizzo di D1 è superiore a 0X8000, la routine non è corretta. Ho provato modelli di codice grandi e piccoli, ma il risultato è lo stesso. Penso che ciò sia dovuto alla limitazione a 16 bit dei puntatori Esiste un metodo per accedere all'indirizzo assoluto di D1 direttamente dal codice. Può essere qualcosa come la funzione integrata o le macro.


Non ho mai usato la serie dsPIC, ma per quale motivo non puoi usare un array C const invece dell'assemblatore? Penso che ci siano opzioni per posizionarlo in posizioni di codice specifiche (se necessario per qualche altro motivo). Solo pensando che forse qualche ottimizzazione del compilatore accorcia i puntatori perché non si aspetta che faccia riferimento ai dati nelle posizioni di memoria più alte.
PeterJ,

Sì, il manuale del compilatore dice che tutti i puntatori, inclusi i puntatori a funzioni, sono a 16 bit. Sto cercando qualche altro metodo per accedere all'indirizzo di memoria. Se usiamo const array, non possiamo usare il byte superiore di una parola di 3 byte in dsPIC. Un altro motivo è che il file assembly viene generato da un software fornito da microchip per comprimere i dati vocali.
Saneesh il

Domanda correlata: electronics.stackexchange.com/questions/56058/… (anche se questo è per PIC, dsPIC probabilmente funziona allo stesso modo)

Si D1suppone che i dati rappresentino una funzione o una matrice di dati?
The Photon,

2
@Saneesh Quindi rispondi alle mie domande. È questo codice o sono dati? Se è un codice, il sistema non lo supporta oltre lo spazio degli indirizzi a 16 bit, quindi ciò che stai cercando di fare è impossibile, indipendentemente da come lo esprimi. Se si tratta di dati, ti preghiamo di dirlo e prova a indirizzarli come const short D1[].
user207421

Risposte:


4

I dati che stai descrivendo (uso completo a 24 bit della memoria del programma per memorizzare i dati) non possono essere definiti e inizializzati in C e non possono essere letti direttamente tramite C; l'unico modo per accedervi è incapsulare in una funzione di assemblaggio richiamabile in C o intrinseca.

Ci sono davvero due domande qui:

  1. come giocare bene con il compilatore, l'assemblatore e il linker, in modo che quando definisci i tuoi dati a 24 bit in un file di assieme come dati trasferibili con un nome simbolico D1, piuttosto che dati senza nome a un indirizzo fisso, il compilatore può vedere questa variabile per determinare il suo indirizzo

  2. come accedere ai dati

Alla seconda domanda (come accedere ai dati) viene data risposta per le parti 33EP in DS70613C e si dovrebbe rispondere per le parti 33FJ in DS70204C (ma gli esempi nel manuale 33FJ usano solo i 16 bit bassi). Ecco un esempio di frammento di codice del manuale di riferimento 33EP che funziona per parti 33EP + dovrebbe per 33FJ (non ho un dispositivo 33FJ facilmente disponibile):

(nota: il codice usa int, mentre sarebbe meglio usare 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)
        ;
}

Noterai che le funzioni integrate __builtin_tblrdl()e __builtin_tblrdh()vengono utilizzate per leggere le parole di dati a 16 bit basse e alte da una posizione di memoria del programma e __builtin_tblpage() and __builtin_tbloffset()possono essere utilizzate per estrarre la pagina e l'offset dell'indirizzo. In questo esempio particolare, l'array highWord è sempre 0 e l'array lowWord corrisponde ai prog_data definiti e inizializzati in C.

Si prega di notare che qui non vengono utilizzati puntatori! Sebbene sia possibile utilizzare le normali variabili con tag const, in modo che siano posizionate dal linker nello spazio del programma di sola lettura e in modo da poter leggere la memoria utilizzando tecniche di puntatore C standard, con il compilatore che gestisce automaticamente i registri di paging per te, puoi solo archiviare dati a 16 bit. È necessario accedere alle funzioni integrate TBLRDL e TBLRDH per ottenere tutti i 24 bit di dati.

Per quanto riguarda come giocare bene con il compilatore / linker / ecc, devi ingannare il compilatore e dire che sta vedendo solo dati a 16 bit. Ecco un esempio che ha funzionato per ottenere la variabile D1 dichiarata altrove:

#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);

Questo legge correttamente i valori a 24 bit e li memorizza nei 24 bit inferiori di un uint32_t. La variabile D1 esterna dichiarata in C è una variabile fittizia che viene utilizzata solo per ottenere l'indirizzo iniziale sfruttando il modo in cui il compilatore / assemblatore / linker lavorano insieme. Le funzioni integrate gestiscono il resto del lavoro.

Quello che non so è come ottenere automaticamente la dimensione dei dati, dal momento che è definito + inizializzato in assembly.


1

Non convertirlo in unsigned long and back. Chiedere problemi. Stai praticamente mentendo al compilatore. La dichiarazione corretta per nowPlaying.file1 è

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

E allo stesso modo per function ():

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

e rimuovi tutti i dattiloscritti.

Oppure, se @PeterJ suggerisce, sono dati, dovrebbero essere dichiarati come D1 esterni [] in entrambi i posti: e non hai davvero bisogno dell'assemblatore; avresti potuto dichiararlo tutto in C come const short D1 [] = {...}; Il compilatore dovrebbe inserirlo nel segmento di codice in quanto è const.


A meno che non stia leggendo qualcosa che D1 non è un puntatore a una funzione, i suoi dati vengono memorizzati nello spazio del codice.
PeterJ,

1
@PeterJ Quindi non dovrebbe essere dichiarato come vuoto esterno D1 (vuoto), dovrebbe essere definito come esterno corto D1 []. Niente di tutto ciò mi convince che la domanda non appartiene a SO.
user207421

Ciò di cui parla l'OP non può assolutamente essere espresso in C, deve essere incapsulato da una funzione di assemblaggio chiamata C o da un valore intrinseco.
Jason S

@JasonS Non ci sono prove di ciò nella domanda e l'OP non è riuscito a chiarire.
user207421

Sì, se hai familiarità con le architetture PIC33F / PIC33E.
Jason S,

0

Sembra che la semplice risposta sia scrivere la subroutine in assembler. Se ricordo bene, C30 non accede alla memoria del programma come dati usando i puntatori a 24 bit. Nella migliore delle ipotesi può accedere alla memoria del programma attraverso la finestra del PSV, ma in questo modo è possibile vedere solo i 16 bit bassi di ogni parola di memoria del programma a 24 bit.

Sarebbe molto semplice scrivere una routine di assemblaggio richiamabile da C30 che restituisce i 24 bit di dati dati un indirizzo di memoria del programma a 24 bit. Tuttavia, i tuoi dati sono una raccolta di valori a 24 bit o in realtà un elenco di byte che risultano impacchettati 3 per parola? In quest'ultimo caso, è ancora più semplice. Scrivi una routine di assemblaggio che ti dia una vista degli indirizzi byte della memoria del programma. L'indirizzo dovrebbe comunque essere 24 bit, ma i valori dei dati ora solo 8 bit.

O semplicemente scrivere l'intera routine in assembler. Se stai facendo questo tipo di byte banging a basso livello e imballaggio della memoria, è probabilmente più facile. In assemblatore puoi semplicemente fare quello che vuoi nel modo in cui la macchina vuole farlo. In C devi capire quali incantesimi mormorare al compilatore per farlo scrivere il codice macchina per te. A volte è più semplice farlo direttamente da soli. L'architettura dsPIC è particolarmente facile da scrivere per il codice assembly, decisamente più semplice di un PIC 16.


TBLRDL e TBLRDH sono la chiave qui, sia che si utilizzi assembly o le __builtin_tblrdX()funzioni. Sono d'accordo con te + ho dato una scossa al tuo fraseggio "In C devi capire quali incantesimi mormorare al compilatore". Ironia della sorte, tuttavia, se stai davvero cercando di ottenere le massime prestazioni, a volte le __builtin()funzioni sono migliori, perché il compilatore può ottimizzare il modo in cui genera il codice, mentre deve trattare le funzioni di assemblaggio codificate a mano come scatole nere che non può alterare .
Jason S,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.