Forum: Mikrocontroller und Digitale Elektronik ARM9 FreeRTOS (asm)


von Olibats (Gast)


Lesenswert?

Hey,

ich versuche gerade das freeRTOS auf meinen ARM9 (sam9263) zum laufen zu 
bringen. Da ich keine Bib für meinen ARM gefunden hab, verändere ich 
jetzt die vorhandene für den ARM sam7.
Dummerweise hänge ich gerade, da ich mich zu wenig mit den Assembler 
Befehlen der 2 auskenne.
In der Bib (die ich nicht auf Lauffähigkeit getestet habe, da ich keinen 
7er habe) heisst es, beim starten des ersten Tasks:

/* Set the LR to the task stack. */
asm volatile ("LDR    R0, =pxCurrentTCB    \n\t");
asm volatile ("LDR    R0, [R0]      \n\t");
asm volatile ("LDR    LR, [R0]      \n\t");
/* The critical nesting depth is the first item on the stack. */
/* Load it into the ulCriticalNesting variable. */          asm volatile 
("LDR    R0, =ulCriticalNesting  \n\t");
asm volatile ("LDMFD  LR!, {R1}        \n\t");
asm volatile ("STR    R1, [R0]      \n\t");
/* Get the SPSR from the stack. */              asm volatile ("LDMFD 
LR!, {R0}        \n\t");
asm volatile ("MSR    SPSR, R0      \n\t");
/* Restore all system mode registers for the task. */
asm volatile ("LDMFD  LR, {R0-R14}^      \n\t");
asm volatile ("NOP          \n\t");
/* Restore the return address. */              /*X*/asm volatile ("LDR 
LR, [LR, #+60]      \n\t");      /* And return - correcting the offset 
in the LR to obtain the */
/* correct address. */                  asm volatile ("SUBS  PC, LR, #4 
\n\t");

  ( void ) ulCriticalNesting;
  ( void ) pxCurrentTCB;

In der mit /*X*/ markierten Zeile ist LR vor der Ausführung 0xAAAAAAAA.
Wenn ich das als Adresse in den Speicher sehe, ist das reservierter 
(also ungültiger(?)) Speicher.
Bei der Ausführung verabschiedet er sich und springt den "daborthandler" 
an (so eine Art Exception?)

Was ich nun eigentlich wissen will:
LDMFD  LR, {R0-R14}^
scheint alle Register auf Startwerte zu setzten. Passiert das mit dem 
ARM7 nicht? Also wird der Befehl auf dem 7er anders ausgeführt als auf 
dem 9er?

Oder hat jemand eine Idee was hier los sein könnte?

Gruß,
Olibats

von Michael G. (michel)


Lesenswert?

Hi

Der LDMFD-Befehl lädt und speichert mehrere Register mit einem Befehl. 
Funktioniert folgendermaßen:
R0 <- [LR]
LR <- LR + 1
R1 <- [LR]
...

(Ich kenne ihn mit folgendem Syntax: LDMFD  LR!, {R0-R14} )

Normalerweise wird dieser Befehl benutzt um Register auf dem Stack zu 
speichern bzw. wieder Herzustellen.

ARM-Prozessoren verfügen über verschiedenen Fehler"interrupts". Heißt: 
Tritt ein Fehler auf, dann springt der ARM in die entsprechende Routine, 
die ähnlich einer ISR angegeben wird. Diese können dann den Fehler 
behandeln.
Eine dieser Routinen reagiert auf "Data Abort", ich nehme an, dass es 
sich bei deiner Funktion "daborthandler" um eben diese Funktion handelt.

Der Fehler "Data Abort" tritt auf, wenn der MC einen Speicherzugriff 
durchführen will, der nicht funktioniert, z.B. Schreibezugriff auf 
FLASH, bzw.  auf nichtexistenden Speicher.

Konkret würde ich in deinem Fall vermuten: Der LR ist bei dir nicht 
korrekt initialisiert und zeigt auf eine falsche Stelle, falls überhaupt 
der LR gemeint ist. Spontan würde ich dafür nämlich den SP benutzten.
Auf jeden Fall ließt du von einem verbotenen Bereich.

Gruß Michel

von Martin Cibulski (Gast)


Lesenswert?

Hallo Olibats,

LR sollte eigentlich die Startadresse der Task +4 enthalten, damit es 
dann mit dem Rücksprungbefehl am Ende funktioniert.
AAAAAAAA ist ein Dummywert aus dem Task-Stack, ich denke schon, dass 
LDMFD funktioniert.
Vermutlich ist der Task-Stack vor dem Aufruf dieser Einsprungroutine 
nicht mit sinnvollen Registerinhalten vorbelegt worden. Das macht eine 
C-Routine, vielleicht geht sie falsch mit Pointern um (Compilerflags!) 
oder sie wurde nicht aufgerufen.

Mit freundlichen Grüßen
Martin

von Olibats (Gast)


Lesenswert?

Hallo,
soweit ich das bisher habe, soll das so gehen:
Wenn der erste Task gestartet wird, dann führt er die asms aus meinem 
ersten Post aus. Dabei sollte er vermutlich irgendwelche sinnvollen 
Werte laden.
In meinem Fall lädt er die Register offensichtlich mit Dummywerten voll:
R1 => 0x01010101
R2 => 0x02020202
...
R13 => wird korrekt auf den anfang gesetzt
R14 => 0xAAAAAAAA

Wenn ich nun R0-R14 durch R0-R13 ersetze geht es erstmal weiter. Und 
zwar bis zum ersten Task. Der wird in wilder Endlosschleife ausgeführt 
und jeder noch so verzweifelte Versuch meinen Arm zum Wechsel zu bewegen 
schlägt fehl.
daborthandler ist übrigens genau dieser beschriebene "Data Abort".

Ein Problem, ist, dass ich den Befehl nach dem NOP nicht wirklich 
verstehe.
Was macht er da?

Das mit dem 4 abziehen wird ja dann beim SUBS ... gemacht. Wie gesagt, 
unterbinde ich das überschreiben vom LR dadurch, dass ich den LDMFD 
Befehl auf R0-R13 ändere, springt er den ersten Task korrekt an. Ändere 
ich das nicht, dann hüpft er mir ans Ende einer Funktion. Ich nehme an, 
dass das dann als undefiniertes Verhalten gilt :-)

von Olibats (Gast)


Lesenswert?

Hallo 2,

Was könnte denn der swihandler sein?

gruß,
Olibats

von Frank (Gast)


Lesenswert?

software interrupt handler?

von Olibats (Gast)


Lesenswert?

Weiss zufällig in welchen registern ich den Supervisor (SWI) 
initialisiere? Ich finde es irgendwie nicht.

von Frank (Gast)


Lesenswert?

was soll denn "der" supervisor sein? es gibt nur einen supervisor mode, 
schau dir mal die doku zu dem/den statusregister(n) an. da könnte man 
z.B. den stack initialisieren wollen...
swi ist definitiv keine gebräuchliche abkürzung für supervisor, sondern 
für software interrupt.

von Olibats (Gast)


Lesenswert?

Hab ich ja! Dort steht nur "blah, gibt einen Software Interrupt und der 
Befehl ist SWI" Dann kann man noch einen Parameter mitgeben, der ist 
aber nur interessant, wenn eine Exception auftritt. Aber nirgends steht 
WO ich die Adresse der Methode hinschreiben muss!!
Deswegen hab ich nach etwas gesucht was es sein könnte. Und Supervisor 
wurde irgendwie in dem Zusammenhang erwähnt. Daher mein Denkfehler.

Ich meine folgendes:
void softw_interrupt()
{ ... }

int main()
{
  unsigned int* x = 0x12345678 // <--
  *x = (unsigned int)softw_interrupt;
  while (1)
     ...
}

statt dem 12345678 muss ich ja irgendwas einsetzen. DAS finde ich nicht. 
Geschweige denn finde ich irgendwelche Register, die das ganze 
strukturieren wie beim AIC, PIT, ...

von Dominic R. (dominic)


Lesenswert?

Mir ist zwar schleierhaft, was du mit deinem Code meinst, aber ich kann 
gerne erklären, was ein SWI ist.

Dein uC führt gerade irgend einen Code aus, z.B. im User Mode. Dieser 
Code enthält dann die Anweisung
0x00001000 SWI #0x0815
0x00001004 mov r0, r1

Daraufhin macht der Core folgendes:
- R14_svc = 0x1004 (Adresse nach dem SWI)
- SPSR_svc = CPSR
- CPSR[4:0] = b10011 (Supervisor)
- CPSR[5] = 0 (ARM State)
- CPSR[7] = 1 (Disable IRQ, FIQ werden nicht verändert)
- PC = 0x8 (oder 0xffff0008 bei high-vecs)

An Adresse 0x8 befindet sich der SWI Vektor, d.h. normalerweise ein 
Branch oder Load zum PC.

Ein Register mit Sprung-Adressen wie beim VIC gibt es da nicht.

Gruß,

Dominic

von Rolf Magnus (Gast)


Lesenswert?

> Hab ich ja! Dort steht nur "blah, gibt einen Software Interrupt und der
> Befehl ist SWI" Dann kann man noch einen Parameter mitgeben, der ist
> aber nur interessant, wenn eine Exception auftritt.

Die Exception ist das, was durch SWI ausgelöst wird. Es wird zum 
SWI-Handler gespungen, und der kann dann den Parameter auslesen. Was der 
bedeutet, hängt von dir ab.

> Aber nirgends steht WO ich die Adresse der Methode hinschreiben muss!!

Die Adresse ist fest vorgegeben. Du wirst das in Assembler machen 
müssen. Ich benutze das mit meinem ARM7TDMI so:

.section .vectors
.global __vectors
.global _start
    .org 0
_start:
__vectors:
    b startup_routine             @ reset vector
    b undef_routine               @ undefined instruction
    b vPortYieldProcessor         @ software interrupt
    b abort_prefetch_routine      @ prefetch abort
    b abort_data_routine          @ data abort
    b undef_routine               @ reserved
    b irq_routine                 @ IRQ
    b undef_routine               @ FIQ

Im Linkerskript habe ich dann eine Sektion .vectors definiert, die immer 
am Anfang von .text landet. Bei mir im FreeRTOS löst portYield den SWI 
aus. Dieses wiederum springt dann wie man sieht einfach zu 
vPortYieldProcessor weiter, welches dann einen Taskwechsel auslöst.

von Olibats (Gast)


Lesenswert?

Hm,
Danke für die Hilfe. Die ffff0008 hab ichgesucht. Ich denke jetzt werde 
ich das hinbekommen.. Danke!

Weiss vielleicht jemand eine Art Tutorial oder eine gute 
Zusammenstellung der Register und Funktionseinheiten des ARM? Ich dachte 
an sowas, wie hier das AVR GCC Tutorial.
In dem Handbuch finde ich mich (offensichtlich) nicht so gut zurecht.

Gruß,
Olibats

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.