assembler - test pamięci ram - avr

0

Witam

Zrobiłem funkcję do testowania ramu dla Atmega16 AVR 8-bitowiec. Oto jej listing :

#define RAM_END   (0x045F)
#define RAM_START (0x0060)

/* function check practical RAM without CPU register space and without SFR register space */
uint8_t ram_test()
{
    volatile register uint8_t failure;
    volatile register uint8_t SaveRamData;
    volatile register uint8_t *pRamAddr;

    failure = FALSE;

    /* RAM addresses: 0x60 to 0x41F : 1KB = 1024 bytes */
    for ( pRamAddr = (uint8_t *)RAM_START; pRamAddr <= (uint8_t *)RAM_END; pRamAddr++ )
    {
        /* save RAM data cell to the register */
        SaveRamData = *pRamAddr;

        /* write test pattern 0xAA to RAM */
        *pRamAddr = RAM_TESTPATTERN_AA;
        /* read test pattern 0xAA back and check - if pattern is failed , set return value as fail */
        if (*pRamAddr != RAM_TESTPATTERN_AA) failure = TRUE;

        /* write test pattern 0x55 to RAM */
        *pRamAddr = RAM_TESTPATTERN_55;
        /* read test pattern 0x55 back and check - if pattern is failed , set return value as fail */
        if (*pRamAddr != RAM_TESTPATTERN_55) failure = TRUE;

        /* restore data back to RAM */
        *pRamAddr = SaveRamData;
    }

    /* return failure register */
    return failure;
}

Przez kompilator zostaje to przetworzone na assemblera w takie coś :

000002ba <ram_test>:
 2ba:	df 93       	push	r29						R29 na stos
 2bc:	cf 93       	push	r28						R28 na stos
 2be:	00 d0       	rcall	.+0      	; 0x2c0 <ram_test+0x6>		
 2c0:	cd b7       	in	r28, 0x3d	; 61				stack pointer 1-st byte do R28
 2c2:	de b7       	in	r29, 0x3e	; 62				stack pointer 2-nd byte do R29
 2c4:	19 82       	std	Y+1, r1	; 0x01			R1 pod adres (stack pointer + 1) - średnio rozumiem tą instrukcję ???
 2c6:	e0 e6       	ldi	r30, 0x60	; 96				załaduj do R30 adres początku ramu 1-st byte
 2c8:	f0 e0       	ldi	r31, 0x00	; 0				załaduj do R31 adres początku ramu 2-nd byte
 2ca:	3a ea       	ldi	r19, 0xAA	; 170				ładuj wzorzec 0xAA do R19
 2cc:	91 e0       	ldi	r25, 0x01	; 1				ładuj wartość 1 do R25
 2ce:	25 e5       	ldi	r18, 0x55	; 85				ładuj wzorzec 0x55 do R18

 2d0:	80 81       	ld	r24, Z					zrzuć wartość badanej komórki ram do rejestru R24
 2d2:	8a 83       	std	Y+2, r24	; 0x02			wrzuć rejestr R24 pod adres (stack pointer + 2) - ???
 2d4:	30 83       	st	Z, r19					zapis badanej komórki ram patternem 0xAA
 2d6:	80 81       	ld	r24, Z					odczytaj badaną komórkę ram i wrzuć do rejestru R24
 2d8:	8a 3a       	cpi	r24, 0xAA	; 170				sprawdź czy w R24 mamy 0xAA
 2da:	09 f0       	breq	.+2      	; 0x2de <ram_test+0x24>jeśli tak to przeskocz kolejną instrukcję
 2dc:	99 83       	std	Y+1, r25	; 0x01			wrzuć R25 (czyli TRUE) na stos pod adres (stack pointer + 1)
 2de:	20 83       	st	Z, r18					zapis badanej komórki ram patternem 0x55
 2e0:	80 81       	ld	r24, Z					odczyt badanej komórki ram do R24
 2e2:	85 35       	cpi	r24, 0x55	; 85				sprawdź czy w R24 mamy 0x55
 2e4:	09 f0       	breq	.+2      	; 0x2e8 <ram_test+0x2e>jeśli tak to przeskocz następną instrukcję
 2e6:	99 83       	std	Y+1, r25	; 0x01			wrzuć R25 (czyli TRUE) na stos pod adres (stack pointer + 1)
 2e8:	8a 81       	ldd	r24, Y+2	; 0x02	wyciągnij daną ze stosu (stack pointer + 2) - powinna być tam oryginalna zawartość
 2ea:	81 93       	st	Z+, r24					przywrócenie wartości komórce ram oraz inkrementacja adr.-pokazuje teraz kolejną
 2ec:	84 e0       	ldi	r24, 0x04	; 4				ładuj 4 pod R24
 2ee:	e0 36       	cpi	r30, 0x60	; 96				sprawdź czy adres doszedł do końca (z przeniesieniem)
 2f0:	f8 07       	cpc	r31, r24					sprawdź czy adres doszedł do końca
 2f2:	71 f7       	brne	.-36     	; 0x2d0 <ram_test+0x16>jeśli nie to rób kolejną iterację (skok do 0x2d0)
 2f4:	89 81       	ldd	r24, Y+1	; 0x01
 2f6:	0f 90       	pop	r0
 2f8:	0f 90       	pop	r0
 2fa:	cf 91       	pop	r28
 2fc:	df 91       	pop	r29
 2fe:	08 95       	ret

