Forum: Mikrocontroller und Digitale Elektronik Bootloader mit Microchip Studio


von Jürgen (derkleinemuck)


Lesenswert?

Ich will an einem Atmel 368 mit Bootloader spielen. Das Prinzip habe ich 
schon verstanden, denke ich.
Wenn ich in Micrchop Studio ein LED-Blinkprogramm ablege, dann läuft das 
auch (Bootsize 2048 words). Dazu in der Toolchain des Projekts bei 
Linker/Memory den Eintrag ".text=0x3800" hinzugefügt. Der Code liegt 
dann auch in der Hex-Datei ab Adresse 0x7000. Zudem die Option "Erase 
entire chip" beim Programmiertool (Snap) gesetzt.
Fuses sind IMHO korrekt:
1
BODLEVEL = DISABLED:
2
RSTDISBL = [ ]
3
DWEN = [ ]
4
SPIEN = [X]
5
WDTON = [ ]
6
EESAVE = [X]
7
BOOTSZ = 2048W_3800
8
BOOTRST = [X]
9
CKDIV8 = [X]
10
CKOUT = [ ]
11
SUT_CKSEL = INTRCOSC_8MHZ_6CK_14CK_65MS
12
13
EXTENDED = 0xFF (valid)
14
HIGH = 0xD0 (valid)
15
LOW = 0x62 (valid)
1
#define F_CPU 1000000UL
2
#include <avr/io.h>
3
#include <util/delay.h>
4
5
int main(void) {
6
  void (*start)( void ) = 0x0000;      
7
  DDRC = (1 << DDC5);        
8
  
9
  for (uint8_t i = 0; i < 10; i++) {
10
    PORTC ^= (1 << PORTC5);    // toggle pin
11
    _delay_ms (200);
12
  }
13
  start();  
14
}

So weit.
Dann ein Hauptprogramm. Macht das gleiche, nur anderer Ausgang
1
#define F_CPU 1000000UL
2
#include <avr/io.h>
3
#include <util/delay.h>
4
5
int main(void) {
6
  DDRC = (1 << DDC4);  
7
  
8
  for (uint8_t i = 0; i < 30; i++) {
9
    PORTC ^= (1 << PORTC4);    
10
    _delay_ms (50);
11
  }
12
}

Hier keine Ergänzung für Linker und kein "erase", sondern "manged by 
script"

So weit klappt das auch beim ersten mal. Ändere ich jetzt aber einen 
Wert im Hauptprogramm (delay oder i), dann funktioniert der Bootloader 
weiterhin aber das Hauptprogramm "spinnt". Also die LED am Ausgang 
blinkt nicht oder nur einmal.

Wo ist mein Denkfehler?

von Wastl (hartundweichware)


Lesenswert?

Jürgen schrieb:
> Ich will an einem Atmel 368 mit Bootloader spielen.

Was ist ein "Atmel 368"?

von Christian M. (christian_m280)


Lesenswert?

Wastl schrieb:
> Was ist ein "Atmel 368"?

Er meint ganz bestimmt "Intel 386"!

Gruss Chregu

von S. L. (sldt)


Lesenswert?

Ich kann beim besten Willen nicht erkennen, wo bzw. wie der 'Bootloader' 
irgendetwas lädt ...

von Cyblord -. (cyblord)


Lesenswert?

Welche AVRs haben überhaupt einen eigenen (Rom) Bootloader?
Haben die nicht nur einen konfigurierbaren Flash Bereich dafür?

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Cyblord -. schrieb:
> Welche AVRs haben überhaupt einen eigenen (Rom) Bootloader?
z.B. ATMega16U2 und seine Brüder
Haben sogar einen HWB Pin und Bit in den Fuses

Untersuche mal Atmel Flip

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Wastl schrieb:
> Was ist ein "Atmel 368"?

ATmega328[P][PB]

von S. L. (sldt)


Lesenswert?

> Das Prinzip habe ich schon verstanden, denke ich.

Dieses Verständnis mit zum Beispiel diesem Artikel abgeglichen?
https://www.mikrocontroller.net/articles/AVR_Bootloader_in_C_-_eine_einfache_Anleitung#Der_%22echte%22_Bootloader

