Forum: Mikrocontroller und Digitale Elektronik Flash CRC Berechnung optimieren/schneller


von Andreas (elektor)


Lesenswert?

Hallo zusammen,
ich habe einen eigenen Bootloader auf dem ATmega1284 erstellt, der beim 
Einschalten des Controllers startet, und den CRC über den gesamten Flash 
berechnet.
Ist dieser gültig wird die Anwendung gestartet.

Nun habe ich das Problem, dass das prüfen ca 1 - 2 Sekunden beansprucht. 
Da die Application erst danach startet schaut das ganze recht unschön 
aus.

Gibt es eine Möglichkeit die berechnung zu beschleunigen oder gibt es 
eine schnellere Möglichkeit den CRC über den gesamten Flash zu 
berechnen?

Compiler ist VisualGDB.
APP_END_ADR = 0x1EFFD
Ich verwende dazu folgenden Codeabschnitt:
1
static uint16_t calculate_flash_crc()
2
{
3
  uint16_t crc = 0xFFFF;
4
  for (uint32_t i = 0; i <= APP_END_ADR; i++)
5
  {
6
    crc = _crc16_update(crc, pgm_read_byte_far(i));  
7
  }
8
  return crc;
9
}

: Bearbeitet durch User
von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Was macht den die Funktion "_crc16_update()" alles?
Und was macht "pgm_read_byte_far()"?

: Bearbeitet durch User
von MaWin O. (mawin_original)


Lesenswert?

Ich würde vorschlagen eine 8-Bit-CRC zu verwenden. Die lässt sich 
naturgemäß auf einem 8-Bit-Controller mindestens doppelt so schnell 
rechnen. Vermutlich noch schneller. Mit geschickter Wahl der 
Polynomkoeffizienten kann man dann die Rechenaufwände der einzelnen 
Byterunden noch einmal reduzieren.

von C-hater (c-hater)


Lesenswert?

Andreas schrieb:

> ich habe einen eigenen Bootloader auf dem ATmega1284 erstellt, der beim
> Einschalten des Controllers startet, und den CRC über den gesamten Flash
> berechnet.
> Ist dieser gültig wird die Anwendung gestartet.

Das ist doch schon vom Konzept her völliger Schwachsinn. Wenn der 
Bootloader wissen möchte, ob die Anwendung "gültig" ist, dann muss er 
natürlich nur einen Prüfsumme über den Flashbereich bilden, in dem die 
Anwendung im Flash liegt.

Dazu muss er wissen (und sich merken, auch wieder im Flash) wo die 
Anwendung beginnt und wie groß sie ist.

Bei einem ATMega ist das ziemlich einfach unzusetzen. Bei einem Tiny 
wird's kitzliger, denn da muß der Bootloader, um funktionieren zu 
können, die Anwendung manipulieren (zwei Bytes davon, den Inhalt des 
Resetvektors).

von Falk B. (falk)


Lesenswert?

C-hater schrieb:
> Das ist doch schon vom Konzept her völliger Schwachsinn. Wenn der
> Bootloader wissen möchte, ob die Anwendung "gültig" ist, dann muss er
> natürlich nur einen Prüfsumme über den Flashbereich bilden, in dem die
> Anwendung im Flash liegt.

Stimmt. Aber auch ein AVR kann einen größeren Flash-Bereich in DEUTLICH 
unter 1s mittels CRC16 prüfen. Dazu darf man natürlich KEINEN Seriellen 
Algorithmus nehmen, sondern einen mit Tabelle, hier sinnvollerweise mit 
256 Einträgen a 2 Byte. OK, das ist TEUER in einem Bootloader. 
Alternativ nur mit Nibblebreite und 16er Tabelle. In ASM geht das flott. 
Hey, das war DEIN Stichwort! ;-)

von Thomas Z. (usbman)


Lesenswert?

Es hängt natürlich davon ab wie schnell deine CRC Funktion ist. mit 
einer Tabelle lässt sich das auf Kosten des Speicherplatzes schon 
schneller machen, die braucht dann aber bis zu 512Bytes im Flash, wird 
also nicht in deinen Bootloader passen.
https://stackoverflow.com/questions/22432066/how-to-use-table-based-crc-16-code

edit: zu spät

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Markus M. schrieb:
> Was macht den die Funktion "_crc16_update()" alles?

