Forum: Compiler & IDEs Multiplizieren geht schief


von Joachim .. (joachim_01)


Lesenswert?

Moin,
ich mach hier gerade erste Versuche mit gemischtem Code aus C und 
Assembler. Beim "Inlinen" muß ich dem Compiler sagen welche Register ich 
zerschmettere, gilt das auch für Assembleraufrufe aus C heraus? 
Irgendwie hab ich das Gefühl ich zerschieße alles was rumliegt. 
Allerdings stürzt der ATMega nicht ab sondern holpert seltsam umher...

von (prx) A. K. (prx)


Lesenswert?

Viel zu viel Information!

von amateur (Gast)


Lesenswert?

Stell' mal 'nen Schild: Vorsicht Stufe dann hört das Stolpern auf.

Ich benutze das Studio 4. Unter "Help->avr-lib Reference Manual" findest 
Du Beispiele (Example Projects) zur Zusammenarbeit von C und S.
Unter den FAQ findest Du auch einen Artikel zum Thema: "What registers 
are used by the C compiler?"
Keine Ahnung, ob sich die obigen Komponenten bei den derzeitigen 
Versionen noch so vorhanden sind.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Joachim ... schrieb:
> Beim "Inlinen" muß ich dem Compiler sagen welche Register ich
> zerschmettere

Deinem Geholpere nach zu urteilen, würde ich dir empfehlen, erst
einmal einen riesegroßen Bogen um den Inlineassembler zu machen.
Der ist ein mächtiges Werkzeug, aber absolut nicht anfängertauglich.
Mit an Sicherheit grenzender Wahrscheinlichkeit brauchst du ihn auch
gar nicht, sondern hast vielleicht nur vergessen, die Optimierung
des Compilers einzuschalten.

von Rolf Magnus (Gast)


Lesenswert?

Joachim ... schrieb:
> ich mach hier gerade erste Versuche mit gemischtem Code aus C und
> Assembler. Beim "Inlinen" muß ich dem Compiler sagen welche Register ich
> zerschmettere, gilt das auch für Assembleraufrufe aus C heraus?

Da mußt du dich selbst drum kümmern.

> Allerdings stürzt der ATMega nicht ab sondern holpert seltsam umher...

Das ist wenigstens mal eine der kreativeren unter den nutzlosen 
Fehlerbeschreibungen.

Übrigens:  Was hat die Frage eigentlich mit dem Betreff zu tun?

von Peter D. (peda)


Lesenswert?

Joachim ... schrieb:
> ich mach hier gerade erste Versuche mit gemischtem Code aus C und
> Assembler.

Diese Aufgabe ist unlösbar, weil:
1.
Um sowas zu können, mußt Du beides (C, Asm) exzellent beherrschen.

2.
Kannst Du aber C, dann besteht überhaupt kein Anlaß mehr, Assembler 
hinzu zu mixen. Dann schreibst Du ganz einfach alles in C.


Peter

von Joachim .. (joachim_01)


Lesenswert?

Ja... hm... ich hab deswegen nix gepostet weil ich glaube das es keinen 
Sinn macht weil der Fehler wohl nicht direkt im Code liegt. Im Simulator 
funktionieren die Code-Schnipsel wie sie sollen. Wenn ich es dann 
verwurste passieren aber bizarre Dinge die keinen Sinn ergeben.
Hab hier AVR-Studio 6 und habe Code der mit dem AVR Assembler 
geschrieben wurde auf gcc übersetzt. Schon das war mit viel 
Kopfschütteln begleitet. Und jetzt passieren wieder so komische Sachen.
Hab jetzt noch zusätzlich das Status-Register gepusht, es wird aber 
nicht besser.
Der Code an sich funktioniert wenn auch die Kommentare sich auf ältere 
Zeilen beziehen. Lediglich die beiden:
   lsl r24 ; port for 6522 address x2
   lsl r24 ; port for 6522 address x2

werfen alles durcheinander.

Wenn ich die Multiplikation x4 stattdessen in main() durchführe dann 
geht's.


1
  
2
syncronize_PHI2_wrt:
3
push  r1
4
push  r0
5
in    r0, 0x3f
6
push  r0
7
push  r22
8
push  r24
9
10
11
12
13
  
14
  
15
  ;part 1 synchronisation macro
