programowanie i kasowanie dwu kostek flash na jednej magistrali

Feb 07, 2017 26 Replies

Czy mial ktos z Was do czynienia z konfiguarcją hardware polegającą na



32-bitowym procku (Arm Cortex M4) obsługującym dwie zewnętrzne pamięci flash 16-bitowe (ST:M29W640GL)?

Wydawałoby się, że zadanie trywialne, a jednak kostki nie chcą współpracować... :-)



Obie kostki pamięci mają wspólne sygnały Cs, Oe, We i A0..Axx a magistrala danych po połowie 0..15 i 16..31 przydzielona odpowiednio do 0..15 każdej kostki.



Procek ustawiony na 32bit i aby używał "address shift mode" (EMC Shift Control bit w SCS registrze jest 0) więc "nie wie" ze sa dwi kostki w żaden inny sposob jak tylko to, że zamiast do kasowania sektora wysylac 16-bitowo



0x00AA i 0x0055 a potem 0x0080 wysylam 32-bitowo 0x00AA00AA, 0x00550055 i
0x00800080.

I do tego problem jest ze czasem to dziala a czasem nie dziala. Jak nie dziala to albo przy zmieniajacym sie bicie 2 jest ustawiony bit 5 (error) a czasem juz na samym koncu przy sprawdzeniu odczytu skasowanej lokacji jest jedna kostka skasowana a druga nieskasowana (na szynie jest np 0xFFFF0000).



Czy do takiej konfiguracji pamieci podchodzi sie jakos inaczej do kasowania/programowania? Ktos sie podzieli doswiadczeniem?


A mo¿e kto¶ zechce popatrzeæ na kod programu i znale¼æ "s³abe" jego strony? Zachêcam gor±co... :-)

// returns 1 if success int STFlashEraseSector32(unsigned int SectorAddress) { U32 Status1, Status2; volatile U32 *pSector = (volatile U32 *)(SectorAddress - (SectorAddress %

0x20000)); volatile U32 *pStatus = pSector;

// EMCSC bit in System Controls and Status register is cleared in Flash_Init(). // It controls how addresses are output on the EMC address pins. // For 32 bit bus the address is shifted so A2 is on A0 pin. U32 *pA1 = (U32 *)(EXTERNAL_FLASH_LOCATION + (0x555 << 2)); //555 16bit mode U32 *pA2 = (U32 *)(EXTERNAL_FLASH_LOCATION + (0x2AA << 2)); //2AA 16bit mode

// Reset

*pA1 = 0x00AA00AA; *pA2 = 0x00550055; *pSector = 0x00F000F0; // *pA1 = 0x00AA00AA; *pA2 = 0x00550055; *pA1 = 0x00800080; //erase *pA1 = 0x00AA00AA; *pA2 = 0x00550055; *pSector = 0x00300030; //erase sector

//check toggle bit2 indicating erase operation pending while(Status1 = *pStatus, Status2 = *pStatus, (Status1 ^ Status2) & (1 <<

