Assembler Interrupt vector table

stinakovits

Mitglied
22. Apr. 2013
90
6
8
Kottingbrunn
Sprachen
  1. Assembler
Hallo zusammen,

ich stehe momentan auf der Leitung, sehe den Wald vor lauter Bäumen nicht, ......

Ein Timer Interrupt löst nicht so aus wie ich mir das vorstelle :rolleyes:

Prinzipielle Vorgangsweise:
  • Capture/Compare Wert setzen
  • Prescaler setzen
  • starten/enablen
  • Interrupt freigen


CodeBox Assembler
    ; TCB - Timer konfigurieren und starten.
    ldi        TEMP1, 0x32                    ; Capture/compare Wert L-Byte. 100ns/Takt  Abtastrate für Rückmeldekontakte 5µs  50 Takte  0x0032  L-Byte ist 0x32
    sts        TCB0_CCMPL, TEMP1
    ldi        TEMP1, 0x00                    ; Capture/compare Wert H-Byte. 100ns/Takt  Abtastrate für Rückmeldekontakte 5µs  50 Takte  0x0032  H-Byte ist 0x00
    sts        TCB0_CCMPH, TEMP1
    ldi        TEMP1, 1<<TCB_CLKSEL0_bp    ; CLK_PER / 2 ... TCB läuft somit mit 10MHz ... 100ns/Takt
    ori        TEMP1, 1<<TCB_ENABLE_bp        ; TCB starten. Nach 5µs tritt ein Interrupt auf.
    sts        TCB0_CTRLA, TEMP1
    ldi        TEMP1, 1<<TCB_CAPT_bp        ; TCB capture Interrupt freigeben
    sts        TCB0_INTCTRL, TEMP1
    ; Interrupts allgemein freigeben
    sei


In der Interrupt vector table steht die Sprungadresse für den Timerinterrupt an 12 Stelle.
Er wird nicht ausgelöst.
Nun habe ich die Sprungadresse für den Timerinterrupt in der Tabelle an die 24. Stelle geschrieben. Also Faktor 2. Nun hab ich eine Auslösung für den Timerinterrupt an jener Stelle die eigentlich für den PORTC vorgesehen wäre.



CodeBox Assembler
.org    0x0000                rjmp    init
.org    CRCSCAN_NMI_vect    reti                    ; NMI - Non-Maskable Interrupt from CRC
.org    BOD_VLM_vect        reti                    ; VLM - Voltage Level Monitor
.org    RTC_CNT_vect        reti                    ; RTC - Overflow or compare match
.org    RTC_PIT_vect        reti                    ; PIT - Periodic interrupt
.org    CCL_CCL_vect        reti                    ; CCL - Configurable Custom Logic
.org    PORTA_PORT_vect        reti                    ; PORTA - External interrupt
.org    TCA0_LUNF_vect        reti                    ; TCA0 - Overflow
.org    TCA0_HUNF_vect        reti                    ; TCA0 - Underflow (Split mode)
.org    TCA0_CMP0_vect        reti                    ; TCA0 - Compare channel 0
.org    TCA0_CMP1_vect        reti                    ; TCA0 - Compare channel 1
.org    TCA0_CMP2_vect        reti                    ; TCA0 - Compare channel 2
.org    TCB0_INT_vect        rjmp    rm_einlesen        ; TCB0 - Capture
.org    TCB1_INT_vect        reti                    ; TCB1 - Capture
.org    TWI0_TWIS_vect        reti                    ; TWI0 - Slave
.org    TWI0_TWIM_vect        reti                    ; TWI0 - Master
.org    SPI0_INT_vect        reti                    ; SPI0 - Serial Peripheral Interface 0
.org    USART0_RXC_vect        reti                    ; USART0 - Receive Complete
.org    USART0_DRE_vect        reti                    ; USART0 - Data Register Empty
.org    USART0_TXC_vect        reti                    ; USART0 - Transmit Complete
.org    PORTD_PORT_vect        reti                    ; PORTD - External interrupt
.org    AC0_AC_vect            reti                    ; AC0 – Compare
.org    ADC0_RESRDY_vect    reti                    ; ADC0 – Result Ready
.org    ADC0_WCOMP_vect        reti                    ; ADC0 - Window Compare
.org    PORTC_PORT_vect        rjmp    rm_einlesen                    ; PORTC - External interrupt
.org    TCB2_INT_vect        reti                    ; TCB2 - Capture


