Forum: Compiler & IDEs Würde gerne Assembler in C umschreiben, aber ich blick's net


von Santiago (Gast)


Lesenswert?

Hallo,

es geht um das Hauptprogramm von hier: 
http://www.myplace.nu/avr/minidds/index.htm

Ich denke mal, dass ich die Tabellen und so, in eine Assembler-Datei 
verpackt, auch weiterverwenden kann.

Sinn der Aktion ist der, dass ich gerne das Benutzer-Interface ändern 
möchte - hin zu ein paar Taster plus Drehencoder und Display.
Auch möchte ich nicht, dass der Ausgang sofort zu arbeiten anfängt, wenn 
Spannung vorhanden ist.

Dafür würde ich gerne eine Art Zustandsautomat coden,der bei 
entsprechendem Zustand die Schleife des jetzigen Hauptprogramms 
abarbeitet. Dafür müsste ich zumindest eine Abfrage auf Abbruchbedingung 
zufügen.

Das Original ist geil - ohne Frage.

Kann mir vielleicht jemand die impliziten Seiteneffekte (die 
zweifelsohne ausgereizt wurden) erklären?

Das Hauptprogramm sieht so aus:
1
;  Copyright (C) 2000 Jesper Hansen <jesperh-at-telia-dot-com>.
2
;
3
; main loop
4
;
5
;  r28,r29,r30 is the phase accumulator
6
;    r24,r25,r26 is the adder value determining frequency
7
;
8
;   add value to accumulator
9
;  load byte from current table in ROM
10
;  output byte to port
11
;  repeat 
12
;
13
LOOP1:
14
    add    r28,r24      ; 1
15
    adc    r29,r25      ; 1
16
    adc    r30,r26      ; 1
17
    lpm            ; 3
18
    out    PORTB,r0    ; 1
19
    rjmp  LOOP1      ; 2 => 9 cycles

Was ich schon rausgefunden habe:
- Der Zähler ist eine 24bit Variable über die Register R28-R30, wobei 
R30 bereits Teil des Z-Pointers ist.

- R24-R26 enthält wohl die Schrittweite (auch als 24bit Variable)

- lpm - lädt den Wert aus einer Tabelle in das Register R0

Also in C könnte ich den 24bit Typ ja über eine Struktur/Bitfeld/Union 
definieren und Zähler und Schrittweite getrennt verwalten.
Aber wie wird der Index in eine Tabelle mit 256 Einträgen aus dem 
24bittigen Zähler erledigt?

Welche Rolle spielt das doppelt verwendete Register R30?


Oder ...
... könnte mir jemand die Schleife in eine Assembler-Funktion verpacken 
(und mir dann noch den Aufruf erklären - bitte), die ein globales 
Bitfeld-Element als Endekriterium abfragt?

z.B. aus folgender Struktur:
1
struct sample {
2
   uint8_t  run:1;
3
   ...
4
} status;

von Benedikt K. (benedikt)


Lesenswert?

Kurz gesagt: Vergiss es.
Das ganze ist extremst auf Timing optimiert, in C wird es sehr viel 
schlechter sein. Auf jedenfall wirst du nicht um eine Analyse des 
erzeugten Assembler Codes herum kommen, denn die benötigte Zeit pro 
Durchlauf ist ein wichtiger Faktor um die Ausgangsfrequenz zu berechnen.

von Santiago (Gast)


Lesenswert?

> Kurz gesagt: Vergiss es.

Das finde ich jetzt weder nett, noch konstruktiv.

Wenn es für mich OK wäre, dass die Schleife um einen Faktor 10 langsamer 
würde - warum nicht? Exakte Zeiten kann man ja auch in C erreichen.

Ich schrieb doch schon, dass ich erkannt habe, dass der Code extrem 
optimiert wurde. Vielleicht brauche ich die "Hochfrequenz" ja garnicht.

Keine Ahnung, was Dich zu dieser pauschalen Ohrfeige veranlasst hat - 
ich hoffe mal, dass es noch andere Leser gibt, die Assembler können und 
mir weiterhelfen mögen.

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


Lesenswert?

Santiago wrote:

>> Kurz gesagt: Vergiss es.
>
> Das finde ich jetzt weder nett, noch konstruktiv.

Aber realistisch.

Ich bin sicher ein großer Freund davon, statt Assembler eine
höhere Sprache zu benutzen, aber derart handgefeilten Code wie
Jaspers Mini-DDS würde auch ich so lassen, wie er ist.

> Wenn es für mich OK wäre, dass die Schleife um einen Faktor 10 langsamer
> würde - warum nicht? Exakte Zeiten kann man ja auch in C erreichen.

Wie denn?

> Aber wie wird der Index in eine Tabelle mit 256 Einträgen aus dem
> 24bittigen Zähler erledigt?

Indem du den entsprechenden Teil deiner union als Index benutzt?

von G. L. (sprintersb)


Lesenswert?

Das Benutzer-Interface kannst Du drumrum proggen, die LOOP1 ist jedoch 
zeitkritisch und sollte so ins C übernommen werden.
1
void dds2 (unsigned int acc, unsigned int func, unsigned long step)
2
{
3
    __asm volatile (
4
       "clt"                          "\n"
5
       "0:"                           "\n\t"
6
       "add  %A[acc],  %A[step]  ; 1" "\n\t"
7
       "adc  %B[acc],  %B[step]  ; 1" "\n\t"
8
       "adc  %A[func], %C[step]  ; 1" "\n\t"
9
       "lpm                      ; 3" "\n\t"
10
       "out  %[port], r0         ; 1" "\n\t"
11
       "brtc  0b                 ; 2 => 9 cycles"
12
        : [acc] "+y" (acc), [func] "+z" (func)
13
        : [step] "r" (step), [port] "n" (_SFR_IO_ADDR (PORTB)) 
14
    );
15
}

Knackpunkt ist, wieder aus der Schleife rauszukommen. Momentan sehe ich 
aber nicht, wie man es einer nicht-naked ISR beibringt, das T-Flag zu 
setzen. Naked-ISR wiederum bedeutet Assembler, so daß Du nicht wirklich 
weiter bist...

Alle anderen Möglichkeiten würden das Zeitverhalten ändern. Wenn Du mit 
10 Ticks leben kannst würde es die Sache deutlich vereinfachen. Du 
könntest einen ATtiny2313 statt des AT90S2313 verwenden (fast 
pinkompatibel, aber regulär bis 20MHz taktbar anstatt 16MHz).

Wie auch immer, es wird n ziemlicher Hack das auf "C"-Ebene zu machen.

Ach ja, Du musst bei den Tabellen Alignments zu 2**8 beachten. Die 
Tabellen selbst sind einfach zu übernehmen, da das Original in GNU-C 
dasteht.

von Benedikt K. (benedikt)


Lesenswert?

Santiago wrote:
>> Kurz gesagt: Vergiss es.
>
> Das finde ich jetzt weder nett, noch konstruktiv.

Die Wahrheit ist manchmal hart, ich weiß.

> Ich schrieb doch schon, dass ich erkannt habe, dass der Code extrem
> optimiert wurde. Vielleicht brauche ich die "Hochfrequenz" ja garnicht.

Das ist alles anderere als Hochfreuquenz: Wenn ein Sinus einigermaßen 
sauber aussehen soll, dann solltest du mindestens 10 Samples pro Periode 
ausgeben. Das sind beim Audiofrequenzbereich von 0-20kHz schon 200kHz 
Samplerate die notwendig sind. Und das ist in C alles andere als 
einfach.

> Keine Ahnung, was Dich zu dieser pauschalen Ohrfeige veranlasst hat -

Wie ich oben schon schrieb: Die Wahrheit.

Wenn ich du wäre, würde ich eine von den folgenden Lösungen wählen:
Die Software so lassen, und das ganze einfach in einen µC brennen und 
diesen dann per UART über einen zweiten µC der in C, BASCOM oder sonst 
was programmiert ist ansteuern (genau so habe ich das vor vielen Jahren 
auch gemacht.)
Oder versuchen das ganze um einiges langsamer in C zu programmieren, und 
anstelle des DACs einen Audio Codec wie z.B. den PCM1753 zu verwenden. 
Dieser hat ein Rekonstruktions und Oversampling Filter eingebaut, so 
dass für 20kHz nur etwas mehr als 40kHz Samplerate notwendig sind.
Dafür sehen andere Kurvenformen außer Sinus dann halt bescheiden aus, da 
die Bandbreite hart auf halbe Samplerate begrenzt ist.

von G. L. (sprintersb)


Lesenswert?

...meinte natürlich in GNU-Assembler ;-)

asm (
   ".section .progmem\n"
   ".align 8\n"
   ".global sine\n"
   "sine:    ; 256 step sinewave table\n"
   ".byte 0x80,0x83,0x86,0x89,...\n"
   ...
);

von G. L. (sprintersb)


Lesenswert?

Hier das Setzen des T-Flags. In einer ISR, welche was von LOOP1 updatet, 
triggerst Du einen Timer0 Overflow-IRQ.

Die ISR dazu deaktiviert die IRQ und setzt T.
1
void __attribute__ ((naked, signal)) 
2
SIG_OVERFLOW0 (void)
3
{
4
    __asm volatile (
5
        "push  r24"            "\n\t"
6
        "clt"                  "\n\t"
7
        "in    r24, %[timsk]"  "\n\t"
8
        "bld   r24, %[ie]"     "\n\t"
9
        "out   %[timsk], r24"  "\n\t"
10
        "pop   r24"            "\n\t"
11
        "set"                  "\n\t"
12
        "reti"
13
        :: [timsk] "n" (_SFR_IO_ADDR (TIMSK)), [ie] "n" (TOIE0));
14
}

Das sollte alles sein, was an asm notwendig ist.

von Santiago (Gast)


Lesenswert?

Hallo,

> @Benedikt K. und Jörg Wunsch:
Mir kommt es so vor, als hättet Ihr nur den Titel und den Link aber 
meinen Post nicht zu Ende gelesen.

