So was in der Art. Grund dafür ist einfach folgender. Der Code wird
eventuell übersichtlicher da ich alle ISR's an einer zentralen Stelle
registrieren kann. Die Definition der Funktionen kann jedoch an der
Stelle erfolgen, wo spezielle Hardware definiert wird.
Folgende Vorgangsweise hatte ich mir dazu überlegt.
1. Zeiger umwandeln (also Zeiger/2 - ISR_vect)
2. Das rjmp mit anbinden für den Sprungbefehl
3. Position an dem Programmspeicher auslesen, wo der ISR_vect liegt
4. Beide Werte vergleichen
5. Ggf. neuen rjmp-Befehl mit Adresse einfügen.
6. Danach ist der Interrupt registriert und kann angesprungen werden.
Nun zu den Fragen.
Erstens, ist das praktikabel?
Zweitens, rjmp kann ja nur an die aktuelle Position + 2k bzw -2k +1
springen. Dies würde Probleme machen, wenn das Programm größer als 4kB
ist. Was gäbe es dort für Möglichkeiten dies zu umgehen? Einen
zwischensprung mit rjmp? Oder die InterruptRoutine durch (ich weiß nicht
welche) Attribute möglichst weit vorne im Programmspeicher anlegen?
Weiterhin würde ich zum testen einen ATmega88 hier haben und
programmiert wird der mittels asm oder avr-gcc und C. Des Weiteren habe
ich kein konkretes Einsatzbeispiel. Ich würde nur mal gerne eure
Meinungen dazu hören und wie man das anstellen könnte.
Meine Kristallkugel sagt, daß du das auf einem AVR machen willst.
Auf der Architektur ist sowas nicht praktikabel, da die Vektortabelle
nicht schreibbar ist -- zumindest nicht einfach vom Programmcode aus.
Die Adresse einer ISR ist also spätestens zur Lokatierzeit festgelegt.
Du kannst allerdings jede ISR implementieren und dort einen Dispatcher
bemühen. Daß du den Aufwand und die Codeverschwendung wirklich willst,
sei mal dahingestellt.
Was ist daran schlecht, wenn nur ein UART-Modul an den UART-ISRs
rumbastelt? Es obliegt diesem Modul, wie es eine UART-Kommunikation
implementiert; somstige Module hat das nichts zu interessieren.
Johann L. schrieb:> Was ist daran schlecht, wenn nur ein UART-Modul an den UART-ISRs> rumbastelt?
Wie gesagt, war das nur mal gerade eine Idee von mir. Aber zu der UART
Sache habe ich mal ein kleines Bsp. In meinem Ausbildungsbetrieb haben
wir Waagen programmiert. Die kamen von der Firma GSE und hatten nen
kleinen m68k Prozessor drin. An der Seriellen Schnittstelle konnte man
verschiedene Interpreter einstellen. Was mit dem Konzept auch relativ
einfach möglich wäre. Nur mal als fixes Bsp.
Johann L. schrieb:> Meine Kristallkugel sagt, daß du das auf einem AVR machen willst.sep schrieb:> Weiterhin würde ich zum testen einen ATmega88 hier haben und> programmiert wird der mittels […]
Jop! ;)
Da du, wie schon gesagt wurde, auf einem AVR die Vektortabelle nicht
ohne Aufwand verändern kannst, müsstes du die Registrierung und
Verwaltung in die ISR legen, und dort eine eigene Sprungatabelle
verwalten. Von dort aus kannst du ohne Probleme über Funktionszeiger in
erst zur Laufzeit ausgewählte Funktionen springen.
Ob das bei solch einem kleinen Prozessor überhaupt sinnvoll ist, glaube
ich allerdings nicht.
Oliver
Oliver schrieb:> Von dort aus kannst du ohne Probleme über Funktionszeiger in> erst zur Laufzeit ausgewählte Funktionen springen.
Das hatte ich mir auch schon gedacht. Da wäre es doch toll, wenn in der
Interrupt-Tabelle kein rjmp erfolgt, sondern ein rcall. Dann könnte man
die Adresse von dem Interrupt-Einstiegspunkt einfach mittels pop vom
Stack nehmen und dann in der eigenen Tabelle nach schauen. Dann hätte
man das alles in einer ISR und könnte mit ISR_ALIAS das allen zuordnen.
Dann einen indirekten Sprung und schon ist man in seinem
Funktionszeiger. Bloß wie sage ich dem GCC dass er die Interrupt-Tabelle
nicht mittels rjmp anlegt, sondern mit rcall? Das wäre dann ein bisschen
weniger Overhead und es gehen vlt 5 - 10 Takte verloren.
Rolf Magnus schrieb:> weil du die ISR> damit zwingst, sämtliche Register zu sichern.
Das kapiere ich gerade nicht so wirklich. Warum zwinge ich die ISR alle
Register zu sichern?
Tim schrieb:> Die Interpreter gehören aber nicht in die ISR.
Sry, hab dich gerade fast überlesen. Ich weiß, das war gerade ein
kleines, schlechtes Bsp. Aber wie gesagt, war auch ein fixes.
Mich dünkt du machst dir den Wolf und ne Baustelle auf wo überhaupt
keine ist. Wizu das ganze. De ist ein AVR -Hänfling, und nicht C++ oder
was auch immer auf nem Boliden ;-)
sep schrieb:> Oliver schrieb:>> Von dort aus kannst du ohne Probleme über Funktionszeiger in>> erst zur Laufzeit ausgewählte Funktionen springen.>> Das hatte ich mir auch schon gedacht. Da wäre es doch toll, wenn in der> Interrupt-Tabelle kein rjmp erfolgt, sondern ein rcall.> Dann könnte man die Adresse von dem Interrupt-Einstiegspunkt einfach> mittels pop vom Stack nehmen und dann in der eigenen Tabelle nach> schauen. Dann hätte man das alles in einer ISR und könnte mit ISR_ALIAS> das allen zuordnen.
Ich verstehe irgendwie den Sinn nicht. Man braucht doch nur sehr selten
die Möglichkeit, im laufenden Betrieb die ISR auszutauschen.
> Rolf Magnus schrieb:>> weil du die ISR>> damit zwingst, sämtliche Register zu sichern.>> Das kapiere ich gerade nicht so wirklich. Warum zwinge ich die ISR alle> Register zu sichern?
Weil der Compiler nicht weiß, welche Register die aufgerufene Funktion
benutzt. Und da eine ISR sicherstellen muß, daß nach ihr alles so ist
wie vorher, ist die Konsequenz eben, daß der Compiler vorsorglich alle
Register sichert.
Den Eintritt in die ISR könnte man via InlineAssembler realisieren. Laut
GCC avr-libc gibt es ja auch Register, welche "frei" zur Verfügung
stehen, welche man nicht aufräumen muss. Diese könnte man dafür nutzen.
Außerdem kümmert, soweit wie ich das bei mir sehe, sich der callee darum
die Register zu sichern. So weit wie er diese braucht.
1
00000620 <limits_isFull>:
2
620: df 93 push r29
3
622: cf 93 push r28
4
624: cd b7 in r28, 0x3d ; 61
5
626: de b7 in r29, 0x3e ; 62
Ich weiß nun nicht genau. Hier ist das ja ein einzelner Funktionsaufruf
und nicht aus einer ISR. Ich werd mir das nochmal ganz genau anschaun.
Aber wenn dem so ist, wie ich mir das Denke, dann sollte das ja kein
Problem sein.
Johann L. schrieb:> Wizu das ganze. De ist ein AVR -Hänfling, und nicht C++ oder> was auch immer auf nem Boliden ;-)
Zum einen, weil ich von den kleinen AVR-Dingern ganz gut begeistert bin.
Was die alles können. ^^ Zum Nächsten, weil ich einfach ein bisschen was
lernen will, Möglichkeiten ausschöpfen und neue Methoden ausprobieren!
;)
Das sind so die Gründe denke ich. Wie ich ja schon zu Anfang sagte, habe
ich noch kein konkretes Einsatzbeispiel.
Rolf Magnus schrieb:> Weil der Compiler nicht weiß, welche Register die aufgerufene Funktion> benutzt.Rolf Magnus schrieb:> Er sichert also zwar nicht alle Register, aber doch erheblich mehr.
Das stimmt wohl. Aber wie gesagt. Der Callee sichert auch nochmal die
Register, welche er braucht. Von den Arbeitsregistern. Also sollte es
doch reichen SREG weg zu sichern und nach dem Austritt aus der ISR
diesen wieder herzustellen.
sep schrieb:> Den Eintritt in die ISR könnte man via InlineAssembler realisieren. Laut> GCC avr-libc gibt es ja auch Register, welche "frei" zur Verfügung> stehen, welche man nicht aufräumen muss.
Das gilt aber nicht für Interrupts. Da musst du alle verwendeten
Register sichern und wiederherstellen.
sep schrieb:> Das stimmt wohl. Aber wie gesagt. Der Callee sichert auch nochmal die> Register, welche er braucht. Von den Arbeitsregistern. Also sollte es> doch reichen SREG weg zu sichern und nach dem Austritt aus der ISR> diesen wieder herzustellen.
Nein, reicht nicht. Der callee sichert nämlich nicht die Register, die
du oben als "frei" bezeichnet hast. In einer ISR sind diese aber nicht
"frei" und daher muss die ISR selber diese zusätzlich sichern.
Stefan Ernst schrieb:> Nein, reicht nicht. Der callee sichert nämlich nicht die Register, die> du oben als "frei" bezeichnet hast.
Kann ich einer Funktion auch nicht sagen sie soll nur call-saved
Register nutzen. Denn diese sichert ja dann der callee automatisch. Ich
habe dazu nun nicht wirklich viel gefunden.
Aber anscheinend wird das wohl doch nichts...
Aus <http://www.mail-archive.com/avr-gcc-list@nongnu.org/msg06782.html>
> The register set is divided into "volatile" or "call-used" registers,> "non-volatile" or "call-saved" registers, and fixed-function registers.
sep schrieb:> Kann ich einer Funktion auch nicht sagen sie soll nur call-saved> Register nutzen.
Nicht, dass ich wüsste.
Was du aber machen kannst, ist der Funktion zu sagen, dass sie eine ISR
ist. Dann sichert sie selber alle verwendeten Register und du musst in
deiner "Verteiler-ISR" nicht mehr vorsorglich alle call-used-Register
sichern. Du darfst dann aber nicht vergessen, ein cli direkt hinter das
call zu setzten, sonst riskierst du nested Interrupts (wegen dem reti am
Ende der gerufenen Funktion). Und du hast dann wieder anderen
zusätzlichen unnötigen Overhead in der Funktion.
Sehr fraglich, ob das alles irgendwie lohnend ist.
sep schrieb:> Stefan Ernst schrieb:>> Nein, reicht nicht. Der callee sichert nämlich nicht die Register, die>> du oben als "frei" bezeichnet hast.>> Kann ich einer Funktion auch nicht sagen sie soll nur call-saved> Register nutzen. Denn diese sichert ja dann der callee automatisch. Ich> habe dazu nun nicht wirklich viel gefunden.>> Aber anscheinend wird das wohl doch nichts...>> Aus <http://www.mail-archive.com/avr-gcc-list@nongnu.org/msg06782.html>
Original:
http://lists.nongnu.org/archive/html/avr-gcc-list/2009-11/msg00032.html>> The register set is divided into "volatile" or "call-used" registers,>> "non-volatile" or "call-saved" registers, and fixed-function registers.
Mit Verlaub, das ist Unsinn.
Es gibt
- Call-clobbered Register (zB r30)
- Call-saved Register (zB r29)
- Fixe Register (zB r1)
Zudem kann man eine Variable global oder lokal an ein Register binden,
was aber nix mit der obigen Einteilung zu tun hat. Ditto Register, in
denen Parameter übergeben werden bzw. die nicht der Parameterübergabe
dienen.
Ein Register volatile zu machen dient bestenfalls der eigenen
Verwirrung: Qualifier volatile dient für Speicher, für (globale)
Register ist er wirkungslos.
Eigentlich muss man das ABI in-und-auswendig kennen, wenn man sowas wie
oben vor hat -- oder man landet früher oder später auf der Nase.
Idealerweise, nachdem es eine Zeit lang funktioniert hat, so daß man
sich den Wolf komplett woanders absucht.
Stefan Ernst schrieb:> Was du aber machen kannst, ist der Funktion zu sagen, dass sie eine ISR> ist. Dann sichert sie selber alle verwendeten Register und du musst in> deiner "Verteiler-ISR" nicht mehr vorsorglich alle call-used-Register> sichern.
Danke. Ich hab nun mal ein bisschen geschaut und auch was feines dazu
gefunden. Wie folgt.
1
__attribute__((signal))
2
voidfoo(void){
3
// ISR
4
}
Der Assembler schaut dann folgendermaßen aus.
1
push __zero_reg__
2
push r0
3
in r0,__SREG__
4
push r0
5
clr __zero_reg__
6
push r24
7
push r29
8
push r28
Also alles erst einmal so ganz toll. Nun habe ich mir das so gedacht den
"Verteiler Interrupt so zu definieren.
1
__attribute__((naked))
2
ISR(BADISR_vect){
3
/* richtige ISR suchen */
4
}
Das sollte dann auch so weit funktionieren. Nun habe ich nur noch ein
Problem. Woher soll die Verteiler ISR wissen, welcher ISR ausgelöst hat.
Dazu hab ich folgede Idee.
1
00000000 <__vectors>:
2
0: 19 c0 rjmp .+50 ; 0x34 <__ctors_end>
3
2: 33 c0 rjmp .+102 ; 0x6a <__bad_interrupt>
4
[…]
So schaut ja die Tabelle für die Interrupts aus. Wenn ich das rjmp durch
ein rcall ersetzen könnte, dann habe ich die Adresse des ausgelösten
Interrupts auf dem Stack und könnte damit weiter arbeiten. Nur habe ich
dazu bisher nichts gefunden. Leider.
Johann L. schrieb:> Eigentlich muss man das ABI in-und-auswendig kennen, wenn man sowas wie> oben vor hat
Aus genau diesem Grund frage ich ja nach. Ich weiß, dass es Leute gibt
welche von dieser Materie sehr viel mehr Ahnung haben als ich.
Und Danke bisher für die vielen nützlichen Anstöße.
sep schrieb:> Stefan Ernst schrieb:>> Was du aber machen kannst, ist der Funktion zu sagen, dass sie eine ISR>> ist. Dann sichert sie selber alle verwendeten Register und du musst in>> deiner "Verteiler-ISR" nicht mehr vorsorglich alle call-used-Register>> sichern.>> Danke. Ich hab nun mal ein bisschen geschaut und auch was feines dazu> gefunden. Wie folgt.>
1
__attribute__((signal))
2
>voidfoo(void){
3
>// ISR
4
>}
> Der Assembler schaut dann folgendermaßen aus.>
1
push __zero_reg__
2
> push r0
3
> in r0,__SREG__
4
> push r0
5
> clr __zero_reg__
6
> push r24
7
> push r29
8
> push r28
9
>
Was du noch brauchst (gesetzt du springst hier mit rcall/call hin) ist
noch dir Anzahl der Register, die im ISR-Prolog auf den Stack
geschrieben wurden. Diese ist a priori nicht bekannt. Im s-File wird ein
Symbol dafür definiert; es wird also inline asm Hack notwendig um
ranzukommen. Oder die Verteiler-ISR steht direkt in asm, was sinnvoller
ist. Vielleicht ist für die ISR-Handler auch OS_task sinnvoll, leider
fehlt dafür die Doku, du musst also durch die GCC-Quellen:
http://gcc.gnu.org/viewcvs/trunk/gcc/config/avr/avr.c?view=markup
resp. für die GCC-Version, die du einsetzt.
Falls das ganze in einem OS eingesetzt werden soll, kann es durchaus
sinnvoll/erforderlich sein, GCC/Binutils/libc darauf anzupassen und
mitzuliefern, damit die Anwender ihre Applikation mit dem erweiterten
GCC übersetzen können.
> Also alles erst einmal so ganz toll. Nun habe ich mir das so gedacht den> "Verteiler Interrupt so zu definieren.>
1
__attribute__((naked))
2
>ISR(BADISR_vect){
3
>/* richtige ISR suchen */
4
>}
> Das sollte dann auch so weit funktionieren. Nun habe ich nur noch ein> Problem. Woher soll die Verteiler ISR wissen, welcher ISR ausgelöst hat.> Dazu hab ich folgede Idee.>
1
00000000 <__vectors>:
2
> 0: 19 c0 rjmp .+50 ; 0x34 <__ctors_end>
3
> 2: 33 c0 rjmp .+102 ; 0x6a <__bad_interrupt>
4
> […]
> So schaut ja die Tabelle für die Interrupts aus. Wenn ich das rjmp durch> ein rcall ersetzen könnte, dann habe ich die Adresse des ausgelösten> Interrupts auf dem Stack und könnte damit weiter arbeiten. Nur habe ich> dazu bisher nichts gefunden. Leider.
Die Vektortabelle steht im crt:
http://svn.savannah.nongnu.org/viewvc/trunk/avr-libc/crt1/gcrt1.S?root=avr-libc&view=markup
Vielleich ist -nostartfiles was du suchst, nebst eigenen crts.
Sollte aber auch mit ld-script gehen, indem nicht Standard-Vektortabelle
genommen wird, sondern eine eigene Tabelle die nicht in -vectors liegt.
> Johann L. schrieb:>> Eigentlich muss man das ABI in-und-auswendig kennen, wenn man sowas wie>> oben vor hat>> Aus genau diesem Grund frage ich ja nach. Ich weiß, dass es Leute gibt> welche von dieser Materie sehr viel mehr Ahnung haben als ich.
Das sollte nicht arrogant klingen. Ich versteh nur nicht, warum man sich
ohne Not den ganzen Huddel aufhalsen will :-)
Johann L. schrieb:> Was du noch brauchst (gesetzt du springst hier mit rcall/call hin) ist> noch dir Anzahl der Register, die im ISR-Prolog auf den Stack> geschrieben wurden. […] Oder die Verteiler-ISR steht direkt in asm
So in der Art habe ich das dann vor. Theoretisch wird doch bei einem
Eintritt in einen Interrupt nur die Adresse auf den Stack geschrieben,
an welcher der Code unterbrochen wurde. Wenn ich dann aus der
Interrupt-Tabelle mit rcall in die Verteiler-ISR springe habe ich dann
noch die Interrupt-Nummer welche ausgelöst wurde.
Dort habe ich das dann wie folgt vor. SREG sichern und ggf. noch
Register sichern, welche ich in der ISR brauche. Dann die
Rücksprungadresse vom Stack nehmen (Die in dem Bereich der
Interrupt-Tabelle liegt). Danach kann ich mit Hilfe der Adresse in
meiner Tabelle nach schauen. Dann kommen wieder alle Register + SREG vom
Stack gepoppt. Auch die Adresse durch den rcall wird wieder vom Stack
genommen. Wenn ich dann aus der dynamischen ISR komme, setzt er wieder
ganz normal fort.
Viel mehr sollte dann doch nicht auf dem Stack sein.
Johann L. schrieb:> Falls das ganze in einem OS eingesetzt werden soll
Mal schauen, was daraus wird! ;)
Johann L. schrieb:>> Johann L. schrieb:>>> Eigentlich muss man das ABI in-und-auswendig kennen, wenn man sowas wie>>> oben vor hat>>>> Aus genau diesem Grund frage ich ja nach. Ich weiß, dass es Leute gibt>> welche von dieser Materie sehr viel mehr Ahnung haben als ich.>> Das sollte nicht arrogant klingen. Ich versteh nur nicht, warum man sich> ohne Not den ganzen Huddel aufhalsen will :-)
Damit ich auch mal arrogant klingen kann! :p
Nein, Spaß beiseite. Zur Zeit ist es bei mir so, dass ich ein bisschen
in der Luft hänge. Da ich Spaß an genau solchen Sachen habe, hab ich mir
gedacht ich kann so was mal probieren. Dann mach ich kein Unfug! ^^
Danke auch für den Beitrag. Ich glaube ich habe nun erst einmal einiges
zu lesen. Ein tolles Konzept ist das auch geworden. Wenn man sich meine
erste Idee so anschaut. Danke also.
sep schrieb:> Johann L. schrieb:>> Was du noch brauchst (gesetzt du springst hier mit rcall/call hin) ist>> noch dir Anzahl der Register, die im ISR-Prolog auf den Stack>> geschrieben wurden. […] Oder die Verteiler-ISR steht direkt in asm>> So in der Art habe ich das dann vor. Theoretisch wird doch bei einem> Eintritt in einen Interrupt nur die Adresse auf den Stack geschrieben,> an welcher der Code unterbrochen wurde.
Auch praktisch.
> Wenn ich dann aus der> Interrupt-Tabelle mit rcall in die Verteiler-ISR springe habe ich dann> noch die Interrupt-Nummer welche ausgelöst wurde.> Dort habe ich das dann wie folgt vor. SREG sichern und ggf. noch> Register sichern, welche ich in der ISR brauche. Dann die> Rücksprungadresse vom Stack nehmen (Die in dem Bereich der> Interrupt-Tabelle liegt). Danach kann ich mit Hilfe der Adresse in> meiner Tabelle nachschauen. Dann kommen wieder alle Register + SREG vom> Stack gepoppt. Auch die Adresse durch den rcall wird wieder vom Stack> genommen. Wenn ich dann aus der dynamischen ISR komme, setzt er wieder> ganz normal fort.
Problem: Um deinen Code vom Dispatcher aus zu erreichen brauchst du ein
ICALL oder PUSH/PUSH/RET. Für beides brauchst du Register, die dir
niemand anderes wieder herstellt als du selbst.
>>> [...]>>>> Das sollte nicht arrogant klingen. Ich versteh nur nicht, warum man sich>> ohne Not den ganzen Huddel aufhalsen will :-)>> Damit ich auch mal arrogant klingen kann! :p> Nein, Spaß beiseite. Zur Zeit ist es bei mir so, dass ich ein bisschen> in der Luft hänge. Da ich Spaß an genau solchen Sachen habe, hab ich mir> gedacht ich kann so was mal probieren. Dann mach ich kein Unfug! ^^
Forscherdrang ist ok.
Johann L. schrieb:> PUSH/PUSH/RET
Hmm, auf das bin ich nicht mal so direkt gekommen. Das werde ich mal im
Hinterkopf behalten.
Ansonsten fange ich nun mal an mich bei den ldscripts und den crts
einzuarbeiten. Vlt kann ich dann nach Ostern was präsentieren.
Also, vielen Dank euch und ein frohes Osterfest!