Опубликован: 04.08.2025 | Доступ: свободный | Студентов: 7 / 0 | Длительность: 02:58:00
Лекция 5:

Разработка цифровых ИС на примере микроконтроллерного ядра SCR1 - прgграммное обеспечение

Для сборки программы bootloader.S может использоваться следующий Makefile:

bootloader.o:   bootloader.S        # Build object file
       riscv64-unknown-elf-gcc -march=rv32im_zicsr -mabi=ilp32 -c bootloader.S -o bootloader.o

build:    bootloader.elf      #   Target build

bootloader.elf:   bootloader.o   common/ram.ld  #  Link object to elf
     riscv64-unknown-elf-gcc   -march=rv32i   -mabi=ilp32   -nostartfiles   -nodefaultlibs   -nolibc -T \
        common/ram.ld bootloader.o -o bootloader.elf

dump:  bootloader.elf
       riscv64-unknown-elf-objdump   -w   -x   -s   -S bootloader.elf > bootloader.dump

clean:    #    Delete  all  compiled files
       rm bootloader.elf bootloader.o bootloader.dump -f

В предоставляемых примерах данного курса используется Makefile, который был создан на основе примеров из репозитория SCR1.

~/scr1-sdk-new/sw/my_boot/Makefile

APP += bootloader

APP_SRC += bootloader.S

COMMON_BASE = common
include $(COMMON_BASE)/common.mk

Makefile в директории, содержащий исходные коды программы, подключает основной Makefile common.mk, содержащий нужные правила и цели.

Значения флагов GCC, указываемых при сборке:

  • -march - Определяет целевую архитектуру. Позволяет указать доступные компилятору набор инструкций и регистры
  • -mabi - Задает используемый ABI
  • -O[X] - Уровень оптимизации. X = g, используемый нами по умолчанию, задаёт уровень оптимизации для отладки, при котором выходном файле сохраняются отладочные символы.

Сборка: Linker Script

Скрипт компоновщинка (Linker script) описывает то, как секции входных объектных файлов должны быть отображены в выходной файл.

Рассмотрим скрипт компоновщика, использующийся в нашем проекте:


RAM - название региона памяти; rwx - права на чтение, запись и исполнение; ORIGIN - базовый адрес региона (здесь 0 - 64K = 0xFFFF0000, базовый адрес RAM этой платформы); LENGTH - размер региона в байтах.

В исходном коде на языке ассемблера используется директива .section, разбивающая код на секции. Эти секции необходимо распределить по нужным адресам в памяти устройства.


Обычно секция .text содержит в себе исполняемый код, а секция .data - данные, используемые программой.

Ещё примеры секций:


Рассмотрим секцию, содержащую reset vector и trap vector, которые так же задаются в описании ядра.


Структура загрузчика


  • Инициализация целочисленных регистров x1-x31 и CSR регистров;
  • Установка вектора прерываний на адрес обработчика прерываний;
  • Инициализация отображаемых в память регистров UART и контроллера прерываний;
  • Обеспечение вывода отладочных сообщений.

Ядро будет принимать исполняемый код по UART, исполнять его и выводить сообщение о результате исполнения.

Bare-metal драйвера

MMR (memory-mapped registers), регистры, отображаемые в память - представляют собой адреса в памяти, запись и чтение значений из которых позволяет осуществлять управление некоторым устройством посредством работы с памятью.

В нашем случае такими устройствами являются контроллер прерываний IPIC и регистры UART. Для упрощения работы с ними в оригинальном репозитории SCR1 имеются заголовочные файлы, содержащие bare-metal драйвера. В этом проекте данные файлы были упрощены до нескольких определений (#define).

Определение MMR:

#define БАЗОВЫЙ_АДРЕС (адрес)
#define ИМЯ_РЕГИСТРА (БАЗОВЫЙ_АДРЕС + смещение)

Bare-metal драйвера: IPIC

Примеры из драйвера IPIC (заголовочный файл ipic.h)

#ifndef IPIC_H
#define IPIC_H

// IPIC memory map
#define PLF_IPIC_MBASE (0xbf0)
#define IPIC_CISV (PLF_IPIC_MBASE + 0)
#define IPIC_CICSR (PLF_IPIC_MBASE + 1)
…
// Status
#define IPIC_IRQ_PENDING             (1 << 0)
#define IPIC_IRQ_ENABLE                (1 << 1)
…
#endif