Разработка цифровых ИС на примере микроконтроллерного ядра SCR1 - прgграммное обеспечение
Из секции .data:
.align 4; message: .string "Core is ready for work!\n"
Побайтовый вывод отладочного сообщения осуществляется следующим образом:
- Устанавливается счётчик выведенных байт и их общее количество
- Проверяется статус очереди TX. Если очередь занята, остаётся в цикле проверки. Если очередь свободна, то считывается заданный символ строки и загружается в TX
- Инкрементируется счётчик байт (a0) и указателя на строку (a6).
После вывода сообщения ядро входит в бесконечный цикл (j .) в ожидании прерывания.
/* Byte count */ li a0, 0 li s10, 24 1: li t0, SC1F_UART_ST_TRDY lw t1, SC1F_UART_STATUS(s0) and t1, t1, t0 beqz t1, 1b lbu t0, 0(a6) sb t0, SC1F_UART_TXD(s0) addi a6, a6, 1 addi a0, a0, 1 bne a0, s10, 1b
Программный код загрузчика: Обработчик прерываний
.align 4 trap_vector: li t5, 0x10000 bne a7, t5, 1f csrr s0, mcause li t0, 0xfff and s0, s0, t0 li t6, CAUSE_MACHINE_ECALL beq s0, t6, transmit_exit_code 1: j mtvec_handler
Договоримся, что в рамках наших тестов прерывание от инструкции ecall и значение регистра a7, равное 0x10000 означает, что требуется перейти к выводу сообщения об успешности выполнения нашего кода.
В случае, если эти условия не выполняются, совершается переход на mtvec_handler, в котором будут обрабатываться остальные прерывания.
.align 4 mtvec_handler: csrr s1, mcause /* Mask the mcause */ li t0, (1 << (__riscv_xlen - 1)) | 0xFFF and s1, s1, t0 /* Compare cause with external interrupt */ li t0, (1 << (__riscv_xlen - 1)) | IRQ_M_EXT beq s1, t0, uart_handler
Причина возникновения прерывания или исключения содержится в регистре mcause. Значение mcause маскируется (нужны только младшие 12 бит и старший (XLEN-1), сигнализирующий о прерывании.
Здесь сравнивается значение mcause и интересующий нас код внешнего прерывания. Если они совпадают, совершается переход по адресу uart_handler.
Далее представлены обработчики для других значений mcause. В случае их обнаружения прерываний или исключений, отличных от внешних, будет выведено сообщение об ошибке.
При желании можно прописать поведение ядра в случае возникновения этих прерываний/исключений. Пример для исключения misaligned fetch:
li t0, (1 << (__riscv_xlen - 1)) | CAUSE_MISALIGNED_FETCH beq s1, t0, misaligned_fetch … misaligned_fetch: li gp, 0x1 j 1f … 1: j fail
Программный код загрузчика Обработчик прерываний UART
Проверка наличия прерывания, ожидающего обработки:
uart_handler: li gp, 0x8 # Check interrupt pending bit of IPIC ICSR csrr s0, IPIC_ICSR li t0, (1 << 1) | (1 << 0) and s0, s0, t0 bne s0, t0, fail
Далее - в прилагаемом архиве. Рассмотренные примеры помогут разобраться в остальном исходном коде.
Тестовая программа
~/scr1-sdk-new/sw/uart_test/test.S
mv t0, zero addi t0, t0, 1 addi t1, t0, 2 addi t2, t1, 3 addi t3, t2, 4 addi t4, t3, 5 addi t5, t4, 6 addi t6, t5, 7 addi s0, t6, 8 addi s1, s0, 9 addi s2, s1, 10 csrr s6, time csrr s7, mstatus jr s10
Тестовая программа выполняет ряд простых операций (сложение чисел и считывание ряда CSR регистров). В конце производится безусловный переход по адресу в регистре s10, который должен быть записан в процессе обработки прерывания UART.
Для корректной обработки прерывания требуется сформировать пакет UART. В этом нам поможет скрипт, который будет представлен на следующем слайде.
Двоичный код данной программы будет загружен в память микроконтроллера по UART.
Формирование пакета UART
Первые 32 бита бинарного файла содержат размер последующих инструкций.
~/scr1-sdk-new/sw/uart_test/size_of.sh
#!/bin/bash make clean make size=$(wc -c < <build_dir>/test.bin) while [ $((size % 4)) -ne 0 ]; do ((size++)) Done size_hex=$(printf "%08x\n" $size) echo $size_hex > <build_dir</size.txt xxd -r -p <build_dir>/size.txt > < build_dir > /size.bin cat < build_dir >/size.bin < build_dir> /test.bin > < build_dir >/test_2.bin mv < build_dir >/test_2.bin < build_dir >/test.bin xxd -p < build_dir >/test.bin > < build_dir >/test_hex
Сформированный пакет UART
После исполнения скрипта мы получим файл следующей структуры:
Первые 4 байта содержат размер последующих данных
Вспомогательные скрипты: TTY
Для отправки данных устройству потребуется определить TTY, к которому подключена отладочная плата. Для этого можно воспользоваться следующим скриптом:
~/scripts/know_tty
#!/bin/bash for sysdevpath in $(find /sys/bus/usb/devices/usb*/ -name dev); do ( syspath="${sysdevpath%/dev}" devname="$(udevadm info -q name -p $syspath)" [[ "$devname" == "bus/"* ]] && exit eval "$(udevadm info -q property --export -p $syspath)" [[ -z "$ID_SERIAL" ]] && exit echo "/dev/$devname - $ID_SERIAL" ) Done
Вывод данного скрипта выглядит следующим образом:
Для приёма и отправки данных ядру SCR1 используется ttyUSB1.
! У вас номер TTY может отличаться. Учтите это в последующих скриптах: send_uart, setup_screen.sh.
Вспомогательные скрипты: Подключение OpenOCD
Для загрузки конфигурационного файла для ПЛИС используется скрипт, вызывающий утилиту openFPGAloader.
~/scripts/connect_ocd.sh - прошивка конфигурации и подключение OpenOCD.
#!/bin/bash if [ "$1" = "reboot" ] then sudo openFPGALoader -b arty_a7_35t /arty_scr1_new.bit fi sudo /sc-dt/tools/bin/openocd -s /sc-dt/tools/share/openocd/scripts \ -f /sc-dt/tools/share/openocd/scripts/interface/ftdi/olimex-arm-usb-tiny-h.cfg \ -f /sc-dt/tools/share/openocd/scripts/target/syntacore_riscv.cfg
Опция reboot позволит загрузить битовый поток в ПЛИС из указанной директории. Эта команда может быть использована отдельно.