Microchip XC8: Wie überlappen sich bestimmte Variablen ohne Union?

Ich habe effektiv drei Sätze von Variablen, die ich an zwei Stellen platzieren kann. Die Sätze 1 und 2 werden nie zusammen verwendet, sodass sie den gleichen Platz einnehmen können. Satz 3 muss eindeutig sein.

Ich weiß, dass ich eine Vereinigung von Strukturen verwenden kann, um das zu bekommen, was ich will, aber das sind eine Menge Daten, die in denselben Bereich gezwungen werden müssen, und es macht die Namen meiner Meinung nach unnötig komplex:

union
{
    struct
    {
        ...
    } set_1;
    struct
    {
        ...
    } set_2;
} shared_variables;

Ich kann jeder Variable auch eine bestimmte Adresse zuweisen, aber das lässt dem Linker keine Freiheit, sie zu optimieren:

unsigned char foo_1 @ 0x1A0;
unsigned int  bar_1 @ 0x1A1;

unsigned long foo_2 @ 0x1A0;    //same address as foo_1

Ich kann jeder Variable einen Abschnittsnamen zuweisen und dem Linker sagen, was er mit den Abschnitten machen soll, aber ich denke immer noch, dass es zu restriktiv ist:

unsigned char __section("set_1") foo_1;    //set_1 is 1 byte
unsigned int  __section("set_1") bar_1;    //set_1 is now 3 bytes

unsigned long __section("set_2") foo_2;    //set_2 is 4 bytes

//linker: -L-Ashared_class=<address_range> -L-Pset_1=shared_class -L-Pset_2=shared_class
//actually concatenates them somewhere in the specified range (total 7 bytes), not sure how to overlay them (total 4 bytes)

Gibt es eine Möglichkeit zu sagen: "Es ist mir egal, ob sich dieser Satz von Variablen mit diesem Satz von Variablen überschneidet, aber dieser andere Satz von Variablen muss eindeutig sein", und es einfach dabei belassen?

Als Antwort auf Kommentare bearbeiten:

  • Satz 1 + Satz 2 + Satz 3 stellen alle Variablen dar, die im gesamten Projekt verwendet werden.
  • Der Code, der sie verwendet, ist derzeit auf 4 Quellmodule verteilt.
  • Wie die meisten, wenn nicht alle von Microchip hergestellten Chips in dieser Reihe, ist der Speicher in der Hardware in 80-Byte-Blöcke fragmentiert, die durch Hardware-Konfigurationsregister getrennt sind. Es gibt einen anderen Adressierungsmodus, der einen Zeiger erfordert, um auf alle Fragmente zuzugreifen, als ob sie zusammenhängend wären, aber das würde die Zeigerregister stark beeinträchtigen . (und es scheint einen Fehler im Debugger zu geben, der in bestimmten Fällen Daten beschädigt, die auf diese Weise verwendet werden; normale Ausführung ist jedoch in Ordnung.)
Ich finde Gewerkschaften eine vollkommen gute Lösung für diesen Job.
Normalerweise ja, aber das gesamte Projekt hat viel mehr Daten, als in eine RAM-Bank passen, und die beiden Sätze, die sich überschneiden können, haben mit völlig separaten Aktivitäten zu tun, die in verschiedenen Quelldateien codiert sind. Eine Gewerkschaft würde also wahrscheinlich die Dinge verwirren.
Wenn sie "nie zusammen verwendet werden", können Sie dies nicht einfach von scope für Sie erledigen lassen? Das Schreiben von C-Code mit Variablen an bestimmten Adressen macht das Schreiben in einer höheren, portablen Sprache zunichte!
@David: Ich glaube nicht, dass XC8 schlau genug ist, das herauszufinden. Der Managercode verwendet eine Variable, um zu bestimmen, ob ein bestimmtes Modul ausgeführt werden soll oder nicht. Wenn ja, ruft es die Funktion(en) des Moduls auf; wenn nicht, tut es nicht. Aus der Sicht des Compilers sieht es so aus, als könnten sie alle gleichzeitig laufen, was nicht stimmt.
Wenn die Funktion(en) nicht verschachtelt sind (rufen sich nicht gegenseitig auf) und nicht von Interrupt-Routinen aufgerufen werden, dann hätte ich gedacht, dass XC8 es für lokale Variablen herausfinden sollte. Benötigen Sie unbedingt globale Variablen (wobei der Compiler natürlich Probleme hat)?
Ich verwende einige von ihnen, um zwischen Funktionen zu kommunizieren, die vom Manager zu unterschiedlichen Zeiten aufgerufen werden (alle verwenden dieselbe Bedingung, um den Aufruf zuzulassen) und einige zwischen Interrupt- und Mainline-Code, also müssen sie leider ja global sein.
Und ich glaube, XC8 findet es für Einheimische heraus.