Wo ist mein Gedankenfehler. Es ist peinlich. Ich komm nicht drauf :eek:

Achja: ATmega809
 
Zuletzt bearbeitet:
Hallo Manfred,

möglicherweise liegt es an einem Fehler bei der Größe der Einsprungadressen seitens Microchip/Atmel.

Installiere dir mal das aktuelle DFP für Atmega

Atmel ATmega Series Device Support (1.5.362)
Atmel.ATmega_DFP.1.5.362.atpack
1.4.346 (2019-12-20) Set interrupt vector size for ATmega809/808 to 4 bytes. Corrected data memory size.


Dirk :ciao:
 
Hallo Dirk

Wie kann ich dieses
Atmel ATmega Series Device Support (1.5.362)
Atmel.ATmega_DFP.1.5.362.atpack
nach dem Download denn installieren - ich habe immer noch AVR Studio4.18...

mfg Hero_123
 
Hallo Dirk,

danke für den Hinweis auf ein neueres Pack für den ATmega809. Jetzt funktioniert die IVT.

Wenn man sich in der Sim die Disassemblierung anschaut macht der jetzt auch nichts anderes als ich zur Hilfestellung versucht habe. Nämlich NOPs einfügen um die Tabelle aufzublasen :yes4:

@Hero_123
Du öffnest das Atmel Studio und gehst im Extras Menü auf Device Pack Manager. Dort suchst du dir den Eintrag 1.4.346, den Dirk weiter oben schon erwähnt hat. Du musst, wenn du nicht auf "neueste Version" vor eingestellt hast, in deinem Projekt händisch das korrekte Pack einstellen. Alles neu starten und das Projekt neu erstellen. Dann funkt die IVT wie sie soll.
1603813201709.png



PS:
Besser noch die neuere Version 1.5.362 installieren. Ist erst 2 Monate alt. Nicht wundern. Er erscheint bei der Erstellung immer 1603814228237.png Einfach ignorieren.
 