16
;  next instruction after macro is at tcnt=7, OC2=1
17
;
18
;           you are here | at end of macro
19
;           _ _ _ _ _ _ _V_                 _ _
20
; Phi2  _ _|               |_ _ _ _ _ _ _ _|
21
;
22
; tcnt  6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1
23
;
24
; 500ns/phase, 62.5ns/count
25
26
phi2_syncloop_wrt:
27
28
   lsl r24 ; port for 6522 address x2
29
   lsl r24 ; port for 6522 address x2
30
   out _SFR_IO_ADDR(PORTC), r24
31
   lds  r0, TCNT0 ;      in    r0, 0xB2; TCNT0    
32
   sbic  _SFR_IO_ADDR(PINB),4   ;sbic  PIND,7     sbic  pind,7            ;wait for phi2 low
33
   rjmp  phi2_syncloop_wrt  
34
   ldi   r30,lo8(gs(phi2_synctable_wrt));  ldi   zlo,lo8(phi2_synctable)
35
   ldi   r31,hi8(gs(phi2_synctable_wrt));   ldi   zhi,hi8(phi2_synctable)   
36
  add   r30,r0              ;calculate offset cycles
37
  ijmp
38
39
  ;.if ((PC & 0xff) > 0xf8  ) ;keep table on same page - avoid adc zh,zero  ...AVR Ssyntax
40
  .if ( ( __AVR_2_BYTE_PC__ & 0xff) > 0xf8  ) ;keep table on same page - avoid adc zh,zero
41
  .align 8 ; new page
