ЛР13. Обновление методички

This commit is contained in:
Andrei Solodovnikov
2023-12-04 00:29:49 +03:00
parent bd58b909db
commit 182727c76d
4 changed files with 616 additions and 107 deletions

View File

@@ -1,15 +1,32 @@
OUTPUT_FORMAT("elf32-littleriscv")
ENTRY(_start)
OUTPUT_FORMAT("elf32-littleriscv") /* Указываем порядок следования байт */
ENTRY(_start) /* мы сообщаем компоновщику, что первая
исполняемая процессором инструкция
находится у метки "start"
*/
_text_size = 0x4000; /* Размер памяти инстр.: 16KiB */
_data_base_addr = 0x8000; /* Стартовый адрес секции данных */
_data_size = 0x4000; /* Размер памяти данных: 16KiB */
_data_end = _data_base_addr + _data_size;
_trap_stack_size = 2560; /* Размер стека обработчика перехватов.
Данный размер позволяет выполнить
до 32 вложенных вызовов при обработке
перехватов.
*/
_irq_stack_size = 1280; /* Размер программного стека.
Данный размер позволяет выполнить
до 16 вложенных вызовов.
*/
SECTIONS
{
PROVIDE( _start = 0x00000000 );
PROVIDE( _memory_size = 1024); /* 1024 байта */
.text : {*(.boot) *(.text*)}
PROVIDE( _start = 0x00000000 ); /* Позиция start в памяти
/*
В скриптах линковщика есть внутренняя переменная, записываемая как '.'
Эта переменная называется счетчиком адресов. Она хранит текущий адрес в
В скриптах компоновщика есть внутренняя переменная, записываемая как '.'
Эта переменная называется "счетчиком адресов". Она хранит текущий адрес в
памяти.
В начале файла она инициализируется нулем. Добавляя новые секции, эта
переменная будет увеличиваться на размер каждой новой секции.
@@ -20,12 +37,34 @@ SECTIONS
Подробнее:
https://home.cs.colorado.edu/~main/cs1300/doc/gnu/ld_3.html#IDX338
*/
. = ALIGN(4);
.data : {*(.data*)}
/*
Значение, присвоенное глобальному указателю (GP) выходит за границы RAM,
однако (для архитектуры RISC-V) общепринято присваивать GP значение равное
началу секции данных, смещенное на 2048 байт вперед.
Следующая команда сообщает, что начиная с адреса, которому в данных момент
равен счетчик адресов (в данный момент, начиная с нуля) будет находиться
секция .text итогового файла, которая состоит из секций .boot, а также всех
секций, начинающихся на .text во всех переданных компоновщику двоичных
файлах.
*/
.text : {*(.boot) *(.text*)}
/*
Поскольку мы не знаем суммарного размера получившейся секции, мы проверяем
что не вышли за границы памяти инструкций и переносим счетчик адресов за пределы. Памяти инструкций в область памяти данных
*/
ASSERT(. < _text_size, ".text section exceeds instruction memory size")
. = _data_base_addr;
/*
Следующая команда сообщает, что начиная с адреса, которому в данных момент
равен счетчик адресов (_data_base_addr) будет находиться секция .data итогового файла, которая состоит из секций всех секций, начинающихся
на .data во всех переданных компоновщику двоичных файлах.
*/
.data : {*(.data*)}
/*
Общепринято присваивать GP значение равное началу секции данных, смещенное
на 2048 байт вперед.
Благодаря относительной адресации со смещением в 12 бит, можно адресоваться
на начало секции данных, а так же по всему адресному пространству вплоть до
4096 байт от начала секции данных, что сокращает объем требуемых для
@@ -34,8 +73,16 @@ SECTIONS
Подробнее:
https://groups.google.com/a/groups.riscv.org/g/sw-dev/c/60IdaZj27dY/m/s1eJMlrUAQAJ
*/
_gbl_ptr = . + 0x800 ;
_gbl_ptr = _data_base_addr + 0x800;
/*
Поскольку мы не знаем суммарный размер всех используемых секций данных,
перед размещением других секций, необходимо выравнять счетчик адресов по 4х-байтной границе.
*/
. = ALIGN(4);
/*
BSS (block started by symbol, неофициально его расшифровывают как
better save space) — это сегмент, в котором размещаются неинициализированные
@@ -65,33 +112,47 @@ SECTIONS
/*=================================
Секция аллоцированных данных завершена, остаток свободной памяти отводится
под программный стек и (возможно) кучу. В соглашении о
под программный стек, стек прерываний и (возможно) кучу. В соглашении о
вызовах архитектуры RISC-V сказано, что стек растет снизу вверх, поэтому
наша цель разместить его в самых последних адресах памяти.
Поскольку стеков у нас два, в самом низу мы разместим стек прерываний, а
над ним программный стек. При этом надо обеспечить защиту программного
стека от наложения на него стека прерываний.
Однако перед этим, мы должны убедиться, что под программный стек останется
хотя бы 256 байт (ничем не обоснованное число, взятое с потолка).
Поскольку указатель стека (SP) должен быть выровнен до 16 байт, мы
обеспечиваем себе максимум 16 вложенных вызовов.
Подробнее:
https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf
хотя бы 1280 байт (ничем не обоснованное число, взятое с потолка).
Такое значение обеспечивает до 16 вложенных вызовов (если сохранять только необерегаемые регистры).
=================================
*/
/* Мы хотим гарантировать, что под стек останется как минимум 256 байт */
ASSERT(. < (_memory_size - 256),
/* Мы хотим гарантировать, что под стек останется как минимум 1280 байт */
ASSERT(. < (_data_end - _trap_stack_size - _irq_stack_size),
"Program size is too big")
/* Перемещаем счетчик адресов над стеком прерываний (чтобы после мы могли
использовать его в вызове ALIGN) */
. = _data_end - _trap_stack_size;
/*
Размещаем указатель программного стека так близко к границе стека
прерываний, насколько можно с учетом требования о выравнивании адреса
стека до 16 байт.
Подробнее:
https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf
*/
_stack_ptr = ALIGN(16) <= _data_end - _trap_stack_size?
ALIGN(16) : ALIGN(16) - 16;
ASSERT(_stack_ptr <= _data_end - _trap_stack_size, "SP exceed memory size")
/* Перемещаем счетчик адресов в конец памяти (чтобы после мы могли
использовать его в вызове ALIGN) */
. = _memory_size;
. = _data_end;
/*
Размещаем указатель программного стека так близко к концу памяти,
насколько это можно с учетом требования о выравнивании адреса
стека до 16 байт.
Обычно память имеет размер, кратный 16, но на случай, если это не так, мы
делаем проверку, после которой мы либо остаемся в самом конце памяти (если
конец кратен 16), либо поднимаемся на 16 байт вверх от края памяти,
округленного до 16 в сторону большего значения
*/
_stack_ptr = ALIGN(16) <= _memory_size ?
ALIGN(16) : ALIGN(16) - 16;
ASSERT(_stack_ptr <= _memory_size, "SP exceed memory size")
}
_trap_stack_ptr = ALIGN(16) <= _data_end ? ALIGN(16) : ALIGN(16) - 16;
ASSERT(_trap_stack_ptr <= _data_end, "ISP exceed memory size")
}