Zuletzt bearbeitet:
Wenn man sich in der Sim die Disassemblierung anschaut macht der jetzt auch nichts anderes als ich zur Hilfestellung versucht habe. Nämlich NOPs einfügen um die Tabelle aufzublasen :yes4:
Ob da nun NOPs oder sinniger Code, irgendwelche Daten (Konstanten) geparkt sind oder irgendwelcher Mist steht, ist Wurst.
RJMP kann im Prinzip bis zu 8kB Flash (4kwords) "treffen" (wenn adresstechnisch unterstützt wird, daß "rückwärts über die Null am Ende auch richtig trifft" (Null minus eins = Flashend, wrap around).
Bei dem Controller sind das dann mal eben 80 Bytes nur für die IVT...

Das entscheidende ist, daß die Adress-Konstanten in den Definitionsdateien stimmen müssen (für die .org-Direktive)
 
Hi
Nur mal kurz zu IVT. Die Adressen dort sind fest vorgegeben, da kann man mit NOPs gar nichts aufblasen. Sieh dir mal die zugehörige .inc-Datei an, die normalerweise mit Include geladen wird. Dort sind mit der Compilerdirektive .equ Werte den Symbolen zugeordnet, die Adresse im Speicher und nicht die Adresse in der Tabelle. Solange die Compilerdirektive .org vor dem Symbol steht, könntest du die Tabelle Kreuz und quer mischen, der Interrupt würde immer die richtige ISR aufrufen. Die Direktive .org weist den Compiler an, den Befehl auf die Speicherstelle zu schreiben, die dem Symbol zugeordnet ist. Entweder der Sprung zur ISR oder eben nur RETI. Wenn ein Interrupt nicht auslöst, dann ist vielleicht das zugehörige Interrupt Enabled Bit nicht gesetzt.
Gruß oldmax
 
Hallo stinakovits

Vielen Dank für Deine Hilfe, ich kann bei meinem AVRStudio 4.18.716 kein "Extras Menü" mit dem Eintrag "Device Pack Manager" finden (ist wahrscheinlich zu alt ...)

mfg Hero_123
 
Mit dem 4er Studio kannst Du die X-Core-AVR eh vergessen. 4.18 (oder war das sogar 4.19??) konnte erst ab irgend'nem Servicepack überhaupt TPI als Programmierschnittstelle.
Die X-Cores hingegen laufen über UPDI als Programmierschnittstelle...

P.S.: X-Core-ATmegas sind keine ATXmegas, die haben zwar ähnliche Peripheriemodule, laufen aber andererseits zB mit 5V.

(P.P.S.: dafür kann man mit den neueren Studio-Versionen die alten Controller (AT90Sxxxx) nicht mehr programmieren)

Nämlich NOPs einfügen um die Tabelle aufzublasen :yes4:
Bei Deinem Listing aus #1 ist sie mit sinnlosen RETIs "aufgeblasen" ;)

Der Flash ist ein Digitaler Speicher (oder wird zumindest digital ausgewertet), da steht also in jeder Zelle immer(!) irgendwas drin. Wenn der Programmdekoder mit dem Inhalt einer Zelle gefüttert wurde, ist das entweder ein gültiger Opcode der dann ausgeführt wird (sinnig oder nicht), oder es ist kein gültiger Opcode, und der Controller macht einen Takt lang nichts (quasi auch ein NOP).

Der Inhalt von Zellen, der weder jemals ausgeführt wird, noch geladen wird (Konstanten), ist irrelevant.
 
da kann man mit NOPs gar nichts aufblasen
Doch, kann man. Hab's gemacht. Funktioniert! Die IVT kann man sich selbst zusammenbasteln. Die einzige Bedingung ist, dass die Sprungadressen zu den ISR an der richtigen Speicherstelle stehen. Natürlich ist es immer besser wenn man das richtige Pack dabei hat.
Mein Irrtum war, bis mich Dirk darauf hingewiesen hat, dass beim ATmega809 der Abstand der Sprungadressen in der IVT 4 Byte und nicht, wie ich dachte, 2 Byte ist. Das war das entscheidende Problem!

Sieh dir mal die zugehörige .inc-Datei an, die normalerweise mit Include geladen wird. Dort sind mit der Compilerdirektive .equ Werte den Symbolen zugeordnet, die Adresse im Speicher und nicht die Adresse in der Tabelle.
Hab ich gemacht. Daher hab ich nach der Installation des neuen Pack gesehen, dass sich die Werte geändert hatten. Jetzt stehen dort die korrekten Zuordnungen drinnen. Immer schön mit dem Abstand von 0x0002.
Zum Beispiel:


CodeBox Assembler
.equ CRCSCAN_NMI_vect = 0x0002           ; 
.equ BOD_VLM_vect = 0x0004               ; 
.equ RTC_CNT_vect = 0x0006               ; 
.equ RTC_PIT_vect = 0x0008               ; 

Also immer 4 Byte Abstand. Wie es sein sollte.

.org weist den Compiler an, den Befehl auf die Speicherstelle zu schreiben
Ist bekannt.

Wenn ein Interrupt nicht auslöst, dann ist vielleicht das zugehörige Interrupt Enabled Bit nicht gesetzt.
Du hast mein Eingangspost nicht gelesen ;)

Bei Deinem Listing aus #1 ist sie mit sinnlosen RETIs "aufgeblasen" ;)
Hehe. Natürlich ist sie das. Mir ist bewusst, dass ich fast alle Einträge in der IVT weglassen könnte. Bringt aber nichts an Platz weil alles unnötige unterhalb des letzten benötigten Sprungs mit 0xFF vollgeschrieben bleibt. Hm, kann mir nicht vorstellen innerhalb des für die IVT benötigten Platzes, auch wenn der Platz für die IVT nicht benötigt wird, Code rein zu schreiben.

