RTV forum PL | NewsGroups PL

Jak efektywnie mnożyć 32-bitowe liczby w AVR GCC bez arytmetyki zmiennoprzecinkowej?

AVR GCC mnozenie wielkich liczb

NOWY TEMAT

elektroda NewsGroups Forum Index - Elektronika Polska - Jak efektywnie mnożyć 32-bitowe liczby w AVR GCC bez arytmetyki zmiennoprzecinkowej?

Greg(G.Kasprowicz)
Guest

Fri Oct 06, 2006 8:37 pm   



Hej
mam taki problem z kompilatorem AVR GCC - na moj gust zglupial lub pewnei
robie cos zle.
jest takie powiedzenie, jesli twierdzisz ze kompilator jest glupi - idz
spac.
ale nie w tym rzecz.
mam wynik pomiaru w postaci liczby 32 bit (wynik z ukladu TCD pomiaru czasu
z rozdzielczoscia 7ps(sic!))
i potrzebuje go przemnozyc przez stala 7.629453
nie chce uzywac arytmetyki zmiennoprzecinkowej, zreszta z jakis powodow ona
tez nie dziala.


wersja zmiennoprzecinkowa
float calc_temp;
uint32_t calc_temp1;
calc_temp1 = TDC_Read(0);
if (calc_temp1 != 0xFFFFFFFF) //overrange detection
{
calc_temp = (calc_temp1*7.629453);
tlcd_write_U32((U32)calc_temp);
tlcd_write_string("ps");
}
else tlcd_write_string(" OVR ");

no i wynik nie dosc ze jest bez sensu to zawija sie gdzies przy 16 bitach,
ale to musze dokaldneij sprawdzic




wersja integer:


uint64_t calc_temp;
U32 calc_temp1;
calc_temp1 = TDC_Read(0);
if (calc_temp1 != 0xFFFFFFFF) //overrange detection
{
calc_temp = (calc_temp1*7629453UL)/1000000UL; //result in ps
tlcd_write_U32((U32)calc_temp);
tlcd_write_string("ps");
}
else tlcd_write_string(" OVR ");

Wynik wydaje zawijac sie gdzies na 16 bitach lub wczesniej.
nie pomaga zadeklarowanie liczb jako 1000000ULL, jedyny efekt uboczny to
taki ze objetosc kodu zwieksza sie o 4KB - widac kompilator wlacza
biblioteki 64bit??
o robie zle? pewnie znow sie okaze ze gdzies do zmiennej powinienem byl
dodac jakas literke...

z innej beczki:
czy spotkaliscie sie z bibliotekami w C konwerujacymi np U32 na BCD?
napisalem sobie wlasne,ale na pewno nie sa optymalne jesli chodzi o kod, a
pewnie istnieja gdzies eleganckie napisane w ASM...

Jarosław Grolik
Guest

Fri Oct 06, 2006 8:55 pm   



Witam.

Użytkownik "Greg(G.Kasprowicz)" <gkasprow@gmail.com> napisał w wiadomości
news:eg6er3$dk$1@news.onet.pl...
Quote:

Hej
mam taki problem z kompilatorem AVR GCC - na moj gust zglupial lub pewnei
robie cos zle.
jest takie powiedzenie, jesli twierdzisz ze kompilator jest glupi - idz
spac.


A jesteś pewien, że TDC daje Ci to czego oczekujesz ?
Na którym TDC pracujesz ? Acam ? MSC ? jeszcze inne ?
Ja czasami dostaje dziwne wyniki z TDC GP1

Pozdrawiam

Jarek Grolik

Greg(G.Kasprowicz)
Guest

Fri Oct 06, 2006 9:12 pm   



Quote:
A jesteś pewien, że TDC daje Ci to czego oczekujesz ?
Na którym TDC pracujesz ? Acam ? MSC ? jeszcze inne ?
Ja czasami dostaje dziwne wyniki z TDC GP1

tak, TDC GP1 - ogldam w hexie i jest idealnie.
Obok porownuje ze zgrubnym odczytem z oscyloskopu

ten sam problem mam z odczytem czasomierza zaimplementowanego w FPGA - chce
przemnozyc liczbe 32bit przez 6.66666ns, i identyczny problem..

BTW: do czego uzywasz GP1?
- ja do pomiaru szeroksoci impulsow z akceleratora oraz dlugosci impulsow
laserowych w dalmierzach.