Berechnet die CRC für das nächste Byte.

> Und was macht "pgm_read_byte_far()"?

Liest ein Byte aus dem Flash über einen FAR-Pointer (>64kB).

von C-hater (c-hater)


Lesenswert?

Falk B. schrieb:

> Stimmt. Aber auch ein AVR kann einen größeren Flash-Bereich in DEUTLICH
> unter 1s mittels CRC16 prüfen.

Natürlich. Es macht eben nur keinerlei Sinn, also warum sollte man das 
wider jede Vernunft trotzdem tun?

> In ASM geht das flott.
> Hey, das war DEIN Stichwort! ;-)

Nicht wirklich. Egal in welcher Sprache ich programmiere: ich 
programmiere nur das, was sinnvoll ist. Und natürlich habe ich eine 
(recht) schnelle CRC16-Routine im Bestand. Die kommt übrigens ohne fette 
Lookup-Table aus. Ist eigentlich eher ein Abfallprodukt aus der 
Verbesserung von V-USB. Setzt halt möglichst effizient den Algorithmus 
um, den USB für DATA-Packets verwendet.

Laufzeitmäßig weniger effizent als was mit Lookup, aber immerhin schnell 
genug, um innerhalb von V-USB ab ca. 16MHz Systemtakt zu funktionieren.

Kostet exakt 23Takte/Byte.

von Andreas (elektor)


Lesenswert?

Danke für eure Antwort.

Genau, die beiden Methoden stammen aus der AVR lib.
Die crc update funktion ist auch in ASM geschrieben.

C-hater schrieb:
> Andreas schrieb:
>
>> ich habe einen eigenen Bootloader auf dem ATmega1284 erstellt, der beim
>> Einschalten des Controllers startet, und den CRC über den gesamten Flash
>> berechnet.
>> Ist dieser gültig wird die Anwendung gestartet.
>
> Das ist doch schon vom Konzept her völliger Schwachsinn. Wenn der
> Bootloader wissen möchte, ob die Anwendung "gültig" ist, dann muss er
> natürlich nur einen Prüfsumme über den Flashbereich bilden, in dem die
> Anwendung im Flash liegt.
>
> Dazu muss er wissen (und sich merken, auch wieder im Flash) wo die
> Anwendung beginnt und wie groß sie ist.
>
> Bei einem ATMega ist das ziemlich einfach unzusetzen. Bei einem Tiny
> wird's kitzliger, denn da muß der Bootloader, um funktionieren zu
> können, die Anwendung manipulieren (zwei Bytes davon, den Inhalt des
> Resetvektors).

Ok, da habe ich etwas missverstande. Ich dachte das macht man eigentlich 
mit einem CRC.
Muss die Prüfsumme über den gesamten Flash Bereich oder nur über die 
Applikation gebildet werden?
Welches Prüfsummenverfahren wird denn hier üblicherweise genutzt bzw. 
hat jemand einen Link wo ich einen schnellen algorithmus finden kann?

von Hans W. (Firma: Wilhelm.Consulting) (hans-)


Lesenswert?

CRC ist schon i.O.

Es gibt von Intel ein paper zu schnellem CRC... Deren verfahren nennen 
sie slice-by-8.

Im Prinzip machst du dir eine lookup table in der von dir gewünschten 
größe und XORst dann wörter zur gesammtCRC.

Beim AVR müssten 16bit passen, wenn ich mich an die Architektur richtig 
erinnere (kann der nicht 16bit PGM in 2 register laden ?)

Das müsste dann deutlich <1s gehen...

Wie breit die CRC (16/32bit) ist übrigens verhältnismäßig unerheblich da 
du im Endeffekt nur XOR machst.

Eine Übersicht zu unterschiedlichen CRC Implementierungen findet sich 
übrigens hier: https://create.stephan-brumme.com/crc32/

73

: Bearbeitet durch User
von C-hater (c-hater)


Lesenswert?

Andreas schrieb:

> Ok, da habe ich etwas missverstande. Ich dachte das macht man eigentlich
> mit einem CRC.

Sachen wie die Prüfung, ob Code gültig ist, macht man tatsächlich z.B. 
per CRC. Das hast du nicht falsch verstanden.

> Muss die Prüfsumme über den gesamten Flash Bereich oder nur über die
> Applikation gebildet werden?