Antworten (2)

Ich finde Gewerkschaften vollkommen in Ordnung, um Variablen zu "teilen". Lassen Sie mich dies mit Code erweitern:

typedef struct
{
  union
  {
     struct
     {
     } set_1;
     struct
     {
     } set_2;
  } set_union;
  struct
  {
  } set_3;
} data_set;

... Dies könnte natürlich mit typedefson set_1und deutlich lesbarer gemacht werden set_2.

Für relativ kleine Datensätze funktioniert das perfekt. Ich mache das eigentlich schon, um zum Beispiel von einem generischen Bit-Eimer in eine organisiertere Struktur zu übersetzen. Aber auf der Ebene, auf die sich diese Frage bezieht, würde Ihre äußere Struktur tatsächlich das gesamte Projekt abdecken. Meistens funktioniert der Code auf eine bestimmte Weise, aber manchmal wird ein Großteil davon deaktiviert und ein anderer Teil für eine Weile aktiviert. Beide alternativen Aktivitäten gehen von einem Neustart aus, wenn sie aktiviert sind, und initialisieren sich selbst neu.
@AaronD Was meinst du damit, das gesamte Projekt abzudecken ? Die Binärgröße wird genau so sein, wie Sie es wollten.
Es gäbe keine Variablen außerhalb der äußeren Struktur. Ein Teil des Codes verwaltet einige gemeinsam genutzte Ressourcen und bestimmt, welche anderen Teile ausgeführt werden können; es verwendet set_3. Die anderen beiden Teile schließen sich gegenseitig aus und bieten spezifischere Funktionen; sie verwenden set_1 bzw. set_2. Es gibt keinen anderen Code.

Es ist einfach, das Problem „macht die Namen unnötig komplex“ zu lösen.

Erweitern Sie Ihr Beispiel:

union
{
    struct
    {
        int fubar;
        ...
    } set_1;
    struct
    {
        int snafu;
        ...
    } set_2;
} shared_variables;

Eine Lösung ist die Verwendung einiger Makros:

#define a (shared_variables.set_1)
#define b (shared_variables.set_2)

a.fubar = ...;

b.snafu += ...;

Ein Teil des Vorteils dieser Lösung besteht darin, dass der Code immer noch gewöhnliches C ist und nicht von Compiler-spezifischen Funktionen abhängt.

Es besteht eine geringe Möglichkeit, dass der Compiler besseren Code generiert, wenn er die gesamte Zuweisung von Variablen durchführt, anstatt einige der Informationen in den Linker zu schieben. Ich weiß jedoch nicht genug über xc8, um zu sagen, dass dies der Fall ist.

Bearbeiten:
Es scheint in Ordnung zu sein, Variablen auf bestimmte Segmente zu lenken. Dies wirkt sich nur auf die beiden Sätze aus, die sich gegenseitig ausschließen, und nicht auf alle Variablen.

Das Benutzerhandbuch des xc8-Compilers beschreibt auch einen anderen Mechanismus zum Platzieren von Objekten in "5.14.4.8 THE #PRAGMA PSECT DIRECTIVE", der möglicherweise vorzuziehen ist, ABER, ein einziger #pragmawürde alle nachfolgenden Variablen platzieren.

Dies erscheint viel einfacher und weniger fehleranfällig als __section("...")bei jeder Variablen, verglichen mit einer einzigen #pragmavor jeder Liste von Variablen.

Es gibt eine spezielle Warnung bezüglich der #pragma, die wahrscheinlich nicht für sich gegenseitig ausschließende Variablen gilt:

Dieses Pragma sollte nicht für Datensektionen (data oder idata) verwendet werden, die initialisierte Variablen enthalten.

Ich würde jedoch eine Union + zwei Strukturen verwenden, es sei denn, es gibt weitere Einschränkungen.

Edit2:
Wenn die Speicherzuweisung sehr eng ist und es wichtig ist , Variablen in Segmente mit fester Größe zu packen, schreibe ich möglicherweise ein Programm, um den C-Code der Deklaration zu generieren . Ich würde eine Sprache mit guter Textverarbeitung verwenden, um es einfacher zu machen.