Dokonałem gruntownej analizy tego podprogramu i mam pytania:

  1. Czy instrukcja
  2c4:	19 82       	std	Y+1, r1	; 0x01 

wkłada na stos rejestr R1 ?
2. Jaki jest tego sens jeśli ani nie jest ten rejestr R1 przywracany oraz to miejsce na stosie jest w dalszej części nadpisywane rejestrem R25 ?
3. Dlaczego na początku mamy dwa razy push a na końcu jest aż cztery razy pop jeśli przecież zwracaną wartością jest uint8_t a procek to Atmega16 czyli ośmiobitowiec ? Jeśli żadna instrukcja poza push i pop nie modyfikuje stack pointera to to bezsensu wg mnie!
4. Czy instrukcje operujące na stosie pośrednio (czyli std Y+1, ...) modyfikują stack pointera ?
5. Zakładając że Y przechowuje aktualną wartość stack pointera to czy instrukcja std Y+1, ... pisze od tego miejsca o jeden adres w górę czy w dół, bo składnia wskazuje że w górę a przecież stos powinien rozrastać się w dół adresów ?
6. Z kodu asm wynika że rejestr R24 będzie zmieniony po zawołaniu tego podprogramu, czy to normalne ?
7. Dlaczego nie są spełnione założenia kompilatora gcc ABI (aplication binary interface) które powinny gwarantować że w ciele rozwinięcia asm R1 będzie zerem ?
8. Czy mechanizm wykryje również uszkodzenie komórki o adresie stack pointer - 2 i stack pointer -1 ?

Ogółem mechanizm działa tak że iteruje od adresu początkowego ramu do adresu końca ramu, jednak stos jest umieszczony właśnie na końcu ramu i rozrasta się w kierunku mniejszych adresów (sprawdzałem to niedawno dokładnie i jestem tego pewien).
Funkcja ta testuje ram zapisując każdą komórkę wzorcem 0xAA odczytując i sprawdzając czy się wzorzec poprawnie zapisał.
Następnie to samo ze wzorcem 0x55. Zwracana wartość którą otrzymuję to false (czyli ram sprawny).
Jednak nie rozumiem jak to ustrojstwo może w ogóle działać jeśli przecież z kodu assemblera wynika że zanim dojdzie do sprawdzenia to dana komórka jest odkładana na stos. Weźmy bowiem pod uwagę moment gdy program sprawdza komórkę stosu na którą jednocześnie chce zrzucać coś. Zrobi tak: zrzuci ją na stos, zapisze wzorcem, posprawdza se a następnie będzie chciał odtworzyć a przecież oryginalna zawartość się nadpisała zatem nie potrafi odtworzyć tej komórki stosu. Nie rozumiem jak to może działać. Bardzo pomocnym byłoby gdyby ktoś po prostu zechciał odpowiedzieć na powyższe ponumerowane pytania.

Priorytetem jest dla mnie pytanie 3, potem 2, następnie 5 potem 6 a na końcu 7. Reszta może poczekać.
Oczywiście mam po swojemu odpowiedzi ale gdyby ktoś był łaskaw to sprawdzić to sobie swoje zweryfikuję i wyciągnę wnioski raz na zawsze!
Obiecuję że w przyszłości nie będę tak zanudzać!

Gorąco pozdrawiam i z góry dziękuję za zainteresowanie.
Adam

1

Myślę, że tylko @hiimJockiee zna odpowiedzi na Twoje pytania.

2

Ad.1 R1 zawiera 0, czyli zerujesz pamięć pod tym adresem.
Ad.2 Muszę to przeanalizować, potem odpowiem
Ad.3 Bo rcall też wkłada adres powrotu na stos. A tak sztucznie go zrzucasz i od razu wracasz dokąd należy.
Ad.4 Głowy nie dam, ale raczej nie bo i po co?
Ad.5 Dlatego ten rcall +0 rezerwuje pamięć na stosie
Ad.6 Tak, zawiera zwróconą wartość.
Ad.7 Pewnie zeruje go w jakimś __start albo podobnym? Jak wchodzisz w asm i popsujesz to będzie to Twoja wina ;)
Ad.8 Jak ad.2, na gorąco: raczej to nie zadziała. ;)

