startupxxxx.s ассемблер

На соседней странице мы разобрались с самым началом работы контроллера (ResetHandler и таблица векторов прерываний).

Теперь переходим к анализу startup_stm32fxxxxx.s файла.

Главные файлы проекта для настройки и анализа кода программы

Файл *.ld - тут память распределяется на области RAM и FLASH - это по сути исходник, такой же как и main.c.

Файл *.map - этот файл появляется в результате сборки и содержит полезную информацию что куда распределилось.
Файл *.list - файл тоже появляется в результате сборки крайне важен для анализа памяти.

startup_stm32f407xx.s

Этот файл чистой воды ассемблер и придется вспомнить , что делают ассемблерные инструкции. Очень рекомендую также пройти отладчиком по ассемблеру и посмотреть регистры.

Я замучился искать в интернете описание ассемблерных словечек . Вот где лучше сразу искать http://www.keil.com/support/man/docs/armclang_ref/armclang_ref_bpl1510589893923.htm.

Суть этого файла в том , что программа не сразу стартует с функции main.


.syntax unified  //это говорит ассемблеру что можно использовать Thumb и не Thumb инструкции одновременно
.cpu cortex-m3  // тип нашего контроллера
.thumb  // тип используемых инструкций Thumb

.global  g_pfnVectors  // это не просто объявление глобальной метки g_pfnVectors . 
// Сюда ляжет таблица векторов (которая описана ниже) . Это при компиляции получится адрес х0000-х0184.

.global  Default_Handler - объявление глобальной метки Default_Handler .
// это были только подготовительные объявления переменных

.word  _sidata   - ( LOADADDR(.data))
.word  _sdata    - start of FLASH data
.word  _edata    - end of FLASH data
.word  _sbss       - start of RAM bss
.word  _ebss      - end of RAM bss

// и это тоже были подготовительные объявления переменных

.section  .text.Reset_Handler 
// section (defines a logical memory section which contains program code or data) , 
// text - это тип данных = код прогаммы
//  .Reset_Handler - это так логически обозвали этот первый участок памяти.

.weak  Reset_Handler  // weak означает , что метку можно переопределить в других файлах (если надо)

.type  Reset_Handler, %function  // указываем переменной тип функция 

Reset_Handler:    // это просто метка участка кода программы

ldr   sp, =_estack     // 0x20020000 , в этот момент pc = 0x80013bc 

Важно для понимания основ : PC (program counter - адрес следующей инструкции выполнения) указывает на память FLASH, т.е. код программы записан и выполняется из FLASH , в RAM помещаются только изменяемые данные. Код программы константен.

Reset_Handler:  

ldr   sp, =_estack    // сразу загружаем в регистр sp (stack pointer) верхнее значение RAM. 
// Это важно , т.е. любое прерывание, любая функция сразу будет 
// использовать стек для промежуточного хранения своих данных.

/* Copy the data segment initializers from flash to SRAM */

movs  r1, #0    // записать значение 0 в r1 

b  LoopCopyDataInit  // безусловный переход на метку LoopCopyDataInit  

CopyDataInit:
  
  ldr  r3, =_sidata   // загрузка значения переменной _sidata в регистр r3 ( см. ld файл _sidata = LOADADDR(.data); )  
 // смотрим отладчиком _sidata = 0x800249c - это адрес FLASH

  ldr  r3, [r3, r1]   // загрузка значения с адреса [r3 r1] в r3 с прединкрементом r1  (и это похоже их FLASH)
  str  r3, [r0, r1]   // загрузка r3 по адресу [r0, r1] 
  // смещение задается в r1 - здесь происходит запись в память RAM   по адресу r0 r1 . 
  // тут важно понимать , что для контроллера RAM это просто адрес 0x2000000 ,  FLASH просто адресс 0х8000000 
  // ему пофигу RAM там или FLASH или что-то еще
  adds  r1, r1, #4  :  r1 = r1  4
    