Hier ist dein "Verständnisproblem". Natürlich braucht (und sollte) man 
zur Überprüfung der App nur den Bereich des Flash betrachten, in dem sie 
liegt. Alles andere ist doch so dermaßen unlogisch, dass es schon weh 
tut, wenn jemand auf irgendwas anderes kommt...

Ich tippe mal: Troll. Recht gut getarnt, aber nicht perfekt. Meine 
Troll-KI kann dich nicht wirklich sehen, ich schon. Geht halt nix über 
Eigen-Intelligenz.

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Andreas schrieb:
> Danke für eure Antwort.
>
> Genau, die beiden Methoden stammen aus der AVR lib.
Das sind Funktionen, ist ja "nur" C, kein C++.

> Die crc update funktion ist auch in ASM geschrieben.

Ja, aber der Aufruf muss ja auch noch ein wenig berechnen. 
pgm_read_byte_far(). Wird nicht extrem ineffizient sein, aber naja.

Ich hab mal schnell was zusammengestrickt, ich komme auf 55 Takte/Byte, 
macht bei 128kB und 16 MHz auf ~7,2M Takte, also ~450ms. Naja.
Das ist nur ein Konzept, die Tabelle habe ich jetzt nicht ausgerechnet.

von C-hater (c-hater)


Lesenswert?

Falk B. schrieb:

> Ich hab mal schnell was zusammengestrickt, ich komme auf 55 Takte/Byte

Tja, so ist das, wenn man Codeffizienz einem Compilergott opfert. Da 
werden sogar potentiell schnellere Algorithmen im Endeffekt leicht mal 
erheblich langsamer als potentiell weniger schnelle.

No mercy. Asm rules. All the time in the past and forever in the future. 
That's a natural law!

von Falk B. (falk)


Lesenswert?

C-hater schrieb:
>> Ich hab mal schnell was zusammengestrickt, ich komme auf 55 Takte/Byte
>
> Tja, so ist das, wenn man Codeffizienz einem Compilergott opfert.

Dir ist schon aufgefallen, daß mein Beispiel in ASM ist? Klar, um LÄNGEN 
schlechter als deine Schöpfungen, aber immerhin schneller als der Status 
Quo.

> No mercy. Asm rules. All the time in the past and forever in the future.
> That's a natural law!

Mensch bist du COOOOOOOL!! Pubertät ist nicht immer einfach, gelle?

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Hier mal die serielle Variante, die leider oder zum Glück fast genau so 
schnell wie die Nibble-Variante ist. Denn das Schieben um 4 Bit ist bei 
mehreren Bytes auf dem AVR auch wieder relativ langsam, selbst die 
Tricks mit swap und Maskierung bringen da nix. Die serielle Variante 
braucht 56 Takte/Byte, ist aber einfacher und braucht weniger Speicher, 
keinerlei RAM.

von Peter D. (peda)


Lesenswert?

Falk B. schrieb:
> Ich hab mal schnell was zusammengestrickt, ich komme auf 55 Takte/Byte

Die Lib in crc16.h des AVR-GCC ist inline-Assembler (23 Zyklen) und 
braucht mit Loop-Overhead zusammen 41 Zyklen.
Die CRC-CCITT ist mit 17 Zyklen etwas kürzer.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Peter D. schrieb:
> Die Lib in crc16.h des AVR-GCC ist inline-Assembler (23 Zyklen) und
> braucht mit Loop-Overhead zusammen 41 Zyklen.
> Die CRC-CCITT ist mit 17 Zyklen etwas kürzer.

Das widerspricht aber der Wahrnehmung des OP, der von 1-2s Verzögerung 
berichtet. OK, kann ein Meßfehler sein.

Wo findet man den Quelltext?

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Falk B. schrieb:
> Wo findet man den Quelltext?
1
C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\avr\include\util\crc16.h

: Bearbeitet durch User
von Andreas (elektor)


Lesenswert?

Falk B. schrieb:
> Hier mal die serielle Variante, die leider oder zum Glück fast
> genau so
> schnell wie die Nibble-Variante ist. Denn das Schieben um 4 Bit ist bei
> mehreren Bytes auf dem AVR auch wieder relativ langsam, selbst die
> Tricks mit swap und Maskierung bringen da nix. Die serielle Variante
> braucht 56 Takte/Byte, ist aber einfacher und braucht weniger Speicher,
> keinerlei RAM.