Mir geht es nicht darum, was Geniales kaputt zu machen, sondern um was 
drum herum zu stricken. Wenn man die Schleife in C nicht machen kann - 
OK, dann eben in Assembler lassen - aber ob die Schleife jetzt 9 Takte 
oder 15 braucht, ist mir völlig Banane (ich denke, dass durch die 
Taktzahl der Schleife die minimale Schrittweite und die maximale 
Frequenz beeinflusst werden. Die Kwalität des Signals sollte davon 
unbenommen sein).


> @Georg-johann L.:
Du scheinst verstanden zu haben, worauf ich hinaus will.
Danke Dir für Deine Mühen!
Ich will in der Tat einen ATtiny2313 einsetzen (da bereits vorhanden). 
Einen schnelleren Kwartz einsetzen ist etwas, was später problemlos 
gehen sollte.
Im Augenblick ist mir das Programm wichtiger.

Wie gesagt - wenn die Schleife statt 9 Takten eben 10 oder 15 braucht 
ist das für mich ok.
Ich denke, eine Abfrage einer Abbruchbedingung ist auch in konstanten 
Taktzahlen machbar. Sobald die Bedingung nicht mehr erfüllt ist, soll ja 
keine Ausgabe mehr erfolgen. Also spielt es keine Geige, ob die 
Bedingungsabfrage im 'Ja'-Zweig andere Taktzahlen braucht, als im 
'Nein'-Zweig.
Und ob die Schleife jetzt in einer Unterfunktion oder direkt im 
Hauptprogramm liegt spielt ja auch keine Rolle. Vor dem Funktionsaufruf 
wird ja kein Signal ausgegeben, also wird auch keines verfälscht.

> Ach ja, Du musst bei den Tabellen Alignments zu 2**8 beachten.
Kann ich die Tabellen nicht in eine asm-Datei kopieren und dann zu 
meinem C-Code dazu linken?

> Hier das Setzen des T-Flags. In einer ISR, welche was von LOOP1 updatet,
> triggerst Du einen Timer0 Overflow-IRQ.

Verzeih mir, aber ich habe nicht wirklich verstanden, was Du mir 
geschrieben hast.
- Wieso muss das T-Flag gesetzt werden?
- Wer braucht das?
- Timer werden im Original ja nicht verwendet - die wollte ich für das 
Benutzerinterface verwenden.

Kann man nicht in der Assemblerschleife eine Bedingung abfragen, die ich 
aus meinem C-Code heraus setzten könnte?
Wenn eine globale Variable zu viel Registerwechsel bedeuten würde, 
könnte man dann nicht auch ein (unbenutztes) Pin abfragen?!?

von Michael U. (amiga)


Lesenswert?

Hallo,

mein Ansatz wäre: die main-Schleife unverändert irgendwie in C 
reinbekommen (programmiere nicht in C, nur ASM.

Ansonsten sein Konzept beibehalten, also Tasten/Encoder an irgendwelche 
Portpins, ein AVR mit Pin-Change-IRQ dazu nehmen.

Die komplette Behandlung in der PinChangeIRQ erledigen, so wie er es in 
der RX-Komplett macht.

Wenn nicht sofort gestartet werden soll, gibt es ein Problem. Eine 
zusätzliche Abfrage würde ich nicht in die main_loop einbauen, wäre mir 
zuviel Umbau.
2 Ansätze: wenn nicht ausgegeben werden soll den R2R-Port einfach auf 
Eingang setzen oder einen TriState-Treiber zwischen AVR und R2R und den 
über ein Portpin schalten.

Ach so: TimerIRQ würde ich nicht benutzen, das Ding funktioniert nur, 
weil die main_loop durch nichts gestört wird.
Entweder sie läuft völlig ungestört (Signal wird erzeugt) oder ganicht 
(Bedienung).

Gruß aus Berlin
Michael

von Peter D. (peda)


Lesenswert?

Michael U. wrote:

> mein Ansatz wäre: die main-Schleife unverändert irgendwie in C
> reinbekommen (programmiere nicht in C, nur ASM.

Ja.
Das mit dem T-Bit ist völlig unnötig.
Die Variablen für die Addition als Registervariablen definieren, dann 
kann sie ein Interrupt verändern.


> Wenn nicht sofort gestartet werden soll, gibt es ein Problem.

Warum?
Wenn 0 addiert wird, passiert am Ausgang garnichts.


> Ach so: TimerIRQ würde ich nicht benutzen, das Ding funktioniert nur,
> weil die main_loop durch nichts gestört wird.

Timer ist aber wichtig fürs Tasten/Drehgeber entprellen.
Aber nicht durchlaufend, sondern durch Pin-Change für 4 Durchläufe 
gestartet.


Peter

von Santiago (Gast)


Lesenswert?

> Wenn 0 addiert wird, passiert am Ausgang garnichts.
mirvordiestirnschlag Natürlich! Danke Peter, für die Glühbirne :)

> Die Variablen für die Addition als Registervariablen definieren, dann
> kann sie ein Interrupt verändern.
Ok, das klingt zumindest plausibel. Muss ich mich allerdings erst 
einlesen, in Assembler werden ja feste Register erwartet.

> Aber nicht durchlaufend, sondern durch Pin-Change für 4 Durchläufe
> gestartet.
Das ist auch eine neue Idee. Vielleicht kann so die Bedienung völlig 
ohne Einfluss von main auskommen. Mal grübeln gehen :)

Danke für die Denkanstöße!

von Michael U. (amiga)


Lesenswert?

Hallo,

Peter Dannegger wrote:
> Michael U. wrote:
>> Wenn nicht sofort gestartet werden soll, gibt es ein Problem.
>
> Warum?
> Wenn 0 addiert wird, passiert am Ausgang garnichts.

Stimmt. Soweit hatte ich beim Schreiben noch nicht gedacht...

>> Ach so: TimerIRQ würde ich nicht benutzen, das Ding funktioniert nur,
>> weil die main_loop durch nichts gestört wird.
>
> Timer ist aber wichtig fürs Tasten/Drehgeber entprellen.
> Aber nicht durchlaufend, sondern durch Pin-Change für 4 Durchläufe
> gestartet.

Auch hier hast Du recht, hätte ich eben erst fertig denken und dann 
schreiben sollen. ;) Meinte damit auch nur, keinen durchlaufenden 
Timer-IRQ, der die main_loop stören könnte.

Ich sehe hier aber auch durchaus einen der Fälle, wo der Timer-IRQ 
gegenüber einer BusyLoop vermutlich keinen Vorteil hat, weil der µC in 
der Zwischenzeit ohnehin nichts sinnvolles machen kann.

PinChange -> entprellen, neue Werte setzen, Anzeige aktualisieren, raus.
Während dieses Ablaufs gibt es eigentlich nichts anderes, was er 
erledigen könnte, egal, wie lange das dauert.

Gruß aus Berlin
Michael

von G. L. (sprintersb)


Lesenswert?

Santiago wrote:
>> @Georg-johann L.:
> Ich will in der Tat einen ATtiny2313 einsetzen (da bereits vorhanden).

Ich denke, der 2313 wird zu klein sein. Für C musst Du Overhead 
einrechnen (zB Startup-Code, wegen C, größere Vectab, ...)

Schau mal was das Original an Programmcode brauch (ohne Tabellen). Nimm 
das x2 und schau ob es immer noch passt. Wenn nicht, hast Du mit einem 
2313 keinen Spaß.

Ein ATmega48 tut es, falls es nicht schon ne fertige Schaltung gibt, 
denn der hat nen anderen Fußabdruck. Dann geht auch gleich ein ATmega168 
und Du hasst mehr Platz für Tabellen. Zudem hat der 1k RAM, Du könntest 
also sogar Tabellen dynamisch nachladen und ausm RAM lesen.

> Einen schnelleren Kwartz einsetzen ist etwas, was später problemlos
> gehen sollte.
> Im Augenblick ist mir das Programm wichtiger.
>
> Wie gesagt - wenn die Schleife statt 9 Takten eben 10 oder 15 braucht
> ist das für mich ok.
> Ich denke, eine Abfrage einer Abbruchbedingung ist auch in konstanten
> Taktzahlen machbar. Sobald die Bedingung nicht mehr erfüllt ist, soll ja
> keine Ausgabe mehr erfolgen. Also spielt es keine Geige, ob die
> Bedingungsabfrage im 'Ja'-Zweig andere Taktzahlen braucht, als im
> 'Nein'-Zweig.
> Und ob die Schleife jetzt in einer Unterfunktion oder direkt im
> Hauptprogramm liegt spielt ja auch keine Rolle. Vor dem Funktionsaufruf
> wird ja kein Signal ausgegeben, also wird auch keines verfälscht.
>
>> Ach ja, Du musst bei den Tabellen Alignments zu 2**8 beachten.
> Kann ich die Tabellen nicht in eine asm-Datei kopieren und dann zu
> meinem C-Code dazu linken?
Alignments müsstest Du dennoch beachten. Besonders ärgerlich, weil das 
unnötig Flash verschwendet -- es sei denn, die Tabellen liegen am 
Flashende.

>> Hier das Setzen des T-Flags. In einer ISR, welche was von LOOP1 updatet,
>> triggerst Du einen Timer0 Overflow-IRQ.
>
> Verzeih mir, aber ich habe nicht wirklich verstanden, was Du mir
> geschrieben hast.
> - Wieso muss das T-Flag gesetzt werden?
> - Wer braucht das?
> - Timer werden im Original ja nicht verwendet - die wollte ich für das
> Benutzerinterface verwenden.
Das T-Flag wurde mit BRTC als Schleifenbedingung verwendet.
Über Timer0-IRQ wurde als Soft-IRQ missbraucht, um T zu setzen.

> Kann man nicht in der Assemblerschleife eine Bedingung abfragen, die ich
> aus meinem C-Code heraus setzten könnte?
> Wenn eine globale Variable zu viel Registerwechsel bedeuten würde,
> könnte man dann nicht auch ein (unbenutztes) Pin abfragen?!?