mk
Guest

Fri Oct 06, 2006 10:14 pm   



Newsuser "Greg(G.Kasprowicz)" wrote:

Quote:
jest takie powiedzenie, jesli twierdzisz ze kompilator jest glupi - idz
spac.

Oj zdarza się, że kompilator robi psikusy.
Kto miał do czynienia z MSVC6 ten wie :-)

Quote:
wersja integer:


uint64_t calc_temp;
U32 calc_temp1;
calc_temp1 = TDC_Read(0);
if (calc_temp1 != 0xFFFFFFFF) //overrange detection
{
calc_temp = (calc_temp1*7629453UL)/1000000UL; //result in ps

W ostatniej linii prowadzisz obliczenia na liczbach 32 bitowych. Dopiero
przy przypisaniu następuje konwersja na liczbę 64 bitową. To za późno.

pzdr
mk

Adam Dybkowski
Guest

Fri Oct 06, 2006 10:51 pm   



Greg(G.Kasprowicz) napisał(a):

Quote:
mam wynik pomiaru w postaci liczby 32 bit
[...]
i potrzebuje go przemnozyc przez stala 7.629453


Quote:
uint64_t calc_temp;
U32 calc_temp1;
calc_temp1 = TDC_Read(0);
if (calc_temp1 != 0xFFFFFFFF) //overrange detection
{
calc_temp = (calc_temp1*7629453UL)/1000000UL; //result in ps

Musisz pamiętać, że sposób obliczania jest niejako narzucony przez
pierwszą operację. Czyli w powyższym mnożeniu liczby 32-bitowej
calc_temp1 przez 32-bitową stałą 7629453UL już masz problem - będzie
przepełnienie i obcięcie. Potem dzielisz to przez kolejną 32-bitową
stałą 1000000UL i nic sensownego z tego działania nie pozostanie.

Powinno zadziałać takie rozwiązanie (sprawdź):
uint64_t a, b, c;
a = TDC_Read(0);
b = (a * 7629453ULL) / 1000000ULL;

Jeżeli a jest 32-bitowe to przed mnożeniem zrzutuj je na 64 bity:
b = (uint64_t) a * 76294...
I jak poszło? Pamiętaj, że stałe 64-bitowe w AVR wymagają dopisku LL / ULL.

--
Adam Dybkowski
http://www.amwaw.edu.pl/~adybkows/

Uwaga: przed wysłaniem do mnie maila usuń cyfry z adresu.

Greg(G.Kasprowicz)
Guest

Fri Oct 06, 2006 11:14 pm   



Quote:
Musisz pamiętać, że sposób obliczania jest niejako narzucony przez
pierwszą operację. Czyli w powyższym mnożeniu liczby 32-bitowej calc_temp1
przez 32-bitową stałą 7629453UL już masz problem - będzie przepełnienie i
obcięcie. Potem dzielisz to przez kolejną 32-bitową stałą 1000000UL i nic
sensownego z tego działania nie pozostanie.

tak tez podejrzewalem. dlatego probowalem z tym ULL, tyle ze troche widac za

pozno.


Quote:
Powinno zadziałać takie rozwiązanie (sprawdź):
uint64_t a, b, c;
a = TDC_Read(0);
b = (a * 7629453ULL) / 1000000ULL;

Jeżeli a jest 32-bitowe to przed mnożeniem zrzutuj je na 64 bity:
b = (uint64_t) a * 76294...
I jak poszło? Pamiętaj, że stałe 64-bitowe w AVR wymagają dopisku LL /
ULL.

tylko teraz, dlaczego dlugosc kodu wzrasta o te 4kB? az tyle zajmuja

procedury mnozenia 64 bit?
w asemblerze 8051 mnozylem nawet liczby 96bit i zajmowalo kilkaset Bajtow.
Czy da sie jakos ograniczyc ilosc tego marnowanego kodu?

Adam Dybkowski
Guest

Fri Oct 06, 2006 11:27 pm   



Greg(G.Kasprowicz) napisał(a):

Quote:
Jeżeli a jest 32-bitowe to przed mnożeniem zrzutuj je na 64 bity:
b = (uint64_t) a * 76294...

tylko teraz, dlaczego dlugosc kodu wzrasta o te 4kB? az tyle zajmuja
procedury mnozenia 64 bit?

Obejrzyj w mapie linkera (i ew. listingu po deasemblacji) jakie funkcje
zajęły najwięcej. Mnożenie 64-bitowe bez problemu napiszesz na kolanie i
zajmie mniej niż kilkaset bajtów. Albo ściągnij gotowe z Sieci.

--
Adam Dybkowski
http://www.amwaw.edu.pl/~adybkows/

Uwaga: przed wysłaniem do mnie maila usuń cyfry z adresu.

J.F.
Guest

Sat Oct 07, 2006 7:33 am   



On Sat, 07 Oct 2006 00:51:00 +0200, Adam Dybkowski wrote:
Quote:
Greg(G.Kasprowicz) napisał(a):
calc_temp = (calc_temp1*7629453UL)/1000000UL; //result in ps

Musisz pamiętać, że sposób obliczania jest niejako narzucony przez
pierwszą operację. Czyli w powyższym mnożeniu liczby 32-bitowej
calc_temp1 przez 32-bitową stałą 7629453UL już masz problem - będzie
przepełnienie i obcięcie.

No i jak chcesz przemnozyc przez 7,629453 to moze szybciej
bedzie przemnozyc przez 32768251121 a potem "podzielic"
przez 2^32

J.

J.F.
Guest

Sat Oct 07, 2006 8:05 am   



On Fri, 6 Oct 2006 23:12:54 +0200, Greg(G.Kasprowicz) wrote:
Quote:
ten sam problem mam z odczytem czasomierza zaimplementowanego w FPGA - chce
przemnozyc liczbe 32bit przez 6.66666ns, i identyczny problem..

Hi hi - a duzo masz czasu ?

Bo jesli na jedno wejscie sumatora podasz liczbe, a na drugie jego
wyjscie podzielone przez 4, to po chwili wyjscie narosnie do
we*1.33333(3)

przemnozmy to przez 2, otrzymamy 2.666666(6), pozostaje dodac 4*we.

Glowy nie dam czy wynik bedzie zawsze stabilny, no i przydaloby sie
dla wiekszej dokladnosci najpierw wejscie "przemnozyc" np *8, a na
koniec podzielic/4

Swoja droga byc moze to ma calkiem ladna postac funkcji
zminimalizowanej .. tylko czym ja wygenerowac :-)

J.

Jarosław Grolik
Guest

Sat Oct 07, 2006 9:06 am   



Użytkownik "Greg(G.Kasprowicz)" <gkasprow@gmail.com> napisał w wiadomości
news:eg6gsm$5t4$1@news.onet.pl...
Quote:
BTW: do czego uzywasz GP1?
- ja do pomiaru szeroksoci impulsow z akceleratora oraz dlugosci impulsow
laserowych w dalmierzach.


Ja staram się zrobić na nim precyzyjną i szybką hercmiarkę, ale niestety nie
udało mi się z nim dogadać zbyt dokładnie poprzez LPT i teraz będę go wpierw
podpinał pod uC i dopiero do kompa. Używam trybu MB2 i dostaje stabilną
część całkowitą , natomiast FineCount gania w te i we w te. Uprzedzę , że
częstotliwość odniesienia to wzorzec rubidowy Smile Robię to do pomiarów QCM.

Pozdrawiam

Jarek Grolik

PS: Jak odczytujesz z niego kolejne wyniki z rejestrów 16 bitowych , to CS
jest cały czas aktywny i tylko podajesz kolejne stroby READ , czy też CS
również podnosisz ?
Mógłbyś podrzucić kawałek kodu odpowiedzialnego za inicjalizację i odczyt ?

PS2 : Jest też ciekawy TDC innej firmy. TDC502 nawet chyba lepiej
dopracowany

J.F.
Guest

Sat Oct 07, 2006 9:21 am   



On Sat, 07 Oct 2006 10:05:36 +0200, J.F. wrote:
Quote:
Hi hi - a duzo masz czasu ?
Bo jesli na jedno wejscie sumatora podasz liczbe, a na drugie jego
wyjscie podzielone przez 4, to po chwili wyjscie narosnie do
we*1.33333(3)

hi hi - jeszce jedno podobne: - nie dodawac, tylko odejmowac polowe.
Wynik sie ustali na 0.666666(6) .. czyli najpierw przemnozyc przez 10
[jeden sumator].

Quote:
Glowy nie dam czy wynik bedzie zawsze stabilny,

Hm, z sumowaniem chyba bedzie, ten z odemowaniem to nie dam glowy :-)