0

Kurcze fajnie że zechciałeś odpisać. Dziękuję.
Wszystko mniej więcej rozumiem, mam jednak pytanie co do odp. na pyt.1.
Na jakiej podstawie twierdzisz że rejestr R1 zawiera zero (i wobec tego to miejsce w pamięci jest zerowane) ?

2

Na podstawie specyfikacji avr gcc.
Na dowód:
http://www.atmel.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_reg_usage.html

Oraz empirycznie:

int main() {
	for(;;);
	return 0;
}

skompilowany przez
E:\temp\avrgcc>"D:\Atmel\Atmel Toolchain\AVR8 GCC\Native\3.4.1061\avr8-gnu-toolchain\bin\avr-gcc" -mmcu=atmega8 main.c -o test.elf deasembluje się do:


test.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0:	12 c0       	rjmp	.+36     	; 0x26 <__ctors_end>
   2:	19 c0       	rjmp	.+50     	; 0x36 <__bad_interrupt>
   4:	18 c0       	rjmp	.+48     	; 0x36 <__bad_interrupt>
   6:	17 c0       	rjmp	.+46     	; 0x36 <__bad_interrupt>
   8:	16 c0       	rjmp	.+44     	; 0x36 <__bad_interrupt>
   a:	15 c0       	rjmp	.+42     	; 0x36 <__bad_interrupt>
   c:	14 c0       	rjmp	.+40     	; 0x36 <__bad_interrupt>
   e:	13 c0       	rjmp	.+38     	; 0x36 <__bad_interrupt>
  10:	12 c0       	rjmp	.+36     	; 0x36 <__bad_interrupt>
  12:	11 c0       	rjmp	.+34     	; 0x36 <__bad_interrupt>
  14:	10 c0       	rjmp	.+32     	; 0x36 <__bad_interrupt>
  16:	0f c0       	rjmp	.+30     	; 0x36 <__bad_interrupt>
  18:	0e c0       	rjmp	.+28     	; 0x36 <__bad_interrupt>
  1a:	0d c0       	rjmp	.+26     	; 0x36 <__bad_interrupt>
  1c:	0c c0       	rjmp	.+24     	; 0x36 <__bad_interrupt>
  1e:	0b c0       	rjmp	.+22     	; 0x36 <__bad_interrupt>
  20:	0a c0       	rjmp	.+20     	; 0x36 <__bad_interrupt>
  22:	09 c0       	rjmp	.+18     	; 0x36 <__bad_interrupt>
  24:	08 c0       	rjmp	.+16     	; 0x36 <__bad_interrupt>

00000026 <__ctors_end>:
  26:	11 24       	eor	r1, r1
  28:	1f be       	out	0x3f, r1	; 63
  2a:	cf e5       	ldi	r28, 0x5F	; 95
  2c:	d4 e0       	ldi	r29, 0x04	; 4
  2e:	de bf       	out	0x3e, r29	; 62
  30:	cd bf       	out	0x3d, r28	; 61
  32:	02 d0       	rcall	.+4      	; 0x38 <main>
  34:	06 c0       	rjmp	.+12     	; 0x42 <_exit>

00000036 <__bad_interrupt>:
  36:	e4 cf       	rjmp	.-56     	; 0x0 <__vectors>

00000038 <main>:
  38:	cf 93       	push	r28
  3a:	df 93       	push	r29
  3c:	cd b7       	in	r28, 0x3d	; 61
  3e:	de b7       	in	r29, 0x3e	; 62
  40:	ff cf       	rjmp	.-2      	; 0x40 <__SREG__+0x1>

00000042 <_exit>:
  42:	f8 94       	cli

00000044 <__stop_program>:
  44:	ff cf       	rjmp	.-2      	; 0x44 <__stop_program>

Zwróć uwagę na to:
00000026 <__ctors_end>:
26: 11 24 eor r1, r1

Ad.2 Sam sobie odpowiedziałeś tu:
2dc: 99 83 std Y+1, r25 ; 0x01 wrzuć R25 (czyli TRUE) na stos pod adres (stack pointer + 1)
tam siedzi po prostu zmienna failure...

0

Słuchaj może to trochę głupie pytanie ale czemu pokazujesz mi jakiś blok asm'a o nazwie "__ctors_end" - chodzi mi skąd w ogóle wiesz kiedy on będzie wywoływany i czemu akurat ten podprogram pokazujesz na dowód tego że r1 jest zerowane ?

1 użytkowników online, w tym zalogowanych: 0, gości: 1