Vielen Dank dir, das werde ich testen!

C-hater schrieb:
> Ich tippe mal: Troll. Recht gut getarnt, aber nicht perfekt. Meine
> Troll-KI kann dich nicht wirklich sehen, ich schon. Geht halt nix über
> Eigen-Intelligenz.

Ja da hast du recht, den Troll haben wir jetzt gefunden mit solchen 
Kommentaren. Da haben wir wieder so einen DAU sitzen...

Falk B. schrieb:
> Das widerspricht aber der Wahrnehmung des OP, der von 1-2s Verzögerung
> berichtet. OK, kann ein Meßfehler sein.

Also wenn ich die CRC berechnung auskommentiere habe ich keinen 
Verzögerung und ansonsten macht der Bootloader auch aktuell nichts.

von Falk B. (falk)


Lesenswert?

Andreas schrieb:
> Falk B. schrieb:
>> Hier mal die serielle Variante, die leider oder zum Glück fast
>> genau so
>> schnell wie die Nibble-Variante ist. Denn das Schieben um 4 Bit ist bei
>> mehreren Bytes auf dem AVR auch wieder relativ langsam, selbst die
>> Tricks mit swap und Maskierung bringen da nix. Die serielle Variante
>> braucht 56 Takte/Byte, ist aber einfacher und braucht weniger Speicher,
>> keinerlei RAM.
>
> Vielen Dank dir, das werde ich testen!

Jain. Die Variante ist dahin nicht getestet, ob sie die korrekte CRC 
berechnet. Ich war zu faul zu prüfen, ob die originale CRC nach links 
oder rechts arbeitet. Kommt in den nächsten Tagen. Mit der Funktion 
kannst du aber prüfen, wie schnell sie arbeitet, da ist die 
Schieberichtung egal.

>> Das widerspricht aber der Wahrnehmung des OP, der von 1-2s Verzögerung
>> berichtet. OK, kann ein Meßfehler sein.
>
> Also wenn ich die CRC berechnung auskommentiere habe ich keinen
> Verzögerung und ansonsten macht der Bootloader auch aktuell nichts.

OK. Trotzdem sollte man versuchen, genauer zu messen. Schalte ein IO-Pin 
von der CRC-Berechnung ein und danach wieder aus. Das kann man per Oszi 
oder Logicanalyzer messen.

von Oliver S. (oliverso)


Lesenswert?

Falk B. schrieb:
> pgm_read_byte_far(). Wird nicht extrem ineffizient sein, aber naja.

Da kann man die ersten 64kB pgm_read_byte nutzen, und nur für den 
Bereich darüber das dafür benötigten pgm_read_byte_far einsetzen. Das 
spart auch wieder einige Takte.

Oliver

von Falk B. (falk)


Lesenswert?

Hmm, wenn ich deine Funktion compiliere und im Simulator laufen lasse, 
komme ich auf ~5,3M Takte und ~333ms. Ok, in meinem Flash steht fast 
nix, was dann bei der CRC oft den etwas kürzeren Pfad bewirkt, aber das 
sind keine Größenordnungen. Außerdem scheint der Assemblercode schon 
recht kompakt, das kriegt man manuell kaum besser hin, außer unserem one 
and only C-hater ;-)

von Andreas (elektor)


Lesenswert?

Oliver S. schrieb:
> Falk B. schrieb:
>> pgm_read_byte_far(). Wird nicht extrem ineffizient sein, aber naja.
>
> Da kann man die ersten 64kB pgm_read_byte nutzen, und nur für den
> Bereich darüber das dafür benötigten pgm_read_byte_far einsetzen. Das
> spart auch wieder einige Takte.
>
> Oliver

Das ist auch eine gute Idee, danke.

Falk B. schrieb:
> Hmm, wenn ich deine Funktion compiliere und im Simulator laufen
> lasse,
> komme ich auf ~5,3M Takte und ~333ms. Ok, in meinem Flash steht fast
> nix, was dann bei der CRC oft den etwas kürzeren Pfad bewirkt, aber das
> sind keine Größenordnungen. Außerdem scheint der Assemblercode schon
> recht kompakt, das kriegt man manuell kaum besser hin, außer unserem one
> and only C-hater ;-)