Ja, das habe ich auch gesehen, und es wirkt sich auf die gesamte Datei aus. Ich muss es verwenden, um einige andere Teile dieses Projekts zum Laufen zu bringen. Ich mag den Spezifizierer __section() viel besser, wenn er funktioniert. Wie auch immer, es scheint immer noch, dass der Linker sich weigert, zwei Psekten zu überlagern, es sei denn, er wird dazu gezwungen, indem er ihnen dieselbe spezifische Adresse gibt, was nur besser ist als eine direkte Adresse in seiner Abstraktion vom Quellcode.
@AaronD- A . Ich verstehe nicht, warum "der Linker sich weigert, zwei Psekten zu überlagern, es sei denn, er ist dazu gezwungen" unerwartet wäre. IMHO ist das das einzige offensichtliche Verhalten für einen Linker. B. _ Das Definieren der gleichen Startadresse für zwei "Psekten" scheint die richtige Informationsebene zu sein, jedoch scheint eine Vereinigung + zwei Strukturen besser zu sein, als mit psectsLinker-Skripten herumzuspielen. Einen Entwickler zu zwingen, Linker- Skripte zu lesen, um ein Programm zu verstehen, scheint eine schlechte Idee zu sein, und viele sich wiederholende Deklarationen zu verwenden, scheint eine schlechte Praxis zu sein. C. _ "Ich mag den Spezifizierer __section() viel besser". Okay. Na und?
Der Spezifizierer __section() ermöglicht es, dass ähnliche Dinge in derselben Datei an verschiedenen Stellen abgelegt werden, und kümmert sich nicht darum, wohin sie gegangen wären. Das #pragma muss das einzige in der Datei sein, da es wirklich global für die Datei ist und nicht "bis auf weiteres", und seine Funktion besteht wirklich darin, einen bereits vorhandenen Abschnitt umzubenennen, den der Compiler erstellt hat. Sie müssen also wissen, wohin das Zeug gegangen wäre, um es zu verwenden, was nicht offensichtlich ist, es sei denn, Sie sind sehr erfahren mit diesem Compiler. Wenn es eine Option gäbe , die bis auf weiteres funktioniert, wäre das noch besser.
Ich sage nicht, dass das Verhalten unerwartet ist oder dass ich es im Allgemeinen nicht mag. Tatsächlich gefällt es mir, dass dies standardmäßig der Fall ist, nur nicht in dieser speziellen Situation.
Was das Lesen von Linker-Skripten betrifft, fürchte ich, dass sie aus anderen Gründen notwendig sein werden. Deshalb versuche ich, sie so generisch wie möglich zu halten, damit sie nicht geändert werden müssen, damit eine geringfügige Überarbeitung funktioniert.
Das Problem mit einer Vereinigung von Strukturen ist, dass diese massiv wäre, sie würde alles über 4 Quelldateien hinweg sammeln, und dieser Chip hat einen schrecklich fragmentierten Speicher als Hardware-Design. Jedes Fragment enthält 80 Bytes, oder ich kann einen anderen Adressraum verwenden, um über weniger effiziente Zeiger, die ständig neu berechnet werden müssen, auf alles zusammenhängend zuzugreifen. Wenn ich erkennen kann, dass diese beiden Gruppen nicht gleichzeitig verwendet werden, und direkte Zugriffe auf denselben Ort generieren kann, wäre das perfekt.
@AaronD - Ich denke, dieser Kommentar enthält neue Informationen, die in Ihre Frage aufgenommen werden sollten. IMHO ist es besser zu versuchen , alle Informationen in die Frage aufzunehmen . Wenn Kommentare dazu führen, dass neue Informationen gefunden werden, fügen Sie sie der Frage hinzu. Es ist schwierig, eine Antwort zu geben, wenn die Frage unvollständig ist, und zusätzliche Arbeit für die Antwortenden, wenn Kommentare wichtige Informationen enthalten.
Du hast recht, ich hatte nicht daran gedacht, das einzufügen. Ich denke, ich sollte nicht so viel über eingebettete Architekturen annehmen.
@AaronD - Sie haben dies wahrscheinlich Tage und Wochen lang gelebt und geatmet, daher ist es schwierig, Annahmen zu vermeiden. Meiner Erfahrung nach hilft mir das Klären und Verfeinern einer Frage als Antwort auf Fragen oder Antworten, die nicht „passen“, manchmal, die Antwort zu erkennen. Ich könnte es auf diese Weise vor allen anderen 'verstehen'. Zumindest sind alle „auf derselben Seite“.