Elektroda.pl
Elektroda.pl
X
SterControl
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

problem - C++ i kłopoty z RAMem

czarusgg 05 Paź 2016 05:13 930 12
  • #1 05 Paź 2016 05:13
    czarusgg
    Poziom 12  

    Witam wszystkich, właśnie klepię się po łysinie, ponieważ wyrwałem już sobie wszystkie włosy :(

    Sytuacja jest następująca:

    Mam takie oto definicje:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Problem polega na tym, że w konstruktorze CMotor przy przypisywaniu do zmiennej prywatnej wszystko gra, to znaczy przy użyciu debugera stwierdziłem, że składowe struktury CMotor::pEnable mają taką samą zawartość jak struktura aEnable w parametrze konstruktora (przepisanie działa prawidłowo).

    Jednak po wejściu do funkcji pinMode Ustawiona jest tylko pierwsza składowa (TPIN::PORT), reszta zaś jest wyzerowana.

    Wartość podglądu zmiennej po wykonaniu pierwszej linii konstruktora (składowa pomiędzy kreskami):

    Code:

    -      this   0x21e2   class CMotor*{registers}@ R07 R06
    -      *(this)   {class CMotor{registers}@0x21e2}   class CMotor{registers}@0x21e2
    -------------------------------------------------------------------------
          pEnable   {struct TPIN{registers}@0x21e2}   struct TPIN{registers}@0x21e2
    +      PORT   0x002b   uint8_t*{registers}@0x21e2
    +      PIN   0x0029   uint8_t*{registers}@0x21e4
    +      DDR   0x002a   uint8_t*{registers}@0x21e6
          MASK   128   uint8_t{registers}@0x21e8
    -------------------------------------------------------------------------
    +      pDir   {struct TPIN{registers}@0x21e9}   struct TPIN{registers}@0x21e9
    +      pStep   {struct TPIN{registers}@0x21f0}   struct TPIN{registers}@0x21f0
          pStepState   0   bool{registers}@0x21f7
          pInvertDir   0   bool{registers}@0x21f8
          pEnableActive   0   bool{registers}@0x21f9
          pStepActive   0   bool{registers}@0x21fa


    A tutaj podgląd parametru aPin w funkcji pinMode z 8 linijki konstruktora, (gdzie przecież podawana jest wartość, która jeszcze przed chwilą była prawidłowa). Do parametry aPin tej funkcji podawana jest zmienna: this->pEnabled:

    Code:

    -      aPin   {struct TPIN{registers}@ R24 R27 R26 R31 R30}   struct TPIN{registers}@ R24 R27 R26 R31 R30
    +      PORT   0x002b   uint8_t*{registers}@0x001e
    +      PIN   0x0000   uint8_t*{registers}@0x0020
    +      DDR   0x0000   uint8_t*{registers}@0x0022
          MASK   0   uint8_t{registers}@0x0024


    O co może chodzić :(

    0 12
  • SterControl
  • #2 05 Paź 2016 05:48
    JacekCz
    Poziom 35  

    gdybanie może bez sensu: masz świadomość, że przekazujesz strukturę przez wartość, tzn od tej pory oryginał i kopia żyją własnym życiem? Masz twarde powody aby nie użyć np const & ?

    Zrzut debuggera to pośrednio pokazuje, temat założyłeś w/s RAM czyli domniemuję tam bys się ich spodziewał, a są ewidentnie w rejestrach.
    Mam wrażenie że volatile dla pól dodałeś nieco "z rozpaczy" próbując dopaść problem? W tym jak ja rozumiem "typowe" użycie struktur nie umiem sobie wyobrazić sensu tego słowa.

    A już redakcyjnie, jak piszesz "przypisanie do zmiennej prywatnej", masz na myśli przypisanie do pola prywatnego?

    0
  • #3 05 Paź 2016 06:04
    czarusgg
    Poziom 12  

    Witaj Jacku.

    Cytat:
    masz świadomość, że przekazujesz strukturę przez wartość, tzn od tej pory oryginał i kopia żyją własnym życiem? Masz twarde powody aby nie użyć np const & ?


    Tak, mam świadomość. Niestety nie mogę użyć ani referencji ani wskaźnika, ponieważ mam podefiniowane wszystkie piny procka w postaci:

    #define PA7 = { .PORT = &PORTA, .PIN = &PINA, .DDR = &DDRA, .MASK = 128 }

    I czasami chaiałbym użyć wygodnego pinMode (PA7, OUTPUT);

    Dlatego przekazywana jest struktura a nie wskaźnik, czy to jawny czy przez dereferencję.

    Cytat:
    Zrzut debuggera to pośrednio pokazuje, temat założyłeś w/s RAM czyli domniemuję tam bys się ich spodziewał, a są ewidentnie w rejestrach.


    Owszem zrzut debugera tak pokazuje (co nie zmienia faktu, ze powinna tam być wierna kopia z RAMu - a nie jest i to jest ten problem :)), ale tylko dlatego właśnie, ze jest to parametr funkcji i kompilatorowi udało się strukturę upchać w rejestrach, to efekt optymalizatora i tutaj faktycznie mogłoby pomóc volatile :) Bo w RAMie jest prawidłowo.

    (Trzeba wyłączyć optymalizator do testów).

    Cytat:
    Mam wrażenie że volatile dla pól dodałeś nieco "z rozpaczy" próbując dopaść problem?


    Nawet o tym nie pomyślałem (wtedy modyfikator zapisałbym przy definicji struktury i objąłby swoim zasięgiem wszystkie składowe), użyłem go dlatego, że mają to być wskaźniki na rejestry portów i nie można ich optymalizować, ponieważ operacje zawsze muszą się odbywać na odpowiednich bajtach przestrzeni IO. Nawet jeśli pozornie zawartość się nie zmieniła, bo tak wynika z kontekstu kodu, to stan pinu może się zmieniać niezależnie od logiki wykonywanych poleceń. Stąd modyfikator.

    :)

    0
  • #4 05 Paź 2016 06:17
    JacekCz
    Poziom 35  

    czarusgg napisał:

    Nawet o tym nie pomyślałem (wtedy modyfikator zapisałbym przy definicji struktury i objąłby swoim zasięgiem wszystkie składowe), użyłem go dlatego, że mają to być wskaźniki na rejestry portów i nie można ich optymalizować, ponieważ operacje zawsze muszą się odbywać na odpowiednich bajtach przestrzeni IO.

    :)[/b]


    Z zamieszczonych fragmentów (to drugie wrażenie, że coś zaszło przy preparowaniu kodu, ty cos wiesz, my nie) nie wynika że adresy portów ulegają zmianom, więc mowa o optymalizacji tu nie zachodzi. Nie ma żadnego znaczenia, że kopia wskaźnika jest tu czy tu, jeśli dalej wskazuje to samo.

    NIE ZNAM reszty kodu. Sam na swój użytek maksymalnie hojnie daję const itd... i kompilator ma większe szanse, i lepiej się czyta.

    Dodano po 6 [minuty]:

    czarusgg napisał:
    Tak, mam świadomość. Niestety nie mogę użyć ani referencji ani wskaźnika, ponieważ mam podefiniowane wszystkie piny procka w postaci:

    #define PA7 = { .PORT = &PORTA, .PIN = &PINA, .DDR = &DDRA, .MASK = 128 }

    I czasami chaiałbym użyć wygodnego pinMode (PA7, OUTPUT);


    To na pewno da się zapisać w normalnym obiektowym duchu a nie w preprocesorze. Nie rozumiem takiego wyboru.

    0
  • SterControl
  • Pomocny post
    #5 05 Paź 2016 08:03
    Freddie Chopin
    Specjalista - Mikrokontrolery

    Ja bym zaczął od początku. Czy kod faktycznie nie działa (uruchomiony normalnie, zostawiony samemu sobie), czy może tylko w debuggerze zachowuje się w dziwny sposób? Bo jeśli to drugie, to jest to wynik optymalizacji, jeśli kod działa to ja nie widzę problemu. Jeśli to pierwsze to można myśleć nad przyczynami i rozwiązaniami.

    0
  • #6 05 Paź 2016 08:19
    czarusgg
    Poziom 12  

    Jacku

    Cytat:
    Z zamieszczonych fragmentów (to drugie wrażenie, że coś zaszło przy preparowaniu kodu, ty cos wiesz, my nie) nie wynika że adresy portów ulegają zmianom, więc mowa o optymalizacji tu nie zachodzi. Nie ma żadnego znaczenia, że kopia wskaźnika jest tu czy tu, jeśli dalej wskazuje to samo.

    NIE ZNAM reszty kodu. Sam na swój użytek maksymalnie hojnie daję const itd... i kompilator ma większe szanse, i lepiej się czyta.


    Masz 100% rację, adres to adres (bez znaczenia, optymalizowany czy nie), byleby wskazywał w to samo miejsce. Volatile byłby potrzebny, gdybym operował na wartościach a nie wskaźnikach (adresach) i w zasadzie wystarczyłby tylko i wyłącznie dla rejestru PIN danego portu, bo tylko ten może się zmienić niezależnie od programu.

    Moim problemem jest to, że argument funkcji powinien być kopią parametru wywołania a nią nie jest! Ten fakt jest istotą mojego problemu. Przy czym tak samo jak zwróciłeś uwagę na optymalizacje wskaźników tak i tutaj nie powinno mieć żadnego znaczenia to, że kopia będąca argumentem siedzi w rejestrach. Znaczenie ma to, że ma inną zawartość niż element źródłowy.

    Spróbuję posługiwać się wskaźnikiem na pierwszy rejestr portu a do pozostałych rejestrów odwoływać się poprzez offset względem pierwszego rejestru :) W ten sposób nie będę trzymał adresów do poszczególnych rejestrów portu, tylko do jednego + oczywiście maska bitu.

    Nie mniej jednak fajnie byłoby rozwikłać ten przypadek. Choćby po to by wiedzieć dlaczego tak się dzieje.

    Co do tego, że nie masz wglądu w cały kod... ...myślę, że reszta kodu jest bez znaczenia, ponieważ pokazałem wszystkie definicje, oraz ciało konstruktora. Problem powstaje na przestrzeni 6 zaledwie instrukcji konstruktora:

    pomiędzy linią: Kopiującą wartość argumentu do składowej prywatnej:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    a wywołaniem funkcji z użyciem tejże składowej prywatnej:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Żaden inny kod się tutaj nie wykonuje, który mógłby mieszać. Przerwań nie ma jeszcze w ogóle w tej aplikacji, pomiędzy tymi wierszami nie ma przypisywania żadnych skomplikowanych struktur, które angażowałyby jakiś inny kod (na przykład przeciążony operator przypisania).

    Małe tego, gdy podglądam zawartość zmiennej this->pEnable w miejscu wywołania funkcji (gdy wskaźnik programu) jest na tej linii, to wszystko jest OK. Zaburzeniu ulega sam moment wywołania funkcji (tworzenia lokalnej kopi dla argumentu).

    Dodano po 4 [minuty]:

    Fredi

    Gdy uruchomię program... nie działa - silnik milczy. Gdy to samo zrobię na szybko, na skróty poprzez ciąg operacji typu DDRA |= 128; PORTA |= 128; itd... To silnik ożywa i wszystko działa.

    Znaczy chciałem właśnie zdebugować, dlaczego stan pinów się nie zmienia i trafiłem na tę sytuację :).

    0
  • #7 05 Paź 2016 08:21
    grko
    Poziom 32  

    Cytat:
    #define PA7 = { .PORT = &PORTA, .PIN = &PINA, .DDR = &DDRA, .MASK = 128 }

    I czasami chaiałbym użyć wygodnego pinMode (PA7, OUTPUT);


    To raczej nie zadziała. Możesz to rozwiązać na kilka sposobów.

    1. PA7 jest stałą.
    Kod: c
    Zaloguj się, aby zobaczyć kod


    2. Zapisujesz definicje PA7 jako compound literal.
    Kod: c
    Zaloguj się, aby zobaczyć kod


    W jednym i drugim przypadku przekazujesz ten parametr przez referencje.

    0
  • Pomocny post
    #8 05 Paź 2016 08:32
    Freddie Chopin
    Specjalista - Mikrokontrolery

    Cała dyskusja na temat volatile powyżej jest błędna - użycie tego słówka przy polach struktury TPIN jest w 100% prawidłowe i KONIECZNE. Proponuję więc szukać problemu w innym miejscu, bo to akurat miejsce jest właśnie takie jak ma być.

    Porównywanie wartości zmiennych z oczekiwaniami przy włączonej optymalizacji jest bezcelowe i doprowadza do takich problemów jak ten tutaj. Albo wyłącz optymalizację i wtedy porównuj, albo zostaw optymalizację i zacznij tylko sprawdzać EFEKT (czyli np. wartość rejestrów DDR, PORT itd.) po zakończeniu całego konstruktora.

    Pokaż kod który działa - najlepiej z widocznie zakomentowanymi liniami które wyłączyłeś i zastąpiłeś innymi operacjami. Bez tego szukamy problemów które równie dobrze mogą być wyimaginowane. Nie obrażaj się, ale twierdzenia że wiesz dokładnie gdzie jest problem wiec nic innego nie musisz pokazywać są na tym forum dosyć typowe. Skoro wiesz gdzie jest problem, to czemu go jeszcze nie znalazłeś? Czyżby błąd w kompilatorze? <:

    Przy okazji niestety muszę napisać, że cały ten kod jest wyjątkowo słaby i pokazuje, że o C++ musisz się jeszcze wiele nauczyć (ukryte użycie kopiowania zamiast konstrukcji, brak list inicjalizacyjnych, bardzo dziwne używanie struktur przez wartość, bezsensowne poprzedzanie typów słówkiem "struct", bezcelowe używanie "this->", ...). Jeśli nie musisz robić tego projektu w C++, proponowałbym zostać przy tym co znasz.

    0
  • #9 05 Paź 2016 09:15
    JacekCz
    Poziom 35  

    Freddie Chopin napisał:
    Cała dyskusja na temat volatile powyżej jest błędna - użycie tego słówka przy polach struktury TPIN jest w 100% prawidłowe i KONIECZNE. Proponuję więc szukać problemu w innym miejscu, bo to akurat miejsce jest właśnie takie jak ma być.


    Objaśnisz kilka słów więcej?
    Jak jak czytam ten kod (i objaśnienia) TPIN to stały "deskryptor", immutable (w językach które mają tą koncepcję). Jak dla mnie brakuje tam raczej potwierdzenia przez 'const'

    Zaznaczam, że interpretacja jest również pochodną niejednoznaczności w materiale i czytania w nocy ;)

    grko napisał:
    ... Możesz to rozwiązać na kilka sposobów.

    1. PA7 jest stałą.
    Kod: c
    Zaloguj się, aby zobaczyć kod



    Mi się klasyczny wariant podoba. Jestem niemal pewien że w razie braku użycia zostanie wyoptymalizowane, więc pamięci nie zużywa niepotrzebnie (dodałbym static o ile jest możliwość - w ogóle widzę programiści uP za często pozostawiają za bardzo proste identyfikatory globalne bez static)

    0
  • #10 05 Paź 2016 09:34
    Freddie Chopin
    Specjalista - Mikrokontrolery

    JacekCz napisał:
    Objaśnisz kilka słów więcej?

    W kodzie jest "volatile uint8_t *PORT;", co oznacza najzwyklejszy na świecie wskaźnik na "volatile uint8_t". To nie wskaźnik (adres) jest volatile, tylko to na co wskazuje. Rejestry sprzętowe zwykle są "volatile", więc tutaj takie użycie jest konieczne. Gdyby usunąć "volatile", to po pierwsze kompilator się przyczepi ("assignment discards qualifiers from pointer target type" czy coś takiego) ponieważ (przykładowo) PORTA ma typ "volatile uint8_t*" a będziesz próbował go przypisać do "uint8_t*", a po drugie użycie czegoś takiego w programie może sprawić że przestanie on działać, bo kompilator bez volatile może sobie takie zapisy poprzestawiać jak mu się podoba albo i nawet całkiem usunąć jak nie będzie dla nich widział sensu (np. przy dwukrotnym zapisie pod ten sam adres - przy sprzętowych rejestrach często koniecznym - pierwszy zapis zostanie wywalony, zostanie tylko drugi).

    Bardzo istotne jest odróżnienie tych czterech przypadków:
    uint8_t* ptr;
    volatile uint8_t* ptr;
    uint8_t* volatile ptr;
    volatile uint8_t* volatile ptr;
    Każda z tych linii ma inne "właściwości". To samo dotyczy "const".

    0
  • #11 05 Paź 2016 09:37
    JacekCz
    Poziom 35  

    Freddie Chopin napisał:
    JacekCz napisał:
    Objaśnisz kilka słów więcej?

    W kodzie jest "volatile uint8_t *PORT;", co oznacza najzwyklejszy na świecie wskaźnik na "volatile uint8_t".



    Dzięki. Zaćmienie, jeszcze się kawa nie rozpuściła, w pełni rozumiem ...

    Żeby było śmieszniej, sam użyłem podobnej idei do stworzenia poziomu abstrakcji nad pojedynczym pinem (ale wolałem użyć referencji do portu niż wskaźnika) :

    Kod: c
    Zaloguj się, aby zobaczyć kod

    0
  • #12 05 Paź 2016 09:59
    grko
    Poziom 32  

    @JacekCz

    Cytat:

    Mi się klasyczny wariant podoba. Jestem niemal pewien że w razie braku użycia zostanie wyoptymalizowane, więc pamięci nie zużywa niepotrzebnie (dodałbym static o ile jest możliwość - w ogóle widzę programiści uP za często pozostawiają za bardzo proste identyfikatory globalne bez static)


    To obrzydliwi programiści C wiedzą, że w C++ stałe są static by default.

    0
  • #13 05 Paź 2016 11:18
    czarusgg
    Poziom 12  

    :) OK, przepraszam wszystkich, dziękuję za udział i pomoc.

    Mając punkt wykonania na deklaracji zmiennej "X" o typie CMotor, nacisnąłem F10 (Czyli wykonaj całość) :) I zajrzałem do rejestrów portów po wykonaniu całego konstruktora, następnie po każdym wykonaniu jednej z metod (STEP) tegoż obiektu X, zaglądałem do rejestrów portów. Są ustawione tak jak być powinny, czyli pierwszy trop od Freddie Chopin.

    By sprawdzić skutek a nie stan pośredni w którym maczał palce optymalizator.

    Niestety, nie mogę debugować na hardwarze, tylko w symulatorze i zbiło mnie z tropu to, że hardware nie reaguje (silnik milczy), Dlatego postanowiłem podejrzeć stan rejestrów w symulatorze i nie poszedłem do końca, tylko zaciąłem się na fragmencie, który "nie działał" :)

    Także tutaj chyba sprawa się wyjaśniła. Jest OK.

    Zrobię jeszcze próbę z wyłączonym optymalizatorem na 0, wtedy zawartość argumentu powinna być taka sama jak parametru :)

    Dodano po 6 [minuty]:

    OK.

    Po wyłączeniu optymalizacji O0 nadal jest "źle", ale działa dobrze, także nie wnikam już w to zagadnienie :)

    Jeszcze raz dziękuję wszystkim, zamykam temat.

    0