Das hört sich doch schon super an. Ich denke mit einer Prüfsumme wirds 
warscheinlich auch nicht mehr kürzer werden oder?

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Andreas schrieb:
>> Da kann man die ersten 64kB pgm_read_byte nutzen, und nur für den
>> Bereich darüber das dafür benötigten pgm_read_byte_far einsetzen. Das
>> spart auch wieder einige Takte.
>>
>> Oliver
>
> Das ist auch eine gute Idee, danke.

Nicht wirklich. Wenn man sich den ASM-Code anschaut, kostet 
pgm_read_byte_far() keine Zusatztakte, denn ELPM ist genau so schnell 
wie lpm und das Register RAMPZ muss nur einmalig geschrieben werden. OK, 
hier wird es immer wieder beschrieben und auch Z immer wieder neu 
geladen, das könnte man in einer manuellen ASM-Funktion optimieren.
Was mir nicht klar ist, wieso die CRC-Berechnung so funktioniert, wie 
sie dort beschrieben ist. Es wird am Anfang das neue Datenbyte mit der 
CRC XOR-verknüpft und dann nur die einzelnen Bits nach und nach 
geschoben und XOR-verknüpft oder nicht. Funktioniert das SO wirklich? 
Ich kenne das anders, uns zwar das die einzelnen Bits immer nacheinander 
in das Byte geschoben werden müssen, wenn man das so seriell bearbeitet.

Aus crc16.h
1
/** \ingroup util_crc
2
    Optimized CRC-16 calculation.
3
4
    Polynomial: x^16 + x^15 + x^2 + 1 (0xa001)<br>
5
    Initial value: 0xffff
6
7
    This CRC is normally used in disk-drive controllers.
8
9
    The following is the equivalent functionality written in C.
10
11
    \code
12
    uint16_t
13
    crc16_update(uint16_t crc, uint8_t a)
14
    {
15
  int i;
16
17
  crc ^= a;
18
  for (i = 0; i < 8; ++i)
19
  {
20
      if (crc & 1)
21
    crc = (crc >> 1) ^ 0xA001;
22
      else
23
    crc = (crc >> 1);
24
  }
25
26
  return crc;
27
    }
28
29
    \endcode */
30
31
static __inline__ uint16_t
32
_crc16_update(uint16_t __crc, uint8_t __data)
33
{
34
  uint8_t __tmp;
35
  uint16_t __ret;
36
37
  __asm__ __volatile__ (
38
    "eor %A0,%2" "\n\t"
39
    "mov %1,%A0" "\n\t"
40
    "swap %1" "\n\t"
41
    "eor %1,%A0" "\n\t"
42
    "mov __tmp_reg__,%1" "\n\t"
43
    "lsr %1" "\n\t"
44
    "lsr %1" "\n\t"
45
    "eor %1,__tmp_reg__" "\n\t"
46
    "mov __tmp_reg__,%1" "\n\t"
47
    "lsr %1" "\n\t"
48
    "eor %1,__tmp_reg__" "\n\t"
49
    "andi %1,0x07" "\n\t"
50
    "mov __tmp_reg__,%A0" "\n\t"
51
    "mov %A0,%B0" "\n\t"
52
    "lsr %1" "\n\t"
53
    "ror __tmp_reg__" "\n\t"
54
    "ror %1" "\n\t"
55
    "mov %B0,__tmp_reg__" "\n\t"
56
    "eor %A0,%1" "\n\t"
57
    "lsr __tmp_reg__" "\n\t"
58
    "ror %1" "\n\t"
59
    "eor %B0,__tmp_reg__" "\n\t"
60
    "eor %A0,%1"
61
    : "=r" (__ret), "=d" (__tmp)
62
    : "r" (__data), "0" (__crc)
63
    : "r0"
64
  );
65
  return __ret;
66
}

> Das hört sich doch schon super an.

Steht aber im Widerspruch zu deinen Beobachtungen. Miss die Zeit GENAU 
mittels Oszi oder Logicanalyzer.

> Ich denke mit einer Prüfsumme wirds
> warscheinlich auch nicht mehr kürzer werden oder?

