Kompilieren von Code zum Ausführen aus dem externen RAM


13

Ich denke über Entwürfe für ein minimalistisches Spielsystem nach, das auf einem PIC18F85J5 basiert . Teil meines Designs ist, dass Spiele von einer SD-Karte geladen werden können, ohne den Chip neu zu programmieren oder den Programmspeicher zu flashen. Ich habe mich für diesen Chip entschieden, weil er über eine externe Speicherschnittstelle verfügt, mit der ich Code von einem externen SRAM ausführen kann.

Die Grundidee ist, dass der interne Programmspeicher eine Schnittstelle zum Durchsuchen der SD-Karte enthält. Sobald der Benutzer ein Programm auswählt, kopiert er eine Hex-Datei von der SD-Karte auf den externen RAM und springt dann zur Ausführung in den externen RAM-Bereich .

Der interne Programmspeicher enthält außerdem verschiedene Bibliotheken für Grafiken, Controller-Eingaben und andere verschiedene Dienstprogramme.

Ich bin ziemlich sicher, dass ich weiß, wie die internen Firmware-Teile funktionieren. Das Problem besteht darin, Programme zu erstellen, die vom externen RAM ausgeführt werden. Es fühlt sich nicht so an, als würde man ein normales Bild anvisieren, und es muss sich der Bibliotheksfunktionen bewusst sein, die im internen Speicher verfügbar sind, sie jedoch nicht neu kompilieren, sondern nur mit ihnen verknüpfen. Es muss auch beginnen, Adressen direkt nach den 32.000 internen Flashs zu verwenden, nicht bei Null. Gibt es eine gute Möglichkeit, ein Programm mit diesen Einschränkungen zu kompilieren?

Ich verwende die MPLab-IDE, bin aber nicht besonders vertraut damit oder weiß nicht, wie man diese Art von Anpassung vornimmt.


Eine der besten Fragen, die ich hier seit einiger Zeit gesehen habe ... Ich freue mich auf die Ideen und Antworten.
Jon L

Einige interessante alternative Ansätze werden in electronics.stackexchange.com/questions/5386/…
Toby Jaffey

Ich habe das gesehen, aber sie empfehlen meistens, auf Flash zu schreiben, was ich gerne vermeiden würde. Mein Hardware-Design wird in der akzeptierten Antwort der Abbildung 6 in der Anwendungsnotiz entsprechen.
Captncraig

Antworten:


8

