Hiho,
Leider hab ich nach längerem suchen herausgefunden, das es nicht möglich
ist ein "unsigned long long" mit sprintf/printf in ein Char Array zu
schreiben um dieses auf der UART auszugeben. Nun hab ich dies durch
folgenden C Code realisiert:
void uart_ulltoa(unsigned long long val)
{
char Buffer[50];
char tmp;
int i=0;
while(val > 0)
{
Buffer[i++]='0'+val%10;
val/=10;
}
for(int j = 0; j < i / 2; ++j ) {
tmp = Buffer[j];
Buffer[j] = Buffer[i-j-1];
Buffer[i-j-1] = tmp;
}
Buffer[i] = 0;
uart_puts(Buffer);
}
Funktioniert auch sehr gut soweit, das Problem dabei ist nur das die
Funktion 8kbyte(!) Flash sowie ~250byte Ram benötigt. Das problem liegt
in folgender Zeile: val/=10;
Gibt es eine Möglichkeit dies nun noch auf "speicher" zu optimieren? CPU
last wäre mir realitiv egal.
Vielen Dank
Blonkel
blonkel schrieb:> sowie ~250byte Ram benötigt.
Genauer gesagt: 256 Bytes. Die kommen aus einer Bibliotheksfunktion,
sie werden für das Array __clz_tab[] belegt. Wenn man danach gugelt,
findet man, dass die Gleitkommabibliothek das zwar für die
Gleitkommadivision ausgeräumt hat, aber offenbar schleppt's der GCC
beim (wenig optimierten) long-long-Code dann wieder mit ein. Du
müsstest wohl die Funktion __udivdi3() reimplementieren, um das
loszuwerden.
p.s.: "Bitte den Controllertyp im Titel mit angeben."
Wenn du sonst keinerlei Division mit "unsigned long long" in deinem Code
brauchst, gibt es einen Weg, die Division (und damit die Einbindung der
Routinen/Tabellen) komplett zu vermeiden. Dazu ist ein kleiner Umweg
über die Packed-BCD-Darstellung notwendig. Gebraucht werden dann (auf
AVR) nur die Register plus ein wenig Stack zum Sichern der benutzten
Register.
Zunächst wird dazu die Zahl komplett in Packed BCD umgewandelt. Dazu
musst du den Algorithmus zu bin2BCD16 aus folgender Appnote verwenden:
http://www.atmel.com/dyn/resources/prod_documents/doc0938.pdf
Die Erweiterung von 16 Bit Input, 3 Byte Output auf 64 Bit Input, 10
Byte Output (2^64-1 hat 20 Dezimalstellen, also 10 BCD-Bytes) ist
ziemlich "straight forward".
Sinnvoll implementieren lässt sich der Algorithmus IMO eigentlich nur in
Assembler, da dabei fast nur auf Bitebene mit Einbeziehung von Carry
gearbeitet wird. Wenn ich mich beim Überschlagen gerade nicht grob
vertan habe, dürfte dieser Part dann so um die 10000 Takte plus/minus
brauchen.
Wenn es nicht auf das letzte Code-Byte ankommt, würde ich die in der
Appnote im Flussdiagramm dargestellte innere Schleife (das untere
Drittel) übrigens nicht als Schleife schreiben, sondern komplett
ausrollen (die Zahl der Durchläufe ist immer konstant). Nur so können
auch die kompletten Outputbytes in Registern gehalten werden.
Anschliessend muss dann noch von BCD nach ASCII umgewandelt werden, das
braucht nur noch Byte-Division bzw. -Modulo mit 16, also per
Bitoperationen zu erledigen. Hierbei könnte es zweckmässig sein, die
generierten Zeichen einfach direkt in den UART zu schieben, anstatt erst
einen String zu schreiben und den dann an uart_puts() zu übergeben.
Andreas
Jörg Wunsch schrieb:> ...aber offenbar schleppt's der GCC> beim (wenig optimierten) long-long-Code dann wieder mit ein...
Wenig optimiert bezieht sich hier wohl schätz ich nicht auf meine
Optimierungstufe sondern auf das verabeiten von ull's von gcc?
> p.s.: "Bitte den Controllertyp im Titel mit angeben."
Tut mir leid, es handelt sich um hierbei um einen AVR atmega162.
Andreas Ferber schrieb:> Sinnvoll implementieren lässt sich der Algorithmus IMO eigentlich nur in> Assembler, da dabei fast nur auf Bitebene mit Einbeziehung von Carry> gearbeitet wird. Wenn ich mich beim Überschlagen gerade nicht grob> vertan habe, dürfte dieser Part dann so um die 10000 Takte plus/minus> brauchen.
Vielen Dank für die vorgeschlagene Lösung, einziger Nachteil dabei ist
das meine Assembler Kenntnisse ein wenig zurück liegen. Sollte es
dennoch keine Alternative geben, werde ich wohl den oben genannten
Algorithmus implementieren.
blonkel schrieb:> Jörg Wunsch schrieb:>> ...aber offenbar schleppt's der GCC>> beim (wenig optimierten) long-long-Code dann wieder mit ein...> Wenig optimiert bezieht sich hier wohl schätz ich nicht auf meine> Optimierungstufe sondern auf das verabeiten von ull's von gcc?
Ja, auf die interne Bibliothek (libgcc.a). Deren 64-bit-Code ist
einfach stur maschinengeneriert, während viele andere Dinge (32-bit
integer und floating-point) handoptimierten oder manuell
nachgebesserten Code benutzen.
blonkel schrieb:> dennoch keine Alternative geben, werde ich wohl den oben genannten> Algorithmus implementieren.
Die unsigned long long überhaupt loszuwerden, geht nicht?
Was ist mit Subtraktionen, brauchen die weniger?
Karl heinz Buchegger schrieb:> Was ist mit Subtraktionen, brauchen die weniger?
Ich denke schon, Addition und Subtraktion lassen sich ja relativ
geradlinig implementieren.
Dieses __clz_bits[] ist irgendwie ein Array, das das Zählen der
führenden Nullen ("count leading zeros") vereinfachen soll, wobei
halt die Implementierer wohl davon ausgegangen waren, dass das Opfern
von 256 Bytes RAM für den erzielten Geschwindigkeitsgewinn (auf
einem PC) "Peanuts" sind. Dummerweise gibt's offenbar halt keine
Möglichkeit, das pro Target umzuschalten, und erst recht fehlt die
Infrastruktur im GCC, um für verschiedenen -Os-Stufen verschiedene
libgcc's zu linken, die ihrerseits wiederum selbst der jeweiligen
Optimierungsstufe gerecht werden.
Jörg Wunsch schrieb:> Karl heinz Buchegger schrieb:>>> Was ist mit Subtraktionen, brauchen die weniger?>> Ich denke schon, Addition und Subtraktion lassen sich ja relativ> geradlinig implementieren.
Schön ist es nicht unbedingt aber zyklenmässig würde man wahrscheinlich
mit fortgesetzter Subtraktion runterbringen können. Problem ist
natürlich, dass ein ull schon sehr hoch reicht und man viele einzelne
Subtraktionsstufen braucht.
Jörg Wunsch schrieb:> und erst recht fehlt die> Infrastruktur im GCC, um für verschiedenen -Os-Stufen verschiedene> libgcc's zu linken
Doch, das geht eigentlich schon, über Änderungen/Ergänzungen der specs.
Der "*libgcc"-Spec-String ist speziell dazu vorgesehen, beim Linken die
passende libgcc-Variante auszuwählen. Beim avr-gcc wird damit z.B. dafür
gesorgt, dass bei den ganz kleinen ATtiny (11, 12, 15, 28) keine
libgcc gelinkt wird. "Verzweigung" anhand der gewählten "-O"-Einstellung
lässt die Spec-Syntax dabei auch zu, z.B. so:
Voraussetzung ist natürlich, dass beim Linken dann auch "-O..." mit
übergeben wird, was normalerweise eigentlich nicht unbedingt nötig ist,
aber auch nicht schadet und oft sowieso im Makefile gemacht wird.
Hilft aber alles nichts, solange sich keiner findet, der entsprechend
auf Größe optimierte Funktionen schreibt ;-)
Andreas
Für alle die das Problem auch irgendwann haben hier meine Lösung (Ansatz
dafür war irgendwo im Netz, leider hab ich die Quelle nicht mehr):
void bin2bcd64(unsigned long long inValue)
{
unsigned char acc[21]={0};
unsigned char carry[22]={0};
for (int loop=0;loop<64;loop++)
{
if (( inValue & 0x8000000000000000) != 0)
carry[0] = 1;
else
carry[0] =0;
inValue <<= 1;
/*
If carry generated to next digit, convert and add carry.
If no carry generated to next digit, shift left 1 step and add carry.
*/
for(int i=1; i < 21;i++)
{
if (carry[i] == 1)
acc[i-1] = ((acc[i-1]-5)*2) + carry[i-1];
else
acc[i-1] = (acc[i-1] << 1) + carry[i-1];
}
// Check each accumulator if >= 5. Generate carry if so.
for(int i=0; i < 21;i++)
{
if (acc[i] >= 5)
carry[i+1] = 1;
else
carry[i+1] = 0;
}
}
//Output without leeding zeros
char zero=1;
for(int i=19; i >= 0;i--)
{
if((zero == 1 && acc[i] != 0) || i == 0)
{
zero=0;
}
if(zero == 0)
{
uart1_putchar('0' + acc[i]);
}
}
}