von Cyblord -. (cyblord)


Lesenswert?

Arduino F. schrieb:
> ATMega16U2 und seine Brüder

Nur die mit USB?

von Jürgen (derkleinemuck)


Lesenswert?

gemeint war ein mega328. mein Fehler.

Erst einmal soll auch nichts geladen werden, sondern einfach nur blinken

S. L. schrieb:
>> Das Prinzip habe ich schon verstanden, denke ich.
>
> Dieses Verständnis mit zum Beispiel diesem Artikel abgeglichen?
> 
https://www.mikrocontroller.net/articles/AVR_Bootloader_in_C_-_eine_einfache_Anleitung#Der_%22echte%22_Bootloader

Ja.

von S. L. (sldt)


Lesenswert?

> Erst einmal soll auch nichts geladen werden, sondern einfach nur blinken

> Ändere ich jetzt aber einen Wert im Hauptprogramm (delay oder i) ...

Diesen Zusammenhang verstehe nun ich nicht ...

PS:
> ... dann funktioniert der Bootloader weiterhin ...
Das heißt doch, dass das Bootloader-Fragment blinkt ("einfach nur 
blinkt") - an welcher Stelle soll nun ein 'Hauptprogramm' ins Spiel 
kommen?

: Bearbeitet durch User
von Wastl (hartundweichware)


Lesenswert?

S. L. schrieb:
> Diesen Zusammenhang verstehe nun ich nicht ...

Ja, hier geht es mal wieder kreuz und quer durch den Gemüsegarten.

Schon allein die Aussage:

Jürgen schrieb:
> Wenn ich in Micrchop Studio ein LED-Blinkprogramm ablege,
> dann läuft das auch

Ein Blinkprogramm ablegen?

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Cyblord -. schrieb:
> Nur die mit USB?

Nein, nicht alle mit USB!
z.B. der 32U4 kommt ab Werk mit einem Bootloader und dem schon 
angesprochenen HWB Pin und Fuse.
Dieser Bootloader ist aber entfernbar.

Die Atmel mit CAN (alle?) haben auch ab Werk einen Bootloader, dann eben 
über CAN und nicht über USB

Darum sagte ich ja:
Arduino F. schrieb:
> Untersuche mal Atmel Flip
Tue das, lese die Dokus, dann machst du dich selber schlau.

von S. L. (sldt)


Lesenswert?

an Jürgen:

Die Intention und das Vorgehen ist mir noch immer nicht ganz klar, aber 
ich arbeite dran (am Verständnis):

Sinn eines Bootloaders ist es, ihn mit dem Hex-File (typisch, kann aber 
auch eine andere Form sein) des Anwenderprogrammes zu füttern (z.B. per 
UART oder SDCard), und er schreibt das dann in das Flash. Der 
entsprechende Flash-Bereich muss gelöscht werden, dazu gibt es den 
intern ausführbaren Befehl 'Page Erase' (per SPMCSR).
  Einen solchen (von außen ausführbaren) Befehl finde ich aber nicht in 
der Liste 'Serial Programming Instruction set', dort gibt es nur 'Chip 
Erase' - wie also (für Ihr 'Hauptprogramm')
> Hier keine Ergänzung für Linker und kein "erase",
> sondern "manged by script"
in diesem 'snap' funktionieren soll, verstehe ich nicht. Meine Vermutung 
ist, dass dort einfach 'drübergeschrieben' wird, was physikalisch einer 
VerUNDung gleichkommt und somit unsinnige Maschinenbefehle produziert - 
eigentlich müsste das Verify meckern.

Aber, wie schon angedeutet, ist Ihr Vorgehen an dieser Stelle wenig 
sinnvoll, was soll ein Anwenderprogramm nützen ohne komplett 
funktionierenden Bootloader?

von Jürgen (derkleinemuck)


Lesenswert?

Was ich im Bootloaderbereich mache, spielt doch gar keine Rolle. Ich 
finde ein "hello world" erst einmal sinnvoll.

Sorry, wenn "ablegen" zu flapsig für hochtechnisch Intelektuelle ist.
Snap ist der Debugger.

S. L. schrieb:
> eine Vermutung
> ist, dass dort einfach 'drübergeschrieben' wird, was physikalisch einer
> VerUNDung gleichkommt und somit unsinnige Maschinenbefehle produziert -
> eigentlich müsste das Verify meckern.

Beim normalen Programmierablauf (codieren und dann Start Without 
Debugging) wodurch auch der Code an die beim Linker angegebene Adresse 
geschrieben wird, kommt es zu keiner Fehlermeldung.
Beim ersten Mal nach dem Übertragen der Bootloader-Sektion klappt's ja 
auch. Was passiert falsches beim zweiten Versuch das Hauptprogramm (mit 
leichten Änderungen) anderes?
Übertrage ich das .hex aber manuell, meckert verify. Was ich aber nicht 
verstehe, denn wieso soll das eine verUNDung sein? Wenn der Bootloader 
tatsächlich den Code ab 0x0000 schreibt, passiert doch eigentlich nichts 
anderes.

S. L. schrieb:
> Der
> entsprechende Flash-Bereich muss gelöscht werden, dazu gibt es den
> intern ausführbaren Befehl 'Page Erase' (per SPMCSR).
>   Einen solchen (von außen ausführbaren) Befehl finde ich aber nicht in
> der Liste 'Serial Programming Instruction set', dort gibt es nur 'Chip
> Erase' - wie also (für Ihr 'Hauptprogramm')

Verstehe ich das dann richtig, daß ab 0x0000 erst einmal der Speicher 
gelöscht (0xff) werden müßte?

von S. L. (sldt)


Lesenswert?

> Was passiert falsches beim zweiten Versuch das Hauptprogramm
> (mit leichten Änderungen) anderes?

Ablauf: der Bootloader wird 'geflashed', das beinhaltet ein 'Chip 
Erase'; damit ist auch der Bereich des Hauptprogrammes (ab 0x0000) 
gelöscht, d.h. mit 0xFF beschrieben. Wird nun das Hauptprogramm ohne 
erase geflashed, dann ist das noch in Ordnung; nun aber stehen dort 
(ab 0x0000) Werte (die Maschinenbefehle) ungleich 0xFF, wird nun 
nachfolgend erneut dorthin geschrieben ohne erase, dann entsteht 
Unsinn: z.B. 0xAB UND 0xFF ergibt wieder 0xAB, aber 0xAB UND 
0xIrgendetwas ergibt keineswegs 0xAB.

> Wenn der Bootloader tatsächlich den Code ab 0x0000
> schreibt, passiert doch eigentlich nichts anderes.

Oh doch, der Bootloader nutzt den internen Befehl 'Page Erase', diese 
Möglichkeit gibt es extern nicht, da gibt es nur 'Chip Erase'.

> Verstehe ich das dann richtig, daß ab 0x0000 erst einmal
> der Speicher gelöscht (0xff) werden müßte?

Ja.

von Jürgen (derkleinemuck)


Lesenswert?

OK. Mir war nicht klar, daß beim programmieren quasi kein löschen der 
Bytes erfolgt. Steht das irgendwo? Oder ist das Wissen um Flash? Auf 
jeden Fall erklärt das meine Frage.

Nur aus Spaß/Verständnis: würde ich eine .hex-Datei erzeugen, die in den 
ersten 0x7000 0xFF beinhaltet und diese manuell übertragen, würde ich 
den Flash bis zum Bootloaderbereich "löschen" und könnte dann immer 
wieder neue Programme in den vorderen Bereich übertragen?

BTW: Auch 
https://www.mikrocontroller.net/articles/AVR_Bootloader_in_C_-_eine_einfache_Anleitung#Der_%22echte%22_Bootloader 
zeigt "hello world" nur mit UART statt LEDs. Also keinesfalls abwegig, 
sich der Thematik so zu nähern.

von Christian M. (christian_m280)


Lesenswert?

Jürgen schrieb:
> würde ich eine .hex-Datei erzeugen, die in den ersten 0x7000 0xFF
> beinhaltet und diese manuell übertragen, würde ich den Flash bis zum
> Bootloaderbereich "löschen

Nein eben nicht, weil

S. L. schrieb:
> z.B. 0xAB UND 0xFF ergibt wieder 0xAB

Gruss Chregu

von S. L. (sldt)


Lesenswert?

> Nur aus Spaß/Verständnis ...

Nein. Ganz elementar: ein Bit im Flash kann nur mit einem speziellen 
Befehl auf 1 gesetzt werden, also intern per 'Page Erase' oder extern 
per 'Chip Erase'.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Jürgen schrieb:
> Steht das irgendwo?

Die Verfahren sind Dokumentiert.
Lesen und verstehen kann helfen.

Ein übliches Verfahren wäre, beim ersten Bespielen per ISP:
1. Anwendung und Bootloader zu einem *.hex Klumpen verschmelzen. Das ist 
auch händisch wenig, bis kein, Problem.
2. Den Brocken per ISP ins Flash brennen.
3. Fuses setzen (auch lock Fuses)

Ab dann kann man das Flash nach belieben über den Bootloader 
beschreiben.



Geht man da noch mal per ISP ans Flash, ist der Bootloader weg/kaputt, 
es sei denn man macht Schritt 1 bis 3 nochmal

von S. L. (sldt)


Lesenswert?

an Jürgen:
> ... Also keinesfalls abwegig ...

Dagegen ist nichts einzuwenden und wurde ja auch nichts eingewandt.
  Aber das Manipulieren des unteren Flash-Bereichs, während oben der 
Bootloader völlig unbeteiligt sitzt, führt, wie gesehen, bestenfalls zu 
Missverständnissen und Problemen.

von Jürgen (derkleinemuck)


Lesenswert?

S. L. schrieb:
> an Jürgen:
>> ... Also keinesfalls abwegig ...
>
> Dagegen ist nichts einzuwenden und wurde ja auch nichts eingewandt.
>   Aber das Manipulieren des unteren Flash-Bereichs, während oben der
> Bootloader völlig unbeteiligt sitzt, führt, wie gesehen, bestenfalls zu
> Missverständnissen und Problemen.

Naja. Die obigen Beiträge waren wie hier üblich sehr überheblich.
Was ist bei Dir "oben". Ich schrieb, daß das Bootloader-Blink-Teil im 
Bootloaderbereich liegt. Das ist für mich unten.

Arduino F. schrieb:
> Jürgen schrieb:
>> Steht das irgendwo?
>
> Die Verfahren sind Dokumentiert.
> Lesen und verstehen kann helfen.

Und wo? Einfach "lesen" ohne Quelle ist gar nicht hilfreich. "Gott hat's 
gesagt"

> S. L. schrieb:
>> z.B. 0xAB UND 0xFF ergibt wieder 0xAB
>

Ah. Mein Denkfehler. Und 0x00 in der hex-Datei bringt nichts, weil das 
nichts löscht. OK.

von S. L. (sldt)


Lesenswert?

Also "Überheblichkeit" lasse ich mir nicht vorwerfen; das müssen Sie 
falsch verstanden haben.

'Unten' ist für mich 0x0000, 'oben' 0x3800.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Jürgen schrieb:
> Und wo? Einfach "lesen" ohne Quelle ist gar nicht hilfreich. "Gott hat's
> gesagt"

Leider muss ich feststellen, dass du damit überfordert bist, auf die 
Seite des Herstellers zu gehen.
Ich helfe dir mal:
z.B. https://www.microchip.com/en-us/product/atmega328p

Dort findest du ca 100 Dokumentationen deinem Chip zugeordnet.
Das lesen fängt mit dem Datenblatt an!


Du siehst:
Gar nicht so schwer zu finden, man muss es nur tun.

Jürgen schrieb:
> Die obigen Beiträge waren wie hier üblich sehr überheblich.
Natürlich darfst du mich als "überheblich" klassifizieren. Aber Freunde 
machst du dir damit nicht.

: Bearbeitet durch User
von S. L. (sldt)


Lesenswert?

> Steht das irgendwo? Oder ist das Wissen um Flash?

Nun ja, unter diesem Begriff steht es sicher irgendwo bei Wikipedia.

Und im Datenblatt des ATmega328 steht unter 'Self-Programming the Flash' 
"Before programming a page with the data stored in the temporary page 
buffer, the page must be erased". Womit sich mehr oder weniger direkt 
durchaus folgern lässt, dass ein einfaches Schreiben ins Flash nicht 
reicht.

Und im übrigen sitzen wir hier ja beisammen, um solche Fragen zu klären. 
Manchmal ist es etwas holprig, zugegeben.

von Jürgen (derkleinemuck)


Lesenswert?

S. L. schrieb:
> Nun ja, unter diesem Begriff steht es sicher irgendwo bei Wikipedia.
>
> Und im Datenblatt des ATmega328 steht unter 'Self-Programming the Flash'
> "Before programming a page with the data stored in the temporary page
> buffer, the page must be erased". Womit sich mehr oder weniger direkt
> durchaus folgern lässt, dass ein einfaches Schreiben ins Flash nicht
> reicht.

Also ich kann nicht aus einer allgemeinen Löschanweisung ablesen, daß 
diese notwendig ist, weil beim Beschreiben von Flash das Byte nicht als 
solches im Speicher landet.
Entspricht ja auch nicht gerade der Lebenserfahrung. Immerhin formatiere 
ich meine Festplatte (und selbst SSDs) nicht erst, bevor ich eine 
geänderte Datei speicher.

Und zu sagen "ließ 100 Dokumentationen, irgendwo wird's da schon stehen" 
empfinde ich als überheblich.

von Cyblord -. (cyblord)


Lesenswert?

Jürgen schrieb:
> Also ich kann nicht aus einer allgemeinen Löschanweisung ablesen, daß
> diese notwendig ist, weil beim Beschreiben von Flash das Byte nicht als
> solches im Speicher landet.

Schreiben ins Flash erfolgt eben immer pro Seite. Die Seite muss erst 
gelöscht werden, dann kann die KOMPLETTE SEITE neu geschrieben werden.
Deshalb gibts den Page Buffer.

Was verstehst du daran nicht? Byteweises schreiben direkt ins Flash geht 
nicht.

von S. L. (sldt)


Lesenswert?

> ... überheblich ...

Ich möchte an das Sprichwort erinnern, wonach es aus dem Wald 
herausklingt wie man hineinruft - schauen wir uns den Eröffnungsbeitrag 
an:

Mit etwas Glück wird Wilhelm Hauffs Märchenfigur noch mit einem gewissen 
Wohlwollen gesehen, mit etwas Pech aber murmelt der neutrale Beobachter 
vor sich hin
1
Bei euch, ihr Herrn, kann man das Wesen
2
Gewöhnlich aus dem Namen lesen
was dann durch den allerersten Satz bestätigt wird:
> Ich will an einem Atmel 368 mit Bootloader spielen.
Pardon, aber mit dieser 'locker vom Hocker'-Attitüde sind schon 90 % des 
Kredits verspielt - also ich meine, Sie haben doch ein Anliegen und 
erwarten Hilfe, d.h. eine Leistung, geben sich aber nicht einmal das 
bisschen Mühe, die Controllerbezeichnung korrekt anzugeben: 'ATmega328'. 
Eine solche Selbstverständlichkeit ist keineswegs auf
> hochtechnisch Intelektuelle
beschränkt.
  Mit dem allgemeinen Umgangston hier im Forum bin auch ich alles andere 
als einverstanden, aber Sie provozieren ihn geradezu. Was nach zwei 
Jahren Mitgliedschaft auch bekannt sein müsste.

Letztlich aber wurde die Frage
> Wo ist mein Denkfehler?
doch zufriedenstellend beantwortet, nicht wahr? Es haben mehrere Leute 
teilweise einige Zeit investiert.
  Dann möchte ich abschließend, ebenfalls aus der Märchen/Fabel-Welt, 
zitieren: "... und den Dank such ich vergebens ...".

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Jürgen schrieb:
> ließ 100 Dokumentationen

Das ist gelogen.
Das habe ich nicht gesagt!

Und ja, es ist dein Problem, wenn du zu faul bist dir da die richtigen 
rauszusuchen.
Zu erwarten, dass ich das für dich tue ist, irrational überzogen.
Ein Verhalten, wie ein verwöhntes trotziges Kleinkind.

Jürgen schrieb:
> Also ich kann nicht aus einer allgemeinen Löschanweisung ablesen,

Du könntest dich gar mal kundig machen wie ein Flash Speicher 
funktioniert.
z.B. bei Wikipedia.
Aber nein ....

Jürgen schrieb:
> Entspricht ja auch nicht gerade der Lebenserfahrung. Immerhin formatiere
> ich meine Festplatte (und selbst SSDs) nicht erst, bevor ich eine
> geänderte Datei speicher.
Dann kann ein Teil deiner Lebenserfahrung in die Tonne.
Denn eine SSD macht intern nix anderes.

Jürgen schrieb:
> überheblich
So so...
Überheblich!
Mir scheint es eher so zu sein, dass ich dir in Sachen AVR Kompetenz 
deutlich überlegen bin.

Bedenke:
Überhebliche Leute (wie z.B. du) neigen dazu, ganz dämlich auf die 
Fresse zu fallen. Sich zu blamieren. Irgendwann. Wenn das dumme Getue 
die Kompetenzschwächen nicht mehr überdecken kann.

Zum Glück kann mir das kaum passieren, da erstens meine AVR Kompetenz 
schon ein ordentliches Level erreicht hat und ich zweitens keine Scheu 
habe weitere Dokus zu lesen, wenn mir da ein Mangel auffällt.


Tipp:
Es gibt einen Unterschied zwischen Überlegen und überheblich.

von 900ss (900ss)


Lesenswert?

Jürgen schrieb:
> Also ich kann nicht aus einer allgemeinen Löschanweisung ablesen, daß
> diese notwendig ist, weil beim Beschreiben von Flash das Byte nicht als
> solches im Speicher landet.

Das ist Grundlagenwissen zum Flashspeicher. Aber auch da gibt es 
Unterschiede zu NAND-Flash und NOR-Flash. Löschen muss man beide vor dem 
Beschreiben. Das Wissen sollte man sich angeeignet haben wenn man mit 
dem Themen wie hier zu tun hat.

Jürgen schrieb:
> Immerhin formatiere ich meine Festplatte (und selbst SSDs) nicht erst,
> bevor ich eine geänderte Datei speicher.

Klar, in einer SSD ist auch Flashspeicher. Aber du hast keinen direkten 
Zugriff. Dort ist ein SSD-Controller verbaut, der den Flash-Block(!), in 
dem sich die Page befindet, erst einmal löscht. Das bekommst du garnicht 
mit. Ja, Flash löschen arbeitet in einer SSD Block basiert. Ein Block 
enthält dabei mehrere Pages. Es wird also zuviel gelöscht. Damit deine 
Daten dabei nicht verloren gehen, werden sie vorher woanders hin 
kopiert. Das bekommst du auch nicht mit. Das macht der Controller alles 
in seinem "Flash Management". Stichworte wie Bad Block Management oder 
auch Wear Leveling sind da wichtig. Aber das zur SSD beschriebene findet 
alles nicht in dem Flash deines AVRs statt. Der ist etwas einfacher 
gestrickt, nur Löschen muss man eine Page (keinen Block!), bevor man 
diese richtig beschreiben kann.

Die Welt um Flash-Speicher ist deutlich komplizierter, als du vermutlich 
gerade ahnst.

Edit: Das Bad Block Management und Wear Leveling ist reichlich komplex. 
Deshalb haben große Hersteller Patente auf ihre Verfahren.

: Bearbeitet durch User
von 900ss (900ss)


Lesenswert?

Jürgen schrieb:
> Naja. Die obigen Beiträge waren wie hier üblich sehr überheblich.
> Was ist bei Dir "oben". Ich schrieb, daß das Bootloader-Blink-Teil im
> Bootloaderbereich liegt. Das ist für mich unten.

Für dich mag das so sein. Es gibt Controller, da liegt der Bootloader im 
oberen Adressbereich und andere, da liegt er am Anfang (bei 0x0).

Um keine Verwirrung aufkommen zu lassen ist es wichtig, sich präzise(!) 
auszudrücken. Diese Forderung hat nichts mit Überheblich zu tun.

: Bearbeitet durch User
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.