Sie haben zwei separate Probleme:

  1. Aufbau des Codes für den externen RAM-Adressbereich.

    Das ist eigentlich sehr einfach. Sie müssen lediglich eine Linker-Datei erstellen, die nur die Adressbereiche enthält, die der Code einnehmen soll. Beachten Sie, dass Sie nicht nur einen bestimmten Programmspeicher-Adressbereich für diese externen Apps reservieren müssen, sondern auch etwas RAM-Speicher. Dieser RAM-Bereich muss ebenso wie die Programmspeicheradressen fest und bekannt sein. Stellen Sie einfach nur die festen und bekannten Adressbereiche zur Verwendung in der Linker-Datei zur Verfügung. Vergessen Sie nicht, sie NICHT in der Basiscode-Linker-Datei verfügbar zu machen.

    Nachdem der Basiscode eine neue App in den externen Speicher geladen hat, muss er wissen, wie er ausgeführt wird. Am einfachsten ist es wahrscheinlich, wenn die Ausführung am ersten externen RAM-Speicherort beginnt. Dies bedeutet, dass Ihr Code einen CODE-Abschnitt an dieser absoluten Startadresse benötigt. Dies enthält ein GOTO auf dem rechten Etikett im Rest des Codes, das alle verlagert werden kann.

  2. Verknüpfen von Apps mit Bibliotheksroutinen im Basiscode.

    Mit den vorhandenen Microchip-Tools ist dies nicht sofort einfach, aber auch nicht so schlimm.

    Ein viel größeres Problem ist, wie Sie mit Änderungen des Basiscodes umgehen möchten. Die vereinfachte Strategie besteht darin, den Basiscode zu erstellen, ein Programm über die resultierende Zuordnungsdatei auszuführen, um globale Adressen zu sammeln, und dann eine Importdatei mit EQU-Anweisungen für alle global definierten Symbole zu schreiben. Diese Importdatei würde dann in allen App-Codes enthalten sein. Es gibt nichts zu verlinken, da der App-Quellcode im Wesentlichen die festen Adressverweise auf die Basiscode-Einstiegspunkte enthält.

    Das ist einfach und wird funktionieren, aber überlegen Sie, was passiert, wenn Sie den Basiscode ändern. Sogar eine kleine Fehlerbehebung könnte dazu führen, dass sich alle Adressen verschieben, und dann wäre der gesamte vorhandene App-Code nicht gut und müsste neu erstellt werden. Wenn Sie nie vorhaben, Basiscode-Updates bereitzustellen, ohne alle Apps zu aktualisieren, können Sie dies möglicherweise verhindern, aber ich halte das für eine schlechte Idee.

    Eine bessere Möglichkeit besteht darin, einen definierten Schnittstellenbereich an einer ausgewählten festen bekannten Adresse im Basiscode zu haben. Für jede Subroutine, die der App-Code aufrufen kann, gibt es ein GOTO. Diese GOTOs würden an festen bekannten Adressen platziert, und die externen Apps würden nur zu diesen Orten aufrufen, die dann dorthin springen würden, wo die Unterroutine tatsächlich in diesem Build des Basiscodes landete. Das kostet 2 Programmspeicherwörter pro exportiertem Unterprogramm und zwei zusätzliche Zyklen zur Laufzeit, aber ich denke, es lohnt sich.

    Um dies richtig zu machen, müssen Sie den Prozess des Erzeugens der GOTOs und der daraus resultierenden Exportdatei automatisieren, die von externen Apps importiert werden, um die Subroutinenadressen (eigentlich GOTO-Redirector-Adressen) abzurufen. Möglicherweise können Sie mit der geschickten Verwendung von MPASM-Makros umgehen, aber wenn ich dies tun würde, würde ich auf jeden Fall meinen Präprozessor verwenden, da dieser zur Vorverarbeitungszeit in eine externe Datei schreiben kann. Sie können ein Präprozessor-Makro schreiben, sodass jeder Redirector durch eine einzelne Codezeile definiert werden kann. Das Makro erledigt alle unangenehmen Aufgaben unter der Haube, die das GOTO, den externen Verweis auf die eigentliche Zielroutine, generieren sollen, und fügt der Exportdatei die entsprechende Zeile mit der bekannten konstanten Adresse dieser Routine hinzu, alle mit den entsprechenden Namen. Möglicherweise erstellt das Makro nur eine Reihe von Präprozessorvariablen mit regulären Namen (wie ein zur Laufzeit erweiterbares Array), und die Exportdatei wird nach allen Makroaufrufen einmal geschrieben. Eine der vielen Möglichkeiten meines Präprozessors, die MPASM-Makros nicht bieten, besteht darin, Zeichenfolgen zu bearbeiten, um neue Symbolnamen aus anderen Namen zu erstellen.

    Mein Präprozessor und eine Reihe anderer verwandter Dinge stehen unter www.embedinc.com/pic/dload.htm kostenlos zur Verfügung .


Ich mag die Idee eines festen Sprungtisches sehr. Ich könnte einfach am Ende des Flash-Speichers beginnen und feste Positionen für jeden Systemaufruf haben. Ich könnte sogar wahrscheinlich mit einer manuell gepflegten Header-Datei mit Adressen aller Subroutinen davonkommen, wenn ich nicht all das Präprozessor-Voodoo herausfinden könnte, das Sie beschreiben.
Captncraig

5

Option 1: Interpretierte Sprachen