2)) { if(Status1 & (1 << 5)) // check error bit on one chip { LEDFRed(); return 0; } } while(Status1 = *pStatus, Status2 = *pStatus, (Status1 ^ Status2) & (1 << (16+2))) { if(Status1 & (1 << (16+5))) // check error bit on second chip { LEDFRed(); return 0; } }

// Reset // *pA1 = 0x00AA00AA; // *pA2 = 0x00550055; // *pSector = 0x00F000F0; // Check the erase if(*pSector != 0xffffffff) { LEDFRed(); return 0; } else return 1; }

// returns 1 if success int STFlashWrite32(unsigned int Address, unsigned char *pSource, unsigned int Size) { int Timeout; U32 Status1, Status2; volatile U32 *pDest = (volatile U32 *)Address; U32 *pData = (U32 *)pSource;

// EMCSC bit in System Controls and Status register is cleared in Flash_Init(). // It controls how addresses are output on the EMC address pins. // For 32 bit bus the address is shifted so A2 is on A0 pin. U32 *pA1 = (U32 *)(EXTERNAL_FLASH_LOCATION + (0x555 << 2)); //555 16bit mode U32 *pA2 = (U32 *)(EXTERNAL_FLASH_LOCATION + (0x2AA << 2)); //2AA 16bit mode

if((Address & 0x01) != 0x00 || (Address & 0x02) != 0x00) return 0; if(((U32)pSource & 0x01) != 0x00 || ((U32)pSource & 0x02) != 0x00) return 0;

for(unsigned int i = 0; i < Size/4; i++) { Timeout = 0; // Reset

*pA1 = 0x00AA00AA; *pA2 = 0x00550055; *pDest = 0x00F000F0; // *pA1 = 0x00AA00AA; *pA2 = 0x00550055; *pA1 = 0x00A000A0; //program *pDest = *pData; //data word //check toggle bit for each chip separatelly while(Status1 = *pDest, Status2 = *pDest, (Status1 ^ Status2) & (1 << 6)) { if(Status1 & (1 << 5)) // check error bit on one chip { LEDEYellow(); return 0; } if(++Timeout > 10000) { LEDDGreen(); return 0; } } while(Status1 = *pDest, Status2 = *pDest, (Status1 ^ Status2) & (1 << (16+6))) { if(Status1 & (1 << (16+5))) // check error bit on second chip { LEDEYellow(); return 0; } if(++Timeout > 10000) { LEDDGreen(); return 0; } } // Reset // *pA1 = 0x00AA00AA; // *pA2 = 0x00550055; // *pDest = 0x00F000F0; // check write if(*pDest != *pData) { LEDDGreen(); LEDEYellow(); return 0; } ++pDest; ++pData; }

Gdybyś tak zrobił jedno miejsce przez które przechodzą wszystkie zapisy i odczyty z printfem jakie zapisy idą do pamięci, to by pewnie było już po problemie. Do sprawdzenia:

  1. Reprezentacja danych w pamięci. Czy aby na pewno bajty/słowa nie są zamienione.
  2. Czy w Twoim przypadku rozmiar sektora jest 0x20000 ? A może 0x40000 ? Masz przesunięty adres.
  3. Jak nie jesteś pewny to oscyloskop do łapy i sprawdź czy jest to co Ci się wydaje.

Adam

Hmmm... Nie bardzo rozumiem ten pomysł... Może zasugerowałbyś jakiś przykład? W czasie programowania piszę przecież do pamięci bezpośrednio odwołując się do wskaźników... Przy odczycie, w czasie kasowania i zapisu pamięć przestawia jeden bit (toggle) co wskazuje na operację w toku...

Domyślam się że nie są zamienione, bo gdyby były to nie udawałoby się czasem skasować i zapisać, a czasem się udaje...

Używam kostek M29W640GL. To są pamięci 64Mb z 128 sektorów po 64KB/16KW każdy. Gdybym adresował

8-bitowo to sektor byłby od 0x0000 do 0xffff. Ale tu mi trochę zadałeś klina, bo z jednej strony każda kostka pamięci ma widzieć swój adres, ze swojego lokalnego punktu widzenia a z drugiej strony mam procesor, który myśli że ma pamięć 32-bitową i interpretuje moje wskazniki zgodnie ze swoim rozumieniem adresowania 32-bitowego z przesuniętym A2 na pin A0. Muszę to jeszcze raz przetrawić...

Dobre :-)

Sorry - mialem napisac 64KB/32KW oczywiscie...

Tak ale można to zrobić poprzez np. makro lub funkcje i przekierować to co wychodzi do flasha i jednocześnie do printfa lub do bufora w ramie tak żeby mieć rejestr z tego co wyszło i przyszło. Jedym słowem logowanie. Tak żeby mieć pewność że sekwencja zapisu zgadza się z teorią.

A nie trzeba tam gdzies czekać na bity ze status registru ?

Tak mi się wydaje.

Czemu dobre ? Życie. No nie mów że dostęp do flasha jest jakiś ekstremalnie szybki lub że nie da się tego zobaczyć. Że o debuggerach nie wspomnę.

Adam Górski

Z tego pytania wnioskuję, że na funkcje zapisu i kasowania jakie tu podałem nawet nie spojrzałeś :-)))

