Hallo,
ich spiel mit einem Raspberry rum und versuch mich jetzt an der MMU
was ich will ist ein identity mapping mit 1 MB Sections
Versteh ich das richtig dass ich dann nur die Basetable brauch (also
keine Subpages)
Kommt in die Basetable Bit 31 .. 20 die physikalische Adresse auf die
gemappt wird?
Gruss Heinz
Hallo Heinz,
so in etwa ja, also die Physicalische Adresse, die Domaine und noch ein
paar Attribut. Ist nur der Eintrag in die Tabelle da 1MB die Größte
Section sein dürfte. Bei kleinerer Unterteilung gibts dann eine weitere
Tabelle.Also Tabelle von der Tabelle usw.
Achtung: Wird die Tabelle aktualisiert müssen die Caches geleert werden,
die MMU abgeschaltet werden, MMU-Caches geleert und neu eingeschalten
werden. Sonst gibts Probleme da im Cache noch etwas anderes über den
Speicherbereich steht. Die MMU verwaltet ja auch die Cache Attribute.
Ich habe das ganze mal vor ca. 2 Jahren auf einem ARM9 gemacht.
Gruß Sascha
Hallo Sascha,
mir gings eigentlich nur um den Cache, der funktioniert ja ohne MMU
nicht.
Hab das jetzt auch mal runterprogrammiert und funktioniert auch, aber
noch nicht ausgetestet.
Ich hab jetzt eine Base Translationtable ohne Coarsepages also auch ohne
Tables. Mir ging es auch um den Speicher so brauch ich halt "nur" 16 KB
Gruss Heinz
Sascha schrieb:> Achtung: Wird die Tabelle aktualisiert müssen die Caches geleert werden,> die MMU abgeschaltet werden, MMU-Caches geleert und neu eingeschalten> werden. Sonst gibts Probleme da im Cache noch etwas anderes über den> Speicherbereich steht. Die MMU verwaltet ja auch die Cache Attribute.
Beim ARM11 (ARMv6) muss man im Gegensatz zum ARM9 ohnehin die Caches und
TLB invalidieren bevor man sie erstmalig verwendet.
Hier geht es anscheinend um eine statische Initialisierung bevor Caches
und MMU überhaupt in Betrieb genommen werden, sodass man sich im
laufenden Betrieb darüber keine Gedanken mehr machen muss.
Solange lediglich die Adresszuordnung dynamisch verändert werden
(gleiche Attribute) müssen die D/I Caches beim ARM11 (ARMv6) ebenfalls
nicht geleert werden, da hier VIPT Caches zum Einsatz kommen. Ein TLB
flush ist dennoch fällig.
Gruß
Marcus
Hallo,
super man lernt immer noch was dazu.
PS. genau so habe ich es auch gehalten MMU ja, aber nur damit man in den
genus der Caches kommt. Übrigens kann man die Caches auch bei
Register(Hardware) und statischem Speicher abschalten (bringt dort
relativ wenig).
Gruß Sascha
Hallo zusammen,
ich hab immer noch ziemliche Probleme mit dem Verständnis der MMU
1
base=(uint32_t*)0;
2
3
4
// pinmod
5
base[GPFSEL1]&=~(7<<18);
6
base[GPFSEL1]|=1<<18;
7
8
// set timer
9
base[C1]=base[CLO]+0x080000;
10
base[CS]=2;
11
12
// set irq controller
13
base[0x2000B210/4]=0x0;
14
base[0x2000B20C/4]=0x80|1;
15
16
// 512 MB RAM cachable
17
for(i=0;i<512;i++){
18
MMUTBL[i]=i<<20|0x0C0E;
19
}
20
21
// rest shared device
22
for(i=512;i<4096;i++){
23
MMUTBL[i]=i<<20|0x0C06;
24
//10987654321098765432109876543210
25
// 0110000000110
26
// 0000TEXapP dom0CB10
27
28
}
29
30
start_mmu(MMUTBL);
31
start_l1cache();
32
// enable_fiq();
33
34
35
36
37
while(1){
38
base[GPCLR0]=1<<16;// LED off
39
tmp=0;
40
while(tmp<1000000){
41
tmp++;
42
}
43
44
base[GPSET0]=1<<16;// LED on
45
tmp=0;
46
while(tmp<1000000){
47
tmp++;
48
}
49
};
So läuft das Programm wie es soll - die LED toggelt
Jetzt versuch ich die LED in einem IRQ Handler zu Toggeln
1
voidc_irq_handler()
2
{
3
staticuint32_tt=0,i;
4
asm("push {r0,r1,r2,r3,r4,r5,r6,r7,lr}");
5
6
base[C1]=base[CLO]+1000000;
7
if(t){
8
t=0;
9
base[GPSET0]=1<<16;
10
}
11
else{
12
t=1;
13
//GotoXY(1,160);
14
//DrawString((uint8_t*)"1");
15
base[GPCLR0]=1<<16;
16
}
17
18
base[CS]=2;
19
asm("pop {r0,r1,r2,r3,r4,r5,r6,r7,lr}");
20
asm("subs pc,lr,#4");
21
}
das funktioniert wenn ich die Zeile
start_l1cache();
auskommentiere. Also kein Cache
Mit Cache bleibt die LED aus
Kommentar vor dem Print weg füllt sich mein Bildschirm mit Einsen
Also versuch ich es in Assembler
1
s_irq_handler:
2
push{r0,r1,r2,r3,r4,r5,r6,r7,lr}
3
4
ldrr9,=0x20003004
5
ldrr10,[r9]
6
addr10,#999424
7
ldrr9,=0x20003010
8
strr10,[r9]
9
10
cmpr12,#0
11
movr9,#0x10000
12
ldrner10,=0x2020001C
13
ldreqr10,=0x20200028
14
strr9,[r10]
15
moveqr12,#1
16
movner12,#0
17
18
ldrr9,=0x20003000
19
movr10,#2
20
strr10,[r9]
21
22
pop{r0,r1,r2,r3,r4,r5,r6,r7,lr}
23
subspc,lr,#4
Ergebniss
Die LED geht mal an und bleibt dann an oder sie leibt aus
Und jetzt weiss ich nicht mehr weiter. Wäre nett wenn mir jemand ein Tip
gibt
Gruss
Heinz
heinz schrieb:> das funktioniert wenn ich die Zeile> start_l1cache();> auskommentiere. Also kein Cache
Hab keine Ahnung, was genau Du in der Funktion anstellst...
Hast Du den Cache invalidiert, oder schaltest Du ihn nur ein?
Ich habe gedacht ich brauch vielleicht ein memory barrier, aber wieso
sollte es dann funktionieren wenn ich eine Schleife in dar Main mache?
hätte ich schreiben sollen
wenn ich es mit dem Version IRQ probiere habe ich am Ende eine Schleife
while(1){};
hmm - Unterschied ist dass ich der ohne IRQ Version in der Main zig mal
einen Zugriff auf den Speicher mache. Ich versuch morgen mal was
passiert wenn ich in der IRQ Version in Schleife sinnlos eine Variable
ändere
Gruss
Heinz
Hab jetzt mal versucht den Speicher in der Schleife zu belasten - bringt
auch nichts
>Eine ISB sollte dem Einschalten der MMU und der Caches folgen
Was ist eine ISB?
Gruss
Heinz
Hab jetzt mal die IRQ Routine und die Endlosschleife geändert
1
voidc_irq_handler()
2
{
3
staticuint32_tt=0;
4
asm("push {r0,r1,r2,r3,r4,r5,r6,r7,lr}");
5
6
DrawString((uint8_t*)" ");
7
DrawDec(t);
8
DrawString((uint8_t*)" ");
9
10
base[C1]=base[CLO]+1000000;
11
12
if(t){
13
t=0;
14
base[GPSET0]=1<<16;
15
}
16
else{
17
t=2;
18
base[GPCLR0]=1<<16;
19
}
20
21
base[CS]=2;
22
asm("pop {r0,r1,r2,r3,r4,r5,r6,r7,lr}");
23
asm("subs pc,lr,#4");
24
}
Schleife
1
while(1){
2
tmp=0;
3
while(tmp<1000000){
4
tmp++;
5
}
6
DrawString((uint8_t*)"1");
7
};
Und was bekomm ich auf den Bildschirm
"11111111 0 2 11111111 0 2 1111111....." Einsen sind es mehr
Die blinkt also schon - nur ein bischen kurz
Gruss
Heinz
heinz schrieb:>>Eine ISB sollte dem Einschalten der MMU und der Caches folgen> Was ist eine ISB?
Instruction Synchronization Barrier. Stellt sicher, dass der nächste
Befehl garantiert aus dem Speicher bzw Cache kommt und kein
möglicherweise "veralteter" Opcode aus der Pipeline genommen wird.
DSB für den Daten Cache entsprechend auch.
Memory Barriers sind nach kontextverändernden Befehlen (context changing
instructions) vorgeschrieben. Siehe ARM ARM.
Auch eine interessante Frage ist, ob die Inline Assembler Befehle der
ISR garantiert an den entsprechenden Stellen eingefügt werden, oder ob
der Compiler da noch mal "nachbessert".
Hallo,
ich habe das Problem auch bei einem CM4 mit dem Interrupt feststellen
können. Wollte dort auch nur eine LED Togglen lassen. So und jetzt zu
dem Problem. Es wird ein Read-Modify-Write Speicherzugriff über die
I/O-Register ausgeführt. Lesen und Schreiben sind aber trotz deiner
Assembler oder C Struktur im Arbeitsablauf richtig, werden aber duch die
Unterstützung des Read und Write Cache nicht richtig ausgeführt,
jedenfalls Zeitlich. Da brauchst du zwingend einen DSB Befehl. Alle
IO-Register (Adressen) solltest du in der MMU-Tabelle als none-cachable
und none-bufferable einstellen. Also keinen Cache und keinen
Write-Buffer für I/O und Hardwareregister.
Gruß Sascha
Okay kann ich mal einfügen.
Die Inline Befehle stehen an der richtigen Stelle (objdump)
Ich lass mir jetzt auch den Stand vom Counter 1 anzeigen und der zählt
hoch.
Beim Wechsel von t=0 zu t=2 in fast Nullzeit
Ich brauch scheinbar in der IRQ Routine eine Memory Barriere. Broadcom
sagt
"In order to keep the system complexity low and data throughput high,
the BCM2835 AXI system does not always return read data in-order"
.....
"You should place:
• A memory write barrier before the first write to a peripheral.
• A memory read barrier after the last read of a peripheral. "
Werde ich dann später mal versuchen
Mal weiter probiert
Eine Data memory barrier bringt nichts
Datacache invalidieren und es funktioniert wie es soll
Für die Performance kann das ja wohl keine Lösung sein?
Ich bin davon aushehangen, dass wenn ich den Speicherbereich über 2 Gig
(da ist die Peripherie eingeblendet) in der Translationbase Tabelle als
nicht cachbar ausweise ein flush nicht nötig ist
Gruss
Heinz
Hallo Heinz,
ja Cache und Performanche ist für viele Anwendungen wie ein trojanisches
Pferd.
Wenn man auf einem solchen Controller programme schreibt wie auf einem
Mikrocontroller mit hochfrequenten Interrupts oder ständiges
Contextswitching dann hat man in der Tat mit dem Cacheverwalten mehr
Arbeit als manchmal Vorteile. Das habe ich auch schon auf meinem ARM9
bemerkt. Diese Teile sind halt schon eher auf die Verarbeitung von
Multimedia ausgelegt. Es gibt aber auch die Möglichkeit im Cache
verschiedene Bereich fest einzulagern sozusagen verschiedenen
Programmteilen zuzuweisen, wie dem Interruptsheduler oder
Betriebssystem.
Gruß Sascha
heinz schrieb:> Datacache invalidieren und es funktioniert wie es soll
Prima!
> Für die Performance kann das ja wohl keine Lösung sein?
Das machst Du doch in Deiner Anwendung nur einmal nach dem Reset. Beim
Invalidieren im laufenden Betrieb kann man durchaus selektiv vorgehen
und muss nicht den gesamten Inhalt verwerfen.
> Ich bin davon aushehangen, dass wenn ich den Speicherbereich über 2 Gig> (da ist die Peripherie eingeblendet) in der Translationbase Tabelle als> nicht cachbar ausweise ein flush nicht nötig ist
Ist er auch nicht. Aber Du hast ja noch andere Datenzugriffe im
Programm.
@Marcus Harnisch
Das hast Du falsch verstanden
Ich versuch im Timerinterupt (zum Testen 1 Hz) eine LED zu toggeln.
Zugriff auf den entsprechenden Ausgang ist Memory Mapped 0x2020001C.
Ich sehe über eingetreute Prints dass meine IRQ Routine einmal nach 1
Sekunde und dann sofort wieder "ohne" Zeitverzögerung aufgerufen wird
Die LED toggelt nur wenn ich jedesmal am Anfang der IRQ Routine den
Daten Cache invalidiere.
1
for(i=512;i<4096;i++){
2
MMUTBL[i]=i<<20|0x0C06;
3
//10987654321098765432109876543210
4
// 0110000000110
5
// 0000TEXapP dom0CB10
6
7
}
Jetzt habe ich meiner Meinung nach (auch) diesen Speicherbereich als
shared Device deklariert.
0x0c06 = 0b110000000110
TEX = 000
B = 1
C = 0
AP = 11 sollte keine Rolle spielen weil domain 0 als Manager defieniert
ist
Eigentlich sollte das also am Cache vorbeigehen. Wobei dann aber wieder
die Aussage von Broadcom entgegen steht.
heinz schrieb:> Das hast Du falsch verstanden>> Die LED toggelt nur wenn ich jedesmal am Anfang der IRQ Routine den> Daten Cache invalidiere.
Nee, hab ich nicht. Ich meinte nur, dass es nicht nötig sein sollte.
Anscheinend haben Deine Probleme auch nichts mit dem Cache zu tun,
sondern vermutlich mit irgendeinem kritischen timing. Statt dem Cache
invalidate hättest Du auch eine Warteschleife ausführen können.
> Eigentlich sollte das also am Cache vorbeigehen. Wobei dann aber wieder> die Aussage von Broadcom entgegen steht.
Die Aussage von Broadcom ist lediglich, dass die Ergebnisse von
Lesezugriffen auf zwei unterschiedliche Peripheriebausteine in
umgekehrter Reihenfolge zurückgegeben werden können. Das hat ebenfalls
nichts mit Deinem Problem zu tun.
Das doppelte Schreiben ist natürlich auch nicht die Lösung sondern
verdeckt nur das ursächliche Problem. Was ist denn "base[CS]" überhaupt?
>Was ist denn "base[CS]" überhaupt?
Im Prinzip das EOI für den Timerbaustein
>zwei unterschiedliche Peripheriebausteine
Das ist halt die Frage wie man das interpretiert
Sind damit auch zwei unterschiedliche Adressen gemeint, dann könnte
base[C1] = base[CLO] + 1000000;
in die Hosen gehen
Näturlich mach ich irgendwo irgendwas falsch, wenn nicht hätte Broadcom
ein riesen Problem (was nicht ist). Das Problem ist nur was.
Ablauf sollte ff. sein
Ich habe eine freilaufenden Timer CLO
Ich habe 4 "compare register" C1 .. C4
sind CLO und Cx gleich gibt es einen Interupt
CS = 2 macht den timecompare wieder scharf
Das letzte steht so nicht in der Doku von Broadcom und wird im Internet
etwas diskutiert.
Was seh ich:
ohne D cache funktionierts wie es soll
mit I cache funktionierts
mit D cache kommt mein IRQ nach 1 Sekunde und sofort im Anschluss noch
einer. Besser gesagt meine IRQ Routine wird sofort nochmal durchlaufen.
Wobei mein Register CLO um 1000000 inkrementiert ist.
CLO ist read only
Was falsch sein kann:
Ich kontrolliere nicht ob der IRQ überhaupt vom Timer C1 ausgelöst wird
Meine Serviceroutine wird über den FIQ getriggert. Steht eigentlich
nirgendwo das das geht.
Werde ich morgen probieren
Gruss
Heinz
Hallo,
nach beendigen der Interrupts FIQ oder IRQ muss man auch aufpassen, das
der Interrupt Controller den Interrupt als Ausgeführt (Terminiert)
sieht.
Wird an dieser stelle keine Memory Data Barriere und Instruction
Barriere eingebaut kann es folgenden Effekt haben. Der Interrupt wird
zwar beendet, durch den Schreibcache und durch die Pipline wird die
Terminierung ausherhalb der ISR-Routine gemacht. Somit gilt der
Interrupt als nicht beendet das Pending-Flag bleibt weiter aktiv, und
die ISR-Routine wird sofort erneut wieder angesprungen.
Ist bei den ARM Controllern ist das je nach Hersteller mal etwas besser
oder weniger gut gelöst.
Gruß Sascha
heinz schrieb:>>Was ist denn "base[CS]" überhaupt?> Im Prinzip das EOI für den Timerbaustein
Das kommt wahrscheinlich zu spät. Man sollte das immer ganz zu Anfang
der ISR erledigen. Merke: Trotz Memory Barriers gibt es kein absolut
narrensicheres Verfahren das garantiert, dass die Schreibzugriffe vor
Ende der ISR durchgeführt wurden. Ein DSB (DMB reicht nicht!) vor
Verlassen der ISR hilft.
>>zwei unterschiedliche Peripheriebausteine> Das ist halt die Frage wie man das interpretiert
Das kann man nur mit Kenntnis der Busmatrix. Typischerweise können die
Daten unterschiedlicher Slaves am AXI Bus out-of-order zurückgegeben
werden.
> Sind damit auch zwei unterschiedliche Adressen gemeint, dann könnte> base[C1] = base[CLO] + 1000000;> in die Hosen gehen
Wieso? Ich sehe maximal zwei Lesezugriffe, deren Reihenfolge egal ist.
Wahrscheinlich nur einer, da die Konstante vom Compiler als immediate
erzeugt wird.
> ohne D cache funktionierts wie es soll
Klar, weil alles (z.B. POP) länger dauert und das Interrupt Clear daher
ausreichend schnell ist.
Hallo,
ich habe heute leider sehr wenig Zeit gehabt.
>Somit gilt der Interrupt als nicht beendet das Pending-Flag bleibt weiter >aktiv
sieht so aus wie wenn das so wäre. Deshalb das doppelte (manchmal)
ausführen der ISR
Ich werde mal das "Basic pending register" anschauen.
>Man sollte das immer ganz zu Anfang der ISR erledigen
was dann halt zu rekursiven Aufrufen führen kann.
>weil alles (z.B. POP) länger dauert
Ich habe das eigentlich so verstanden, dass der Schreibvorgang noch im
Cache ist und ich die ISR schon verlassen habe?
Wieso werden die Lese/Schreibzugriffe überhaupt gecached - diese
Adressen liegen in einem Bereich der in der Translationtabelle als
nonbuffered,noncachable ausgewiesen sind?
und Danke für die Mühe die Ihr euch macht
Gruss
Heinz
heinz schrieb:> Wieso werden die Lese/Schreibzugriffe überhaupt gecached - diese> Adressen liegen in einem Bereich der in der Translationtabelle als> nonbuffered,noncachable ausgewiesen sind?
Device memory ist buffered, d.h. die CPU wird durch Schreibzugriffe auf
langsame Peripherie nicht aufgehalten. Das Lesen vom Device wird nicht
vom Cache bedient, wohl aber die Stackzugriffe, die dann schneller
beendet sind als das Interrupt Clear.
printf("bcm2835_peri_write paddr %08X, value %08X\n",(unsigned)paddr,value);
7
}
8
else
9
{
10
// Make sure we don't rely on the first write, which may get
11
// lost if the previous access was to a different peripheral.
12
*paddr=value;
13
*paddr=value;
14
}
15
}
was bei mir
base[CS] = 2;
base[CS] = 2;
ist.
88d0: e59f2030 ldr r2, [pc, #48] ; 8908
88d4: e3a01002 mov r1, #2
88d8: e7831002 str r1, [r3, r2]
88dc: e7831002 str r1, [r3, r2]
"zwingt" das den Prozessor den Store auszuführen, oder ist das Timing
einfach so kritisch das der zusätzliche Store ausreicht
Mit einer DSB vor dem pop gibt es immer noch sporatisch doppelte
Ausführung der ISR, während es mit dem doppelten Store selbst ohne DSB
stabil läuft.
Gruss
Heinz
heinz schrieb:> // Make sure we don't rely on the first write, which may get> // lost if the previous access was to a different peripheral.> *paddr = value;> *paddr = value;
Das scheint eine Eigenart des SoC zu sein. Falls das Device an einem
APB(us) hängt hinge eine mögliche Erklärung damit zusammen, dass dessen
Busprotokoll (APBv2) keine Waitstates kennt. Das Slave Interface muss
dann die Daten abnehmen und damit dem Prozessor das Ende des Buszyklus
signalisieren, bevor sie im eigentlichen Register landen. Keine memory
barrier kann in so einem Fall helfen. Daher meine frühere Bemerkung,
dass es kein universelles, absolut sicheres Verfahren geben kann.
Es kann sich ganz schlicht auch um eine Schwäche der verwendeten
Busmatrix handeln. Das weiß nur Broadcom. Solange es irgenwo
dokumentiert ist kann man damit wohl ganz gut leben.