Dies beantwortet die Frage nicht direkt (was übrigens eine hervorragende Frage ist, und ich hoffe, aus einer Antwort zu lernen, die sie direkt anspricht), aber es ist sehr häufig, wenn Projekte ausgeführt werden, in die externe Programme geladen werden können, um die externen Programme zu schreiben eine interpretierte Sprache. Wenn die Ressourcen knapp sind (welche Ressourcen auf diesem Prozessor sind, haben Sie darüber nachgedacht, einen PIC32- oder kleinen ARM-Prozessor zu verwenden?), Ist es üblich, die Sprache auf eine Teilmenge der vollständigen Spezifikation zu beschränken. Noch weiter unten in der Kette befinden sich domänenspezifische Sprachen, die nur ein paar Dinge tun.

Das elua-Projekt ist beispielsweise ein Beispiel für eine ressourcenarme (64 kB RAM) interpretierte Sprache. Sie können dies auf 32 KB RAM reduzieren, wenn Sie einige Funktionen entfernen (Hinweis: Auf Ihrem aktuellen Prozessor, einer 8-Bit-Architektur, funktioniert dies nicht. Die Verwendung von externem RAM ist für Grafiken wahrscheinlich zu langsam). Es bietet eine schnelle, flexible Sprache, in der neue Benutzer Spiele problemlos programmieren können, wenn Sie eine minimale API bereitstellen. Für die Sprache Online steht eine Vielzahl von Dokumentationen zur Verfügung. Es gibt andere Sprachen (wie Forth und Basic), die Sie auf ähnliche Weise verwenden könnten, aber ich denke, dass Lua derzeit die beste Option ist.

In ähnlicher Weise können Sie Ihre eigene domänenspezifische Sprache erstellen. Sie müssten eine umfassendere API und externe Dokumentation bereitstellen, aber wenn die Spiele alle ähnlich wären, wäre dies nicht allzu schwierig.

In jedem Fall ist der PIC18 wahrscheinlich nicht der Prozessor, den ich für etwas verwenden würde, das kundenspezifische Programmierung / Skripterstellung und Grafiken umfasst. Sie sind vielleicht mit dieser Prozessorklasse vertraut, aber ich würde vorschlagen, dass dies ein guter Zeitpunkt ist, um etwas mit einem Bildschirmtreiber und mehr Speicher zu verwenden.

Option 2: Programmieren Sie einfach das Ganze neu

Wenn Sie jedoch bereits vorhaben, alle Spiele selbst in C zu programmieren, müssen Sie nicht nur die Spielelogik von der SD-Karte laden . Sie müssen nur 32 kB Flash neu programmieren und können problemlos eine 4-GB-microSD-Karte dafür erwerben. (Hinweis: Bei größeren Karten handelt es sich häufig um SDHC-Karten, mit denen eine Schnittstelle schwieriger ist.) Angenommen, Sie verwenden jedes letzte Byte Ihrer 32 kB, so bleibt auf der SD-Karte Platz für 131.072 Kopien Ihrer Firmware mit der von Ihnen benötigten Spielelogik.

Es gibt viele Appnotes zum Schreiben von Bootloadern für PICs wie AN851 . Sie müssen Ihren Bootloader so gestalten, dass er einen bestimmten Speicherbereich belegt (wahrscheinlich den oberen Bereich des Speicherbereichs, den Sie im Linker angeben würden), und angeben, dass die vollständigen Firmware-Projekte diesen Bereich nicht erreichen. In der Appnote wird dies näher erläutert. Ersetzen Sie einfach "Boot-Bereich des PIC18F452" durch "Boot-Bereich, den ich im Linker angegeben habe", und alles macht Sinn.