Nie chodzi o szybkość, raczej o ilość potrzebnych do monitorowania wejść. Oscyloskop, nawet 4-kanałowy niewiele pomoże. Moj analizator stanow logicznych też tylko 16 wejsc... No i good luck aby je podłączyć pod procesor w obudowie LQFP208 albo flasha 48-pin TSOP... Nie mam niestety adapterów, musiałbym lutować druty do kostki :-(

W dniu 2017-02-08 o 15:07, Pszemol pisze:

--------------------^^^^---------------^^^^ Status zapisu sprawdzasz tylko z jednej pamięci, bo skąd kompilator ma wiedzieś który "*pStatus" jest od której paMIĘCI? podstawia dwa razy to samo. Jedna się wyrobi, druga nie i są błędy.

Zerknij proszę jeszcze raz na deklarację wskaźnika pStatus oraz zmiennych Status1 i Status2. To są obiekty 32-bitowe a więc obie kostki pamięci odpowiedzialne są za ich zapis.

Pamietaj że jedna kostka pamięci podłączona jest do jednej połowy szyny danych (D0..D15) a druga do drugiej połowy (D16..D31). Procesor jest ustawiony aby wszystkie cykle robił 32-bitowe z 4-bajtowym "alignem" adresow... (jak się to po polsku mówi?)

Dwa kolejne odczyty tego samego adresu są robione w pętli aby sprawdzić czy pamięć jest zajęta.

Lecę więc po kolei, zakładając że kostki mogą mieć rózne potrzeby czasu na zapis/kasowanie: najpierw w pętli jednej, czekam aż jedna kostka skończy kasowac testujac bit D2 potem wskakuję w drugą pętle i sprawdzam czy druga kostka też skończyła testujac bit 2+16 czyli D18 a na końcu, dla pewności, sprawdzam czy skasowana lokacja odczytuje się 0x FFFF FFFF czyli czy obie kostki są skasowane. I tu najczęściej się program wywala w debuggerze: test pokazuje odczyt np 0x FFFF 0087. Czyli jedna kostka skasowana a druga ma stara zawartosc....

Teraz na ten przykład krokuję program (breakpoint na wejsciu do funkcji kasowania sektora) i widzę, że tak traktowany sprzęt zachowuje się ładniej

- zamiast wywalić się na kasowaniu pierwszego sektora (0x8000 0000) przeszedł ladnie do kasowania

0x800C 0000, 0x800E 0000, 0x8010 0000 zapisujac je po kolei bez zgłaszania błędów i po skasowaniu 0x8012 0000 wykorbił się w czasie zapisu nowych danych:

Górna kostka, ta obsługująca bity D16..D31, zgłosiła błąd zapisu: ustawiony bit D5 kostki (u mnie cpu widzi go jako D21) przy przestawianym wciąż bicie D6 (u mnie D22).

I co teraz? Zaczynam kurna podejrzewać nasz hardware... Może coś jest nie tak z jakością 3V3... Czas przynieść oscylka :-)

Kodensatorek osprzegajacy był na innej nodse podpiętej do 3v3 ale po podpieciu dodatkowego 220nF do nozki 43 nic sie nie zmieniło na plus... Dalej sie proces wykorbia w czasie kasowania lub programowania...

Spojrzałem. Nie podejmuje się analizowania czegoś takiego jak while( A, B,C ). Nigdzie nie widziałem jeszcze takiej konstrukcji.

Czy możesz podpowiedzieć jaki jest oczekiwany rezultat takiej pętli ? Czy w takim wypadku oczekujesz wszystkich warunków niezerowych czy wystarczy jeden ? Inaczej mówiąc to jest bardziej jak && czy ||. A może tylko jedno jest warunkiem ?

Widziałeś gdzieś definicję takiej pętli ? Wybacz moje niedouczenie.

Z drugiej strony , na ilu szt. hardwaru masz takie zachowanie ? Może normalnie masz zwalony hardware.

Adam