LoopCopyDataInit:

  ldr  r0, =   - start of FLASH data   // смотрим отладчиком  _sdata = 0x2000000 , 
  //т.е. это адрес не FLASH , а RAM ! , т.е. куда копировать из FLASH

  ldr  r3, =_edata  - end of FLASH data  
  // _edata =0x20000068 (маленькая у нас программка), т.е до какого адреса RAM 

  adds  r2, r0, r1   // добавляет к r2 : r0  r1
  cmp  r2, r3   // сравниваем 
  bcc  CopyDataInit   // и если  r2 != r3 переходим на CopyDataInit  

 // в результате блок data скопирован из FLASH в RAM 

Таким образом с нулевого адреса RAM ( 0x2000000) у нас копия того, что во FLASH с адреса _sidata = 0x800249c .
А что это? В файле *.list это область .data. Здесь наши переменные данные.

Самое время посмотреть на *.list. Это распредение областей во FLASH.

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .isr_vector   00000184  08000000  08000000  00010000  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .text         000021d4  08000184  08000184  00010184  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .rodata       00000134  08002358  08002358  00012358  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .ARM          00000008  0800248c  0800248c  0001248c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA

  4 .init_array   00000004  08002494  08002494  00012494  2**2
                  CONTENTS, ALLOC, LOAD, DATA

  5 .fini_array   00000004  08002498  08002498  00012498  2**2
                  CONTENTS, ALLOC, LOAD, DATA

  6 .data         00000068  20000000  0800249c  00020000  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  .data первой копируется в  RAM на адрес 0х2000000

  7 .bss          00000070  20000068  08002504  00020068  2**2
  .bss копируется в RAM на адрес 0х2000068 сразу после .data

                  ALLOC
  8 ._user_heap_stack 00006000  200000d8  08002504  000200d8  2**0
                  ALLOC

Теперь внимание обратите на VMA и LMA колонки и станет понятно , что копируется в RAM , а что во FLASH.

.rodata - это данные с префиксом const , они тоже остаются во FLASH.


//  Продолжаем изучение startup файла:

  ldr  r2, =_sbss   - bss start RAM address   // это у нас 0x20000068
  b  LoopFillZerobss   безусловный переход на LoopFillZerobss   

/* Zero fill the bss segment. */

FillZerobss:

  movs  r3, #0
  str  r3, [r2], #4  записываем значение r3 по адресу [r2]  4  // это у нас 0x20000068 4
    
LoopFillZerobss:
  ldr  r3, = _ebss   bss end RAM address  // 0x200000d8
  cmp  r2, r3
  bcc  FillZerobss

/* Call the clock system initialization function.*/

  bl  SystemInit    // выполнить подпрограмму , из файла на языке C уже : см. system_stm32f2xx.c 
 // (там Reset the RCC clock configuration to the default reset state и Configure the Vector Table location)

/* Call static constructors */
    bl __libc_init_array // здесь не совсем понятно

/* Call the application's entry point.*/

  bl  main // ну и переход на main
  bx  lr      // Возврат из подпрограммы

.size  Reset_Handler, .-Reset_Handler

Но самое интересное в самом конце файла.

Создание Таблицы веторов прерываний

isr_vector


.section  .isr_vector,"a",%progbits 
//.section name [, "flags" [, %type [, entry_size] [, group_name [, linkage]] [, link_order_symbol] [, unique, unique_id] ]]
// здесь мы создаем таблицу векторов прерываний
// флаги секции a :SHF_ALLOC: the section is allocatable , x: SHF_EXECINSTR: the section is executable.
// %progbits  секция содержит инициализарованные данные(data) и/или код

.type  g_pfnVectors, %object  
// .type : directive sets the type of a symbol.
// g_pfnVectors symbol
// %object   is a data object :  The following types are accepted: %function,%object,%tls_object

.size  g_pfnVectors, .-g_pfnVectors
   

g_pfnVectors:
  .word  _estack
  .word  Reset_Handler

  .word  NMI_Handler
  .word  HardFault_Handler
  .word  MemManage_Handler
  .word  BusFault_Handler
  .word  UsageFault_Handler
  .word  0
  .word  0
  .word  0
  .word  0
  .word  SVC_Handler
  .word  DebugMon_Handler
  .word  0
  .word  PendSV_Handler
  .word  SysTick_Handler

А можно ли здесь создать вторую таблицу векторов прерываний ? Можно ,читайте далее