Dann muss Ihr Bootloader dem Benutzer lediglich erlauben, ein Programm auszuwählen, das von der SD-Karte ausgeführt werden soll, und das Ganze zu kopieren. Eine Benutzeroberfläche kann sein, dass der Benutzer einen Druckknopf gedrückt halten muss, um in den Auswahlmodus zu gelangen. Normalerweise überprüft der Bootloader beim Zurücksetzen nur den Status dieser Schaltfläche und bootet das Spiel, wenn er nicht gedrückt gehalten wird. Wenn es gedrückt gehalten wird, muss der Benutzer eine Datei auf der SD-Karte auswählen, das Programm kopieren und mit dem Booten des [neuen] Spiels fortfahren können.

Dies ist meine aktuelle Empfehlung.

Option 3: Deep Magic, bei dem nur ein Teil der Hex-Datei gespeichert wird

Das Problem mit Ihrem geplanten Mechanismus besteht darin, dass der Prozessor keine APIs und Funktionsaufrufe verarbeitet, sondern nur Zahlen - Adressen, zu denen der Befehlszeiger springen kann, und erwartet, dass Code vorhanden ist, der einen Funktionsaufruf gemäß einer API-Spezifikation ausführt. Wenn Sie versuchen, nur einen Teil des Programms zu kompilieren, weiß der Linker nicht, was zu tun ist, wenn Sie check_button_status()oder aufrufen toggle_led(). Möglicherweise wissen Sie, dass diese Funktionen in der Hex-Datei auf dem Prozessor vorhanden sind, aber es muss genau bekannt sein, unter welcher Adresse sie sich befinden.

Der Linker unterteilt Ihren Code bereits in mehrere Abschnitte. Sie könnten dies theoretisch in zusätzliche Abschnitte mit einigen -sectionund #pragmaBeschwörungsformeln aufteilen. Ich habe das noch nie gemacht und weiß nicht wie. Solange die beiden oben genannten Methoden nicht erfolgreich sind (oder jemand hier eine großartige Antwort veröffentlicht), lerne ich diesen Mechanismus wahrscheinlich nicht und kann es Ihnen daher nicht beibringen.


Mein Einwand gegen Nummer 2 ist, dass der Flash-Speicher eine begrenzte Anzahl von Löschzyklen in der Lebensdauer des Chips aufweist. Ich möchte kein bulligeres mcu verwenden, weil ich 8-Bit-Minimalismus anstrebe.
Captncraig

@CMP - Es hat mindestens 10.000 Löschzyklen. Wenn jemand jeden Tag ein anderes Spiel spielt, dauert es bis zum Jahr 2039. Der Blitz wird mit ziemlicher Sicherheit den Rest der Strecke überdauern. Ich glaube nicht, dass Sie sich darüber Sorgen machen müssen, es sei denn, es wird in einer Spielhalle gespielt, die jeden Tag Dutzende Male gespielt wird.
Kevin Vermeer

1
Zweitens mag 8-Bit-Minimalismus cool aussehen, aber für die Programmierung ist er scheiße. Es ist viel einfacher, einen robusten Mikrocontroller zu bekommen und ihn retro aussehen zu lassen, als gezwungen zu sein, ihn retro aussehen zu lassen, weil Sie die Grenzen Ihres Prozessors erweitern.
Kevin Vermeer

1
Beides sehr faire Punkte. Selbst wenn die Anzahl der Teile niedrig ist, unterscheidet sich ein pic32 in Bezug auf Kosten oder externe Komponenten nicht wesentlich von diesem und wenn es über 512 KB internen Flash verfügt, gewinnt es sogar.
captncraig

Es sieht aus wie eine praktische Lösung wäre, ein Pic32 zu verwenden und einen Bootloader zu schreiben, um von der SD-Karte zu reflashen. Ich würde es schwer haben, Funktionen, die sowohl der Bootloader als auch der Benutzercode benötigen, wiederzuverwenden, aber solange ich sie im 12-KB-Boot-Flash behalte, sollte sie dem Benutzercode den gesamten Chip geben und sie können beliebige Bibliotheken an der Quelle einschließen Niveau.
Captncraig
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.