J.

Greg(G.Kasprowicz)
Guest

Sat Oct 07, 2006 12:15 pm   



Quote:
No i jak chcesz przemnozyc przez 7,629453 to moze szybciej
bedzie przemnozyc przez 32768251121 a potem "podzielic"
przez 2^32
tez o tym pomyslalem, bedzie chyba szybciej,skoro i tak juz mnoze, to

odpadnie dzielenie, bo potem tylko wezme starsza czesc

Greg(G.Kasprowicz)
Guest

Sat Oct 07, 2006 12:18 pm   



Quote:
Mógłbyś podrzucić kawałek kodu odpowiedzialnego za inicjalizację i odczyt
?




#include "TDC.h"


U32 TDC_RES_0;
U32 TDC_RES_1;
U32 TDC_RES_2;
U32 TDC_RES_3;
U32 TDC_STAT;
U32 TDC_REG_1;








void TDC_setup(void)
{

TDC_Write(addr_TDC_REG_0,TDC_REG0_default);
TDC_Write(addr_TDC_REG_1,TDC_REG1_default);
TDC_Write(addr_TDC_REG_2,TDC_REG2_default);
//TDC_Write(addr_TDC_REG_3,TDC_REG3_default);
//TDC_Write(addr_TDC_REG_4,TDC_REG4_default);
//TDC_Write(addr_TDC_REG_5,TDC_REG5_default);

}


