Я работаю над университетским проектом, где я пишу программное обеспечение для микроконтроллера Atmel SAM7S256 с нуля. Это более подробно, чем другие MCU, с которыми я работал раньше, поскольку на этот раз необходимо знание сценариев компоновщика и языка ассемблера.
Я действительно тщательно изучал примеры проектов для чипов SAM7S, чтобы полностью понять, как начать проект SAM7/ARM с нуля. Примечательным примером является Miro Samek "Построение Bare-Metal ARM Systems with GNU", который нашел здесь (где код в этом вопросе из). Я также потратил много времени на чтение документации компоновщика и ассемблера с сайта sourceware.org.
Я очень доволен, что по большей части я понимаю следующий компоновщик script. Есть только одна вещь, включающая счетчик местоположения, который не имеет для меня смысла. Ниже представлен компоновщик script с приведенным выше руководством:
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_vectors)
MEMORY { /* memory map of AT91SAM7S64 */
ROM (rx) : ORIGIN = 0x00100000, LENGTH = 64k
RAM (rwx) : ORIGIN = 0x00200000, LENGTH = 16k
}
/* The sizes of the stacks used by the application. NOTE: you need to adjust */
C_STACK_SIZE = 512;
IRQ_STACK_SIZE = 0;
FIQ_STACK_SIZE = 0;
SVC_STACK_SIZE = 0;
ABT_STACK_SIZE = 0;
UND_STACK_SIZE = 0;
/* The size of the heap used by the application. NOTE: you need to adjust */
HEAP_SIZE = 0;
SECTIONS {
.reset : {
*startup.o (.text) /* startup code (ARM vectors and reset handler) */
. = ALIGN(0x4);
} >ROM
.ramvect : { /* used for vectors remapped to RAM */
__ram_start = .;
. = 0x40;
} >RAM
.fastcode : {
__fastcode_load = LOADADDR (.fastcode);
__fastcode_start = .;
*(.glue_7t) *(.glue_7)
*isr.o (.text.*)
*(.text.fastcode)
*(.text.Blinky_dispatch)
/* add other modules here ... */
. = ALIGN (4);
__fastcode_end = .;
} >RAM AT>ROM
.text : {
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.glue_7) /* glue arm to thumb (NOTE: placed already in .fastcode) */
*(.glue_7t)/* glue thumb to arm (NOTE: placed already in .fastcode) */
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* global symbol at end of code */
} >ROM
.preinit_array : {
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(SORT(.preinit_array.*)))
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >ROM
.init_array : {
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >ROM
.fini_array : {
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(.fini_array*))
KEEP (*(SORT(.fini_array.*)))
PROVIDE_HIDDEN (__fini_array_end = .);
} >ROM
.data : {
__data_load = LOADADDR (.data);
__data_start = .;
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .;
} >RAM AT>ROM
.bss : {
__bss_start__ = . ;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = .;
} >RAM
PROVIDE ( end = _ebss );
PROVIDE ( _end = _ebss );
PROVIDE ( __end__ = _ebss );
.heap : {
__heap_start__ = . ;
. = . + HEAP_SIZE;
. = ALIGN(4);
__heap_end__ = . ;
} >RAM
.stack : {
__stack_start__ = . ;
. += IRQ_STACK_SIZE;
. = ALIGN (4);
__irq_stack_top__ = . ;
. += FIQ_STACK_SIZE;
. = ALIGN (4);
__fiq_stack_top__ = . ;
. += SVC_STACK_SIZE;
. = ALIGN (4);
__svc_stack_top__ = . ;
. += ABT_STACK_SIZE;
. = ALIGN (4);
__abt_stack_top__ = . ;
. += UND_STACK_SIZE;
. = ALIGN (4);
__und_stack_top__ = . ;
. += C_STACK_SIZE;
. = ALIGN (4);
__c_stack_top__ = . ;
__stack_end__ = .;
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ : {
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
}
В этом примере (например, в разделах .ramvect,.fastcode и .stack) существуют обозначения символов, такие как __ram_start = .;
. Эти адреса используются кодом начальной сборки и кодом инициализации C, чтобы инициализировать правильные местоположения в ОЗУ MCU.
У меня проблема с пониманием, так это то, как эти определения символов приводят к назначению правильных значений. Это действительно так, script правильный, я просто не понимаю, как это сделать.
Как я понимаю, когда вы используете счетчик местоположений в разделе, он содержит только относительное смещение от адреса виртуальной памяти (VMA) самого раздела.
Так, например, в строке __ram_start = .;
я ожидал, что __ram_start будет присвоено значение 0x0 - поскольку ему присваивается значение счетчика местоположения в самом начале раздела .ramvect. Однако, чтобы код инициализации работал правильно (что он делает), __ram_start должен получать назначение как 0x00200000 (адрес для начала ОЗУ).
Я бы подумал, что это будет работать только по назначению, если строка была вместо __ram_start = ABSOLUTE(.);
или __ram_start = ADDR(.ramvect);
.
То же самое относится к __fastcode_start
и __stack_start__
. Они не могут быть определены как адрес 0x0, иначе программа не будет работать. Но документация связанная здесь, кажется, предполагает, что это должно произойти. Вот цитата из документации:
Примечание:. фактически относится к смещению байта от начала текущего содержащего объект. Обычно это оператор SECTIONS, чей начальный адрес равен 0. может использоваться как абсолютный адрес. Если. однако используется в описании раздела, но это относится к смещению байта от начала этого раздела, а не к абсолютному адресу.
Таким образом, значения счетчика местоположения во время этих присвоений символов должны быть смещениями из соответствующих разделов VMA. Значит, эти символы "_start" должны быть установлены в 0x0. Что нарушит программу.
Так что, очевидно, я что-то упускаю. Полагаю, это может быть просто то, что присвоение значению счетчика местоположения символу (внутри раздела) приводит к использованию ABSOLUTE() по умолчанию. Но я не смог найти четкое объяснение в любом месте, которое подтверждает это.
Заранее благодарим, если кто-нибудь может это очистить.