mirror of
https://github.com/MPSU/APS.git
synced 2025-09-17 18:10:39 +00:00
ЛР14. Рефактор методички
This commit is contained in:
@@ -8,53 +8,39 @@
|
||||
See https://github.com/MPSU/APS/blob/master/LICENSE file for licensing details.
|
||||
* ------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
OUTPUT_FORMAT("elf32-littleriscv") /* Указываем порядок следования байт */
|
||||
|
||||
ENTRY(_start) /* мы сообщаем компоновщику, что первая
|
||||
исполняемая процессором инструкция
|
||||
находится у метки "start"
|
||||
*/
|
||||
|
||||
_text_size = 0x1000; /* Размер памяти инстр.: 4KiB */
|
||||
_data_size = 0x4000; /* Размер памяти данных: 16KiB */
|
||||
|
||||
ASSERT(!(_text_size & (_text_size-1)), /* Проверка что размеры памяти */
|
||||
"Instr mem size is not power of 2") /* являются степенью двойки */
|
||||
ASSERT(!(_data_size & (_data_size-1)),
|
||||
"Data mem size is not power of 2")
|
||||
|
||||
_data_base_addr = _text_size >= _data_size ? /* Стартовый адрес секции данных */
|
||||
_text_size : _data_size; /* указан как больший из размеров*/
|
||||
/* секции инструкций/данных */
|
||||
|
||||
_data_end = _data_base_addr + _data_size;
|
||||
|
||||
_trap_stack_size = 2560; /* Размер стека обработчика перехватов.
|
||||
Данный размер позволяет выполнить
|
||||
до 32 вложенных вызовов при обработке
|
||||
перехватов.
|
||||
*/
|
||||
|
||||
_stack_size = 1280; /* Размер программного стека.
|
||||
Данный размер позволяет выполнить
|
||||
до 16 вложенных вызовов.
|
||||
находится у метки "_start"
|
||||
*/
|
||||
|
||||
/*
|
||||
В данном разделе указывается структура памяти:
|
||||
Сперва идет регион "rom", являющийся памятью с исполняемым кодом
|
||||
(об этом говорит аттрибут 'x'). Этот регион начинается
|
||||
с адреса 0x00000000 и занимает _text_size байт.
|
||||
Далее идет регион "ram", начинающийся с адреса _data_base_addr и занимающий
|
||||
_data_size байт. Этот регион является памятью, противоположной региону "rom"
|
||||
(в том смысле, что это не память с исполняемым кодом).
|
||||
Сперва идет регион "instr_mem", являющийся памятью с исполняемым кодом
|
||||
(об этом говорит аттрибут 'x'). Этот регион начинается
|
||||
с адреса 0x00000000 и занимает 1024 байта.
|
||||
Далее идет регион "data_mem", начинающийся с адреса 0x00000000 и занимающий
|
||||
2048 байт. Этот регион является памятью, противоположной региону "instr_mem"
|
||||
(в том смысле, что это не память с исполняемым кодом).
|
||||
*/
|
||||
MEMORY
|
||||
{
|
||||
rom (x) : ORIGIN = 0x00000000, LENGTH = _text_size
|
||||
ram (!x) : ORIGIN = _data_base_addr, LENGTH = _data_size
|
||||
instr_mem (x) : ORIGIN = 0x00000000, LENGTH = 1K
|
||||
data_mem (!x) : ORIGIN = 0x00000000, LENGTH = 2K
|
||||
}
|
||||
|
||||
_trap_stack_size = 640; /* Размер стека обработчика перехватов.
|
||||
Данный размер позволяет выполнить
|
||||
до 8 вложенных вызовов при обработке
|
||||
перехватов.
|
||||
*/
|
||||
|
||||
_stack_size = 640; /* Размер программного стека.
|
||||
Данный размер позволяет выполнить
|
||||
до 8 вложенных вызовов.
|
||||
*/
|
||||
|
||||
/*
|
||||
В данном разделе описывается размещение программы в памяти.
|
||||
@@ -67,7 +53,7 @@ MEMORY
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
PROVIDE( _start = 0x00000000 ); /* Позиция start в памяти*/
|
||||
|
||||
/*
|
||||
В скриптах компоновщика есть внутренняя переменная, записываемая как '.'
|
||||
Эта переменная называется "счетчиком адресов". Она хранит текущий адрес в
|
||||
@@ -89,32 +75,20 @@ SECTIONS
|
||||
секций, начинающихся на .text во всех переданных компоновщику двоичных
|
||||
файлах.
|
||||
Дополнительно мы указываем, что данная секция должна быть размещена в
|
||||
регионе "rom".
|
||||
регионе "instr_mem".
|
||||
*/
|
||||
.text : {*(.boot) *(.text*)} >rom
|
||||
.text : {
|
||||
PROVIDE(_start = .);
|
||||
*(.boot)
|
||||
*(.text*)
|
||||
} > instr_mem
|
||||
|
||||
|
||||
/*
|
||||
Поскольку мы не знаем суммарного размера получившейся секции, мы проверяем
|
||||
что не вышли за границы памяти инструкций и переносим счетчик адресов за
|
||||
пределы памяти инструкций в область памяти данных.
|
||||
Дополнительно мы указываем, что данная секция должна быть размещена в
|
||||
регионе "ram".
|
||||
*/
|
||||
ASSERT(. < _text_size, ".text section exceeds instruction memory size")
|
||||
. = _data_base_addr;
|
||||
|
||||
/*
|
||||
Следующая команда сообщает, что начиная с адреса, которому в данных момент
|
||||
равен счетчик адресов (_data_base_addr) будет находиться секция .data
|
||||
итогового файла, которая состоит из секций всех секций, начинающихся
|
||||
на .data во всех переданных компоновщику двоичных файлах.
|
||||
Дополнительно мы указываем, что данная секция должна быть размещена в
|
||||
регионе "ram".
|
||||
*/
|
||||
.data : {*(.*data*)} >ram
|
||||
|
||||
/*
|
||||
В скобках после оператора AT указывается Load Memory Address (LMA). Чтобы
|
||||
адреса памяти инструкций и памяти данных при компоновке не пересекались, мы
|
||||
будем использовать заведомо несуществующий LMA, который в последствии будем
|
||||
игнорировать.
|
||||
.data : AT (0x80000000) {
|
||||
/*
|
||||
Общепринято присваивать GP значение равное началу секции данных, смещенное
|
||||
на 2048 байт вперед.
|
||||
Благодаря относительной адресации со смещением в 12 бит, можно адресоваться
|
||||
@@ -124,13 +98,15 @@ SECTIONS
|
||||
уже хранит базовый адрес и нужно только смещение).
|
||||
Подробнее:
|
||||
https://groups.google.com/a/groups.riscv.org/g/sw-dev/c/60IdaZj27dY/m/s1eJMlrUAQAJ
|
||||
*/
|
||||
_gbl_ptr = _data_base_addr + 0x800;
|
||||
*/
|
||||
_gbl_ptr = . + 2048;
|
||||
*(.*data*)
|
||||
} > data_mem
|
||||
|
||||
|
||||
/*
|
||||
Поскольку мы не знаем суммарный размер всех используемых секций данных,
|
||||
перед размещением других секций, необходимо выравнять счетчик адресов по
|
||||
перед размещением других секций, необходимо выровнять счетчик адресов по
|
||||
4х-байтной границе.
|
||||
*/
|
||||
. = ALIGN(4);
|
||||
@@ -159,11 +135,14 @@ SECTIONS
|
||||
https://en.wikipedia.org/wiki/.bss
|
||||
|
||||
Дополнительно мы указываем, что данная секция должна быть размещена в
|
||||
регионе "ram".
|
||||
регионе "data_mem".
|
||||
*/
|
||||
_bss_start = .;
|
||||
.bss : {*(.*bss*)} >ram
|
||||
_bss_end = .;
|
||||
.bss : {
|
||||
_bss_start = .;
|
||||
*bss*
|
||||
*(COMMON)
|
||||
_bss_end = .;
|
||||
} > data_mem
|
||||
|
||||
|
||||
/*=================================
|
||||
@@ -174,20 +153,17 @@ SECTIONS
|
||||
Поскольку стеков у нас два, в самом низу мы разместим стек прерываний, а
|
||||
над ним программный стек. При этом надо обеспечить защиту программного
|
||||
стека от наложения на него стека прерываний.
|
||||
Однако перед этим, мы должны убедиться, что под программный стек останется
|
||||
хотя бы 1280 байт (ничем не обоснованное число, взятое с потолка).
|
||||
Такое значение обеспечивает до 16 вложенных вызовов (если сохранять только
|
||||
необерегаемые регистры).
|
||||
Однако перед этим, мы должны убедиться, что под оба стека хватит места.
|
||||
=================================
|
||||
*/
|
||||
|
||||
/* Мы хотим гарантировать, что под стек останется как минимум 1280 байт */
|
||||
ASSERT(. < (_data_end - _trap_stack_size - _stack_size),
|
||||
/* Мы хотим гарантировать, что под стек останется место */
|
||||
ASSERT(. < (LENGTH(data_mem) - _trap_stack_size - _stack_size),
|
||||
"Program size is too big")
|
||||
|
||||
/* Перемещаем счетчик адресов над стеком прерываний (чтобы после мы могли
|
||||
использовать его в вызове ALIGN) */
|
||||
. = _data_end - _trap_stack_size;
|
||||
. = LENGTH(data_mem) - _trap_stack_size;
|
||||
|
||||
/*
|
||||
Размещаем указатель программного стека так близко к границе стека
|
||||
@@ -196,13 +172,14 @@ SECTIONS
|
||||
Подробнее:
|
||||
https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf
|
||||
*/
|
||||
_stack_ptr = ALIGN(16) <= _data_end - _trap_stack_size?
|
||||
_stack_ptr = ALIGN(16) <= LENGTH(data_mem) - _trap_stack_size?
|
||||
ALIGN(16) : ALIGN(16) - 16;
|
||||
ASSERT(_stack_ptr <= _data_end - _trap_stack_size, "SP exceed memory size")
|
||||
ASSERT(_stack_ptr <= LENGTH(data_mem) - _trap_stack_size,
|
||||
"SP exceed memory size")
|
||||
|
||||
/* Перемещаем счетчик адресов в конец памяти (чтобы после мы могли
|
||||
использовать его в вызове ALIGN) */
|
||||
. = _data_end;
|
||||
. = LENGTH(data_mem);
|
||||
|
||||
/*
|
||||
Обычно память имеет размер, кратный 16, но на случай, если это не так, мы
|
||||
@@ -210,6 +187,6 @@ SECTIONS
|
||||
конец кратен 16), либо поднимаемся на 16 байт вверх от края памяти,
|
||||
округленного до 16 в сторону большего значения
|
||||
*/
|
||||
_trap_stack_ptr = ALIGN(16) <= _data_end ? ALIGN(16) : ALIGN(16) - 16;
|
||||
ASSERT(_trap_stack_ptr <= _data_end, "ISP exceed memory size")
|
||||
_trap_stack_ptr = ALIGN(16) <= LENGTH(data_mem) ? ALIGN(16) : ALIGN(16) - 16;
|
||||
ASSERT(_trap_stack_ptr <= LENGTH(data_mem), "ISP exceed memory size")
|
||||
}
|
||||
|
Reference in New Issue
Block a user