Das ginge auch, globale Variable zum Austausch ist je net so dramatisch.
1
uint8_t volatile update;
2
3
void dds3 (const uint8_t * base, uint32_t step)
4
{
5
    uint16_t pwert;
6
    uint16_t acc = 0;
7
    uint8_t idx=0;
8
    
9
    __asm volatile (
10
       "0:"                              "\n\t"
11
       "; Index berechnen"               "\n\t"
12
       "add  %A[acc],  %A[step]  ; 1"    "\n\t"
13
       "adc  %B[acc],  %B[step]  ; 1"    "\n\t"
14
       "adc  %A[idx],  %C[step]  ; 1"    "\n\t"
15
       "; Adresse berechnen"             "\n\t"
16
       "mov  %A[pwert], %A[base]  ; 1"    "\n\t"
17
       "mov  %B[pwert], %B[base]  ; 1"    "\n\t"
18
       "add  %A[pwert], %A[idx]   ; 1"    "\n\t"
19
       "adc  %B[pwert], __zero_reg__ ; 1" "\n\t"
20
       "; Wert lesen & ausgeben"         "\n\t"
21
       "lpm  r0, Z               ; 3"    "\n\t"
22
       "out  %[port], r0         ; 1"    "\n\t"
23
       "; update?"                       "\n\t"
24
       "lds   r0, update         ; 2"    "\n\t"
25
       "sbrs  r0, 0              ; 2"    "\n\t"
26
       "rjmp  0b                 ; 2 => 17 cycles"
27
        : [acc] "+r" (acc), [pwert] "=&z" (pwert), [idx] "=&r" (idx)
28
        : [step] "r" (step), [port] "n" (_SFR_IO_ADDR (PORTB)) , [base] "r" (base)
29
    );
30
}

Falls ein Admin mitliest : Da fehlen Underscores im als C-Code 
gekennzeichneten Teil, die irgendein Skript wegwischt.

Mit dem Code sind keine Alignments mehr erforderlich, er braucht 
allerdings 17 Ticks. Mit einem NOP und einem 22.118400 MHz hättest Du 
das gleiche Verhalten wie ursprünglich (inclusive Übertaktung ;-))

Mit einem missbrauchten I/O-Bit und alignten Tabellen sind 11 Ticks 
machbar

von Santiago (Gast)


Lesenswert?

> @Georg-johann L.:
>> Santiago wrote:
>> Ich will in der Tat einen ATtiny2313 einsetzen (da bereits vorhanden).

> Ich denke, der 2313 wird zu klein sein. Für C musst Du Overhead
> einrechnen (zB Startup-Code, wegen C, größere Vectab, ...)
>
> Schau mal was das Original an Programmcode brauch (ohne Tabellen). Nimm
> das x2 und schau ob es immer noch passt. Wenn nicht, hast Du mit einem
> 2313 keinen Spaß.
>
> Ein ATmega48 tut es, falls es nicht schon ne fertige Schaltung gibt,

Also wenn die Tina zu klein ist, hätte ich noch einen mega88.
Den Speicherbedarf zeigt ja das AVR-Studio an - sollte also 
rauszubekommen sein.
Denke mal, dass ich es hinbekomme, die Schaltung anzupassen.

Ich bin ja noch blutiger Anfänger mit mcs und da verstehe ich getrixtend 
Code noch nicht (wohl weil ich die Zusammenhänge mit der HW nicht 
kenne).

> Das T-Flag wurde mit BRTC als Schleifenbedingung verwendet.
> Über Timer0-IRQ wurde als Soft-IRQ missbraucht, um T zu setzen.

Ah jetzt ja - also auch getrixt?!?

> Das ginge auch, globale Variable zum Austausch ist je net so dramatisch.

Danke für das Beispiel. Übrigens:
Mir gefällt Deine Art, inline zu coden. Die abgesetzte Formatierung 
bringt den Code noch besser zu Geltung. Sehr schön.

> Mit dem Code sind keine Alignments mehr erforderlich, er braucht
> allerdings 17 Ticks. Mit einem NOP und einem 22.118400 MHz hättest Du
> das gleiche Verhalten wie ursprünglich (inclusive Übertaktung ;-))

Habe ich das richtig verstanden, dass die Alignments nötig sind, damit 
der (eine Teil des) Z-Pointer nicht geändert werden muss?

Ansonsten klingt das ja genial.
Bin mir jetzt nicht ganz sicher, ob ich es verstanden habe, aber in 
Deinem Beispiel ist ja jetzt weniger Getrixe - wäre das dann nicht auch 
mit C so machbar? Du verwendest ja schon 32bit Werte.
Mal hören was der Compiler dazu meint :)

Nochmals ein Dankeschön für Deine Unterstützung.

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


Lesenswert?

Georg-johann L. wrote:

> Falls ein Admin mitliest : Da fehlen Underscores im als C-Code
> gekennzeichneten Teil, die irgendein Skript wegwischt.

Es gibt nur einen Admin hier: Andreas.  Die andere Leute sind
Moderatoren und haben mit der Forumssoftware selbst nichts zu tun
(und zumindest ich habe Andreas auch noch nicht persönlich getroffen).

Mach am besten einen Thread in 
http://www.mikrocontroller.net/forum/website
dafür auf.

von Santiago (Gast)


Lesenswert?

Hallo,

muss nochmal nachfragen. Irgendwie bekomme ich es nicht auf die Reihe.

Ich wollte den Tip von Peter mit den benannten Registern umsetzen.
Dazu habe ich die Register wie folgt definiert:
1
register unsigned char acc1 asm("r28");
2
register unsigned char acc2 asm("r29");
3
register unsigned char acc3 asm("r30");
4
register unsigned char inc1 asm("r24");
5
register unsigned char inc2 asm("r25");
6
register unsigned char inc3 asm("r26");

Jetzt meint der Compiler (zu den Registern r28 und r29):
>register specified for 'acc1' isn't suitable for data type

Bedeutet der Fehler, dass die beiden Register tabu für Variablen sind, 
oder was habe ich übergesehen?

zu den restlichen Registern meint er:
>call-clobbered register used for global register variable

Da letzteres eine Warnung ist, entnehme ich, dass die Register bei einem 
Funktionsaufruf überschrieben würden?!?
Da ich wohl kaum ohne Funktionsaufrufe auskomme, müsste ich die Register 
direkt vor der Aktivierung des Loops neu setzen?


Andere Frage :
- Könnte ich den Spieß auch umdrehen - und aus der Assemblerdatei das 
ganze serielle Zeugs löschen und statt der Zeilen zwischen RESET: und 
LOOP1: eine C-Funktion namens init() aufrufen?
- Zu der Assemblerdatei müsste ich dann eine C-Datei linken, in der 
einige Interrupts stehen

- Geht das so zu kombinieren? - wenn ja, wie rufe ich eine C-Funktion 
aus Assembler auf?

- Kann ich die Register der Hauptschleife via Inline-Assembler aus einer 
ISR direkt setzen? Oder werden die Register für den Interruptaufruf 
gesichert und nach Rückkehr aus der ISR wieder zurückgeschrieben?

von G. L. (sprintersb)


Lesenswert?

Wenn's Dir leiber ist übersetze den ASM-Schnippsel, schau was avr-gcc 
draus macht (.s-File) und bastel ne .S-Datei draus.
1
#include <avr/io.h>
2
3
.arch attiny2313
4
    
5
.text    
6
.global dds
7
8
dds:
9
  clr 18   ;  acc
10
  clr 19   ;  acc
11
0:
12
  ; Index berechnen
13
  add  18, 20         ; 1   ;  acc, step
14
  adc  19, 21         ; 1   ;  acc, step
15
  adc  26, 22         ; 1   ;  idx, step
16
  ; Adresse berechnen
17
  mov  30, 24         ; 1   ;  pwert, base
18
  mov  31, 25         ; 1   ;  pwert, base
19
  add  30, 26         ; 1   ;  pwert, idx
20
  adc  31, 1          ; 1   ;  pwert
21
  ; Wert lesen & ausgeben
22
  lpm  0, Z           ; 3
23
  out  _SFR_IO_ADDR(PORTB), 0         ; 1
24
  ; update?
25
  lds   0, update     ; 2
26
  sbrs  0, 0          ; 2
27
  rjmp  0b            ; 2 => 17 cycles
28
  ret

Globale Register kannst Du verwenden wennn Du genau weisst, was Du 
tust und genau, was gcc tut. Insbesondere, welches ABI er 
implementiert. Weiterlesen zB hier:

Beitrag "Re: Ausführungszeit von C code, ist in funktionen langsamer?"

Und für das .S musst Du ebenfalls genau wissen was abgeht (Inline 
Assembler ebenso). Die Parameterübergabe im Inline Asm sieht zwar übel 
aus, aber so weiß gcc immerhin was abgeht.

Ein Hauptgrund, warum man einen Compiler verwendet, ist daß man sich 
nicht mehr um die nervige Registerverwendung kümmern muss. Wenn man doch 
was dran schraubt, sollte man nicht Features nutzen, die globales 
Verhalten ändern, sondern den Hack auf wenige chirurgische Eingriffe 
beschränken.

von Santiago (Gast)


Lesenswert?

Hallo,

also das mit Assembler und C kombinieren habbich ned hinbekommen :(
Irgendwie kennt C die Bezeichner von Assembler nich un wennich se in C 
als extern deklariere, bekomme ich Linker-Fehler.
Dann bleibe ich lieber bei Inline-Assembler - das Zusammenspiel habe ich 
(halbwegs) verstanden.

Jetzt hab ich das Ganze mal nur in C gemacht und die 
Benutzerschnittstelle ist auch soweit, dass ich mich den Bits wieder 
nächern kann (Prost). Habe jetzt für jede Wellenform eine eigene 
Funktion, sodass ich mit der Schleifenzeit in die Nähe des Originals 
kommen könnte.

Deshalb würde ich gerne nochmal auf die Sache mit dem T-Flag zurück 
kommen. Das habe ich noch nicht wirklich verstanden.
In den obigen Beispielen finde ich zwar wie man es benutzt, aber wofür 
ist das T-Flag zuständig? Wie kann ich damit Konflikte 
verursachen/vermeiden?

Im Handbuch zum mega88 habe ich nix darüber gefunden und die Erklärung 
im "Instruction manual" ist so dürftig, dass sich mir der Sinn, bzw. die 
normale Verwendung nicht erschließt.
Falls mir jemand hier etwas Licht ins Dunkel bringen könnte, würde mir 
das sehr freuen tun :)

