ATtiny, div und mod

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
Antworten
Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

ATtiny, div und mod

Beitrag von Mathias »

Bei folgenden Zeilen motzt er, das zu wenig Speicher vorhanden ist.

Code: Alles auswählen

var
  zahl: array[0..3] of byte;
 
procedure Timer0_Interrupt; public Name 'TIMER0_COMPA_ISR'; interrupt;
  d: integer;
begin
...
  zahl[1] := d div 100;
  zahl[2] := d mod 100 div 10;
  zahl[3] := d mod 10;

Code: Alles auswählen

Projekt kompilieren, OS: embedded, CPU: avr, Ziel: Project1: Exit code 256, Fehler: 1, Warnungen: 1, Hinweise: 1
Project1.pas(69,22) Hint: Mixing signed expressions and longwords gives a 64bit result
Project1.pas(123,63) Warning: Calling convention directive ignored: "OldFPCCall"
Assembling project1
Linking Project1
/usr/bin/avr-ld: Project1.elf section `.text' will not fit in region `text'
/usr/bin/avr-ld: Project1.elf section `.data' will not fit in region `data'
/usr/bin/avr-ld: region `text' overflowed by 1000 bytes
/usr/bin/avr-ld: region `data' overflowed by 206 bytes
....
Project1.pas(128,3) Error: Error while linking


Ich habe den selben Code schon mal mit Arduino programmiert.

Code: Alles auswählen

 
   zahl[1] = d / 100;
   zahl[2] = d % 100 / 10;
   zahl[3] = d % 10;

Und hier gibt es keine Probleme.

Die Idee dahinter, ich wollte ein Arduino-Sketch nach Pascal portieren.

Im Anhang noch der komplette Code von beiden Plattformen.
Dateianhänge
Project1.pas.tar.gz
(1.12 KiB) 95-mal heruntergeladen
BCD_7_Segment.ino.tar.gz
(1.07 KiB) 87-mal heruntergeladen
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: ATtiny, div und mod

Beitrag von Timm Thaler »

Kannst Du mal schauen, ob der Compiler eine *.s Datei baut und diese hier posten.

Prinzipiell:
- Es ist keine gute Idee, derart lange in der Interrupt-Routine zu verbringen.
- Es ist keine gute Idee, Zahlen so zu zerlegen. Divisionen durch alles, was nicht 2^n ist kostet Zeit und Speicher.

Das Problem könnte sein, dass FPC hier eine 16-bit Division einbaut. Die allein ist noch nicht so groß. Aber vielleicht bringt die noch andere Routinen in der Lib mit, die ebenfalls eingebaut werden - und dann platzt der Speicher.

Eine Routine zum Zerlegen einer Zahl in Ziffern hab ich hier:

Code: Alles auswählen

// Anzeige Wert als #####
 
procedure disp_valnnnnn(vval : uint16; pos : uint8);
var
  achr : uint8;
  leer : boolean = true;
begin
  achr := uint8('0');
  while (vval >= 10000) do begin
    vval := vval - 10000;
    achr := achr + 1;
    leer := false;
  end;
  if leer then begin
    achr := uint8(' ');
  end;
  dispbuf[pos] := char(achr);
 
  achr := uint8('0');
  while (vval >= 1000) do begin
    vval := vval - 1000;
    achr := achr + 1;
    leer := false;
  end;
  if leer then begin
    achr := uint8(' ');
  end;
  dispbuf[pos + 1] := char(achr);
 
  achr := uint8('0');
  while (vval >= 100) do begin
    vval := vval - 100;
    achr := achr + 1;
    leer := false;
  end;
  if leer then begin
    achr := uint8(' ');
  end;
  dispbuf[pos + 2] := char(achr);
 
  achr := uint8('0');
  while (vval >= 10) do begin
    vval := vval - 10;
    achr := achr + 1;
    leer := false;
  end;
  if leer then begin
    achr := uint8(' ');
  end;
  dispbuf[pos + 3] := char(achr);
 
  achr := uint8('0');
  while (vval >= 1) do begin
    vval := vval - 1;
    achr := achr + 1;
  end;
  dispbuf[pos + 4] := char(achr);
end


Die Idee ist, dass man nicht dividiert, sondern immer eine Stelle runterzählt und gleichzeitig die Ziffer hochzählt. Klingt erstmal umständlich, ist aber deutlich schneller als wenn man das mit Division macht.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: ATtiny, div und mod

Beitrag von Mathias »

Danke, dein Listing klappt, ich musste die Char entfernen.

Code: Alles auswählen

...
  achr := 0;
  while (vval >= 100) do begin
    Dec(vval, 100);
    Inc(achr);
    leer := false;
  end;
  if leer then begin
    achr := 16;
  end
...


Dabei habe ich noch etwas interessantes entdeckt. Ein x := x - 10, ist nicht das Gleiche wie Dec(x, 10).