42
  ;.org  ((__AVR_2_BYTE_PC__ + 0x100) & 0xff00   ...AVR Ssyntax
43
  .endif 
44
45
phi2_synctable_wrt:            ;delay -1 cycle for each count in tcnt
46
   nop                     ;0/1 - tcnt=0/OC2=1           
47
   nop                     ;1/1
48
   nop                     ;2/1
49
   nop                     ;3/1
50
   nop                     ;4/1
51
   nop                     ;5/1
52
   nop                     ;6/1
53
;  next                     7/1 - output to next cycle, input from previous cycle
54
;
55
;part 2 synchronized write
56
;
57
;                    output data valid +
58
;      address valid +                 |
59
;     after sync +   |                 |
60
;                |   |    _ _ _ _ _ _ _|_ _ _ _ _ _ _
61
; chip select _ _|_ _|_ _|             |             |_ _ _
62
;         _ _ _ _V_  |              _ _V_ _ _ _ _ _       
63
; Phi2             |_V_ _ _ _ _ _ _|               |_ _ _ _
64
;
65
; tcnt    3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3
66
;
67
;.macro   phi2_wrt
68
69
   cbi   _SFR_IO_ADDR(PORTC),7 ;R/W     ;7/1 generate R/-W - tcnt=7/OC2=1
70
   sbi   _SFR_IO_ADDR(PORTC),6   ;CS = 1  ;1/0 CS strobe high >300ns before phi2
71
   nop2                    ;3/0
72
   nop2                    ;5/0
73
   nop                     ;7/0
74
   out   _SFR_IO_ADDR(PORTD),r22       ;Data out    ;0/1 precharge data to be written
75
   nop;  out   dbusddr,allon   ????  ;1/1 write data valid 125 ns after phi2 le
76
   nop2                    ;2/1
77
   nop2                    ;4/1
78
   nop2                    ;6/1
79
   cbi   _SFR_IO_ADDR(PORTC),6   ;CS = 0  ;0/0 end strobe 60ns after phi2 te
80
81
;   out   dbusddr,zero      ;data hold ends
82
83
pop    r24
84
pop    r22
85
pop    r0
86
out    0x3f, r0
87
pop    r0
88
pop    r1
89
ret

von Peter D. (peda)


Lesenswert?

Joachim ... schrieb:
> Beim "Inlinen" muß ich dem Compiler sagen welche Register ich
> zerschmettere

Der Compiler ist das ganz leidenschaftslos. Sobald er das Schlüsselwort 
"asm" sieht, klinkt er sich einfach aus. Für ihn haben alle (Status-/) 
Register nach dem Assembleraufruf den alten Wert.
Alles, was Du zerstörst, mußt Du auch selber sichern.

Joachim ... schrieb:
> gilt das auch für Assembleraufrufe aus C heraus?

Es gelten die Aufrufkonventionen des C-Compilers.
Einige Register sind zerstörbar, andere müssen gesichert werden.


Peter

von Peter D. (peda)


Lesenswert?

Erzähl dochmal, was das überhaupt werden soll. Dann sieht man bestimmt 
viel klarer.

Man übersetzt nicht zeilenweise Assembler in C. Man übersetzt nur die 
Funktionsweise.


Peter

von Joachim .. (joachim_01)


Lesenswert?

>Man übersetzt nicht zeilenweise Assembler in C
Ich hab nicht Assembler in C übersetzt sondern AVR Assembler-Dialekt in 
vom gcc lesbaren Dialekt (kann man das so sagen?).

Was mich halt wundert, ist, daß das r24 scheinbar so kritisch ist. Oder 
zB die vielen nops; wenn der Compiler da was wegoptimiert stimmt die 
Tabelle die ich zur Synchronisation benötige nimmer. Ich denke aber das 
ist es nicht - es scheint als ob der Fehler von ausserhalb kommt.
Aufgerufen wird mit:

extern void syncronize_PHI2_wrt(unsigned char, unsigned char);      // 
6522 addr, 6522 data

im asm-File findet sich entsprechend:

.global syncronize_PHI2_wrt  ; The assembly function must be declared as 
global

Die Register zur Übergabe sind:
// 1. unsigned char : R24
// 2. unsigned char : R22

Also alles eingentlich so wie es auf dem Kochrezept steht.


Wenn ich nach dem Initialisieren des 6522 dann alle paar zehntel 
Sekunden nen Bitmuster für ein Hello-World-Lauflicht auf den Port lege 
verschluckt sich das ganze.

  syncronize_PHI2_wrt(VIA_IOR_A, 0x01);
  wait();

  syncronize_PHI2_wrt(VIA_IOR_A, 0x02);
  wait();

  syncronize_PHI2_wrt(VIA_IOR_A, 0x04);
  wait();

  syncronize_PHI2_wrt(VIA_IOR_A, 0x08);
  wait();

wait() ist zum debuggen nur ein _delay_ms(100);

von Peter D. (peda)


Lesenswert?

Joachim ... schrieb:
> Wenn ich nach dem Initialisieren des 6522 dann alle paar zehntel
> Sekunden nen Bitmuster für ein Hello-World-Lauflicht auf den Port lege

Dann ist es doch völliger Unsinn, auf 62ns genau sein zu wollen.
Machs in C und gut is.
Man verrennt sich da gerne mal in unnötig umständliche Lösungen.
Nicht die genaueste Lösung ist die beste, sondern die ausreichende.

In 99,9% wo der Assemblerprogrammierer meint, mit Zyklen knausern zu 
müssen, trifft das nicht zu. Aber um das einzuschätzen, müßte man eben 
die eigentliche Aufgabe kennen.


Um in C etwas auf 62ns genau auszugeben, würde ich völlig anders 
vorgehen.  Man setzt den Write-Pin auf einen Output-Compare-Pin und den 
schaltet dann der Timer auf 1 Zyklus genau. Die Software muß nur alles 
vorher vorbereitet haben. Ganz ohne Assembler-Gefrickel.

Man kann sogar 100 Ausgänge exakt gleichzeitg ausgeben. Man kaskadiert 
einfach entsprechend viele 74HC595 und setzt mit dem Output-Compare-Pin 
den Registertakt.


Peter

von Joachim .. (joachim_01)


Lesenswert?

Schnauf. Schnaubb...

Peter!
Es geht wieder wie so oft am Eingangsthema vorbei.
62 oder 62.5ns, das ist nicht der Punkt. Ich teile einfach 16MHz durch 
16 und muß in den Low-Pegel eines 1MHz Taktes springen. Der ist 500ns 
lang, davon müssen wir noch vorne und hinten zuammen etwa 100ns abziehen 
(setup time), dann bleiben noch ca 400ns. Das funktioniert alles. Wenn 
ich das multiplizieren in C löse funktioniert der Code. Nur wenn ich die 
ASM- Verschiebebefehle 'lsl' einflechte funktioniert es nicht.
Mir geht's nicht um einen Workaround. Ich möchte einfach herausfinden 
was in diesem speziellen Fall quer läuft.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Joachim ... schrieb:
>>Man übersetzt nicht zeilenweise Assembler in C
> Ich hab nicht Assembler in C übersetzt sondern AVR Assembler-Dialekt in
> vom gcc lesbaren Dialekt (kann man das so sagen?).

Fast.  Der Code wird nicht vom GCC ausgewertet, sondern vom 
GNU-Assembler (gas), hier also avr-as.

> Was mich halt wundert, ist, daß das r24 scheinbar so kritisch ist.

Ist es nicht.  Die Funktion ist keine ISR und

- R24  muss nicht gesichert / restauriert werden
- R22  muss nicht gesichert / restauriert werden
- R0   muss nicht gesichert / restauriert werden
- SREG muss nicht gesichert / restauriert werden (Ausser evtl. I-Flag)
- R1   muss nicht gesichert und nur bei Überschreiben wieder auf
  0 gesetzt werden, etwa wenn ein MUL verwendet wurde.

Siehe ABI

http://gcc.gnu.org/wiki/avr-gcc#Register_Layout

> wenn der Compiler da was wegoptimiert

Es ist Assembler-Code, der Compiler bekommt ihn nie zu sehen.

> es scheint als ob der Fehler von ausserhalb kommt.

Wenn ISRs aktiv sind, erzeugen die einen riesigen Jitter, der die 
Timing-Sequenz komplett unbrauchbar macht.

Beachte auch Effekte wie parasitäre Port-Kapazitäten etc., das kann die 
Signale um mehrere Ticks verziehen.

von Peter D. (peda)


Lesenswert?

Joachim ... schrieb:
> Ich teile einfach 16MHz durch
> 16 und muß in den Low-Pegel eines 1MHz Taktes springen. Der ist 500ns
> lang, davon müssen wir noch vorne und hinten zuammen etwa 100ns abziehen
> (setup time), dann bleiben noch ca 400ns. Das funktioniert alles.

Bloß weiß hier keiner, warum Du das so kompliziert machst und was 
"funktioniert" bedeutet.

Joachim ... schrieb:
> Nur wenn ich die
> ASM- Verschiebebefehle 'lsl' einflechte funktioniert es nicht.

Und wieder, keiner weiß, was "nicht funktioniert" oder "holpern" 
bedeutet.


Ein Funktionieren kann man erst prüfen, wenn man den Sinn hinter dem 
ganzen versteht. Du steckst da drin, wir aber nicht. Wir sehen nur ein 
merkwürdiges klitze kleines Teilchen irgendeines unbekannten Codes mit 
unbekannter Funktion.

Joachim ... schrieb:
> Mir geht's nicht um einen Workaround.

Mir auch nicht, sondern um Lösungen. Aber dazu muß man erstmal das 
Problem kennen.
Andere im Nebel stochern lassen, bringt Dich bestimmt nicht weiter.


Peter

von Joachim .. (joachim_01)


Lesenswert?

Mein lieber Peter, ich schätze deine Kompetenz und deine große 
Erfahrung. Wirklich. Aber wir wollen jetzt nicht ausdiskutieren was 
"funktionieren" oder "funktionieren nicht" bedeutet, oder? Denn dann 
können wir auch diskutieren ob eine "0" eine "1" ist...

Ich seh' schon - da muß ich alleine durch. Übrigens, in den 
Ascii-Skizzen zum Code ist praktisch alles enthalten worum es geht. Mehr 
hatte ich nicht bekommen. Aber wie gesagt - das alles hat mit dem 
Problem nichts zu tun.

Ich brech jetzt ab. Vielleicht liegt's ja an mir.

Johann: Danke für die Hinweise auf die Register. Ich hatte ähnliches 
vermutet, war mir aber überhaupt nicht sicher.

Danke an die anderen Helfer.




E N D E

von Rolf Magnus (Gast)


Lesenswert?

Joachim ... schrieb:
> Aber wir wollen jetzt nicht ausdiskutieren was "funktionieren" oder
> "funktionieren nicht" bedeutet, oder?

Doch, genau das will er, denn "funktioniert nicht" ist als 
Fehlerbeschreibung unzureichend. Erwartest du auch, daß dein Arzt eine 
Diagnose stellt, wenn du ihm am Telefon sagst, daß dir was weh tut, ohne 
weitere Angabe, was, wo und in welcher Situation?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Joachim ... schrieb:
> Aber wir wollen jetzt nicht ausdiskutieren was
> "funktionieren" oder "funktionieren nicht" bedeutet, oder?

Doch. Wenn Du in die Werkstatt gehst und sagst, "mein Auto funktioniert 
nicht" und Du verschweigst, dass lediglich der elektrische Scheibenheber 
kaputt ist, dann muss derjenige in der Werkstatt ziemlich lange suchen.

> Ich brech jetzt ab. Vielleicht liegt's ja an mir.

Ja, es liegt eindeutig an Dir. Besser wäre es gewesen, den konkreten 
"funktionierenden" C-Code (also die Multiplikation und das drumherum) 
dem "nicht funktionierendem" Assemblercode gegenüberzustellen.

Dann hätte das jeder mit C-Compiler bzw. Assembler nachstellen können 
und evtl. auch erklären können, warum der Assembler-Code nicht 1:1 das 
macht, was Dein C-Code tut.

EDIT:
Einfach ein paar Aufrufe der völlig unbekannten Funktion 
syncronize_PHI2_wrt() in ein Posting zu klatschen, ohne zu erklären, was 
die mit dem geposteten Assembler-Code zu tun hat, grenzt an 
Unverschämtheit.

von Rolf Magnus (Gast)


Lesenswert?

Frank M. schrieb:
> Einfach ein paar Aufrufe der völlig unbekannten Funktion
> syncronize_PHI2_wrt() in ein Posting zu klatschen, ohne zu erklären, was
> die mit dem geposteten Assembler-Code zu tun hat, grenzt an
> Unverschämtheit.

Naja, die "völlig unbekannte Funktion syncronize_PHI2_wrt()" ist der 
gepostete Assembler-Code, wie man an dessen erster Zeile erkennen kann.

Zum Rest deines Postings gebe ich dir recht.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Rolf Magnus schrieb:
> Naja, die "völlig unbekannte Funktion syncronize_PHI2_wrt()" ist der
> gepostete Assembler-Code, wie man an dessen erster Zeile erkennen kann.

Ich meinte aber die C-Variante, die ich hier vermisse, damit man auch 
einen Soll-Ist-Vergleich machen kann.

> Zum Rest deines Postings gebe ich dir recht.

Danke :-)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Frank M. schrieb:

> Einfach ein paar Aufrufe der völlig unbekannten Funktion
> syncronize_PHI2_wrt() in ein Posting zu klatschen, ohne zu erklären, was
> die mit dem geposteten Assembler-Code zu tun hat, grenzt an
> Unverschämtheit.

Die "unbekannte Funktion" steht in Assembler:

Beitrag "Re: Multiplizieren geht schief"

Allerdings erzeugt der seltsame Prolog einen Offset, der vermutlich bei 
der Synchronisation zu beachten ist — was aber nicht gemacht wird?

Um genau solche Dinge verstehen zu können, braucht es eine bessere 
Beschreibung von seiten des OP; z.B verstehe ich nicht, was passiert 
wenn R24 durch die LSL leer wird (und bleibt).

Ohne solche Erklärungen kann man hier bestenfalls Anmerkungen zur Syntax 
machen, aber das kann der avr-as auch...

von Peter D. (peda)


Lesenswert?

Der Fehler wird wohl das hier sein, zumindest sehe ich keinen Sinn 
darin:

Joachim ... schrieb:
> phi2_syncloop_wrt:
>    lsl r24 ; port for 6522 address x2
>    lsl r24 ; port for 6522 address x2
...
>    rjmp  phi2_syncloop_wrt

Letzendlich ist das Problem, daß man sich nicht die Mühe macht, in 
Worten zu beschreiben, was abgehen soll.
Eine Beschreibung ist nicht nur für andere da.
Sie ist vor allem für den Programmierer selber, den Durchblick zu 
behalten!
Naja, etwas Beschreibung ist ja da, aber wohl zu wenig.
Zum Verstehen muß man wohl das Datenblatt diese komischen 6522 lesen.
Ich würde lieber erst gar keine Chips aus dem Museum einsetzen. Zu der 
umständlichen Programmierung kommt noch die schwere und teure 
Beschaffung hinzu.

Manche Leute schreiben über jede Funktion einen Header, was sie machen 
soll. Ich finde das gut.


Peter

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.