Skip to content

Commit

Permalink
ЛР15. Добавление методички
Browse files Browse the repository at this point in the history
  • Loading branch information
HepoH3 committed Dec 31, 2023
1 parent cefe144 commit 1374a14
Show file tree
Hide file tree
Showing 8 changed files with 892 additions and 1 deletion.
Binary file added .pic/Labs/lab_15_coremark/fig_01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
73 changes: 73 additions & 0 deletions Labs/15. Coremark/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
CC_PATH = /c/riscv_cc/bin
CC_PREFIX = riscv-none-elf

CC = $(CC_PATH)/$(CC_PREFIX)-gcc
OBJDUMP = $(CC_PATH)/$(CC_PREFIX)-objdump
OBJCOPY = $(CC_PATH)/$(CC_PREFIX)-objcopy
SIZE = $(CC_PATH)/$(CC_PREFIX)-size

ifndef src
src = core_main.o
endif

OBJS = $(src) startup.o core_list_join.o core_matrix.o core_portme.o core_state.o core_util.o cvt.o ee_printf.o


LINK_SCRIPT = linker_script.ld
OUTPUT = coremark
OUTPUT_PROD = $(addprefix $(OUTPUT), .mem _instr.mem _data.mem .elf _disasm.S)
# OUTPUT_PROD :=$(OUTPUT_PROD) $(addprefix tb_$(OUTPUT), .mem _instr.mem _data.mem .elf _disasm.S)

INC_DIRS = "./"
SRC_DIR = ./src
CC_FLAGS = -march=rv32i_zicsr -mabi=ilp32 -I$(INC_DIRS)
LD_FLAGS = -Wl,--gc-sections -nostartfiles -T $(LINK_SCRIPT)

.PHONY: all setup clean clean_all size harvard princeton

all: clean setup harvard

setup:
cp barebones/*.c barebones/*.h ./

harvard: $(OUTPUT).elf $(OUTPUT)_disasm.S size
# $< означает "первая зависимость"
${OBJCOPY} -O verilog --verilog-data-width=4 -j .data -j .sdata -j .bss $< $(OUTPUT)_data.mem
${OBJCOPY} -O verilog --verilog-data-width=4 -j .text $< $(OUTPUT)_instr.mem
${OBJCOPY} -O verilog -j .data -j .sdata -j .bss $< tb_$(OUTPUT)_data.mem
${OBJCOPY} -O verilog -j .text $< tb_$(OUTPUT)_instr.mem
sed -i '1d' $(OUTPUT)_data.mem

princeton: $(OUTPUT).elf $(OUTPUT)_disasm.S size
${OBJCOPY} -O verilog --verilog-data-width=4 --remove-section=.comment $< $(OUTPUT).mem

$(OUTPUT).elf: $(OBJS)
# $^ Означает "все зависимости".
${CC} $^ $(LD_FLAGS) $(CC_FLAGS) -o $(OUTPUT).elf

$(OUTPUT)_disasm.S: $(OUTPUT).elf
# $< означает "первая зависимость", $@ — "цель рецепта".
${OBJDUMP} -D $< > $@


# Шаблонные рецепты (см. https://web.mit.edu/gnu/doc/html/make_10.html#SEC91)
# Здесь говорится как создать объектные файлы из одноименных исходников
%.o: %.S
${CC} -c $(CC_FLAGS) $^ -o $@

%.o: %.c
${CC} -c $(CC_FLAGS) $^ -o $@

%.o: %.cpp
${CC} -c $(CC_FLAGS) $^ -o $@

size: $(OUTPUT).elf
# $< означает "первая зависимость"
$(SIZE) $<

clean:
rm -f $(OBJS)
rm -f core_portme.* cvt.c ee_printf.c

clean_all: clean
rm -f $(OUTPUT_PROD)
343 changes: 343 additions & 0 deletions Labs/15. Coremark/README.md

Large diffs are not rendered by default.

197 changes: 197 additions & 0 deletions Labs/15. Coremark/linker_script.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
OUTPUT_FORMAT("elf32-littleriscv") /* Указываем порядок следования байт */

ENTRY(_start) /* мы сообщаем компоновщику, что первая
исполняемая процессором инструкция
находится у метки "start"
*/

_text_size = 0x10000; /* Размер памяти инстр.: 16KiB */
_data_base_addr = _text_size; /* Стартовый адрес секции данных */
_data_size = 0x04000; /* Размер памяти данных: 16KiB */

_data_end = _data_base_addr + _data_size;

_trap_stack_size = 2560; /* Размер стека обработчика перехватов.
Данный размер позволяет выполнить
до 32 вложенных вызовов при обработке
перехватов.
*/

_stack_size = 1280; /* Размер программного стека.
Данный размер позволяет выполнить
до 16 вложенных вызовов.
*/

/*
В данном разделе указывается структура памяти:
Сперва идет регион "rom", являющийся read-only памятью с исполняемым кодом
(об этом говорят атрибуты 'r' и 'x' соответственно). Этот регион начинается
с адреса 0x00000000 и занимает _text_size байт.
Далее идет регион "ram", начинающийся с адреса _data_base_addr и занимающий
_data_size байт. Этот регион является памятью, противоположной региону "ram"
(в том смысле, что это не read-only память с исполняемым кодом).
*/
MEMORY
{
rom (x) : ORIGIN = 0x00000000, LENGTH = _text_size
ram (!x) : ORIGIN = _data_base_addr, LENGTH = _data_size
}