CRC IST eine Prüfsumme, wenn gleich eine mathematisch komplexere. Du 
meinst wahrscheinlich eine einfache Prüfsumme, die wirklich nur addiert 
oder die Parität berechnet. Die ist schon schneller, dafür aber deutlich 
schlechter in der Fehlererkennung. Aber auch eine 16 Bit CRC ist für 
128kB Flash eigentlich zu klein, da sollte man eher mit 32 Bit arbeiten, 
wenn das Thema Fehlererkennung wirklich ernst genommen wird.

von Oliver S. (oliverso)


Lesenswert?

Falk B. schrieb:
> OK,
> hier wird es immer wieder beschrieben und auch Z immer wieder neu
> geladen, das könnte man in einer manuellen ASM-Funktion optimieren.

Mit hätte hätte Fahrradkette hätte der TO seine ganzen Probleme erst gar 
nicht.

Zudem kann die erste Schleife unterhalb der 64kB Grenze mit einem 16 Bit 
Schleifenzähler laufen, die darüber braucht einen 32bit Zähler. Sind 
auch wieder ein paar Takte Unterschied.

Oliver

von Falk B. (falk)


Lesenswert?

Oliver S. schrieb:
> Mit hätte hätte Fahrradkette hätte der TO seine ganzen Probleme erst gar
> nicht.

Ohne eine solide Messung weiß man gar nichts.

> Zudem kann die erste Schleife unterhalb der 64kB Grenze mit einem 16 Bit
> Schleifenzähler laufen,

Stimmt.

> die darüber braucht einen 32bit Zähler.

Nö, in ASM geht das auch mit 24 Bit. Außerdem kann das der AVR in ASm 
auch in Hardware, elpm R1, Z+ funktiniert wunderbar und kostet KEINERLEI 
Zusatztakte.

> Sind
> auch wieder ein paar Takte Unterschied.

Nö, nicht in ASM, wenn man weiß was man tut. Und das Macro in crc16.h 
ist Inline Assembler, wenn gleich das nicht die Schleife für die 
Datenbytes enthält.

von Falk B. (falk)


Lesenswert?

Ok, hier nochmal explizit die Frage zur CRC-Berechnung, auch wenn sie 
nicht direkt zum Thema passt. Ist die Berechnung in der crc16.h korrekt?
1
/** \ingroup util_crc
2
3
    Optimized CRC-16 calculation.
4
    Polynomial: x^16 + x^15 + x^2 + 1 (0xa001)<br>
5
    Initial value: 0xffff
6
    This CRC is normally used in disk-drive controllers.
7
    The following is the equivalent functionality written in C.
8
    \code
9
    uint16_t
10
    crc16_update(uint16_t crc, uint8_t a)
11
    {
12
      int i;
13
      crc ^= a;
14
      for (i = 0; i < 8; ++i)
15
      {
16
        if (crc & 1)
17
          crc = (crc >> 1) ^ 0xA001;
18
        else
19
        crc = (crc >> 1);
20
  }
21
  return crc;
22
}
23
24
    \endcode */
25
26
static __inline__ uint16_t
27
28
_crc16_update(uint16_t __crc, uint8_t __data)
29
{
30
31
  uint8_t __tmp;
32
  uint16_t __ret;
33
34
  __asm__ __volatile__ (
35
    "eor %A0,%2" "\n\t"
36
    "mov %1,%A0" "\n\t"
37
    "swap %1" "\n\t"
38
    "eor %1,%A0" "\n\t"
39
    "mov __tmp_reg__,%1" "\n\t"
40
    "lsr %1" "\n\t"
41
    "lsr %1" "\n\t"
42
    "eor %1,__tmp_reg__" "\n\t"
43
    "mov __tmp_reg__,%1" "\n\t"
44
    "lsr %1" "\n\t"
45
    "eor %1,__tmp_reg__" "\n\t"
46
    "andi %1,0x07" "\n\t"
47
    "mov __tmp_reg__,%A0" "\n\t"
48
    "mov %A0,%B0" "\n\t"
49
    "lsr %1" "\n\t"
50
    "ror __tmp_reg__" "\n\t"
51
    "ror %1" "\n\t"
52
    "mov %B0,__tmp_reg__" "\n\t"
53
    "eor %A0,%1" "\n\t"
54
    "lsr __tmp_reg__" "\n\t"
55
    "ror %1" "\n\t"
56
    "eor %B0,__tmp_reg__" "\n\t"
57
    "eor %A0,%1"
58
    : "=r" (__ret), "=d" (__tmp)
59
    : "r" (__data), "0" (__crc)
60
    : "r0"
61
  );
62
  return __ret;
63
}