Die Byte geben den Speicherverbrauch an.

Code: Alles auswählen

vval := vval - 10; // 1254Byte
vval := vval + 10; // 1256Byte
Dec(vval, 10);     // 1246Byte
Inc(vval, 10);     // 1248Byte
Dec(vval);         // 1246Byte
Inc(vval);         // 1248Byte

Dies ist nur der Fall bei Byte, bei Integer gibt es kein Unterschied.

Dies ist auch unterschiedlich goss:

Code: Alles auswählen

        vval := vval - byte(10);
        vval := vval - 10;


- Es ist keine gute Idee, Zahlen so zu zerlegen. Divisionen durch alles, was nicht 2^n ist kostet Zeit und Speicher.
Da stimme ich dir zu, der ATtiny kann keine Hardware-Division und -Addition. Nur habe ich in meiner Schaltung genügend Reserven.

- Es ist keine gute Idee, derart lange in der Interrupt-Routine zu verbringen.

Ich könnte die Vorzeichen abfrage und das disp_val in den Haupt-Loop nehmen und im Timer nur den Multiplex.
ShiftIn74HC165 muss ich im Mutiplex machen, da alle Shiftregister hintereinander verschalten sind.

So wie es scheint hat der AVR-Compiler einen Bug, sont würde das Ganze in C++ auch nicht funktionieren.

PS:Noch eine Speicherdifferenz.

Code: Alles auswählen

var
  leer: boolean = true;
braucht 4 Bytes mehr als

Code: Alles auswählen

var
  leer: boolean;
begin
  leer := true


Was wird da anders gemacht ?
Zuletzt geändert von Mathias am Do 26. Okt 2017, 21:58, insgesamt 1-mal geändert.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: ATtiny, div und mod

Beitrag von Timm Thaler »

Was da gemacht wird? Guckst Du in die *.s- Datei.

Das Laden aus einem "unverhersagbaren" Register UDR0 ist nötig, weil der Compiler ja nicht ganz blöd ist. Bei einer Zuweisung "vval := 127" setzt er einfach vorberechnete Werte ein.

Code: Alles auswählen

var
  vval : uint8; 
begin
    vval := UDR0;
    vval := vval - 10; // 1254Byte
    vval := UDR0;
    vval := vval + 10; // 1256Byte
    vval := UDR0;
    Dec(vval, 10);     // 1246Byte
    vval := UDR0;
    Inc(vval, 10);     // 1248Byte
    vval := UDR0;
    Dec(vval);         // 1246Byte
    vval := UDR0;
    Inc(vval);         // 1248Byte
end


Daraus wird der Assembler-Code, der dann direkt in die Maschinensprache (das Hexfile) umgesetzt wird.

Code: Alles auswählen

   lds   r18,(198)  ;das Laden aus dem UDR0
   subi   r18,10  ;"-" Subtrahieren mit einer Konstanten, easy
   lds   r19,(198)
   ldi   r18,10
   add   r19,r18  ;"+" Addieren mit einem Register, es gibt kein addi, es wäre aber ein subi r18, 246 möglich
   lds   r18,(198)
   subi   r18,10  ;"dec" wie "-"
   lds   r19,(198)
   ldi   r18,10
   add   r19,r18  ;"inc" wie "-"
   lds   r18,(198)
   dec   r18  ;"dec" nutzt das eingebaute dec
   lds   r18,(198)
   inc   r18  ;"dec" nutzt das eingebaute inc
 


Fazit: Fast optimal, bis auf das Addieren. Das Ganze wird zwar doppelt so groß, wenn man vval als integer nimmt, klar. Aber nichts erzeugt hier den Zuwachs um 10 Byte pro Anweisung, wie bei Dir. Da musst Du wohl selbst mal nachschauen.

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: ATtiny, div und mod

Beitrag von Timm Thaler »

Mathias hat geschrieben:

Code: Alles auswählen

var
  leer: boolean = true;
braucht 4 Bytes mehr als

Code: Alles auswählen

var
  leer: boolean;
begin
  leer := true

Was wird da anders gemacht ?


Bei der ersten Zuweisung wird die Variable im Speicher angelegt und beim Start aus dem Flash mit dem Wert "1" initialisiert, im Programm wird der Wert mit lds (load direct from sram) aus dem Speicher übernommen. Bei der zweiten Zuweisung wird die Variable - wenn sie nicht anderweitig benutzt wird, als Register angelegt und direkt mit ldi (load immediate) mit einer Konstante initialisiert.

Zweiteres ist, obwohl im Programm eine Zeile mehr, auf dem AVR weniger Code.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: ATtiny, div und mod

Beitrag von Mathias »

Kannst Du mal schauen, ob der Compiler eine *.s Datei baut und diese hier posten.

Da hat sich bei mir ein falsvhe Zitat eingeschlichen. :oops:
Eigentlich wollt ich sagen, ich habe keine *.s Datei.

