Pull to refresh

Comments 38

Вообще то, на С нормального программиста Ваша строка
GPIOB_CTL0 = (GPIOB_CTL0 &~(0b1111<<(RLED*4))) | 0b0011 << (RLED*4);

будет выглядеть совсем по другому, что то вроде
PortB.Pin5=PinMode_Out+PinMode_OpenDrain;

хотя еще лучше
SetPinMode(PortB, Pin5, PinMode_Out_OpenDrain);
там ниже будет замена магических чисел 0b1111 и 0b0011 на константы. А в следующей части будет файл макросов, где это делается еще проще:
#define RLED B, 5, 1, GPIO_PP50
...
GPIO_config( RLED );
GPO_ON( RLED );
Вот с таким вариантом я склонен согласится, но то, что Вы написали сначала — «трэш, угар и содомия».
Это попытка пошагового описания от констант, указанных в даташите, к более-менее нормальному коду. Оно все — переходные варианты, поэтому много внимания им не уделял. Но вы правы, раз уж заменил номер порта на константу, надо было и битовую константу оформить по-человечески

Это сишка курильщика. Здоровые атлеты используют K&R и KNF.

Предположу, что из готовых платок с этим камушком которые можно относительно дешево и доступно купить на ali это Sipeed Longan Nano. А вот где можно было-бы приобрести сами чипы?
Будет ли обзор RISC ассемблера? Большая часть инструкций непонятна, человеку который никогда с этим ядром не работал. Есть ли возможность как в ARM сохранять на стек или снимать с него сразу пачку регистров? Есть ли возможность выбора направления роста стека?
Нормального справочника по ассемблеру я тоже не нашел, что вообще-то странно. Кое-что можно найти в спецификации ядра (например, вот)
Если вы имеете в виду что приведенные в статье исходные коды непонятны без расшифровки, то да, надо будет не забыть сделать приложение с описанием инструкций.
Есть ли возможность как в ARM сохранять на стек или снимать с него сразу пачку регистров?

А вот это в статье описано. Собственно стека в RISCV нет, так что все манипуляции делаются ручками. Хотите чтобы рос вверх — пожалуйста, увеличивайте sp вместо уменьшения. Хотите вместо sp использовать, скажем, a5 — пожалуйста. Просто другие процедуры этого могут не понять. Как и компилятор Си.
добавил в конец список использованных команд ассемблера
Собственно стека в RISCV нет, так что все манипуляции делаются ручками.

Так же, как и нет всем привычной команды mov. В RISC-V пересылка содержимого одного регистра в другой делается через АЛУ, т.е. mov x1, x2 является частным сучаем инструкции сложения: add x1, x0, x2 — что инструктирует процессор сложить содержимое регистра x2 с содержимым регистра x0, которые всегда содержит константу 0, и сохранить результат в регистр x1.

Всем начинающим настоятельно рекомендую прочитать базову спецификацию RISC-V — она очень короткая, всего 218 страниц, с четким и понятным описанием всех основных (unprivileged) инструкций, в отличии от сорока томов ARM-овской или Intel-овской. :-)

А вот тут есть хорошая шпоргалка по RISC-V ISA в формате PDF на два листочка.

ниоткуда не следует, что физически процессор будет эту команду выполнять через ALU. он вполне может декодировать сразу такую команду как mov и далее выполнить её тупо переименованием регистров, не доводя до конвеера ALU.

Мощные Риски это может и умеют, но контроллеры вряд ли. Да и чтобы команда добралась до процессорного «мозга» ее надо как-то закодировать, пусть и через addi

Да. В доке на риск-в во многих местах говорится, что вот эти команды можно 'фьюзить' в одну макрооперацию (в данном случае это будет загрузка 32-битной константы в регистр). Ессно в мелочи типа как у вас никто этим не занимается, а вот в жирных суперскалярах такое вполне можно делать. Ну как х86 может mov+op фьюзить в одну 3-адресную внутреннюю операцию.

ниоткуда не следует, что физически процессор будет эту команду выполнять через ALU.


Это хорошо прослеживается из формата инструкции.