Ich habe sowohl das Inline-ASM Macro als auch die im Kommentar 
dargestellte C-Funktion mit ein paar Daten getestet, beide liefern das 
gleiche Ergebnis. Aber das entspricht nicht der mir bekannten 
CRC-Berechnung. Das hier ist meiner Meinung nach falsch.
1
     crc ^= a;

Das kann man nicht einfach so am Anfang tun. Der meiner Meinung nach 
korrekte Algorithmus sieht so aus. Man muss bei serieller CRC-Berechnung 
jedes Bit einzeln reinschieben und das XOR machen. Damit kommt natürlich 
eine ganz andere CRC-Prüfsumme zu stande.
1
    uint16_t
2
    crc16_update(uint16_t crc, uint8_t a)
3
    {
4
      int i;      
5
      for (i = 0; i < 8; ++i)
6
      {
7
        if (crc & 1) {
8
          crc  = (crc >> 1);
9
          if (data & 1) crc |= 0x8000;  // LSB in MSB kopieren
10
          crc ^= 0xA001;
11
        } else {
12
          crc = (crc >> 1);
13
          if (data & 1) crc |= 0x8000;  // LSB in MSB kopieren
14
        }
15
        data >>= 1;
16
  }
17
  return crc;
18
}

Welche Rechnung ist korrekt? Die Rechnung oben habe ich auch schon für 
oneWire benutzt und das Ergebnis war korrekt. In der crc16.h gibt es 
dafür auch ein ASM-Macro, welches das gleiche Konzept anwendet und damit 
falsche Ergebnisse liefert. Hat schon mal einer die crc16.h in der 
realen Welt geprüft?

von Oliver S. (oliverso)


Lesenswert?

Falk B. schrieb:
>> die darüber braucht einen 32bit Zähler.
>
> Nö, in ASM geht das auch mit 24 Bit.

Wäre aber unsinnig, denn in ASM reicht die 16Bit des Z-Registers mit lpm 
rN, Z+. Einmal rum, dann RAMPZ gesetzt, und noch ´ne Runde. Wenn du 
schon mit ASM hier rumprahlst, dann wenigstens richtig.

Oliver

von MaWin O. (mawin_original)


Lesenswert?

Falk B. schrieb:
> Das kann man nicht einfach so am Anfang tun.

Doch, kann man.

von Falk B. (falk)


Lesenswert?

MaWin O. schrieb:
>> Das kann man nicht einfach so am Anfang tun.
>
> Doch, kann man.

Toll, und warum?
Mein Wissen beruht hier rauf, der Klassiker

http://www.ross.net/crc/download/crc_v3.txt

Und auch wenn dort die vielen Spielarten mit reflected etc. dargestellt 
sind, ist die Variante mit direktem XOR der Daten dort nicht zu finden.

Naja. Die verschiedenen Online CRC Tools spucken alle das gleiche 
Ergebnis für meine Test aus.

CRC-16 Modbus mit Polynom 0x8005 und 0xFFFF initialwert, Ein- und 
Ausgang reflektiert. Testdaten
1
42 4D 36 F0 00 00 00 00 00 00 36 00 00 00 28 00

CRC ist immer 0x05EE

https://www.texttool.com/crc-online
https://www.lddgo.net/en/encrypt/crc
https://www.tahapaksu.com/crc/
http://www.sunshine2k.de/coding/javascript/crc/crc_js.html
https://crccalc.com/

Na gut, dann ist das halt so.

von Falk B. (falk)


Lesenswert?

Oliver S. schrieb:
>> Nö, in ASM geht das auch mit 24 Bit.
>
> Wäre aber unsinnig, denn in ASM reicht die 16Bit des Z-Registers mit lpm
> rN, Z+. Einmal rum, dann RAMPZ gesetzt, und noch ´ne Runde. Wenn du
> schon mit ASM hier rumprahlst, dann wenigstens richtig.

Du hast es nötig. Man muss RAMPZ nicht nach einer Runde rum setzen, das 
geht alles in einer Schleife.

von Falk B. (falk)


Lesenswert?