Ich habe auch mal ausprobiert, die Abbruchbedingung als Registervariable 
anzulegen - das hat mal so richtig überhaupt nix gebracht. Der Compiler 
hat die Variable in ein anderes Register umkopiert und dann zusätzliche 
Befehle eingefügt, sodass sich kein Zeitgewinn ergab.

Insgesamt sind es inzwischen 3k geworden - passt also nimmer zur Tina. 
Aber mit einem mega48 könnte es locker reichen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Santiago wrote:
> Hallo,
>
> also das mit Assembler und C kombinieren habbich ned hinbekommen :(
> Irgendwie kennt C die Bezeichner von Assembler nich un wennich se in C
> als extern deklariere, bekomme ich Linker-Fehler.

1) Normale H-header schreiben
2) C-Quelle compilieren uns assemblieren
3) Im .S die globalen Objekte (zB Funktionen) mit .global kennzeichnen
4) Im .S die C-ABI beachten (Parameterübergabe, Registerverwendung)
5) .S assemblieren mit -x assembler-with-cpp
6) Objekte linken

> Habe jetzt für jede Wellenform eine eigene Funktion

hmmm grübel

> Deshalb würde ich gerne nochmal auf die Sache mit dem T-Flag zurück
> kommen. Das habe ich noch nicht wirklich verstanden.
> In den obigen Beispielen finde ich zwar wie man es benutzt, aber wofür
> ist das T-Flag zuständig? Wie kann ich damit Konflikte
> verursachen/vermeiden?

Das T-Flag ist ein User-Flag. Es hat keine Einfluss auf die Maschine und 
wir auch nicht durch Arithmetik gesetz/gelöscht. Man kann es von Hand 
setzen, löschen, abhängig davon springen und es in ein Registerbit hin- 
nud herschieben.

> Ich habe auch mal ausprobiert, die Abbruchbedingung als Registervariable
> anzulegen - das hat mal so richtig überhaupt nix gebracht. Der Compiler
> hat die Variable in ein anderes Register umkopiert und dann zusätzliche
> Befehle eingefügt, sodass sich kein Zeitgewinn ergab.

Jau. GCC ist eigensinnig.


Das T-Flag könnte man auch anders setzen:

Nehmen wir mal an, es wird der USART_RX_vect benutzt (USART receive des 
ATtiny2313)

Dazu schreiben wir eine Funktion usart_rx, die als ISR fungiert:
1
SIGNAL (usart_rx) // = void __attribute__((signal)) usart_rx (void)
2
{
3
    // mach was
4
}

Damit bekommt usart_rx den üblichen ISR-Prolog und Epilog und taugt als 
ISR. Allerdings gibt es keinen Eintrag in die VecTab, weil die ISR 
keinen Standard-ISR-Name hat (man bekommt dafür ne Warnung von avr-gcc).

Zum Einsprung implementieren wir die Standard-ISR als naked, also ohne 
Prolog und Epilog:
1
void __attribute__((naked)) 
2
USART_RX_vect (void)
3
{
4
    __asm volatile ("set" "\n\t"
5
                    "rjmp usart_rx");
6
}

Damit erhalten wir den Eintrag in die VecTab und den Einsprungpunkt für 
unserer ISR. Nach Setzen des T-Flags geht's weiter mit usart_rx und 
gewohnter Abhandlung der IRQ.

Damit ist die BRTC-Version der DDS-Schleife mit 9 Ticks verwendbar.

Hack ist auch diese Lösung...aber die Hacks werden langsam besser :-)

von Santiago (Gast)


Lesenswert?

>> also das mit Assembler und C kombinieren habbich ned hinbekommen :(
>> Irgendwie kennt C die Bezeichner von Assembler nich un wennich se in C
>> als extern deklariere, bekomme ich Linker-Fehler.
>
> 1) Normale H-header schreiben
> 2) C-Quelle compilieren uns assemblieren
> 3) Im .S die globalen Objekte (zB Funktionen) mit .global kennzeichnen
> 4) Im .S die C-ABI beachten (Parameterübergabe, Registerverwendung)
> 5) .S assemblieren mit -x assembler-with-cpp
> 6) Objekte linken

Danke für das Rezept. Schätze ich hatte das .global 
vergessen/übergesehen.
Werde es also nomml probieren.

Die "C-ABI" ist noch ein Fremdwort. Hätte mir jemand vielleicht einen 
Link, sodass ich mich zu dem Thema etwas schlauer machen könnte?

>> Habe jetzt für jede Wellenform eine eigene Funktion
>
> hmmm grübel

Na ich wollte die Varianten aus der Schleife rausbekommen - wenn man so 
will, also die Tabelle hart codieren. Habbich dann zuerst mit Gotos und 
verschiedenen Schleifen probiert - und das hat mir garnicht gefallen.
Dann bin ich draufgekommen, dass ein Funktionsaufruf ja genau das Goto 
auch macht, nur eben C-like. Sieht jetzt viel besser aus und ich nähere 
mich so dem Original (aba nur da wo es nötig is).

> Das T-Flag könnte man auch anders setzen:

Hm, das scheint mir ein sehr interessantes Verfahren zu sein.
Muss ich aber erst etwas setzen lassen (geistiger Wiederkäuer :D ).
Wird das T-Flag beim ISR-pro- und -Epilog berücksichtigt?

Im Moment ist es so, dass ich das Benutzerinterface abschalte, bevor der 
Funktionsgenerator loslegt. Per Pin-Change-Interupt wird die 
Benutzerschnittstelle wieder eingeschaltet.
Habe die Schaltung nocht nicht konzipiert - deshalb könnte es sein, dass 
ich mehrerere Pin-Change-Interrupts verwenden müsste.
Das Prinzip mit dem nackichen Interrupt kann ich auch mehrfach 
einsetzen?

Ach ja, ich hatte es bislang so verstanden, dass SIGNAL kwasi als 
deprecated eingestuft ist und deshalb verwende ich bei den ISRs eben das 
ISR-Makro.
Schätze bei dem Häck wäre jetzt doch SIGNAL einzusetzen?

Und nur damit ich es richtig verstanden habe:
Der Name "usart_rx" ist ein frei erfundener Name und könnte auch 
"hebbelbebbel" sein?
Wenn ich dann per Inline-Assembler die Funktion anspringen will, woher 
kennt der Assembler den Namen "usart_rx"?

> Hack ist auch diese Lösung...aber die Hacks werden langsam besser :-)

Ja, ich lebe langsam wieder auf und freue mich, dass der Dickkopf sich 
manchmal doch auszahlt (un nich immer Leute wie Benedikt Recht behalten) 
;)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Santiago wrote:
> Die "C-ABI" ist noch ein Fremdwort. Hätte mir jemand vielleicht einen
> Link, sodass ich mich zu dem Thema etwas schlauer machen könnte?
"Application Binary Interface"

Erste und einzig Sichere Referen sind die Compiler-Quellen
$GCC_SOURCE/gcc/config/avr/avr.*

Ein bissl steht bestimmt auch im avr-gcc Tutorial hier und in

http://www.roboternetz.de/wissen/index.php/Avr-gcc/Interna#Registerverwendung



>> Das T-Flag könnte man auch anders setzen:
>
> Hm, das scheint mir ein sehr interessantes Verfahren zu sein.
> Muss ich aber erst etwas setzen lassen (geistiger Wiederkäuer :D ).
> Wird das T-Flag beim ISR-pro- und -Epilog berücksichtigt?

Das T-Flag gehört zum SREG (PSW) und wird dementsprechend 
gesichert/restauriert.

> Das Prinzip mit dem nackichen Interrupt kann ich auch mehrfach
> einsetzen?

Ja.

> Ach ja, ich hatte es bislang so verstanden, dass SIGNAL kwasi als
> deprecated eingestuft ist und deshalb verwende ich bei den ISRs eben das
> ISR-Makro.
> Schätze bei dem Häck wäre jetzt doch SIGNAL einzusetzen?

Ich nehm das gute, alte SIGNAL oder INTERRUPT mit dem guten, alten 
avr-gcc 3.4.6. Zu GCC 4 kann ich keine Tipps geben. Schau eben man in 
die Compiler-Header und was cpp und gcc draus basteln mit (-save-temps).

> Und nur damit ich es richtig verstanden habe:
> Der Name "usart_rx" ist ein frei erfundener Name und könnte auch
> "hebbelbebbel" sein?

Ja.

> Wenn ich dann per Inline-Assembler die Funktion anspringen will, woher
> kennt der Assembler den Namen "usart_rx"?

Der Assembler braucht keine Namen zu kennen, er fügt für Symbole FixUps 
ein, die der Linker/Locator auflöst und ausfüllt.

>> Hack ist auch diese Lösung...aber die Hacks werden langsam besser :-)
>
> Ja, ich lebe langsam wieder auf und freue mich, dass der Dickkopf sich
> manchmal doch auszahlt (un nich immer Leute wie Benedikt Recht behalten)
> ;)

Benedikt hat Recht behalten. Du schriebst was von "Umschreiben nach C" 
und er sagte "Vergiss es". Keine der bisher erwägten Varianten des 
asm-Schnippsels mit ähnlichem Zeitverhalten ist in C.

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


Lesenswert?

Georg-johann Lay wrote:

> "Application Binary Interface"
>
> Erste und einzig Sichere Referen sind die Compiler-Quellen
> $GCC_SOURCE/gcc/config/avr/avr.*

Dieser FAQ-Eintrag:

http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage

erhebt für sich den Anspruch, das ABI zu dokumentieren.

> Ich nehm das gute, alte SIGNAL oder INTERRUPT mit dem guten, alten
> avr-gcc 3.4.6. Zu GCC 4 kann ich keine Tipps geben.