Ostatnie jest warunkiem. Reszta ma się po prostu wykonać przed wyliczeniem warunku.

Piotrek

Dzięki. We wszystkich definicjach jakie widziałem zawsze jest while( condition ). Masz może namiar na opis standardu dopuszczającego takie użycie ?

Adam Górski

No i właśnie to condition jest grupą wyrażeń połączonych *operatorem* ,

Było od zawsze ("The C programming Language" A.7.18). Chociaż rzeczywiście widziałem to użyte w kodzie może z pięć razy.

Piotrek

W dniu 2017-02-08 o 20:31, Pszemol pisze:

while(Status1 = *pStatus, Status2 = *pStatus, (Status1 ^ Status2) & (1 << 2)) Przepraszam że zawracam głowę, ale jaki jest sens robienia XOR na zmiennych pobierających wartość z tego samego adresu? Tam (praktycznie) zawsze będzie false, chyba że procesor akurat trafi na zmianę statusu drugiej kostki między jednym a drugim odczytem *pStatus, ale z takim szczęściem to lepiej kupony wypełniać niż babrać się w kodzie ;)

U¿ytkownik "Pszemol" napisa³ w wiadomo¶ci grup dyskusyjnych:o7f8jr$e99$ snipped-for-privacy@dont-email.me... //check toggle bit2 indicating erase operation pending while(Status1 = *pStatus, Status2 = *pStatus, (Status1 ^ Status2) & (1 <<

2))

Tez mi sie ten kod nie podoba, ale patrze, patrze ... i bledu nie widze :-) Program sprawdzony z 1 koscia ? Pierwsze co mi sie nie podoba ... czytam status1, pamiec konczy czysczenie, do status2 czytam juz dane. Ale nieszczescia nie bedzie ... jak bit niezgodny, to przeczytamy jeszcze raz, a ewentualny blad bierzemy z bitow status1. Jak zgodny, to jak rozumiem pamiec zakonczyla kasowanie poprawnie ?

{ if(Status1 & (1 << 5)) // check error bit on one chip { LEDFRed(); return 0;

A te bledy sie zgadzaja ? Co wtedy robisz ? Bo tu mi jedna mozliwosc sie rysuje - pierwsza kostka zglasza blad, robimy return ... a druga sie ciagle kasuje. Jesli teraz zacznies jakies resety robic, to kto wie, jak to sie dla drugiej skonczy.

} } while(Status1 = *pStatus, Status2 = *pStatus, (Status1 ^ Status2) & (1 << (16+2)))

To mi sie nie podoba - bo najpierw sprawdzalismy pierwsza koste, a druga lezala odlogiem. Ale jesli w tym czasie druga sie skonczyla ... to program pojdzie dalej, jak nie skonczyla, to poczeka, jak zglasza blad ... dlugo to wachlowanie bitem wtedy trwa ? Czy nie ma sytuacji takiej, ze druga kosc ma blad, a Ty ciagle w pierwszej petli sprawdzasz pierwsza. Pierwsza sie skasuje, a drugiej juz sie znudzi wachlowanie..

{ if(Status1 & (1 << (16+5))) // check error bit on second chip { LEDFRed(); return 0; } }

No i ... przerwania wylaczyles ? Bo jesli jakis odczyt sie zapl±ta miedzy odczyt status1 i 2, to zakonczysz czekanie przed czasem

// Check the erase if(*pSector != 0xffffffff) { LEDFRed(); return 0;

Moze sprawdzic caly blok, a nie tylko poczatek ? Predkosciowo pamiec wyrabia ? moze trzeba troche opoznien dodac ?

} else return 1; }

// returns 1 if success int STFlashWrite32(unsigned int Address, unsigned char *pSource, unsigned int Size)

A problem jest w kasowaniu czy w zapisie ?

