-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
891 additions
and
0 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
} |
Oops, something went wrong.