Das hat nichts mit der GCC-Version zu tun, wohl aber mit der
avr-libc-Version.  Ist generell keine gute Idee, neu hinzu kommenden
Nutzern die Verwendung von als `deprecated' markierten Interfaces
kommentarlos nahe zu legen.  Ja, ISR ist einfach nur ein neuer
(und hoffentlich/offensichtlich zu weniger Missverständnissen
führender) Name für das alte SIGNAL bzw. INTERRUPT (letzteres mit
zusätzlichen Argumenten für ISR).

Ob GCC 3.4.6 nun wirklich ,,gut und alt'' ist, da stimme ich dir für
den zweiten Teil uneingeschränkt zu, für den ersten würde ich durchaus
Widerspruch anmelden. ;-)

von Santiago (Gast)


Lesenswert?

Hallo Georg-johann,

herzlichen Dank für Deine Unterstützung und Geduld!

> Ein bissl steht bestimmt auch im avr-gcc Tutorial hier und in
>
> http://www.roboternetz.de/wissen/index.php/Avr-gcc...

Danke für den Link. Die Seite kannte ich noch nicht. Scheint sogar 
Dau-proof zu sein (schließlich verstehe ich etwas mehr als vorher) ;)

Werde mich heute daran machen, die Tips mal umzusetzen.

>>> Hack ist auch diese Lösung...aber die Hacks werden langsam besser :-)
>>
>> Ja, ich lebe langsam wieder auf und freue mich, dass der Dickkopf sich
>> manchmal doch auszahlt (un nich immer Leute wie Benedikt Recht behalten)
>> ;)
>
> Benedikt hat Recht behalten. Du schriebst was von "Umschreiben nach C"
> und er sagte "Vergiss es". Keine der bisher erwägten Varianten des
> asm-Schnippsels mit ähnlichem Zeitverhalten ist in C.

Dank meiner grenzenlosen Unkenntnis der Materie habe ich es doch 
geschafft, mein Anliegen falsch zu formulieren. Auch wenn ich schon im 
ersten Post schrieb, dass es mir darum geht, dass Verhalten und die 
Benutzerschnittstelle zu ändern.

Da ich nur C konnte, erschien mir das der einzig gangbare Weg. Dank 
meines Dickkopfes und Eurer Unterstützung habe ich jetzt soviel dazu 
gelernt, dass es auch einen anderen Weg zum gleichen Ziel gibt.

Auch wenn Benedikt faktisch recht behalten hat, dass man das 
Hauptprogramm nicht umschreiben kann - ich halte nix davon, Anfänger 
erstmal abzuwatschen, ohne sich wirklich mit dessen Problem zu 
beschäftigen. Schätze mal, ich werde nicht der Einzige sein, der aus 
Unwissenheit die Frage nach Unterstützung falsch stellt?!?

Wie heißt es doch so schön: Wahrheiten sind die Illusionen, von denen 
wir vergessen haben, dass sie welche sind.

von Santiago (Gast)


Lesenswert?

Hallo Jörg,

>> Erste und einzig Sichere Referen sind die Compiler-Quellen
>> $GCC_SOURCE/gcc/config/avr/avr.*
>
> Dieser FAQ-Eintrag:
>
> http://www.nongnu.org/avr-libc/user-manual/FAQ.htm...
>
> erhebt für sich den Anspruch, das ABI zu dokumentieren.

Schon komisch - das Manual der avr-libc liegt bei mir auf Platte und ist 
immer offen, wenn ich an der Firmware schraube.
Die FAX habe ich auch schon öfters gelesen, nur wenn ich hier von C-ABI 
lese, konnte ich das nicht mit der entsprechenden Frage aus dem FAQ 
zusammen bringen.
Jetzt, da ich weiß, dass die Frage 13 sich mit dem C-ABI beschäftigt, 
bekomme ich die Sachen auch wieder zusammen.

Oftmals sind es Kleinigkeiten, die ein Scheitern verursachen :)

Danke für die Aufklärung.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch wrote:
>> Ich nehm das gute, alte SIGNAL oder INTERRUPT mit dem guten, alten
>> avr-gcc 3.4.6. Zu GCC 4 kann ich keine Tipps geben.
>
> Das hat nichts mit der GCC-Version zu tun, wohl aber mit der
> avr-libc-Version.  Ist generell keine gute Idee, neu hinzu kommenden
> Nutzern die Verwendung von als `deprecated' markierten Interfaces
> kommentarlos nahe zu legen.  Ja, ISR ist einfach nur ein neuer
> (und hoffentlich/offensichtlich zu weniger Missverständnissen
> führender) Name für das alte SIGNAL bzw. INTERRUPT (letzteres mit
> zusätzlichen Argumenten für ISR).
>
> Ob GCC 3.4.6 nun wirklich ,,gut und alt'' ist, da stimme ich dir für
> den zweiten Teil uneingeschränkt zu, für den ersten würde ich durchaus
> Widerspruch anmelden. ;-)

Ji Jörg,

hmmm, ich hatte den avr-gcc 4.x mal angetestet, fand es aber nicht sehr 
verlockend darauf umzusteigen, weil er wohl noch was wacklig ist auf den 
Beinen. Vielleicht hast Du ja n paar Tipps was ich falsch mache? Näheres 
dazu und weil die Frage hier OT ist

F'UP

Beitrag "avr-gcc: 3.4.6 contra 4.3.0"

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


Lesenswert?

Georg-johann Lay wrote:

> hmmm, ich hatte den avr-gcc 4.x mal angetestet, fand es aber nicht sehr
> verlockend darauf umzusteigen, weil er wohl noch was wacklig ist auf den
> Beinen. [...]

4.3.0 ist ja nun wirklich kein Vergleich. ;-)

Aber 4.2.x ist mindestens genauso stabil wie die alte 3.xer Linie.

4.3.0 hat Eric nur deshalb so schnell unter die Massen bringen müssen,
weil sein Brötchengeber natürlich Interesse daran hatte, dass WinAVR
die neuen Xmega-Prozessoren möglichst schnell nach deren offizieller
Ankündigung unterstützen können sollte.  Da Xmega teilweise gravierende
Änderungen in der CPU-Architektur hat, die sich im Compiler nieder-
schlagen, war dies nicht mehr mit der vorhandenen man power auf 4.2.x
zurück portierbar.  Andererseits kann Erics Installer leider keine
optionalen Komponenten, d. h. er konnte damit den Nutzern nicht
anbieten, in einem WinAVR-Release alternativ GCC 4.2.x (stabil, aber
kein Xmega) oder 4.3.0 (taufrisch + Xmega) zu installieren.

Wie schon öfter geschrieben, wenn jemand mit Windows-Installern und
deren Konfiguration bewandert ist, dann wäre das eine Stelle, an der
er Eric gut unterstützen kann.

Die aufgekommenen Probleme mit 4.3.0, auch im Bereich der Optimierung,
sind übrigens der Grund, warum ich die FreeBSD-Ports noch nicht auf
diese Version angepasst habe.  Wenn, dann werde ich aber dort auf
jeden Fall erstmal zweigleisig fahren, d. h. der Nutzer kann sich
wählen, ob er 4.2.x oder 4.3.x nimmt.

von 900ss (900ss)


Lesenswert?

Jörg Wunsch wrote:
>
> Wie schon öfter geschrieben, wenn jemand mit Windows-Installern und
> deren Konfiguration bewandert ist, dann wäre das eine Stelle, an der
> er Eric gut unterstützen kann.
>

Ähhh, das verstehe ich nicht. Er hat doch in dem verwendeten Installer 
die Möglichkeit, Optionen anzubieten ([ ] Notepad z.B.). Warum kann er 
da nicht genauso die Option einbauen, welchen AVR-GCC man verwenden 
möchte? Wenn das AVR-GCC-Paket so getrennt werden kann, dann ist es doch 
ein leichtes diese 2 Optionen dort zu integrieren. Oder hat ihm jemand 
anderes dieses jetzigen Installer so hingebogen, dass er ihn "stumpf" 
nur verwendet, ihn aber nicht ändern kann? -kopfkratz-

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


Lesenswert?

Ich vermute mal, dass er den jetzigen Installer von jemandem anders
so gebogen bekommen hat.  Keine Ahnung.

von Santiago (Gast)


Lesenswert?

Hallo,

habe jetzt die Tabellen und Funktionen nochmal etwas umstrukturiert, 
sodass ich jetzt "nur" in einer Funktion inline assembler haben muss.

Leider bekomme ich eine Fehlermeldung.
Ich vermute mal, dass es mit dem Zeiger (pWaveTable) zusammenhängt, 
jedoch komme ich auch mit den Inline-Assembler-Tutorials nicht weiter.