Ist UDRO nicht für die Zeichen des UART ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: ATtiny, div und mod

Beitrag von Timm Thaler »

Mathias hat geschrieben:Eigentlich wollt ich sagen, ich habe keine *.s Datei.


Schau mal in Deinem Projektverzeichnis, da sollte ein Unterverzeichnis lib/avr-embedded sein, in dem sind die compilierten *.o und *.ppu Dateien und auch die *.s Dateien. Die müssen irgendwo sein, denn das ist das was der FPC-Compiler an den Linker schickt, aus dem der dann mit Hilfe des ASM-Compilers Dein Hexfile baut.

Mathias hat geschrieben:Ist UDRO nicht für die Zeichen des UART ?


Ja. Du brauchst halt eine Quelle für die Werte, die der Compiler nicht vorhersagen kann. Wenn Du einfach vval := 127 machst, dann guckt der Compiler, oh, vval := vval - 10, vval = 127, dann kann ich ja gleich vval 117 zuweisen:

Code: Alles auswählen

    vval := 127;
    vval := vval - 10; // 1254Byte

wird zu

Code: Alles auswählen

   ldi   r18,127
   ldi   r18,117

Klever. Bringt Dich aber nicht weiter in der Evaluierung, was der Compiler macht. Der hat Dich ausgetrickst.

Das gilt für Compilereinstellung -o3, bei -o1 sieht das anders aus:

Code: Alles auswählen

   ldi   r18,127
   sts   (U_sPsTEST_ss_VVAL),r18
   lds   r18,(U_sPsTEST_ss_VVAL)
   subi   r18,10
   sts   (U_sPsTEST_ss_VVAL),r18

Hier wird das Programm eher "wörtlich" übersetzt, erzeugt aber auch viel unnötigen Code.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: ATtiny, div und mod

Beitrag von Mathias »

Klever. Bringt Dich aber nicht weiter in der Evaluierung, was der Compiler macht. Der hat Dich ausgetrickst.

Wen das Klever wäre, dann würde es keine Sinn machen

Code: Alles auswählen

ldi   r18,127
aufzurufen, er könnte doch direkt

Code: Alles auswählen

ldi   r18,117
machen. :roll:
Oder habe ich etwas übersehen ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: ATtiny, div und mod

Beitrag von Timm Thaler »

Ja, da steht vorher vval := 127;, und genau das macht der Compiler, obwohl vval gleich danach überschrieben wird.

Ein "besserer" Compiler würde das vielleicht noch wegoptimieren. Aber das kann auch schiefgehen.

Code: Alles auswählen

procedure rtc_interrupt; alias: 'TIMER2_COMPA_ISR'; interrupt; public;
begin
  TCCR2B := TCCR2B;


Sieh auch erstmal aus, als könne das weg. Das Neuschreiben des Registers mit dem gleichen Wert ist aber essentiell wichtig, um das Aktualisieren diverser Flags anzustoßen.

Würde mir der Compiler das wegoptimieren wäre ich sauer. ;-)

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: ATtiny, div und mod

Beitrag von Mathias »

Sind dies nicht zweierlei Schuhe ?

TCCR2B ist ein Register/Port, welches im PC vergleichbar ist mit Port[$F8] oder Mem[$B800:0000] welches auf Hardware/Speicherbereich zugreift.
r18 ist ein Processoerregister, verglich bar mit ax, bc, ebx etc.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: ATtiny, div und mod

Beitrag von siro »

Timm Thaler hat geschrieben:Ja, da steht vorher vval := 127;, und genau das macht der Compiler, obwohl vval gleich danach überschrieben wird.

Ein "besserer" Compiler würde das vielleicht noch wegoptimieren. Aber das kann auch schiefgehen.

Code: Alles auswählen

procedure rtc_interrupt; alias: 'TIMER2_COMPA_ISR'; interrupt; public;
begin
  TCCR2B := TCCR2B;


Sieh auch erstmal aus, als könne das weg. Das Neuschreiben des Registers mit dem gleichen Wert ist aber essentiell wichtig, um das Aktualisieren diverser Flags anzustoßen.

Würde mir der Compiler das wegoptimieren wäre ich sauer. ;-)


Würde mir der Compiler das wegoptimieren wäre ich sauer.

gibt es für den Pascalcompiler so etwas wie "volatile" in "C"
damit man dem Compiler mitteilen kann, dass er NICHT optimieren darf ?
Das ist ja SEHR wichtig für spezielle Prozessorregister.
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: ATtiny, div und mod

Beitrag von Mathias »

damit man dem Compiler mitteilen kann, dass er NICHT optimieren darf ?

Code: Alles auswählen

  {$o-} // aus
  {$o+} // ein
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: ATtiny, div und mod

Beitrag von siro »

Okay, Danke
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: ATtiny, div und mod

Beitrag von Mathias »

Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten