Absolute Adresse einer Funktion in Microchip XC16

8

Gerät: dsPIC33FJ128GP802

Ich habe einige * .s-Dateien wie folgt

.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

Ich habe das gleiche in einem * .h erklärt

extern void D1(void);

Jetzt übergebe ich den D1 an eine Tabellenlesefunktion

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

Mein Problem ist, dass die Routine nicht korrekt ist, wenn die Adresse von D1 über 0X8000 liegt. Ich habe große und kleine Codemodelle ausprobiert, aber das Ergebnis ist das gleiche. Ich denke, das liegt an der 16-Bit-Beschränkung der Zeiger. Gibt es eine Methode, um direkt aus dem Code auf die absolute Adresse von D1 zuzugreifen? Kann so etwas wie eine eingebaute Funktion oder Makros sein.

Saneesh AT
quelle
Ich habe die dsPIC-Serie noch nie verwendet, aber aus irgendeinem Grund können Sie kein C const-Array anstelle des Assemblers verwenden? Ich denke, es gibt Optionen, um das an bestimmten Code-Positionen zu platzieren (wenn Sie aus einem anderen Grund müssen). Ich denke nur, dass eine Compileroptimierung die Zeiger verkürzt, weil nicht erwartet wird, dass Daten an den höheren Speicherorten referenziert werden.
PeterJ
Ja, das Compiler-Handbuch sagt, dass alle Zeiger einschließlich Funktionszeiger 16-Bit sind. Ich suche nach einer anderen Methode, um auf die Speicheradresse zuzugreifen. Wenn wir das const-Array verwenden, können wir das obere Byte eines 3-Byte-Wortes in dsPIC nicht verwenden. Ein weiterer Grund ist, dass die Baugruppendatei von einer Computersoftware generiert wird, die von einem Mikrochip zum Komprimieren von Sprachdaten bereitgestellt wird.
Saneesh
Verwandte Frage: electronic.stackexchange.com/questions/56058/… (obwohl dies für PIC ist, funktioniert dsPIC wahrscheinlich genauso)
Sollen die Daten D1eine Funktion oder ein Array von Daten darstellen?
Das Photon
2
@Saneesh Also beantworte meine Fragen. Ist das Code oder sind es Daten? Wenn es sich um Code handelt, unterstützt das System ihn nicht über den 16-Bit-Adressraum hinaus. Was Sie also versuchen, ist unmöglich, unabhängig davon, wie Sie ihn ausdrücken. Wenn es sich um Daten handelt, sagen Sie dies bitte und versuchen Sie, diese als zu adressieren const short D1[].
user207421

Antworten:

4

Die von Ihnen beschriebenen Daten (vollständige 24-Bit-Nutzung des Programmspeichers zum Speichern von Daten) können in C nicht definiert und initialisiert werden und können nicht direkt über C gelesen werden. Der einzige Weg, darauf zuzugreifen, ist die Kapselung in eine C-aufrufbare Assembly-Funktion oder eine intrinsische.

Hier gibt es wirklich zwei Fragen:

  1. So spielen Sie gut mit dem Compiler, Assembler und Linker, sodass D1der Compiler diese Variable sehen kann , wenn Sie Ihre 24-Bit-Daten in einer Assembly-Datei als verschiebbare Daten mit einem symbolischen Namen und nicht als unbenannte Daten an einer festen Adresse definieren um seine Adresse zu bestimmen

  2. wie man auf die Daten zugreift

Die zweite Frage (Zugriff auf die Daten) wird für 33EP-Teile in DS70613C beantwortet und sollte für 33FJ-Teile in DS70204C beantwortet werden (in den Beispielen im 33FJ-Handbuch werden jedoch nur die niedrigen 16 Bit verwendet). Hier ist ein Beispiel-Code-Snippet aus dem 33EP-Referenzhandbuch, das für 33EP-Teile + für 33FJ funktioniert (ich habe kein leicht verfügbares 33FJ-Gerät):

(Anmerkung: Code Anwendungen int, während es besser wäre , zu verwenden uint16_tund #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)
        ;
}

Sie werden feststellen, dass die integrierten Funktionen __builtin_tblrdl()und __builtin_tblrdh()zum Lesen der niedrigen und hohen 16-Bit-Wörter von Daten aus einem Programmspeicher __builtin_tblpage() and __builtin_tbloffset()verwendet werden und zum Extrahieren der Seite und des Versatzes der Adresse verwendet werden können. In diesem speziellen Beispiel ist das highWord-Array immer 0, und das lowWord-Array stimmt mit den in C definierten und initialisierten prog_data überein.

Bitte beachten Sie, dass hier keine Zeiger verwendet werden! Es ist zwar möglich, normale Variablen zu verwenden, die mit Tags versehen sind const, so dass sie vom Linker im schreibgeschützten Programmbereich lokalisiert werden und Sie den Speicher mit Standard-C-Zeigertechniken lesen können, wobei der Compiler die Paging-Register automatisch verwaltet Für Sie können Sie nur 16-Bit-Daten speichern. Sie müssen auf die integrierten Funktionen TBLRDL und TBLRDH zugreifen, um alle 24 Datenbits abzurufen.

Um mit dem Compiler / Linker / etc gut zu spielen, müssen Sie den Compiler täuschen und ihm mitteilen, dass nur 16-Bit-Daten angezeigt werden. Hier ist ein Beispiel, das funktioniert hat, um an die an anderer Stelle deklarierte Variable D1 zu gelangen:

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

Dadurch werden 24-Bit-Werte korrekt gelesen und in den unteren 24 Bit eines uint32_t gespeichert. Die in C deklarierte externe D1-Variable ist eine Dummy-Variable, die nur verwendet wird, um an die Startadresse zu gelangen, indem die Zusammenarbeit von Compiler / Assembler / Linker ausgenutzt wird. Die eingebauten Funktionen erledigen den Rest der Arbeit.

Was ich nicht weiß, ist, wie die Größe der Daten automatisch ermittelt wird, da sie in der Assembly definiert + initialisiert sind.

Jason S.
quelle
1

Konvertieren Sie es nicht in unsigned long and back. Ärger suchen. Sie lügen im Grunde den Compiler an. Die korrekte Deklaration für nowPlaying.file1 lautet

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

Und ähnlich für function ():

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

und entfernen Sie alle Typecasts.

Oder, wenn @PeterJ vorschlägt, dass es sich um Daten handelt, sollten diese an beiden Stellen als extern short D1 [] deklariert werden: und Sie brauchen den Assembler nicht wirklich; Sie hätten alles in C als const short D1 [] = {...} deklarieren können; Der Compiler sollte es in das Codesegment einfügen, da es const ist.

user207421
quelle
Wenn ich etwas nicht falsch lese, ist D1 kein Zeiger auf eine Funktion, sondern es werden Daten im Codebereich gespeichert.
PeterJ
1
@PeterJ Dann sollte es nicht als extern void D1 (void) deklariert werden, sondern als extern short D1 [] definiert werden. Nichts davon überzeugt mich davon, dass die Frage nicht zu SO gehört.
user207421
Worüber das OP spricht, kann überhaupt nicht in C ausgedrückt werden, es muss durch eine C-aufrufbare Assembly-Funktion oder eine intrinsische gekapselt werden.
Jason S
@JasonS Es gibt keine Beweise dafür in der Frage, und das OP hat es nicht geklärt.
user207421
Ja, wenn Sie mit den PIC33F / PIC33E-Architekturen vertraut sind.
Jason S
0

Es scheint, als ob die einfache Antwort darin besteht, das Unterprogramm in Assembler zu schreiben. Wenn ich mich recht erinnere, greift C30 nicht mit 24-Bit-Zeigern auf den Programmspeicher als Daten zu. Bestenfalls kann es über das PSV-Fenster auf den Programmspeicher zugreifen, aber dann können Sie nur die niedrigen 16 Bits jedes 24-Bit-Programmspeicherworts sehen.

Es wäre sehr einfach, eine Assembler-Routine zu schreiben, die von C30 aus aufgerufen werden kann und die 24 Bit Daten bei einer 24-Bit-Programmspeicheradresse zurückgibt. Sind Ihre Daten jedoch eine Sammlung von 24-Bit-Werten oder wirklich eine Liste von Bytes, die zufällig 3 pro Wort gepackt sind? Wenn letzteres der Fall ist, ist es noch einfacher. Schreiben Sie eine Assembler-Routine, die Ihnen eine Ansicht der Byteadressen des Programmspeichers bietet. Die Adresse müsste noch 24 Bit sein, aber die Datenwerte sind jetzt nur noch 8 Bit.

Oder schreiben Sie einfach die gesamte Routine in Assembler. Wenn Sie diese Art von Low-Level-Byte-Banging und Speicherpacken ausführen, ist dies wahrscheinlich einfacher. In Assembler können Sie einfach das tun, was Sie wollen, so wie es die Maschine will. In C müssen Sie herausfinden, welche Beschwörungsformeln Sie an den Compiler murmeln müssen, damit dieser den Maschinencode für Sie schreibt. Manchmal ist es einfach einfacher, es direkt selbst zu tun. Die dsPIC-Architektur ist besonders einfach zu schreiben, definitiv einfacher als ein PIC 16.

Olin Lathrop
quelle
TBLRDL und TBLRDH sind hier der Schlüssel, unabhängig davon, ob Sie Assembly oder die __builtin_tblrdX()Funktionen verwenden. Ich stimme Ihnen zu + habe einen Kick aus Ihrer Formulierung "In C müssen Sie herausfinden, welche Beschwörungsformeln Sie dem Compiler murmeln müssen". Ironischerweise sind die __builtin()Funktionen manchmal besser , wenn Sie wirklich versuchen, die maximale Leistung zu erzielen, da der Compiler die Art und Weise, wie Code generiert wird, optimieren kann, während er handcodierte Assembly-Funktionen als Black Box behandeln muss, die er nicht ändern kann .
Jason S