Dabei dachte ich schon, ich hätte die Parametergeschichte verstanden :(
1
void genSine()      { genWave(sine);     }
2
void genSawtooth()  { genWave(sawtooth); }
3
void genTriangle()  { genWave(triangle); }
4
void genSquare()    { genWave(square);   }
5
6
void genWave(uint8_t * pWaveTable) {
7
  __asm volatile (
8
    // setup wavetable address
9
    "ldi  r31, hi8([pWaveTable])  ; setup Z pointer hi"           "\n\t"
10
    "ldi  r30, lo8([pWaveTable])  ; setup Z pointer lo"           "\n\t"
11
12
    // clear accumulator 
13
    "ldi   r29, 0x00              ; clear accumulator"            "\n\t" 
14
    "ldi   r28, 0x00              ; clear accumulator"            "\n\t" 
15
16
    "set                          ; work until T-flag is cleared" "\n\t"
17
18
    "0:"                                                          "\n\t"
19
    "add  r28, %A[step]           ; 1"                            "\n\t"
20
    "adc  r29, %B[step]           ; 1"                            "\n\t"
21
    "adc  r30, %C[step]           ; 1"                            "\n\t"
22
    "lpm                          ; 3"                            "\n\t"
23
    "out  %[port], r0             ; 1"                            "\n\t"
24
    "brts  0b                     ; 2 => 9 cycles"
25
26
/* out     */:
27
/* in      */: [step] "r" (work.step), [pWaveTable] "z" (pWaveTable), [port] "n" (_SFR_IO_ADDR (PORT_DAC)) 
28
/* clobber */: "r28", "r29", "r30", "r31"
29
   );
30
}
Da der Zähler im Rest des Proggies ohne Bedeutung ist, habe ich keine 
Ladebefehle oder ähnliches.

So gesehen habe ich keinerlei Ausgabe, deshalb ist der entsprechende 
Bereich leer.
Die Fehlermeldung lautet:
1
../DDS.c:32: error: can't find a register in class 'POINTER_Z_REGS' while reloading 'asm'

Für Hilfe / Aufklärung wäre ich sehr dankbar.

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


Lesenswert?

Wenn du ihm r30/r31 als clobbers deklarierst, wie soll er dann in
der Lage sein, für sich noch einen Z-Pointer zu benutzen?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Zudem: Z wird bereits mit pWaveTable vorgeladen. Die ersten beiden 
Instruktionen sind überflüssig (und zudem nicht korrekt).

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


Lesenswert?

r28/r29 würde ich auch nicht hart codieren, sondern dem Compiler
die Wahl überlassen.  Es kann gut sein, dass er für dieses
Zeigerregisterpaar eine bessere Verwendung hätte, die durch die
Clobber-Deklaration unnütz behindert wird.  Für die eigentliche
Berechnung darf es ja jedes beliebige Register >= 16 sein.

von Santiago (Gast)


Lesenswert?

Hallo,

dank Euch, für Eure Unterstützung.

> Wenn du ihm r30/r31 als clobbers deklarierst, wie soll er dann in
> der Lage sein, für sich noch einen Z-Pointer zu benutzen?

Ok, da haben wohl meine Englisch-Kenntnisse nicht ausgereicht.
Ich hatte es so verstanden, dass man die Register bei clobber angibt, 
die durch den Assemblercode verändert werden.

> Zudem: Z wird bereits mit pWaveTable vorgeladen. Die ersten beiden
> Instruktionen sind überflüssig (und zudem nicht korrekt).

Ok, habe sie auskommentiert und die clobbers gelöscht (Fehlermeldungen 
sind wech).
Nur der Vollständigkeit halber: wie müsste ich denn einen C-pointer 
richtig übergeben/übernehmen?

> r28/r29 würde ich auch nicht hart codieren ...
> Für die eigentliche
> Berechnung darf es ja jedes beliebige Register >= 16 sein.

In Bezug auf die Berechnung stimmt das sicher. Nur hatte ich es so 
verstanden, das lpm die Register r28, r29 und r30 brauchen würde.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Santiago wrote:
> Hallo,
>
> Ich hatte es so verstanden, dass man die Register bei clobber angibt,
> die durch den Assemblercode verändert werden.

Nein. Man muss dem Compiler mitteien, welche Register verändert werden. 
Das kann über clobbers oder über Constraints wie "=r", "+r" oder "=&r" 
geschehen. R1 muss man von Hand wieder herstellen falls verändert, da es 
nicht vom Compiler verwaltet wird.

> Nur der Vollständigkeit halber: wie müsste ich denn einen C-pointer
> richtig übergeben/übernehmen?

Oben gibt's genügend Beispiel-Schnippsel wie's geht.

> In Bezug auf die Berechnung stimmt das sicher. Nur hatte ich es so
> verstanden, das lpm die Register r28, r29 und r30 brauchen würde.

Das Handbuch verrät Dir zu LPM, daß es r30 und r31 (z-Reg) als Adresse 
verwendet.

von Santiago (Gast)


Lesenswert?

>> In Bezug auf die Berechnung stimmt das sicher. Nur hatte ich es so
>> verstanden, das lpm die Register r28, r29 und r30 brauchen würde.
>
> Das Handbuch verrät Dir zu LPM, daß es r30 und r31 (z-Reg) als Adresse
> verwendet.

Stimmt. Ich bin verwirrt :{
... dachte ich hätte das Programm verstanden.

Das Assembler-Listing (*.lss) verwirrt mich noch mehr:
1
00000034 <tab>:
2
  34:  00 ca 9a 3b 00 e1 f5 05 80 96 98 00 40 42 0f 00     ...;........@B..
3
  44:  a0 86 01 00 10 27 00 00 e8 03 00 00 64 00 00 00     .....'......d...
4
  54:  0a 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00     ................
5
  ...
6
7
00000100 <sine>:
8
 100:  80 83         st  Z, r24
9
 102:  86 89         ldd  r24, Z+22  ; 0x16
10
 104:  8c 8f         std  Y+28, r24  ; 0x1c
11
 106:  92 95         swap  r25
12
 108:  98 9c         mul  r9, r8
tab ist eine Tabelle aus C heraus angelegt, sine die Tabelle über 
inline-Assembler angelegt. Die Hex-Werte stimmen wohl, aber irgendwie 
scheint die Tabelle für den Compiler Programm statt Daten zu sein?!?

Was habbich da wieder verbockt?

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


Lesenswert?

Nicht für den Compiler, aber für den Disassembler: sie ist als zu
.text gehörig deklariert, damit wird sie disassembliert.  Richtig
wäre .progmem.data (oder so ähnlich).

Ist aber ein reiner Schönheitsfehler.

von Santiago (Gast)


Lesenswert?

> Nicht für den Compiler, aber für den Disassembler: sie ist als zu
> .text gehörig deklariert...

Ich habe es wie hier Beitrag "Re: Würde gerne Assembler in C umschreiben, aber ich blick's net" 
gemacht. Aber ok - wenn nur Schönheitsfehler, solls mir recht sein :)

> r28/r29 würde ich auch nicht hart codieren, sondern dem Compiler
> die Wahl überlassen.

Das habe ich jetzt so umgesetzt, dass ich eine 2Byte-Variable definiert 
und die als Parameter übergeben habe.
Kann man das Gleiche auch ohne lokale Variable erreichen?

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


Lesenswert?

Santiago wrote:

> Das habe ich jetzt so umgesetzt, dass ich eine 2Byte-Variable definiert
> und die als Parameter übergeben habe.

Ja, so hätte ich das auch gemacht, wobei du natürlich auch zwei
einzelne Variablen hättest nehmen können.  Es gibt ja keinen
richtigen Grund, dass beide Register hinteinander liegen müssen.

> Kann man das Gleiche auch ohne lokale Variable erreichen?

Nein, da du dem Compiler ja die Auswahl überlassen willst (nur dann
kann er das alles in sein Optimierungskonzept einbeziehen), braucht
er ein eigenes Objekt, an dem er das festmachen kann.  Das ist also
völlig OK so.  Diese lokalen Variablen sind ja nur Platzhalter für
die Register, die der Compiler dafür einsetzen muss und benötigen
sonst keine anderweitigen Ressourcen.

von Santiago (Gast)


Angehängte Dateien:

Lesenswert?

> Diese lokalen Variablen sind ja nur Platzhalter für
> die Register, die der Compiler dafür einsetzen muss und benötigen
> sonst keine anderweitigen Ressourcen.

Wunderbar. Gut zu wissen. Danke für die Aufklärung.

Bei dem nackichen ISR bekomme ich einen internen Compiler-Fehler (gleich 
nach der prophezeiten Warnung).
Habe sowohl ISR, als auch SIGNAL probiert - kein Unterschied.

Ich hänge mal das ganze Proggy an.
Vielleicht mag ja jemand mal drüber schaun.

Für Kritik und/oder Anregung bin ich immer zu haben :)

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


Lesenswert?

Ich habe mir mal ein Makefile dazu erfinden lassen von Mfile, da
bekomme ich:
1
In file included from main.c:6:
2
DDS.h:27: warning: function declaration isn't a prototype
3
DDS.h:28: warning: function declaration isn't a prototype
4
DDS.h:29: warning: function declaration isn't a prototype
5
DDS.h:30: warning: function declaration isn't a prototype
6
In file included from main.c:7:
7
Data.h:32: warning: function declaration isn't a prototype
8
Data.h:33: warning: function declaration isn't a prototype
9
In file included from main.c:8:
10
Counter.h:8: warning: function declaration isn't a prototype
11
In file included from main.c:9:
12
UI.h:42: warning: function declaration isn't a prototype
13
In file included from main.c:10:
14
SPI.h:15: warning: function declaration isn't a prototype
15
main.c:13: warning: function declaration isn't a prototype
16
main.c:19: warning: function declaration isn't a prototype
17
main.c:19: warning: return type of 'main' is not 'int'
18
main.c: In function 'main':
19
main.c:26: error: 'run' undeclared (first use in this function)
20
main.c:26: error: (Each undeclared identifier is reported only once
21
main.c:26: error: for each function it appears in.)
22
main.c: At top level:
23
main.c:81: warning: function declaration isn't a prototype
24
make: *** [main.o] Error 1

Ziemlich viele Warnungen, und insbesondere scheint ja noch was zu
fehlen.

Bezeichner, die mit einem Unterstrich anfangen, sind übrigens tabu
für dich als Anwender, vermeide also besser sowas:
1
#ifndef __DDS_DATA_H__
2
#define __DDS_DATA_H__
3
...
4
#endif

Schreibe stattdessen:
1
#ifndef DDS_H
2
#define DDS_H
3
...
4
#endif

(Es ist mehr als sinnvoll, dass der Name dieses Makros mit dem Namen
der Datei übereinstimmt.)

Sowas solltest du auch vermeiden:
1
#ifndef __PGMSPACE_H_
2
#include <avr/pgmspace.h>
3
#endif

Erstens hast du dich sowieso vertan (PGMSPACE_H hat vorn und hinten
je zwei Unterstriche), zweitens sind ja eben diese Makros dafür da,
dass nichts passiert, wenn man eine Headerdatei mehr als einmal
includet.

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


Lesenswert?

Jörg Wunsch wrote:

>
1
...
2
> main.c: In function 'main':
3
> main.c:26: error: 'run' undeclared (first use in this function)
4
...
5
>

Wenn ich die Zeile rauskommentiere, compiliert es zumindest durch.
(AVR-GCC 4.2.2, -mmcu=atmega48, -Os)

von Santiago (Gast)


Lesenswert?

> Ich habe mir mal ein Makefile dazu erfinden lassen von Mfile, da
> bekomme ich:

Hm - hast Du da einen besonderen "strict" mode eingeschaltet?
Ich wüßte nicht, was da falsch sein soll.