Теоритически, никто не запрещает реализовать микроархитектуру так, как вам это угодно, главное обеспечить совместимость по системе команд. Но, основной посыл RISC-V это минимизация количества внутренних цепей и повторное использование существующих блоков. Зачем тратиться на дополнительные цепи при реализации копирования регистров если можно прогнать через существующий AЛУ. По этой причение ядра RISC-V очень компактные и очень быстрые (до 5ГГц в железе).
Вы правы, я не делал акцента на псевдоинструкциях вроде mv, call, ret, scrw, la. Не думаю, что с ними будут какие-то проблему у изучающих, особенно если они будут смотреть дизассемблерный файл.
А вот за шпаргалку по инструкциям спасибо, добавлю ее в статью и доки на гитхабе.
А готовых библиотек для работы с периферией и поддержкой gcc нет? Про примеры использования и т.п. не спрашиваю, это даже производители армов не всегда предоставляют. Ассемблер чаще нужен для старта контроллера и выскоптимизированных вставок. Для новых архитектур более интересен готовый комплект библиотек и примеров, на котором можно пощупать работу с периферией.
Вот тут не оно? github.com/riscv-mcu/GD32VF103_Firmware_Library
Но там нет скриптов сборки, так что непонятно как их вообще компилировать, какие флаги, какой стартап код, как подключить стандартные библиотеки и т.п. В общем, не разобрался я с тамошними примерами, пришлось ковырять с нуля.
Да один из вариантов для работы с ним.
Тут описание как компилить. Я обычно такие проекты пересобираю под qbs, qtcreator как по мне работает получше, чем eclipse. У меня была статейка по qbs можете попробовать по ней сделать сборку в qtcreator. По вопросам могу проконсультировать.
За makefile спасибо, ознакомлюсь. Адаптировать его под eclipse, qbs или qtcreator не буду точно: меня вполне устраивает текстовый редактор и вызов make && make prog && screen из консоли.
А вот по чему не отказался бы от консультации, так это по подключению стандартных Си-шной библиотек вроде math или string.
Стандартные библиотеки стандартно идут в комплекте с компилятором. Подключаться должны как и в других случаях.
Текстовый редактор удобно, когда у вас один файл. Когда в проекте появляется полный обвес периферии, прокладки между драйвером и основным кодом, в этом случае будет уже тяжело. Но тут каждый сам себе злой ТОС-1.
https://gitlab.com/wicrus/gd32vf103-qbs
Накидал примерный проект на основе ссылки выше, проверить не смог готовый gcc не нашёл, а компилить было лень. Может кому пригодиться.
(1). JTAG — теоретически, самый правильный способ. Вот только подобрать правильное заклинание для него мне так и не удалось

а мне удалось, как раз на sipeed lognan nano (наверное). достаточно было найти патченный под рискв openocd, собрать под него же gdb и всё. железкой был клон jlink'а. удалось в gdb прошагать работающую программу и полюбоваться на дизасм рискв. очевидно что и остальное получилось бы, но я дальше не стал копать, плату отдал.

Патченный это не интересно :) Но, наверное это действительно единственный способ его завести.
Автору спасибо, однако он не раскрыл интереснейший ньюанс об который могут споткнуться начинающие программисты-ассемблеристы. Дело в том, что RISC-V, как и большинство других настоящих RISC машин, не имеет инструкций для загрузки 32-битного слова в регистр. Автор использует команду:
la  a5, 0x40021018

для загрузки 32-х битного адреса порта управления GPIO. На самом деле это не инструкция процессору, а макрокоманда ассемблеру которая при компиляции расширится в нечто вроде:
lui a5,0x40021
addi a5,a5,24

Вроде бы все логично и прозрачно, скажете вы, а вот и не совсем. Все непосредственные операнды в RISC-V попадающие на вход АЛУ проходят через блоки расширения знака, причем блоки эти разные и зависят от формата инструкции. В данном случае 12-ти битная непосредственная константа 24 будет расширена до 32-х бит, при этом старший бит будет скопирован и использован в качестве заполнителя для отсутствующих бит, и дальнейшее сложение в АЛУ будет проведено с учетом знака. У обеих приведенных здесь констант старшие биты равны нулю, а значит после расширения и сложения мы получим искомую положительную константу. Но что произойдет если у обеих констант старший бит будет равен единице? Фактически мы получим сложение двух отрицательных чисел и результ будет не совсем очевидным для программистов начинающих изучать RISC-V.

Допустим, мы хотим загрузить константу 0xDCBA9876 в регистр a5, мы даем ассемблеру команду li a5, 0xdcba9876 которая генерируется в следующую неочевиную последовательность инструкций:
lui     a5,0xdcbaa
addi    a5,a5,-1930

В более общем виде, макрорасширение команды li выглядит вот так:
# sign extend low 12 bits
M=(N << 20) >> 20

# Upper 20 bits
K=((N-M) >> 12) <<12

# Load upper 20 bits
LUI x2,K

# Add lower bits
ADDI x2,x2,M

По этому будьте внимательны при дизассемблировании своего же когда. Я наткнулся на эти грабли и протоптался на них дня три, пока понял в чем прикол.
Что-то мне кажется, вы слишком закопались.
Команда lui загружает 0xDCBAA в старшие 20 бит, то есть x5 = 0xDBCAA000 = 3'687'489'536
Потом addi вычитает прибавляет к нему -1930. Раз уж в команду можно писать отрицательные числа, наверное она умеет с ними работать правильно и расширяет до нужного диапазона
x5 = x5 — 1930 = 3'687'487'606 = 0xDBCA'9876.
Для меня гораздо большим выносом мозга была загрузка относительно pc.
Впрочем, objdump умеет это сам писать в комментариях что же туда будет сохранено и какой метке / константе оно соответствует:
19c: 20000597 auipc a1,0x20000
1a0: e6458593 addi a1,a1,-412 # 20000000 <_data_end>