1603897728712.png

Von daher ist meine Aussage von vorher nicht ganz korrekt. Es wird nichts mit NOPs aufgefüllt. Die stehen standardmäßig eh schon drinnen :) Werden nur durch RETI oder die Sprungadresse ersetzt.
Somit könnte man ab der letzten benötigten Sprungadresse die IVT beenden und mit dem Code anfangen.
 
Nach einem Chiperase sind alle Speicherzellen 0xFF. Wenn Du Dein Programm compilierst, wird quasi ein Abbild des Flash-Musters generiert (mit dem Rahmen des Intel-Hex-Formates drumrum, klar).
Quasi nur, weil es eben nicht vollständig ist - wenn Dein Programm nicht den gesamten Flash erschöpft, wird der ungenutzte Platz nicht beschrieben. Und wenn Du mit .org Lücken laßt, bleiben diese auch leer.
Wenn also vor dem Flashen ein Chiperase stattfand, steht in all diesen Zellen 0xFF. 0xFFFF ist übrigens kein gültiger Opcode. Der wird zwar (wie jeder ungültige Opcode) als NOP behandelt(*), aber NOP ist 0x0000.

Natürlich kannst Du Dir die Definitionen der Definitionsdatei auch selbst schreiben - Du kannst auch ganz drauf verzichten (theoretisch, da das Studio(zumindest die neuen) ja automatisch 'ne Definitionsdatei inkludiert) - und direkt am Anfang die Sprungbefehle setzen.
Oder Du verwendest statt der Konstanten (ob aus der Definitionsdatei oder selbst definiert) einfach die tatsächlichen Adressen aus dem Datenblatt...

Aber da bläst nichts auf...

(*) (nur was für echte Nerds...)
So, wenn man in den aktuellen Instruction Sets sucht (ich hatte mir mal das doc0856i ausgedruckt) findet man für SBRS:
1111 111r rrrr 0bbb


CodeBox Assembler
SBRS r31, 7
sollte also zu 1111 1111 1111 0111 werden.

Interessanterweise soll sich im doc0856a aber 1111 111r rrrr Xbbb
X = dont care
@dino03 kannst Du das mal checken?

Ansonsten könnte man das auch mal in echter Hardware ausprobieren...

Somit könnte man ab der letzten benötigten Sprungadresse die IVT beenden und mit dem Code anfangen.
Das passiert doch automatisch, wenn Du nach dem letzten

CodeBox Assembler
.org ivt-adresse
(R)JMP isr-adresse

Das Label für den Einsprung aus dem Reset (also der Sprung, der in 0x0000/0x0001 steht) setzt
 
Wir meinen das Gleiche. Du beschreibst es besser als ich.

Aber da bläst nichts auf...

Damit meinte ich bloß, dass sich der Platzbedarf von der fehlerhafte IVT auf eine funktionierende IVT verdoppelt hat.

Danke für deine Ausführungen.
 
Zuletzt bearbeitet:
Die 0xFFFF-NOP-SBRS-Frage würde mich trotzdem interessieren - NOP ist mit 0x0000 halt der kleinste Opcode, SBRS der größte.
(Wenn ich das richtig überblicke, gab es bis RevisionE 81 mögliche Opcodes, in F kam dann DES dazu, in I LAS, LAC, LAT und XCH. Das sind aber alles XMega-Instruktionen.
Da es (zumindest bisher) in den Opcodes Lücken gibt, muß der Instruction-Decoder (eines jeden spezifischen Controllers) diese natürlich auch auswerten können (und bei nicht implementierten Opcodes dann eben irgend'ne definierte Instruktion ausführen (zB ein NOP)).
Das Don't-care-X aus dem alten Datenblatt legt aber nahe, daß am Ende nicht zwischen 0xFFFF und 0xFFF7 unterschieden wird (weil man die Decoder-Kaskade ja an den fixen höherwertigen bits durchgeht, und dann variable Bits (Register/Bits)kommen, und da nochmal dazwischen fixe Nullen kommen.
Ähnlich konnte das auch bei BST, BLD und SBRC sein...

(Ich seh mich hier schon noch 'ne komplette sortierte Opcode-Liste erstellen :hmmmm: )
P.S.: sorry für's :offtopic:
 

Anhänge

  • InstructionSetbit.pdf
    28,5 KB · Aufrufe: 5
Aber da steht ja alles doppelt und dreifach drin ;)
Ich komme lediglich auf 81+5 (XMega-only) Opcodes... (ok, bei den TPI-Tinies und den X-Core-TinyMegas muß ich nochmal genauer schauen)

Die ganzen bedingten Sprünge zB gibts gar nicht als Opcode - da steht in Wirklichkeit ein BRBS/BRBC.
Ebenso das Setzen und Löschen der SREG-Bits -> BSET/BCLR.
Die ganzen Loads und Stores schmelzen auch weitgehend zusammen - das entsprechende Pointer-Register wird in Bit3..2 codiert.
Linksschieben/-rollen wird durch ADD/ADC (mit demselben Register) umgesetzt.
SER ist ein LDI mit 0xFF, CLR ein EOR (mit demselben Register)
CBR/SBR werden durch ANDI/ORI umgesetzt.
TST ist ein AND...
 
Ich komme lediglich auf...
Die Anzahl hat mich nie interessiert, die steht für jeden Controller im zugehörigen Datenblatt ganz am Anfang.
...
Linksschieben/-rollen wird durch ADD/ADC (mit demselben Register) umgesetzt.
SER ist ein LDI mit 0xFF, CLR ein EOR (mit demselben Register)
CBR/SBR werden durch ANDI/ORI umgesetzt.
TST ist ein AND...
Das steht da extra drin, damit man auf den ersten Blick sieht, daß es verschiedene Mnemonics für den gleichen Opcode gibt.
Wenn Du das anders haben möchtest, kannst Du ja alles rausschmeißen, was Du nicht brauchst. ;)
 
Hi
Manchmal hab ich das Gefühl, es liegt an meinem Alter, das ich nur noch Bahnhof verstehe.. sorry, für diese Aussage. Aber
Damit meinte ich bloß, dass sich der Platzbedarf von der fehlerhafte IVT auf eine funktionierende IVT verdoppelt hat.
läßt mich zweifeln.
Ich glaub ja schon, das du um die Technik der Controller weißt, dennoch möchte ich hier mal mein "Wissen" kundtun.
Eine IVT ist ein fest vorgegebener Speicherbereich, der eine Art "Hardwareadressierung" besitzt, die durch die Interruptart aufgerufen wird. Wenn ein Interrupt freigegeben ist, der in dieser Tabelle nicht mit einem "JMP" oder "RETI"-Befehl in der fest vorgegebenen Adresse programmiert ist, macht der Controller solange weiter, bis er einen Befehl findet, den er ausführen kann. Also er adressiert die nächste Speicherzelle, liest Befehl usw. Auch so läßt sich manchmal ein "komisches" Verhalten erklären. Du gibst "INT0" frei, programmierst aber kein RETI oder ISR-Aufruf. Beim Timer hast du eine ISR-aufgerufen. Schön, der "int0" Interrupt adressiert "0002", findet nur "NOP" und setzt die Adresse hoch, bis er auf Adresse "000C", der ja eigentlich "TIMER1 COMPA" sein soll, einen "JMP"/ "RJMP"-Befehl findet und das macht er dann auch, ob es dir gefällt oder nicht.
Nun zur Adressierung: Da ein Befehl in einigen Controllern aus zwei Bytes besteht, ist klar, das die Befehle in Speicherzellen mit geraden Adressen stehen. Also benötigt man zur Adressierung der Befehle ein Bit weniger, nämlich das Bit 0 einer Adresse. Dieses Bit kann der Befehlscodierung zugute kommen und das fehlende Adressbit Bit 0 wird einfach mit "0" zur Adresse ergänzt. Also ist klar, das die Adressen um 2 aufsteigen. Wer ohne die IVT arbeitet, findet auch die erste Adresse hinter der IVT in der "xxxdef.INC"-Datei. Beim Atmega 16 ist es das Symbol "INT_VECTORS_SIZE" und ist in der "m16def.inc"-Datei mit " .equ INT_VECTORS_SIZE = 42 ; size in words" eingetragen. Da die Tabelle auf Adresse 0 beginnt und 21 Interruptadressen , ist bei Adresse 21*2 wieder freier Programmbereich. Vielleicht hilft es dir. Übrigends, der erste Befehl auf Adresse 0 ist im Datenblatt als Interrupt mit "RESET" angegeben und gehört demnach auch zur IVT.
Gruß oldmax
 
Hallo oldmax :ciao:

läßt mich zweifeln

Hm, mich nicht. Ich weiß, dass es so ist wie ich geschrieben habe. Ist auch keine Hexerei und man braucht nicht theoretisieren. Das sieht man schwarz auf weiß wenn man sich den Code anschaut. Kannst du ganz leicht nachstellen, wenn du Lust hast.

Ich glaub ja schon, das du um die Technik der Controller weißt,

Ich bin keine Spezialist. Viele hier wissen mehr als ich! jedoch mit der IVT habe ich mich nun lange rumgeschlagen. Die birgt nun für mich keine Geheimnisse mehr. Die hab ich im Griff, hoffe ich mal. Auch wenn ich mich oft nicht so gewandt wie andere hier ausdrücke :cool:

Vielleicht sollte ich es mal so sagen: Der Platzbedarf einer IVT ist für jeden Chip vorgegeben, hardwaretechnisch. Wenn aber jetzt die Software einen Bug hat, so wie in meinem Fall die fehlerhafte ...def.inc-Datei, dann passt das nicht zusammen und es tritt der beschriebene Effekt ein. Aufgrund des Bug ist der Platzbedarf der IVT nur die Hälfte des tatsächlich von der Hardware geforderten Platzes gewesen. Daher stand die Sprungadresse zu der ISR an der falschen Stelle. Natürlich wurde der Interrupt ausgelöst, fand an der durch die Hardware festgelegten Stelle keine Sprungadresse sondern nur einen RETI. Davon merkte ich anfangs nichts. Das Ergebnis war bloß, dass der Interrupt scheinbar nicht ausgelöst wurde.
Sodale, und jetzt hoffe ich, mit meinem Halbwissen keinen Blödsinn geschrieben zu haben :party:
 
Hallo zusammen,

zu bedenken ist aber auch folgendes ...
in älteren Controllern besitzt die IVT nur Platz für einen Befehl pro Sprungpunkt. Das wurde bei neueren Controllern erweitert.
Dort besitzen die IVT-Sprungpunkte Platz für 2 Befehle. Heißt also, es geht bei den älteren mit 2Byte Sprungweite zum nächsten
Vektoreintrag und bei den neueren mit 4Byte Sprungweite.

Gruß
Dino
 
in älteren Controllern besitzt die IVT nur Platz für einen Befehl pro Sprungpunkt
??
Der Mega8 hat ein Word pro Vektor, der Mega16 zwei. Der Mega48/88 eins, der Mega168/328 zwei.

Wie ich schon in #5 angemerkt hatte, liegt das (meiner Meinung nach) an der Größe des Program Flash. RJMP kann nur 4kWords adressieren.
Für mehr als 8kB brauchst Du also JMP, und das belegt zwei Words.

Zumindest beim, hier verwendeten X-0-Core-Atmega gilt das aber eben nicht - der 809 hat 4k-Words Flash und trotzdem 'ne Doppelwort-IVT...
Scheinbar scheint das aber derjenige, der die Definitionsdatei bei Micromel geschrieben hat nicht gewußt zu haben...

(wollte jetzt grad nochmal bei den X-1-Core-Attinies drüberschauen, weder beim 212/412 ( DS40001911C )noch beim 416/816 ( DS40001913C )hält man es für nötig, die Adressen ins Datenblatt zu pinseln...)

(jaja, bei den einzelnen Peripheriemodulen findet man ... äh ... Subadressen, aber auf den Offset bin ich noch nicht gestoßen...)
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)