Dass noch eine Zuweisung auf "run" in main vorhanden ist - gut, soweit 
kam der Compiler bei mir nicht. Kann ersatzlos gestrichen werden (ist ja 
jetzt das T-Flag)

> Sowas solltest du auch vermeiden:
>
> #ifndef __PGMSPACE_H_
> #include <avr/pgmspace.h>
> #endif
>
> Erstens hast du dich sowieso vertan (PGMSPACE_H hat vorn und hinten
> je zwei Unterstriche), zweitens sind ja eben diese Makros dafür da,

Sorry, aber da bin ich anderer Meinung.
Mein Gelerntes mag ja bei kleinen Projekten überflüssig sein, dennoch 
denke ich, dass es heute noch genauso gültig ist, wie vor 10 Jahren. Ich 
habe es jedenfalls von Lakos: "large scale C++ software design" gelernt 
und seither ist es in den Fingern drin. Tut ja auch niemand weh.

> Bezeichner, die mit einem Unterstrich anfangen, sind übrigens tabu
> für dich als Anwender, vermeide also besser sowas:

Hm, dass ich Anfänger bei Mikrokontrollern und Hardware bin, bedeutet 
nicht, dass ich auch Anfänger in C wäre. Auch wenn ich sicher mehr C++ 
als C codiert habe.

... und Nein! ich habe mich nicht verschrieben.
Ich habe extra in den Dateien nachgeschaut. Dort sind 2 Unterstriche am 
Anfang und einer am Ende.
Zumindest in der Version die ich habe (WinAVR vom 12.01.07)

von Santiago (Gast)


Lesenswert?

Ok, unsere Posts haben sich wohl überschnitten.

Ich verwende WinAVR im AVR-Studio (weil ich dort gleich debuggen kann).
Unter linux habe ich das Ganze noch nicht probiert.

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


Lesenswert?

Santiago wrote:

> Hm - hast Du da einen besonderen "strict" mode eingeschaltet?

-Wall -Wextra

> Ich wüßte nicht, was da falsch sein soll.

Eine Deklaration im Stil der 1980er Jahre.

> Sorry, aber da bin ich anderer Meinung.

Warum?

Du testet ein nicht dokumentiertes Feature.  Der Name des
Makros _PGMSPACE_H_ ist ,,Eigentum'' der avr-libc und kann
in dieser kommentarlos im nächsten Release ganz anders heißen,
da er 1.) zum implementation name space gehört und 2.) von der
Dokumentation nicht beschrieben ist, also keinerlei offizielles
Interface darstellt.

> Mein Gelerntes mag ja bei kleinen Projekten überflüssig sein, dennoch
> denke ich, dass es heute noch genauso gültig ist, wie vor 10 Jahren.

Es war vor 10 Jahren schon genauso falsch, aus gleichen Gründen
wie heute. ;-)

Wenn du partout denkst, dass die Tests in den Bibliotheksheadern
unzureichend für deine Belange sind, dann müsstest du dir deinen
eigenen Test drumrum schreiben:
1
#ifndef PGMSPACE_H
2
#  include <avr/pgmspace.h>
3
#define PGMSPACE_H
4
#endif

Ich sehe aber keinen Gewinn.  In einem Zeitalter, in dem selbst
Windows einen brauchbaren filesystem cache hat (anders als MS-DOS
seinerzeit) kostet das zusätzliche Öffnen der Datei praktisch
nichts, und hernach würde sie der Präprozessor ohnehin komplett
überspringen.

> Ich habe extra in den Dateien nachgeschaut. Dort sind 2 Unterstriche am
> Anfang und einer am Ende.

Ja, da gehören sie auch hin.  avr-libc versteht sich selbst als
Teil des application name space, schließlich ist sie die Standard-
bibliothek zum AVR-GCC.  Sie muss sich natürlich noch mit dem GCC
,,absprechen'' (in irgendeiner Form), damit die reservierten
Bezeichner zwischen Bibliothek und Compiler keine Konflikte
verursachen, aber das ist für den Endanwender nicht von Belang.
Du bist Endanwender, damit solltest du bitte keine reservierten
Bezeichner in deinem Code benutzen, es sei denn, die Dokumentation
beschreibt dies in irgendeiner Form (wie z. B. _BV() oder
__STDIO_FDEVOPEN_COMPAT_12).  In letztgenanntem Falle fühlt sich
dann aber avr-libc auch verpflichtet, dass diese Bezeichner nicht
im nächsten Release gleich mal wieder ihren Namen wechseln oder
eine ganz andere Bedeutung bekommen.

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


Lesenswert?

Jörg Wunsch wrote:

>> Ich habe extra in den Dateien nachgeschaut. Dort sind 2 Unterstriche am
>> Anfang und einer am Ende.
>
> Ja, da gehören sie auch hin.

**plong**  Jetzt sehe ich, was du meinst.  Das ist ein Bug, und damit
hast du übrigens schon einen Grund, warum das möglicherweise in
einer späteren avr-libc-Version geändert werden könnte: wir könnten
uns entschließen, die Namen der idempotency-Makros innerhalb der
gesamten Bibliothek zu vereinheitlichen.  Im Moment ist da leider
recht viel Wildwuchs, wie ich gerade sehe. :-(

von Santiago (Gast)


Lesenswert?

> Du testet ein nicht dokumentiertes Feature.  Der Name des
> Makros PGMSPACE_H ist ,,Eigentum'' der avr-libc und kann
> in dieser kommentarlos im nächsten Release ganz anders heißen,
> da er 1.) zum implementation name space gehört und 2.) von der
> Dokumentation nicht beschrieben ist, also keinerlei offizielles
> Interface darstellt.
>
>> Mein Gelerntes mag ja bei kleinen Projekten überflüssig sein, dennoch
>> denke ich, dass es heute noch genauso gültig ist, wie vor 10 Jahren.
>
> Es war vor 10 Jahren schon genauso falsch, aus gleichen Gründen
> wie heute. ;-)

Mit dem nicht dokumentierten Viehtscher hast Du natürlich recht.
Deshalb habe ich mir auch angewöhnt, in den Dateien nach zu schauen.

Zum Punkt Richtig/Falsch nur soviel:
Wenn Du mal wieder in einem gut sortierten Buchladen bist, nimm Dir doch 
einfach oben genanntes Buch zur Hand und schau mal "Figure 2-9" und 
"Figure 2-10". Sind imho beeindruckende Fakten.
Das Buch wurde meines Wissens erst Mitte der 90er geschrieben - also nix 
mit DOS und Co :)

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


Lesenswert?

Buchladen habe ich gerade nicht am Weg liegen.  Wenn ich nicht
gerade zu faul wäre, würde ich in unserem aktuellen Mammutprojekt
mal die entsprechenden ifdefs reinbauen.  Obwohl da ziemlich viel
Spaghetti mit den includes drin ist (dessen Aufräumen tatsächlich
sehr viel wichtiger wäre als das Anbringen weiterer ifdefs, aber
selbst dafür haben wir keine rechte Zeit), sollte es mich arg
wundern, wenn mehr als 5 % Geschwindigkeitszuwachs beim Compilieren
davon zu erhalten wären.  Das liegt unter der Wahrnehmbarkeitsgrenze.

Zurück zum Subject: vielleicht legst du ja dein Makefile nochmal
mit hin.  Wo genau bekommst du den internal compiler error (ICE)?
Welche Compilerversion (avr-gcc -v)?

von 900ss (900ss)


Lesenswert?

Santiago wrote:
1
#ifndef PGMSPACE_H
2
#  include <avr/pgmspace.h>
3
#define PGMSPACE_H
4
#endif

Warum willst Du denn unbedingt diesen Test implementieren, wenn er in 
der Header-Datei auch drin ist? Das verstehe ich nicht. Hast Du Angst, 
dass der schon implementierte Test schlechter funktioniert oder manchmal 
nicht?
Und das zitierte Buch werde ich mir nicht extra kaufen :-)

von Santiago (Gast)


Angehängte Dateien:

Lesenswert?

Leutz - ich will jetzt hier nicht als Bekehrer auftreten. Ich habe meine 
Überzeugung und andere haben andere Überzeugungen :)
Das halte ich auch für völlig in Ordnung.

Wie ich oben schon schrieb, denke ich nicht, dass die Auswirkungen bei 
einem µC-Projekt merkbar wären, schaden tut es aber auch niemand.
Nur soviel: ich bin als Teamplayer aufgezogen worden und habe viel in 
Teams > 15 Personen gearbeitet. Dort werden so scheinbare 
Lächerlichkeiten plötzlich lebensnotwendig, bzw. lassen sich sogar in 
unproduktiver Arbeitszeit ausrechnen.

> Wo genau bekommst du den internal compiler error (ICE)?
1
../init.c:68: internal compiler error: in start_function, at c-decl.c:6014

> Welche Compilerversion (avr-gcc -v)?
1
Win> avr-gcc -v
2
Using built-in specs.
3
Target: avr
4
Configured with: ../gcc-4.1.1/configure --prefix=/c/WinAVR --target=avr --enable-languages=c,c++ --with-dwarf2 --enable-win32-registry=WinAVR --disable-nls --disable-libssp --disable-fixincludes --disable-libada --with-gnu-ld --with-gnu-as --enable-doc
5
Thread model: single
6
gcc version 4.1.1 (WinAVR 20070122)

Anbei auch die Makedatei.

@Jörg:
Danke für Deine Zeit und Unterstützung!

von Santiago (Gast)


Angehängte Dateien:

Lesenswert?

OT:

Ich habe die entsprechende Seite mal abfotografiert und angehängt.
Auch wenn die Zeiten aus den 90er Jahren stammen, die prozentualen 
Unterschiede dürften auch heute noch gelten.

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


Lesenswert?

Santiago wrote:

> Wie ich oben schon schrieb, denke ich nicht, dass die Auswirkungen bei
> einem µC-Projekt merkbar wären, schaden tut es aber auch niemand.

