Зеркало сайта: vivacious-stockings-frog.cyclic.app

Отладка прошивки в Linux с помощью эмулятора SimulAVR и отладчика AVR-GDB. Чтение из Flash памяти AVR

разделы: AVR , дата: 20 марта 2016г.

Обычно, чем больше размер программы, тем чаще приходится пользоваться отладчиком. В AVR Studio встроен замечательный эмулятор микроконтроллеров, на котором можно проверить работоспособность прошивки, и можно порадоваться за Windows пользователей, но что делать пользователям Linux?

В Linux в качестве эмулятора может выступить SimulAVR, а отладчиком avr-gdb. Собственно, работе с этим отладчиком и посвящен этот пост, потому-что им же производится отладка по JTAG. Но, пока я JTAG не разжился, придется пользоваться эмулятором. SimulAVR поддерживает небольшой набор микроконтроллеров и к том уже не всю периферию он эмулирует, но таймеры и порты преимущественно работают.

При запуске SimulAVR без параметров отобразится справка по ключам и список поддерживаемых микроконтроллеров:
Supported devices: at90can128 at90can32 at90can64 at90s4433 at90s8515 atmega128 atmega1284 atmega1284a atmega16 atmega164 atmega164a atmega168 atmega32 atmega324 atmega324a atmega328 atmega48 atmega644 atmega644a atmega8 atmega88 attiny2313 attiny25 attiny45 attiny85

К сожалению XMega не поддерживается ;) поэтому тренироваться будем на ATmega8. Для работы с отладчиком нужно будет скомпилировать прошивку с опцией -ggdb и убрать оптимизацию кода опцией -O0.

SimulAVR и AVR-GDB работают по клиент-серверной архитектуре. Первый выступает в роли сервера, второй в роли клиента.

Для отладки возьмем такой пример:

#include <avr/io.h>
#include <util/delay.h>
#include <uart.h>

#define LED PB5
#define NUM 10
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);


int main(void)
{
 //---- init
 init_uart();
 DDRB |= (1<<LED); // led indicator

 //---- ready indication
 blink13(3);
 stdout = &mystdout;
 printf("Ok, I'm ready!\n");

 uint8_t i,count;
 uint8_t a[NUM];
        count=0;

 for(;;) {
  for(i=0;i<NUM;i++) {
   a[i]=++count;
   printf("a[%d]= %d",i, count);
  }
  printf("\n");
  _delay_ms(1000);
  count++;
 }
 return 0;
}

Прошивка весит более 2Кбайт, то что надо.

Сейчас нужно запустить simularvr в режиме сервера:

$  simulavr -g -p 1200 -d atmega8 -F 8000000
Waiting on port 1200 for gdb client to connect...

Затем запустить отладчик avr-gdb:

$ avr-gdb ./sim.elf
GNU gdb (GDB) 7.8.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-slackware-linux --target=avr".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./sim.elf...done.

подключиться им к simulavr серверу:

(gdb) target remote localhost:1200
Remote debugging using localhost:1200
0x00000000 in __vectors ()

и загрузить на сервер прошивку:

(gdb) load
Loading section .text, size 0xbae lma 0x0
Loading section .data, size 0x28 lma 0xbae
Start address 0x0, load size 3030
Transfer rate: 295 KB/sec, 757 bytes/write.
(gdb)

Теперь можно поставить точно останова - breakpoint. Из всех брейков, мой самый любимый: break main. Можно иметь сколь угодно запутанную программу, состоящую их десятков исходников, но когда брейк на main, всегда попадаешь в начало кода:

(gdb) break main
Breakpoint 1 at 0x72: file main.c, line 13.

После чего следует запустить программу на исполнение. Для этого вместо run, в avr-gdb служит команда continue:

(gdb) continue
Continuing.

Breakpoint 1, main () at main.c:13
warning: Source file is more recent than executable.
13              init_uart();
(gdb)

Ok, программа запустилась и остановилась на breakpoint. Мы внутри работающей программы. Для начала можно осмотреться. Командой list можно распечатать листинг исходной программы:

(gdb) list
8
9
10      int main(void)
11      {
12              //---- init
13              init_uart();
14              DDRB |= (1<<LED); // led indicator
15
16              //---- ready indication
17              blink13(3);

а командой disass посмотреть ассемблерный код:

(gdb) disass
Dump of assembler code for function main:
   0x0000005e <+0>:     push    r28
   0x00000060 <+2>:     push    r29
   0x00000062 <+4>:     in      r28, 0x3d       ; 61
   0x00000064 <+6>:     in      r29, 0x3e       ; 62
   0x00000066 <+8>:     sbiw    r28, 0x1a       ; 26
   0x00000068 <+10>:    in      r0, 0x3f        ; 63
   0x0000006a <+12>:    cli
   0x0000006c <+14>:    out     0x3e, r29       ; 62
   0x0000006e <+16>:    out     0x3f, r0        ; 63
   0x00000070 <+18>:    out     0x3d, r28       ; 61
=> 0x00000072 <+20>:    rcall   .+366           ;  0x1e2 <init_uart>
   0x00000074 <+22>:    ldi     r24, 0x37       ; 55
   0x00000076 <+24>:    ldi     r25, 0x00       ; 0
   0x00000078 <+26>:    ldi     r18, 0x37       ; 55
   0x0000007a <+28>:    ldi     r19, 0x00       ; 0
   0x0000007c <+30>:    movw    r30, r18
   0x0000007e <+32>:    ld      r18, Z
   0x00000080 <+34>:    ori     r18, 0x20       ; 32
   0x00000082 <+36>:    movw    r30, r24
   0x00000084 <+38>:    st      Z, r18
   0x00000086 <+40>:    ldi     r24, 0x03       ; 3
   0x00000088 <+42>:    rcall   .+406           ;  0x220 <blink13>
   0x0000008a <+44>:    ldi     r24, 0x60       ; 96
   0x0000008c <+46>:    ldi     r25, 0x00       ; 0
   0x0000008e <+48>:    sts     0x008B, r25
   0x00000092 <+52>:    sts     0x008A, r24
   0x00000096 <+56>:    ldi     r24, 0x6E       ; 110
   0x00000098 <+58>:    ldi     r25, 0x00       ; 0
   0x0000009a <+60>:    rcall   .+1378          ;  0x5fe <puts>
   0x0000009c <+62>:    std     Y+1, r1 ; 0x01
   0x0000009e <+64>:    rjmp    .+86            ;  0xf6 <main+152>
   0x000000a0 <+66>:    ldd     r24, Y+1        ; 0x01
   0x000000a2 <+68>:    mov     r24, r24
   0x000000a4 <+70>:    ldi     r25, 0x00       ; 0
   0x000000a6 <+72>:    ldd     r18, Y+2        ; 0x02
   0x000000a8 <+74>:    subi    r18, 0xFF       ; 255
   0x000000aa <+76>:    std     Y+2, r18        ; 0x02
   0x000000ac <+78>:    movw    r18, r28
   0x000000ae <+80>:    subi    r18, 0xEF       ; 239
   0x000000b0 <+82>:    sbci    r19, 0xFF       ; 255
   0x000000b2 <+84>:    add     r24, r18
   0x000000b4 <+86>:    adc     r25, r19
   0x000000b6 <+88>:    ldd     r18, Y+2        ; 0x02
   0x000000b8 <+90>:    movw    r30, r24
   0x000000ba <+92>:    st      Z, r18
   0x000000bc <+94>:    ldd     r24, Y+2        ; 0x02
   0x000000be <+96>:    mov     r18, r24
   0x000000c0 <+98>:    ldi     r19, 0x00       ; 0
   0x000000c2 <+100>:   ldd     r24, Y+1        ; 0x01
   0x000000c4 <+102>:   mov     r24, r24
   0x000000c6 <+104>:   ldi     r25, 0x00       ; 0
   0x000000c8 <+106>:   mov     r20, r19
   0x000000ca <+108>:   push    r20
   0x000000cc <+110>:   push    r18
   0x000000ce <+112>:   mov     r18, r25
   0x000000d0 <+114>:   push    r18
   0x000000d2 <+116>:   push    r24
   0x000000d4 <+118>:   ldi     r24, 0x7D       ; 125
   0x000000d6 <+120>:   ldi     r25, 0x00       ; 0
   0x000000d8 <+122>:   mov     r24, r25
   0x000000da <+124>:   push    r24
   0x000000dc <+126>:   ldi     r24, 0x7D       ; 125
   0x000000de <+128>:   ldi     r25, 0x00       ; 0
   0x000000e0 <+130>:   push    r24
   0x000000e2 <+132>:   rcall   .+1256          ;  0x5cc <printf>
   0x000000e4 <+134>:   pop     r0
   0x000000e6 <+136>:   pop     r0
   0x000000e8 <+138>:   pop     r0
   0x000000ea <+140>:   pop     r0
   0x000000ec <+142>:   pop     r0
   0x000000ee <+144>:   pop     r0
   0x000000f0 <+146>:   ldd     r24, Y+1        ; 0x01
   0x000000f2 <+148>:   subi    r24, 0xFF       ; 255
   0x000000f4 <+150>:   std     Y+1, r24        ; 0x01
   0x000000f6 <+152>:   ldd     r24, Y+1        ; 0x01
   0x000000f8 <+154>:   cpi     r24, 0x0A       ; 10
   0x000000fa <+156>:   brcs    .-92            ;  0xa0 <main+66>
   0x000000fc <+158>:   ldi     r24, 0x0A       ; 10
   0x000000fe <+160>:   ldi     r25, 0x00       ; 0
   0x00000100 <+162>:   rcall   .+1264          ;  0x5f2 <putchar>
   0x00000102 <+164>:   ldi     r24, 0x00       ; 0
   0x00000104 <+166>:   ldi     r25, 0x00       ; 0
   0x00000106 <+168>:   ldi     r26, 0x7A       ; 122
   0x00000108 <+170>:   ldi     r27, 0x44       ; 68
   0x0000010a <+172>:   std     Y+3, r24        ; 0x03
   0x0000010c <+174>:   std     Y+4, r25        ; 0x04
   0x0000010e <+176>:   std     Y+5, r26        ; 0x05
   0x00000110 <+178>:   std     Y+6, r27        ; 0x06
   0x00000112 <+180>:   ldi     r18, 0x00       ; 0
   0x00000114 <+182>:   ldi     r19, 0x00       ; 0
   0x00000116 <+184>:   ldi     r20, 0xFA       ; 250
   0x00000118 <+186>:   ldi     r21, 0x44       ; 68
   0x0000011a <+188>:   ldd     r22, Y+3        ; 0x03
   0x0000011c <+190>:   ldd     r23, Y+4        ; 0x04
   0x0000011e <+192>:   ldd     r24, Y+5        ; 0x05
   0x00000120 <+194>:   ldd     r25, Y+6        ; 0x06
   0x00000122 <+196>:   rcall   .+914           ;  0x4b6 <__mulsf3>
   0x00000124 <+198>:   movw    r26, r24
   0x00000126 <+200>:   movw    r24, r22
   0x00000128 <+202>:   std     Y+7, r24        ; 0x07
   0x0000012a <+204>:   std     Y+8, r25        ; 0x08
   0x0000012c <+206>:   std     Y+9, r26        ; 0x09
   0x0000012e <+208>:   std     Y+10, r27       ; 0x0a
   0x00000130 <+210>:   ldi     r18, 0x00       ; 0
   0x00000132 <+212>:   ldi     r19, 0x00       ; 0
   0x00000134 <+214>:   ldi     r20, 0x80       ; 128
   0x00000136 <+216>:   ldi     r21, 0x3F       ; 63
   0x00000138 <+218>:   ldd     r22, Y+7        ; 0x07
   0x0000013a <+220>:   ldd     r23, Y+8        ; 0x08
   0x0000013c <+222>:   ldd     r24, Y+9        ; 0x09
   0x0000013e <+224>:   ldd     r25, Y+10       ; 0x0a
   0x00000140 <+226>:   rcall   .+626           ;  0x3b4 <__nesf2>
   0x00000142 <+228>:   and     r24, r24
   0x00000144 <+230>:   brge    .+10            ;  0x150 <main+242>
   0x00000146 <+232>:   ldi     r24, 0x01       ; 1
   0x00000148 <+234>:   ldi     r25, 0x00       ; 0
   0x0000014a <+236>:   std     Y+12, r25       ; 0x0c
   0x0000014c <+238>:   std     Y+11, r24       ; 0x0b
   0x0000014e <+240>:   rjmp    .+118           ;  0x1c6 <main+360>
   0x00000150 <+242>:   ldi     r18, 0x00       ; 0
   0x00000152 <+244>:   ldi     r19, 0xFF       ; 255
   0x00000154 <+246>:   ldi     r20, 0x7F       ; 127
   0x00000156 <+248>:   ldi     r21, 0x47       ; 71
   0x00000158 <+250>:   ldd     r22, Y+7        ; 0x07
   0x0000015a <+252>:   ldd     r23, Y+8        ; 0x08
   0x0000015c <+254>:   ldd     r24, Y+9        ; 0x09
   0x0000015e <+256>:   ldd     r25, Y+10       ; 0x0a
   0x00000160 <+258>:   rcall   .+844           ;  0x4ae <__gesf2>
   0x00000162 <+260>:   cp      r1, r24
   0x00000164 <+262>:   brge    .+78            ;  0x1b4 <main+342>
   0x00000166 <+264>:   ldi     r18, 0x00       ; 0
   0x00000168 <+266>:   ldi     r19, 0x00       ; 0
   0x0000016a <+268>:   ldi     r20, 0x20       ; 32
   0x0000016c <+270>:   ldi     r21, 0x41       ; 65
   0x0000016e <+272>:   ldd     r22, Y+3        ; 0x03
   0x00000170 <+274>:   ldd     r23, Y+4        ; 0x04
   0x00000172 <+276>:   ldd     r24, Y+5        ; 0x05
   0x00000174 <+278>:   ldd     r25, Y+6        ; 0x06
   0x00000176 <+280>:   rcall   .+830           ;  0x4b6 <__mulsf3>
   0x00000178 <+282>:   movw    r26, r24
   0x0000017a <+284>:   movw    r24, r22
   0x0000017c <+286>:   movw    r22, r24
   0x0000017e <+288>:   movw    r24, r26
   0x00000180 <+290>:   rcall   .+570           ;  0x3bc <__fixunssfsi>
   0x00000182 <+292>:   movw    r26, r24
   0x00000184 <+294>:   movw    r24, r22
   0x00000186 <+296>:   std     Y+12, r25       ; 0x0c
   0x00000188 <+298>:   std     Y+11, r24       ; 0x0b
   0x0000018a <+300>:   rjmp    .+30            ;  0x1aa <main+332>
   0x0000018c <+302>:   ldi     r24, 0xC8       ; 200
   0x0000018e <+304>:   ldi     r25, 0x00       ; 0
   0x00000190 <+306>:   std     Y+14, r25       ; 0x0e
   0x00000192 <+308>:   std     Y+13, r24       ; 0x0d
   0x00000194 <+310>:   ldd     r24, Y+13       ; 0x0d
   0x00000196 <+312>:   ldd     r25, Y+14       ; 0x0e
   0x00000198 <+314>:   sbiw    r24, 0x01       ; 1
   0x0000019a <+316>:   brne    .-4             ;  0x198 <main+314>
   0x0000019c <+318>:   std     Y+14, r25       ; 0x0e
   0x0000019e <+320>:   std     Y+13, r24       ; 0x0d
   0x000001a0 <+322>:   ldd     r24, Y+11       ; 0x0b
   0x000001a2 <+324>:   ldd     r25, Y+12       ; 0x0c
   0x000001a4 <+326>:   sbiw    r24, 0x01       ; 1
   0x000001a6 <+328>:   std     Y+12, r25       ; 0x0c
   0x000001a8 <+330>:   std     Y+11, r24       ; 0x0b
   0x000001aa <+332>:   ldd     r24, Y+11       ; 0x0b
   0x000001ac <+334>:   ldd     r25, Y+12       ; 0x0c
   0x000001ae <+336>:   sbiw    r24, 0x00       ; 0
   0x000001b0 <+338>:   brne    .-38            ;  0x18c <main+302>
   0x000001b2 <+340>:   rjmp    .+38            ;  0x1da <main+380>
   0x000001b4 <+342>:   ldd     r22, Y+7        ; 0x07
   0x000001b6 <+344>:   ldd     r23, Y+8        ; 0x08
   0x000001b8 <+346>:   ldd     r24, Y+9        ; 0x09
   0x000001ba <+348>:   ldd     r25, Y+10       ; 0x0a
   0x000001bc <+350>:   rcall   .+510           ;  0x3bc <__fixunssfsi>
   0x000001be <+352>:   movw    r26, r24
   0x000001c0 <+354>:   movw    r24, r22
   0x000001c2 <+356>:   std     Y+12, r25       ; 0x0c
   0x000001c4 <+358>:   std     Y+11, r24       ; 0x0b
   0x000001c6 <+360>:   ldd     r24, Y+11       ; 0x0b
   0x000001c8 <+362>:   ldd     r25, Y+12       ; 0x0c
   0x000001ca <+364>:   std     Y+16, r25       ; 0x10
   0x000001cc <+366>:   std     Y+15, r24       ; 0x0f
   0x000001ce <+368>:   ldd     r24, Y+15       ; 0x0f
   0x000001d0 <+370>:   ldd     r25, Y+16       ; 0x10
   0x000001d2 <+372>:   sbiw    r24, 0x01       ; 1
   0x000001d4 <+374>:   brne    .-4             ;  0x1d2 <main+372>
   0x000001d6 <+376>:   std     Y+16, r25       ; 0x10
   0x000001d8 <+378>:   std     Y+15, r24       ; 0x0f
   0x000001da <+380>:   ldd     r24, Y+2        ; 0x02
   0x000001dc <+382>:   subi    r24, 0xFF       ; 255
   0x000001de <+384>:   std     Y+2, r24        ; 0x02
   0x000001e0 <+386>:   rjmp    .-326           ;  0x9c <main+62>
End of assembler dump.
(gdb)

Здесь в правой колонке после точки с запятой есть метки, что бы понимать к какому участку кода относится дизассемблерный листинг.

Команда info registers печатает содержимое регистров:

(gdb) info registers
r0             0x0      0
r1             0x0      0
r2             0xaa     170
r3             0xaa     170
r4             0xaa     170
r5             0xaa     170
r6             0xaa     170
r7             0xaa     170
r8             0xaa     170
r9             0xaa     170
r10            0xaa     170
r11            0xaa     170
r12            0xaa     170
r13            0xaa     170
r14            0xaa     170
r15            0xaa     170
r16            0xaa     170
r17            0x0      0
r18            0xaa     170
r19            0xaa     170
r20            0xaa     170
r21            0xaa     170
r22            0xaa     170
r23            0xaa     170
r24            0xaa     170
r25            0xaa     170
r26            0x8e     142
r27            0x0      0
r28            0x41     65
r29            0x4      4
r30            0xd6     214
r31            0xb      11
SREG           0x0      0
SP             0x800441 0x800441
PC2            0x72     114
pc             0x72     0x72 
(gdb)

где первая колонка - наименование регистра, вторая колонка - шестнадцатеричное значение, третья - десятичное значение. Не обязательно каждый раз распечатывать все регистры, часто бывает достаточно знать значение одного:

(gdb) info registers r17
r17            0x0      0
(gdb)

к регистрам ввода-ввыода(РВВ), так обращаться не получится. Технически, РВВ это обычная ячейка памяти, обращаться к ней надо по адресу. DDRB в ATmega8 имеет адрес 0x17, PORTB - 0x18 что бы вывести их значение потребуется команда печати дампа оперативки x/xb:

(gdb) x/xb 0x17
0x800017:       0xaa
(gdb) x/xb 0x18
0x800018:       0xaa
(gdb)

теперь "перепрыгнем" на вложенный цикл for, это 26-я строка исходника:

(gdb) break main.c:26
Breakpoint 2 at 0x9c: file main.c, line 26.
(gdb) continue
Continuing.

Breakpoint 2, main () at main.c:26
26                     for(i=0;i<NUM;i++) {
(gdb)

информацию по точкам останова можно посмотреть командой info breakpoint:

(gdb) info breakpoint
Num     Type           Disp Enb Address    What
1       breakpoint     keep y   0x00000072 in main at main.c:13
        breakpoint already hit 1 time
2       breakpoint     keep y   0x0000009c in main at main.c:26
        breakpoint already hit 1 time
(gdb)
точки останова можно удалять, отключать, снова включать:
(gdb) d 1
(gdb) info breakpoint
Num     Type           Disp Enb Address    What
2       breakpoint     keep y   0x0000009c in main at main.c:26
        breakpoint already hit 1 time
(gdb) disable 2
(gdb) info breakpoint
Num     Type           Disp Enb Address    What
2       breakpoint     keep n   0x0000009c in main at main.c:26
        breakpoint already hit 1 time
(gdb) enable 2
(gdb) info breakpoint
Num     Type           Disp Enb Address    What
2       breakpoint     keep y   0x0000009c in main at main.c:26
        breakpoint already hit 1 time
(gdb) continue
Continuing.

Breakpoint 2, main () at main.c:26
26                      for(i=0;i<NUM;i++) {
(gdb)

распечатать значение переменной можно командой print, ее акронимом p, или той же x/xb:

(gdb) p i
$4 = 10 '\n'
(gdb) x/xb i
0x80000a:       0xaa
(gdb) p count
$5 = 22 '\026'
(gdb) x/xb count
0x800016:       0x10
(gdb) p a
$6 = "\f\r\016\017\020\021\022\023\024\025"
(gdb) x/xb a
0x800452:       0x0c
(gdb) x/10xb a
0x800452:       0x0c    0x0d    0x0e    0x0f    0x10    0x11    0x12    0x13
0x80045a:       0x14    0x15
(gdb) x/10xb b
No symbol "b" in current context.
(gdb)

Пошаговое выполнение осуществляется командами next(акроним n) - без захода в подпрограммы, или step (акроним s) - с заходом в подпрограммы:

(gdb) n
27                              a[i]=++count;
(gdb) p i
$7 = 0 '\000'
(gdb) p count
$8 = 22 '\026'
(gdb) n
28                              printf("a[%d]= %d",i, count);
(gdb) p count
$9 = 23 '\027'
(gdb) n
26                      for(i=0;i<NUM;i++) {
(gdb)
27                              a[i]=++count;
(gdb)
28                              printf("a[%d]= %d",i, count);
(gdb)
26                      for(i=0;i<NUM;i++) {
(gdb)
27                              a[i]=++count;
(gdb)
28                              printf("a[%d]= %d",i, count);
(gdb) p count
$10 = 25 '\031'
(gdb) p i
$11 = 2 '\002'
(gdb)

изменить значение переменной из отладчика можно командой set:

(gdb) set var count=100
(gdb) p count
$12 = 100 'd'
(gdb) n 15
28                              printf("a[%d]= %d",i, count);
(gdb) p i
$13 = 7 '\a'
(gdb) x/10xb a
0x800452:       0x17    0x18    0x19    0x65    0x66    0x67    0x68    0x69
0x80045a:       0x14    0x15
(gdb)

одной из самых распространенных ошибок на Си, является выход за границу массива. На этапе компиляции такие ошибки не отлавливаются, часто такая такая обшивка "всплывает" при крайних значениях, границах массиво. Попробуем сымитировать такую ситуацию:

(gdb) n
26                      for(i=0;i<NUM;i++) {
(gdb) set var i=7
(gdb) n
27                              a[i]=++count;
(gdb) p i
$15 = 8 '\b'
(gdb) set var i=10
(gdb) x/12xb a
0x800452:       0x17    0x18    0x19    0x65    0x66    0x67    0x68    0x69
0x80045a:       0x14    0x15    0x04    0x5f
(gdb) n
28                              printf("a[%d]= %d",i, count);
(gdb) x/xb count
0x80006a : 0x00
(gdb) p  count
$16 = 106 'j'
(gdb) x/12xb a
0x800452:       0x17    0x18    0x19    0x65    0x66    0x67    0x68    0x69
0x80045a:       0x14    0x15    0x6a    0x5f
(gdb) p i
$17 = 10 '\n'
(gdb) p count
$18 = 106 'j'
(gdb)

в итоге, мы наглядно увидели как программа начинает переписывать памяь других переменных. В современных операционных системах, такие штуки приводят к срабатыванию защитных механизмов ядра, и программа валится с ошибкой.

Работа с Flash памятью в AVR

С азами, разобрались, теперь попробуем с помощью отладчика разобраться в таком непростом вопросе как работа с Flash памятью. AVR позволяет хранить там различные константы и массивы. Flash память доступна только для чтения.

Немного погуглив по теме я выяснил, что чтобы скопировать массив из Flash памяти в ram, я должен получить адрес массива в flash и затем, с помощью макроса pgm_read_byte считать его в свой буффер.

В результате была написана такая пробная программа:

#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <uart.h>

#define LED PB5
#define NUM 10

const unsigned char mydata[NUM] PROGMEM = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09};

static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);


int main(void)
{
	//---- init
	init_uart();
	DDRB |= (1<<LED); // led indicator

	//---- ready indication
	blink13(3);
	stdout = &mystdout;
	printf("Ok, I'm ready!\n");

	uint8_t i;
	uint8_t bf[NUM];

	char * ptr= (char *)pgm_read_word(&mydata[0]);

	for(;;)	{
                PORTB ^= (1<<LED);

		for(i=0;i<NUM;i++) {
			bf[i]=pgm_read_byte(ptr+i);
			printf("%d ",bf[i]);

		}
		printf("\n");
		_delay_ms(1000);

	}
	return 0;
}

здесь массив mydata записаный во флеш-памяти копируется в буффер в оперативке, содержимое которого выводится через UART. Однако, стоит залесть отладчиком внутрь программы, то обнаруживается такая картинка:

$ avr-gdb --silent ./sim.elf
Reading symbols from ./sim.elf...done.
(gdb) target remote localhost:1200
Remote debugging using localhost:1200
0x00000000 in __vectors ()
(gdb) load
Loading section .text, size 0xc02 lma 0x0
Loading section .data, size 0x22 lma 0xc02
Start address 0x0, load size 3108
Transfer rate: 1011 KB/sec, 621 bytes/write.
(gdb) break main.c:38
Breakpoint 1 at 0x156: file main.c, line 38.
(gdb) continue
Continuing.

Breakpoint 1, main () at main.c:38
38                      printf("\n");
(gdb) x/10xb bf
0x800452:       0x24    0x91    0x2a    0x87    0x4a    0x85    0x9e    0x01
0x80045a:       0x27    0x5e
(gdb) print ptr
$1 = 0x800100 '\252' ...

т.е. ничего даже близко похожего на содержимое исходного массива в буфере нет, а указатель указывает на 0x100. Однако если дизассемблировать прошивку:

$ avr-objdump -S sim.elf|head -n 40

sim.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0:   17 c0           rjmp    .+46            ; 0x30 <__ctors_end>
   2:   31 c0           rjmp    .+98            ; 0x66 <__bad_interrupt>
   4:   30 c0           rjmp    .+96            ; 0x66 <__bad_interrupt>
   6:   2f c0           rjmp    .+94            ; 0x66 <__bad_interrupt>
   8:   2e c0           rjmp    .+92            ; 0x66 <__bad_interrupt>
   a:   2d c0           rjmp    .+90            ; 0x66 <__bad_interrupt>
   c:   2c c0           rjmp    .+88            ; 0x66 <__bad_interrupt>
   e:   2b c0           rjmp    .+86            ; 0x66 <__bad_interrupt>
  10:   2a c0           rjmp    .+84            ; 0x66 <__bad_interrupt>
  12:   29 c0           rjmp    .+82            ; 0x66 <__bad_interrupt>
  14:   28 c0           rjmp    .+80            ; 0x66 <__bad_interrupt>
  16:   27 c0           rjmp    .+78            ; 0x66 <__bad_interrupt>
  18:   26 c0           rjmp    .+76            ; 0x66 <__bad_interrupt>
  1a:   25 c0           rjmp    .+74            ; 0x66 <__bad_interrupt>
  1c:   24 c0           rjmp    .+72            ; 0x66 <__bad_interrupt>
  1e:   23 c0           rjmp    .+70            ; 0x66 <__bad_interrupt>
  20:   22 c0           rjmp    .+68            ; 0x66 <__bad_interrupt>
  22:   21 c0           rjmp    .+66            ; 0x66 <__bad_interrupt>
  24:   20 c0           rjmp    .+64            ; 0x66 <__bad_interrupt>

00000026 <__trampolines_end>:
  26:   00 01           movw    r0, r0
  28:   02 03           mulsu   r16, r18
  2a:   04 05           cpc     r16, r4
  2c:   06 07           cpc     r16, r22
  2e:   08 09           sbc     r16, r8

00000030 <__ctors_end>:
  30:   11 24           eor     r1, r1
  32:   1f be           out     0x3f, r1        ; 63
  34:   cf e5           ldi     r28, 0x5F       ; 95
  36:   d4 e0           ldi     r29, 0x04       ; 4
  38:   de bf           out     0x3e, r29       ; 62

где красным выделен исходный массив. И начинается на с адреса 0х26.

Тогда пробуем изменть указатель ptr с 0x100 на 0x26 и считать исходный массив еще раз:

(gdb) set var  ptr=0x26
(gdb) continue
Continuing.

Breakpoint 1, main () at main.c:38
38                      printf("\n");
(gdb) x/10xb bf
0x800452:       0x00    0x01    0x02    0x03    0x04    0x05    0x06    0x07
0x80045a:       0x08    0x09

ну, так граздо лучше. Методом "нучного тыка" было обнаружено, что в программе нужно строку:

char * ptr= (char *)pgm_read_word(&mydata[0]);

заменить на:

 char * ptr= (char *)(&mydata);

проверяем:

$ avr-gdb --silent ./sim.elf
Reading symbols from ./sim.elf...done.
(gdb) target remote localhost:1200
Remote debugging using localhost:1200
0x00000000 in __vectors ()
(gdb) load
Loading section .text, size 0xbe6 lma 0x0
Loading section .data, size 0x22 lma 0xbe6
Start address 0x0, load size 3080
Transfer rate: 1002 KB/sec, 616 bytes/write.
(gdb) break main.c:38
Breakpoint 1 at 0x13a: file main.c, line 38.
(gdb) continue
Continuing.

Breakpoint 1, main () at main.c:38
38                      printf("\n");
(gdb) x/10xb bf
0x800452:       0x00    0x01    0x02    0x03    0x04    0x05    0x06    0x07
0x80045a:       0x08    0x09
(gdb) p ptr
$1 = 0x800026 ""

На этот раз все Ok

скачать архив с последним примером: скачать