/*
В данном разделе описывается размещение программы в памяти.
Программа разделяется на различные секции:
- секции исполняемого кода программа;
- секции статических переменных и массивов, значение которых должно быть
"вшито" в программу;
и т.п.
*/

SECTIONS
{
PROVIDE( _start = 0x00000000 ); /* Позиция start в памяти
/*
В скриптах компоновщика есть внутренняя переменная, записываемая как '.'
Эта переменная называется "счетчиком адресов". Она хранит текущий адрес в
памяти.
В начале файла она инициализируется нулем. Добавляя новые секции, эта
переменная будет увеличиваться на размер каждой новой секции.
Если при размещении секций не указывается никакой адрес, они будут размещены
по текущему значению счетчика адресов.
Этой переменной можно присваивать значения, после этого, она будет
увеличиваться с этого значения.
Подробнее:
https://home.cs.colorado.edu/~main/cs1300/doc/gnu/ld_3.html#IDX338
*/

/*
Следующая команда сообщает, что начиная с адреса, которому в данных момент
равен счетчик адресов (в данный момент, начиная с нуля) будет находиться
секция .text итогового файла, которая состоит из секций .boot, а также всех
секций, начинающихся на .text во всех переданных компоновщику двоичных
файлах.
Дополнительно мы указываем, что данная секция должна быть размещена в
регионе "rom".
*/
.text : {*(.boot) *(.text*)} >rom


/*
Поскольку мы не знаем суммарного размера получившейся секции, мы проверяем
что не вышли за границы памяти инструкций и переносим счетчик адресов за
пределы памяти инструкций в область памяти данных.
Дополнительно мы указываем, что данная секция должна быть размещена в
регионе "ram".
*/
ASSERT(. < _text_size, ".text section exceeds instruction memory size")
. = _data_base_addr;

/*
Следующая команда сообщает, что начиная с адреса, которому в данных момент
равен счетчик адресов (_data_base_addr) будет находиться секция .data
итогового файла, которая состоит из секций всех секций, начинающихся
на .data во всех переданных компоновщику двоичных файлах.
Дополнительно мы указываем, что данная секция должна быть размещена в
регионе "ram".
*/
.data : {*(.*data*)} >ram

/*
Общепринято присваивать GP значение равное началу секции данных, смещенное
на 2048 байт вперед.
Благодаря относительной адресации со смещением в 12 бит, можно адресоваться
на начало секции данных, а так же по всему адресному пространству вплоть до
4096 байт от начала секции данных, что сокращает объем требуемых для
адресации инструкций (практически не используются операции LUI, поскольку GP
уже хранит базовый адрес и нужно только смещение).
Подробнее:
https://groups.google.com/a/groups.riscv.org/g/sw-dev/c/60IdaZj27dY/m/s1eJMlrUAQAJ
*/
_gbl_ptr = _data_base_addr + 0x800;


/*
Поскольку мы не знаем суммарный размер всех используемых секций данных,
перед размещением других секций, необходимо выравнять счетчик адресов по
4х-байтной границе.
*/
. = ALIGN(4);


/*
BSS (block started by symbol, неофициально его расшифровывают как
better save space) — это сегмент, в котором размещаются неинициализированные
статические переменные. В стандарте Си сказано, что такие переменные
инициализируются нулем (или NULL для указателей). Когда вы создаете
статический массив — он должен быть размещен в исполняемом файле.
Без bss-секции, этот массив должен был бы занимать такой же объем
исполняемого файла, какого объема он сам. Массив на 1000 байт занял бы
1000 байт в секции .data.
Благодаря секции bss, начальные значения массива не задаются, вместо этого
здесь только записываются названия переменных и их адреса.
Однако на этапе загрузки исполняемого файла теперь необходимо принудительно
занулить участок памяти, занимаемый bss-секцией, поскольку статические
переменные должны быть проинициализированы нулем.
Таким образом, bss-секция значительным образом сокращает объем исполняемого
файла (в случае использования неинициализированных статических массивов)
ценой увеличения времени загрузки этого файла.
Для того, чтобы занулить bss-секцию, в скрипте заводятся две переменные,
указывающие на начало и конец bss-секции посредством счетчика адресов.
Подробнее:
https://en.wikipedia.org/wiki/.bss

Дополнительно мы указываем, что данная секция должна быть размещена в
регионе "ram".
*/
_bss_start = .;
.bss : {*(.bss*)} >ram
_bss_end = .;


/*=================================
Секция аллоцированных данных завершена, остаток свободной памяти отводится
под программный стек, стек прерываний и (возможно) кучу. В соглашении о
вызовах архитектуры RISC-V сказано, что стек растет снизу вверх, поэтому
наша цель разместить его в самых последних адресах памяти.
Поскольку стеков у нас два, в самом низу мы разместим стек прерываний, а
над ним программный стек. При этом надо обеспечить защиту программного
стека от наложения на него стека прерываний.
Однако перед этим, мы должны убедиться, что под программный стек останется
хотя бы 1280 байт (ничем не обоснованное число, взятое с потолка).
Такое значение обеспечивает до 16 вложенных вызовов (если сохранять только
необерегаемые регистры).
=================================
*/

/* Мы хотим гарантировать, что под стек останется как минимум 1280 байт */
ASSERT(. < (_data_end - _trap_stack_size - _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) */
. = _data_end;

/*
Обычно память имеет размер, кратный 16, но на случай, если это не так, мы
делаем проверку, после которой мы либо остаемся в самом конце памяти (если
конец кратен 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")
}
Loading

0 comments on commit 1374a14

Please sign in to comment.