Naja, das von dem ich schrieb ist auch etwas größer als das Mini-DDS,
und dürfte irgendwas zwischen 50 und 100 Quelldateien haben.  Hab
sie nicht gezählt.  Ist übrigens auch in einem Team von bis zu 10
Leuten entstanden.  Wie gesagt, es krankt an anderen Dingen als an
der Zeit, doppelte Headerdateien zu öffnen um festzustellen, dass
man die gar nicht braucht. ;-)

>> Wo genau bekommst du den internal compiler error (ICE)?
>
1
> ../init.c:68: internal compiler error: in start_function, at
2
> c-decl.c:6014
3
>

Achso.  Das trat aus irgendwelchen Gründen immer nur bei Erics
Windows-Version auf, auf Unixen konnte ich das nie nachvollziehen.
Das ist hier:
1
void __attribute__((naked)) PCINT0_vect(void) {
2
   __asm volatile ("clt"            "\n\t"
3
                   "rjmp wakeup");
4
}

Davon abgesehen, dass du meines Erachtens viel besser bedient wärst,
all diese in C mächtig verrenkt aussehenden Assembler-Teile in
eine eigene Assembler-Quelldatei zu schreiben, für diesen ICE gibt's
einen einfachen Workaround: man trenne die Deklaration von der
Definition der Funktion.
1
void __attribute__((naked)) PCINT0_vect(void);
2
void PCINT0_vect(void) {
3
   __asm volatile ("clt"            "\n\t"
4
                   "rjmp wakeup");
5
}

Aber: mit dem aktuellen ISR-Makro geht das viel eleganter:
1
ISR(PCINT0_vect, ISR_NAKED) {
2
  __asm volatile ("clt"            "\n\t"
3
                   "rjmp wakeup");
4
}

(Das volatile ist hier übrigens unnötig: der Compiler könnte die
Anweisung sowieso nirgendwo anders hin schieben.)

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


Lesenswert?

Santiago wrote:

> Ich habe die entsprechende Seite mal abfotografiert und angehängt.
> Auch wenn die Zeiten aus den 90er Jahren stammen, die prozentualen
> Unterschiede dürften auch heute noch gelten.

Danke erstmal.  Schade, dass es nirgends einen runterladbaren Satz
an Quelldateien gibt, mit denen man das ausprobieren könnte. ;-)
Ich müsste mir mal ein aktuelles C++-Projekt ansehen und versuchen,
ob man die Guards da automatisch reinbauen kann.  Was meiner
Vermutung ist: das war noch der alte CFront, und der hat ja die
gesamte C++-zu-C-Umsetzung in einem Präprozessor erledigt.  In
heutiger Zeit verbraucht zwar der Compiler bei C++ immer noch massig
viel Zeit, aber der Präprozessoranteil wird vergleichsweise gerade
bei C++ eher verschwindend gering sein (bei C ist er relativ noch
höher).

von Santiago (Gast)


Lesenswert?

Moin Jörg,

> für diesen ICE gibt's
> einen einfachen Workaround: man trenne die Deklaration von der
> Definition der Funktion.

Yepp, das war's. Musste hier (leider?) die erste Variante nehmen, das 
erweiterte ISR-Makro scheint in meiner Version von WinAVR noch ned drin 
zu sein.

> Davon abgesehen, dass du meines Erachtens viel besser bedient wärst,
> all diese in C mächtig verrenkt aussehenden Assembler-Teile in
> eine eigene Assembler-Quelldatei zu schreiben

Ich habe jetzt alle möglichen Varianten durchprobiert und bei meinem 
Kenntnisstand der Materie ist es die einzige Variante die funktioniert.
Deshalb halte ich mich an die alte Weisheit:
> First make it work, then optimize.

Ich würde es auch lieber anders machen, aber mir fehlt noch zuviel - 
unter anderem die Sicherheit, es richtig zu machen.

Immerhin habe ich jetzt - dank Eurer Hilfe - einen Stand erreicht, bei 
dem ich mit der HW-Entwicklung loslegen könnte. Mal sehen, ob es dann so 
tuht wie erwartet :)

von Santiago (Gast)


Lesenswert?

> In heutiger Zeit verbraucht zwar der Compiler bei C++ immer noch massig
> viel Zeit, aber der Präprozessoranteil wird vergleichsweise gerade
> bei C++ eher verschwindend gering sein (bei C ist er relativ noch
> höher).

Der Zeitgewinn beschäftigt sich nicht mit dem Fall, dass der 
Präprozessor was zu tun hätte, sondern genau mit dem Gegenteil. Und da 
ist die Sprache rel. unerheblich. In C++ gibt es idR viel mehr Header 
als in einen C-Projekt.
Selbst wenn ein Header schon verarbeitet wurde, wenn die 
include-Anweisung erneut auftaucht, kennt der Präprozessor zwar den 
Blocker, er muss aber dennoch jede Zeile überprüfen. Schließlich kann 
der Präprozessor nicht davon ausgehen, dass die ganze Datei geblockt 
wird. Es könnte zwischendrin auch ein aktiver Part existieren.
Er muss also mindestens einen Logikbaum erstellen und dann verwerfen.

> Ich müsste mir mal ein aktuelles C++-Projekt ansehen und versuchen,
> ob man die Guards da automatisch reinbauen kann.

Perl kann bei solchen Aufgaben wahre Wunder bewirken :)

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


Lesenswert?

Santiago wrote:

> Yepp, das war's. Musste hier (leider?) die erste Variante nehmen, das
> erweiterte ISR-Makro scheint in meiner Version von WinAVR noch ned drin
> zu sein.

Dann nimm eine neuere.  Die Weihnachtsversion 2007 ist OK, es gibt 
keinen
echten Grund, stattdessen eine nochmal ein Jahr ältere zu nehmen.

>> Davon abgesehen, dass du meines Erachtens viel besser bedient wärst,
>> all diese in C mächtig verrenkt aussehenden Assembler-Teile in
>> eine eigene Assembler-Quelldatei zu schreiben

> Ich habe jetzt alle möglichen Varianten durchprobiert und bei meinem
> Kenntnisstand der Materie ist es die einzige Variante die funktioniert.

Ich habe gerade erst richtig geschnallt, was du damit überhaupt
machst.  Das ist eigentlich das Musterbeispiel für ISR_ALIASOF().
Damit kann man das kurz und prägnant so ausdrücken:
1
ISR(PCINT0_vect) {
2
   asm volatile ("clt");
3
   listen2User(1);
4
}
5
6
ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect));
7
ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect));

Das spart dann sogar noch deine extra RJMPs:
1
Disassembly of section .text:
2
3
00000000 <__vectors>:
4
   0:   3e c3           rjmp    .+1660          ; 0x67e <__ctors_end>
5
   2:   58 c3           rjmp    .+1712          ; 0x6b4 <__bad_interrupt>
6
   4:   57 c3           rjmp    .+1710          ; 0x6b4 <__bad_interrupt>
7
   6:   cf c3           rjmp    .+1950          ; 0x7a6 <__vector_3>
8
   8:   ce c3           rjmp    .+1948          ; 0x7a6 <__vector_3>
9
   a:   cd c3           rjmp    .+1946          ; 0x7a6 <__vector_3>
10
...
11
  20:   d4 c3           rjmp    .+1960          ; 0x7ca <__vector_16>
12
...

Alle drei Vektoren zeigen auf die gleiche Routine.

> Deshalb halte ich mich an die alte Weisheit:
> First make it work, then optimize.

Apropos optimize: wenn du listen2User() static inline machst, spart
das die elendige push/pop-Orgie im PCINT-Handler und insgesamt um
die 50 Bytes an Code, trotz der inline-Erweiterung an mehreren
Stellen.  Leider ignoriert der Compiler das inline bei -Os, man
muss entweder eine höhere Optimierungsstufe wählen oder die Funktion
noch mit __attribute__((always_inline)) zu ihrem Glück zwingen.

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


Lesenswert?

Santiago wrote:

> Selbst wenn ein Header schon verarbeitet wurde, wenn die
> include-Anweisung erneut auftaucht, kennt der Präprozessor zwar den
> Blocker, er muss aber dennoch jede Zeile überprüfen. Schließlich kann
> der Präprozessor nicht davon ausgehen, dass die ganze Datei geblockt
> wird. Es könnte zwischendrin auch ein aktiver Part existieren.
> Er muss also mindestens einen Logikbaum erstellen und dann verwerfen.

Er muss nur das Auftreten weiterer Präprozessor-Direktiven scannen.
Die ganze u. U. recht komplexe Syntax der ausgeblendeten Teile darf
er sich klemmen.

Hab's gerade mal probiert: #if 0 ... #endif, dazwischen per copy&paste
,,Datenmüll'' aus einem Terminal reinkopiert.  Kein Problem.

Möglicherweise hat der alte CFront das aus irgendwelchen Gründen nicht
gemacht (nicht machen können?), keine Ahnung.  Das Alter des Buches
lässt in Zusammenhang mit der Analyse des Autors jedenfalls darauf
schließen, dass das Wort `preprocessor' dort weniger den klassischen
C-Präprozessor als mehr das CFront meint, das damals noch gängige
Technologie für die C++-Implementierung war.

>> Ich müsste mir mal ein aktuelles C++-Projekt ansehen und versuchen,
>> ob man die Guards da automatisch reinbauen kann.
>
> Perl kann bei solchen Aufgaben wahre Wunder bewirken :)

Jaja, schon klar, braucht trotzdem Gehirnschmalz.

von Santiago (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

habe inzwischen ein update auf winAVR-071221 gemacht, die Anregungen 
umgesetzt, die Steine des Anstoßes beseitigt und die fehlende 
Funktionalität eingebaut ...

Jetzt bekomme ich Warnungen, mit denen ich nix anfangen kann und die ich 
auch keinem Fehler meinerseits zuordnen kann.
1
Util.s:203: Warning: expression dangerous with linker stubs

Wäre schön, wenn jemand nomml drüberschauen könnte.

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


Lesenswert?

Santiago wrote:
1
Util.s:203: Warning: expression dangerous with linker stubs

Bitte drücke gedanklich die Taste ,,Ingnorieren''.  Ich bin in der
Pflicht, den Bugfix dafür endlich mal zu testen... (der weiter nichts
macht, als die Meldung rauszuwerfen).

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.