{ int Timeout; U32 Status1, Status2; volatile U32 *pDest = (volatile U32 *)Address; U32 *pData = (U32 *)pSource;

// EMCSC bit in System Controls and Status register is cleared in Flash_Init(). // It controls how addresses are output on the EMC address pins. // For 32 bit bus the address is shifted so A2 is on A0 pin. U32 *pA1 = (U32 *)(EXTERNAL_FLASH_LOCATION + (0x555 << 2)); //555

16bit mode U32 *pA2 = (U32 *)(EXTERNAL_FLASH_LOCATION + (0x2AA << 2)); //2AA 16bit mode

moze jakis volatile by sie przydal - bo jak kompilator wykryje, ze stale pod jeden adres zapisuje to samo, to moze zoptymalizowac.

Cache jakiegos ten procesor nie ma ? Te zapisy wychodza do pamieci ?

J.

Piotrze, świetne pytanie. Bo to jest ciekawy fragment procesu kasowania pamieci flash i zapisu do niej.

Taka pamięć ma wewnątrz sterownik ktory kontroluje te procesy niejako z wewnątrz kostki. Obsługujesz go przez wspólną magistralę danych i adresów - scalak pracuje w dwu trybach: odczyt danych (wtedy działa jak klasyczny ROM) oraz kasowanie/zapis: wtedy uzyskujesz dostep do wewnetrznego sterownika umówioną sekwencję bajtôw w roli "sezamie otwórz się!" I wtedy możesz wydawać sterownikowi polecenia kasowania/zapisu konkretnych lokalizacji pamięci... A sterownik informuje Cie o postepie operacji kasowania i o błedach ustawiajac stan bitow szyny danych. Miedzy innymi zmienia on stan bitu D6 gdy operacja jest wciaz w toku. Stad w kodzie dwa kolejne odczyty i stad użycie sugestii "volatile" do kompilatora aby tych dwu odczytów nie "zoptymalizowal" myslac ze są bez sensu, tak jak Ty pomyślałes :-))

Nawiasem mówiąc zmiana testu bitu D2 na bit D6 w czasie kasowania pomogla - nie mam juz błędów o ktorych pisałem wcześniej. Muszę jeszcze raz się przyjrzeć tym flołczartom z dataszyta :-) i przypomnieć sobie dlaczego pisząc ten kod uznałem że testowanie D6 będzie dobre w czasie zapisu flash a powinienem testować D2 w czasie kasowania...

Widziałem deklarację z volatile, więc wiem że powinny być 2 odczyty. Zastanawia mnie jaki jest sens odczytania danych z jednego adresu i zrobienie na nich XORa. Jeżeli między jednym a drugim odczytem nie będzie zmiany to wynik będzie zerowy i program nie wejdzie do pętli - takie jest moje rozumowanie. Może w praktyce jakoś to działa, ale zastanawiam się jaki jest margines błędu w takim rozwiązaniu. Jak najbardziej rozumiem pobranie jednego statusu i sprawdzenie D6, bez XOR to nie pójdzie? Ja bym dał w pętli odczyt statusu i sprawdzenie D6. Wyjście z pętli jeżeli D6 ma odpowiednią wartość lub liczba sprawdzeń przekroczyła np.

100 (i sygnalizacja błędu przekroczonego czasu trwania operacji).

Chyba że czegoś nie ogarniam i ten odczyt z pamięci ma jakieś dodatkowe skutki i może spowodować zmianę odczytanej wartości, ale bez takiej wiedzy nie do końca mi się to składa w całość.

Tak przy okazji pokazywania kodu - myślę że nawet po jednym zdaniu komentarza przed kawałkiem kodu byłoby dobra zachętą do analizowania przez grupowiczów, a i koledzy w pracy pewnie tez docenią jak będą chcieli coś pozmieniać :)

U¿ytkownik "Piotr Dmochowski" napisa³ w wiadomo¶ci grup W dniu 2017-02-10 o 15:55, Pszemol pisze:

Taka ta pamiec - poki sie kasuje, to odczytuje nie dane z pamieci, tylko rejestr statusu, w ktorym sa dwa "toogle bit".

A jak sie skonczy kasowac, to przestaje migac bitami i petla ma sie zakonczyc.

Jak odczytasz raz, to nie wiesz czy bit miga, a wiec nie wiesz czy to rejestr statusu czy dane z pamieci.

J.

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required