Falk B. schrieb:
>>> Das kann man nicht einfach so am Anfang tun.
>>
>> Doch, kann man.
>
> Toll, und warum?
> Mein Wissen beruht hier rauf, der Klassiker
>
> http://www.ross.net/crc/download/crc_v3.txt

Hmm, das hier scheint mir ein Hinweis zu sein.
1
These facts, combined with the XOR property
2
3
   (A xor B) xor C = A xor (B xor C)
4
5
mean that message bytes need not actually travel through the W/4 bytes
6
of the register. Instead, they can be XORed into the top byte just
7
before it is used to index the lookup table. This leads to the
8
following modified version of the algorithm.
9
10
11
         +-----<Message (non augmented)
12
         |
13
         v         3    2    1    0   Bytes
14
         |      +----+----+----+----+
15
        XOR----<|    |    |    |    |
16
         |      +----+----+----+----+
17
         |                ^
18
         |                |
19
         |               XOR
20
         |                |
21
         |     0+----+----+----+----+       Algorithm
22
         v      +----+----+----+----+       ---------
23
         |      +----+----+----+----+       1. Shift the register left by
24
         |      +----+----+----+----+          one byte, reading in a new
25
         |      +----+----+----+----+          message byte.
26
         |      +----+----+----+----+       2. XOR the top byte just rotated
27
         |      +----+----+----+----+          out of the register with the
28
         +----->+----+----+----+----+          next message byte to yield an
29
                +----+----+----+----+          index into the table ([0,255]).
30
                +----+----+----+----+       3. XOR the table value into the
31
                +----+----+----+----+          register.
32
                +----+----+----+----+       4. Goto 1 iff more augmented
33
             255+----+----+----+----+          message bytes.

Das funktioniert auch mit dem seriellen Algorithmus. Der Rest des 
ASM-Macro, welche für die CRC16 mit 0xA001 KEINERLEI Verzweigungen 
enthält ist somit vermutlich eine Handoptimierung der XORs auf dieses, 
spezielle Polynom.

Beitrag #7399305 wurde vom Autor gelöscht.
von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Ok, hier die kleine Optimierung, wenn man die Schleife um die CRC in ASM
per Hand gestaltet. Macht 33 Takte/Byte. Die eigentliche, trickreiche
CRC-Berechnung braucht 23 Takte, habe ich einfach kopiert. Die Variante
in C mit Inline-ASM Macro ist nicht sooo viel schlechter mit 41
Takte/Byte.

von Foobar (asdfasd)


Lesenswert?

Mach's halt wie die Großen: während der CRC-Berechnung eine Animation 
abspielen.  Dauert dann zwar noch länger, aber der Anwender ist 
beschäftigt, findet das evtl gar noch "geil".  Alternativ einen 
Screenshot der Anwendung darstellen bevor man mit dem langsamen Kram 
loslegt ... den Anwendern ist antrainiert worden, dass das ganz normal 
ist, wenn in den ersten paar Sekunden/Minuten nichts geht.

von Andreas (elektor)


Lesenswert?

Foobar schrieb:
> Mach's halt wie die Großen: während der CRC-Berechnung eine
> Animation
> abspielen.  Dauert dann zwar noch länger, aber der Anwender ist
> beschäftigt, findet das evtl gar noch "geil".  Alternativ einen
> Screenshot der Anwendung darstellen bevor man mit dem langsamen Kram
> loslegt ... den Anwendern ist antrainiert worden, dass das ganz normal
> ist, wenn in den ersten paar Sekunden/Minuten nichts geht.

Ja, das hatte ich mir tatsächlich auch schon überlegt, da das wirklich 
oft gemacht wird.

Falk B. schrieb:
> Ok, hier die kleine Optimierung, wenn man die Schleife um die CRC
> in ASM
> per Hand gestaltet. Macht 33 Takte/Byte. Die eigentliche, trickreiche
> CRC-Berechnung braucht 23 Takte, habe ich einfach kopiert. Die Variante
> in C mit Inline-ASM Macro ist nicht sooo viel schlechter mit 41
> Takte/Byte.

Vielen Dank für eure Hilfe!
Werde die ASM routine von Falk als Basis nutzen, eventuell noch etwas 
verändern und diesen dann nutzen. Der liefert gute Ergebnisse.
Vielen Dank.

von Foobar (asdfasd)


Lesenswert?

.oO(Ich hätte Zyniker-Tags setzen sollen)

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.