void TDC_command(U8 command)
{
SPCR |= (1<<CPHA); //set SPI in falling edge mode
PORT_TDC |= (1<< SPI_src); // SPI input = TDC
PORT_TDC &= ~(1<<TDC_CSn);
SPDR = command;
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
{;}
PORT_TDC |= (1<<TDC_CSn);
SPCR &= ~(1<<CPHA); //set SPI in rising edge mode
}



void TDC_Write(U8 reg,U32 val)
{
SPCR |= (1<<CPHA); //set SPI in falling edge mode
PORT_TDC |= (1<< SPI_src); // SPI input = TDC
PORT_TDC &= ~(1<<TDC_CSn);
SPDR = (reg&0x07)|Write_into_address_ADR;
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
{;}
SPDR = ((val>>16)&0xff);
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
{;}
SPDR = ((val>>Cool&0xff);
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
{;}
SPDR = (val&0xff);
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
{;}
PORT_TDC |= (1<<TDC_CSn);
SPCR &= ~(1<<CPHA); //set SPI in rising edge mode
}




U32 TDC_Read(U8 reg)
{
SPCR |= (1<<CPHA); //set SPI in falling edge mode
U32 temp,temp1;
PORT_TDC |= (1<< SPI_src); // SPI input = TDC
PORT_TDC &= ~(1<<TDC_CSn);
SPDR = (reg&0x07)|Read_from_address_ADR;
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
{;}
SPDR = (0x00);
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
{;}
temp1 = SPDR;
temp = temp1<<24;
SPDR = (0x00);
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
{;}
temp1 = SPDR;
temp|= temp1<<16;
SPDR = (0x00);
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
{;}
temp1 = SPDR;
temp|= temp1<<8;

SPDR = (0x00);
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
{;}
temp|= SPDR;
PORT_TDC |= (1<<TDC_CSn);
SPCR &= ~(1<<CPHA); //set SPI in rising edge mode
return temp;
}



U16 TDC_STAT_Read(void)
{

SPCR |= (1<<CPHA); //set SPI in falling edge mode
U16 temp;
PORT_TDC |= (1<< SPI_src); // SPI input = TDC
PORT_TDC &= ~(1<<TDC_CSn);
SPDR = (addr_TDC_STAT)|Read_from_address_ADR;
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
{;}

SPDR = (0x00);
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
{;}
temp= SPDR<<8;

SPDR = (0x00);
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
{;}
temp|= SPDR;
PORT_TDC |= (1<<TDC_CSn);
SPCR &= ~(1<<CPHA); //set SPI in rising edge mode
return temp;
}






TDC.h






//TDC register 0 default content
#define NEG_START 0
#define NEG_STOP1 0
#define NEG_STOP2 0
#define MRange2 1
#define DisAutoCal 0
#define Calibrate 1
#define SelClkT 1
#define No_FAKE 0
#define TCycle 0
#define PORT 1
// 2 bit
#define START_ClkHS 1
#define ClkHSDiv 0
#define CALRES 0
//4 bit
#define DIV_FIRE 0
#define FIRE 0

//TDC register 0 position

#define pos_NEG_START 0
#define pos_NEG_STOP1 1
#define pos_NEG_STOP2 2
#define pos_MRange2 3
#define pos_DisAutoCal 4
#define pos_Calibrate 5
#define pos_SelClkT 6
#define pos_No_FAKE 7
#define pos_TCycle 8
#define pos_PORT 9
// 2 bit
#define pos_START_ClkHS 10
#define pos_ClkHSDiv 12
#define pos_CALRES 14
//4 bit
#define pos_DIV_FIRE 16
#define pos_FIRE 20






//TDC register definition
//OPCODES
#define Write_into_address_ADR 0b10000000
#define Read_from_address_ADR 0b10110000
#define Init 0b01110000
#define Power_On_Reset 0b01010000
#define Start_Cycle 0b00000001
#define Start_Temp 0b00000010
#define Start_Cal_Resonator 0b00000011
#define Start_Cal_TDC 0b00000100
// 20 16 12 8 4 0
// | | | | | |
#define TDC_REG0_default 0b000000010011011001100010
#define TDC_REG1_default 0b000000010100000100000000
// 20 16 12 8 4 0
// | | | | | |
#define TDC_REG2_default 0b010000000000000000000000
#define TDC_REG3_default 0b001000000000000000000000
// 20 16 12 8 4 0
// | | | | | |
#define TDC_REG4_default 0b001100000000000000000000
#define TDC_REG5_default 0b000000000000000000000000

#define addr_TDC_RES_0 0
#define addr_TDC_RES_1 1
#define addr_TDC_RES_2 2
#define addr_TDC_RES_3 3
#define addr_TDC_STAT 4

#define addr_TDC_REG_0 0
#define addr_TDC_REG_1 1
#define addr_TDC_REG_2 2
#define addr_TDC_REG_3 3
#define addr_TDC_REG_4 4
#define addr_TDC_REG_5 5





void TDC_setup(void);
void TDC_command(U8 command);
void TDC_Write(U8 reg,U32 val);
U32 TDC_Read(U8 reg);
U32 TDC_Read(U8 reg);
U16 TDC_STAT_Read(void);

Greg(G.Kasprowicz)
Guest

Sat Oct 07, 2006 12:19 pm   



Quote:

Ja staram się zrobić na nim precyzyjną i szybką hercmiarkę, ale niestety
nie udało mi się z nim dogadać zbyt dokładnie poprzez LPT i teraz będę go
wpierw podpinał pod uC i dopiero do kompa. Używam trybu MB2 i dostaje
stabilną część całkowitą , natomiast FineCount gania w te i we w te.
Uprzedzę , że częstotliwość odniesienia to wzorzec rubidowy Smile Robię to
do pomiarów QCM.

ja dla odroznienia uzywam trybu 1-szego
interesuje mnei zakres 2ns...1us
kody podalem w innym watku

Greg(G.Kasprowicz)
Guest

Sat Oct 07, 2006 2:48 pm   



Quote:

Powinno zadziałać takie rozwiązanie (sprawdź):
uint64_t a, b, c;
a = TDC_Read(0);
b = (a * 7629453ULL) / 1000000ULL;

Jeżeli a jest 32-bitowe to przed mnożeniem zrzutuj je na 64 bity:
b = (uint64_t) a * 76294...
I jak poszło? Pamiętaj, że stałe 64-bitowe w AVR wymagają dopisku LL /
ULL.

dzieki, zadaialalo
jednakze ostatecznie podzielilem wynik pomiaru na 3 zakresy co 1024 Smile
i od razu mi wyswietla w ns, us, ms co jest bardziej intuicyjne niz rzad 9
cyfr, z czego i tak 2 ostatnie mrugaj na najwyzszym zakresie (1s range/6ns
bin)

elektroda NewsGroups Forum Index - Elektronika Polska - Jak efektywnie mnożyć 32-bitowe liczby w AVR GCC bez arytmetyki zmiennoprzecinkowej?

NOWY TEMAT

Regulamin - Zasady uzytkowania Polityka prywatnosci Kontakt RTV map News map