0x19C + 0x20'00'00'00 — 412 = 0x20'00'00'00
22a: 400117b7 lui a5,0x40011
22e: c0078793 addi a5,a5,-1024 # 40010c00 <GPIOB_BASE>

0x40'01'10'00 -1024 = 0x40'01'0c'00
это не инструкция процессору, а макрокоманда ассемблеру
К слову, аналогом в ARM является псевдо-инструкция MOV32.
Разумеется, если у вас изначально есть понимание и вы держите этот алгоритм в голове — то проблем нет. Мне как человеку программировавшему на асме только под х86 с десяток лет назад было не просто вкурить происходящее. :) Я запнулся на том, что обе константы отрицательные.
Отрицательная в lui не влияет вообще ни на что, ведь она сдвигается влево и старшие биты обрезаются.
С addi, конечно, посложнее, но, согласитесь, было бы нелогично чтобы addi a0, t0, -5 складывала с 4091.

кстати, возник ещё вопрос. насколько периферия f103 и этого vf103 одинакова? можно ли тут использовать инклуды с адресами регистров и положениями битов от обычного stm32f103? речь ессно не про NVIC, а про обычную периферию типа RCC, GPIO или того же UARTа.

Некоторую можно. Адреса тех же GPIO и UART совпадают, биты регистров тоже. Но полностью я бы на это не полагался. Да и зачем?
Ах да, еще как минимум USB отличается. Но насколько — не знаю, не ковырял пока.
Издеваться мы будем над микросхемой GD32VF103CBT6, являющейся аналогом широко известной STM32F103, с небольшим, но важным отличием: вместо ядра ARM там используется ядро RISC-V.
В этом месте ожидал увидеть краткие преимущества ядра RISC-V по сравнению с ARM, чтобы понять имеет ли смысл интересоваться RISC-V.

Для этого традиционно используется мигание светодиодом (привет отладочным платам, на которых его нет!).
Неужто такие платы существуют?

Не забывайте, что в современных системах доступ к COM и USB портам считается опасным действием и разрешен только руту.
О том, что используемый софт предназначен для Linux следовало упомянуть раньше, в разделе «Настройка программного окружения».
В этом месте ожидал увидеть краткие преимущества ядра RISC-V по сравнению с ARM, чтобы понять имеет ли смысл интересоваться RISC-V.
А я их не знаю :) Ну то есть теоретически у risc более приятный ассемблер, но программируют-то все равно чаще на Си, так что какая разница. Теоретически, он быстрее, но именно vf103 в сравнении с f130 выигрывает скорее за счет каких-то аппаратных ухищрений. А аналогов чего-то другого GigaDevice не делает. Так что просто другая архитектура, почему бы ее не рассмотреть.
Неужто такие платы существуют?

Та же bluepill, у которой единственный диод на PC13, та же arduino, у которой диоды на TX, RX и SCK.
О том, что используемый софт предназначен для Linux следовало упомянуть раньше, в разделе «Настройка программного окружения».
Насколько я знаю, под Windows есть порты всего этого, да еще там вроде виртуалку линуксовую завезли.
С другой стороны, сколько статей я здесь читал, никто не делает акцента что софт предназначен, скажем, для windows. Вот разработчики на Mac иногда упоминают.
А существенных преимуществ для разработчика систем у RISC-V перед ARM и нет.
Где то чуть (ну совсем чуть-чуть) лучше, где то чуть (тоже чуть-чуть) хуже ARM, потребление сравнимо, быстродействие сравнимо.
Главное преимущество с точки зрения изготовителя — отсутствие необходимости лицензирования, но к нам это не относится, вряд ли готовые кристаллы станут дешевле.
Скорее даже наоборот. В микросхемах ведь чем больше партия тем дешевле. А ARM'ов гораздо больше производят.

Основное преимущество risc-v — что она реально открытая, можно брать и делать свой проц, не платя денег за лицензию и роялти. А можно купить проц у того, кто его уже сделал. Есть выбор.


И насчёт рута. Можно всегда добавить права доступа в конкретный усб-девайс (по vendor/device ID) в конфиг-файлы udev'а, будет доступно всем. Ну а чтоб в ком лазить, и вовсе достаточно юзера добавить в какую-то группу, забыл как она называется.

dialout, в статье об этом написано, и про usb тоже. В конце первой главы, если точнее.
Основное преимущество risc-v — что она реально открытая, можно брать и делать свой проц, не платя денег за лицензию и роялти.
Хорошо, преимущество для производителей микросхем есть. А для нас преимущество в чем?
Sign up to leave a comment.

Articles