Pull to refresh

Comments 12

  • Xr0=[j0+0] наверное не перекладывает из J в X, а использует J как указатель на память?
  • .align наверное не "задаёт шаг", а выравнивает адрес следующего объекта?
  • для организации стека компилятор С явно придерживается своих соглашений, отличных от ваших: указатель — J27, стек растёт вниз
  • .ldf-файлы не ниспосланы свыше, имена секций оттуда не "удобно брать", а сами мы их там и описываем (назначая адреса и атрибуты), причём у секций .data, .cdata, .bss, .ehframe очень разные смыслы
  • xr5=lc1;;//назначаем количество циклов — наверное наоборот, lc1=xr5 ?
  • несколько мест, где внутри пакета команд двойная ;;
  • у данного ядра, по всей видимости, не конвейер, а VLIW-архитектура и четыре параллельно работающих исполнительных модуля?

Так, отвечу в соответствующем порядке:

  • Первое, да, вы полностью правы. Текст писал одновременно с первыми шагами, и он много раз дописывался, так что некоторые косяки от исходной версии в том числе и этот остались мной не замечены.

  • Про алигн аналогично + выше того места я уже упомянул о выравнивании данных.

  • Касательно стека, про него писал разработчикам, он действительно не реализован там аппаратно, а значит каждый ворочит что он хочет в программном виде.

  • Про ldf, думаю вы тут разбираетесь больше меня, ибо как писал в самом начале статьи, я еще не волшебник а только учусь:) Если снабдите хорошим источником информации кто они такие эти ldf и с чем, а главное зачем их есть то буду очень благодарен

  • Про циклы также опечатка, спасибо что заметили

  • Удвоенная ;; там не просто так, она обозначает границы одного такта. Если этого не делать то компилятор будет ругаться.

  • Про vliw вы также правы, спасибо что заметили.

Если честно, впервые вижу данное ядро :) Просто похоже на другие VLIW, а toolchain - на другие GNU-подобные.

По стеку - имел в виду что компилятор C уже определил свой (с указателем в j27), можно не плодить сущности. Всмотритесь в пример с вызовом деления из стандартной библиотеки - все эти манипуляции с памятью по [j27+x] и есть работа со стеком (сохранение/восстановление регистров, подготовка параметров). Где-то должна существовать документация на то, как данный компилятор представляет себе стек, передачу параметров функций, возврат значений, необходимость сохранения регистров (всё это вместе называется ABI), если начать следовать ей, ваш ассемблерный код сразу начнёт нормально сосуществовать с С-шным.

Ldf - почитайте документацию на компоновщик GNU LD, тут явно что-то на его основе.

Двойные ;; - у вас в одной и той же конструкции call xx; nop; nop; nop;; после call где-то одинарная, где-то двойная ;

Посмотрел на сайте Миландра что это за процессор такой, так он оказался на основе давно существующего Analog Devices ADSP-TS201, а на тот и документация, и примеры есть. Вот описание их компилятора: https://www.analog.com/media/en/dsp-documentation/software-manuals/50_ts_cc_man.4.1.pdf, идём, к примеру, в раздел C/C++ Run-Time Model and Environment и находим описание того самого стека на j27.

Статья довольно сумбурная. Просто список заметок без вступления и разъяснений. Например:


Сначала подрубаем все нужные нам библиотеки и дефайним нужные нам дефайны…

Это текст, который может написать какой-нибудь стажер. Инженеры такие фразы не используют.


Смысл этого действия состоит в том по сколько байт мы выравниваем информацию при размещении в памяти.
В примере от производителя вообще представлена какая-то такая конструкция

Это не похоже на нормальное объяснение. Стоило детально изучить синтаксис каждой конструкции, а не писать "я не понимаю, что это такое, но просто скопируйте и все заработает".


Теперь, что касается регистров. На мой взгляд, если вы пишете большую программу, состоящую из разных функций, то использовать регистры неудобно. Надо постоянно помнить, в каком регистре что лежит, какой регистр надо сохранить, какой можно не сохранять. Легко случайно что-то перепутать или затереть и потом долго искать источник ошибок. Вручную писать номера регистров — это каменный век.


Удобным решением было бы разработать транслятор, который позволяет использовать в исходном коде переменные. Транслятор строит граф вызовов функций, отслеживает время жизни переменных и распределяет переменные по регистрам автоматически, гарантируя корректность программы. Такой транслятор должны сделать либо разработчики чипа, либо сообщество инженеров, которые пользуются чипом.


Что касается отсутствия стека. Это не такая уж проблема. Если функции не рекурсивные (что чаще всего и бывает), то можно просто выделить каждой функции фиксированную ячейку, в которой она хранит адрес возврата. Для локальных переменных использовать регистры или фиксированные ячейки памяти. Если это невыгодно (из-за абсолютных адресов получаются жирные команды), то да, можно использовать один из регистров как указатель стека.


Расскажу еще немного полезных улучшений, которые могут упростить жизнь разработчику на ассемблере. Можно реализовать защиту памяти. При написании программы вы размечаете память на массивы и переменные. Ко всем обращениям в память вы добавляете аннотацию, в какой массив вы обращаетесь. Эмулятор при выполнении кода сверяет адрес обращения с аннотацией и выдает ошибку при несоответствии. Это позволяет отлавливать выход за границы массивов, обращения по неправильным адресам, что должно экономить время на отладку программы. Также для косвенных переходов (switch) тоже можно указать список возможных целей и проверять корректность адреса перехода. Опять же, таких возможностей обычно нет в стандартных инструментах (почему, интересно?), но это должно быть несложно реализовать в эмуляторе.


То есть, эмулятор не обязан в точности соответствовать реальному железу. Он может давать дополнительные фичи (защита памяти, контроль границ массивов и переходов, контроль переполнения стека), полезные при отладке.


Еще я заметил, что все ваши примеры не используют VLIW и вы выполняете по одной команде за раз. По моему, это плохо, зачем платить за более дорогой чип с поддержкой VLIW и не использовать его возможности? Можно взять более дешевый и медленный чип без VLIW.


Этот способ мягко говоря не серьёзный и вообще делать так неприлично, ибо если мы внутри одной из вызываемых программ будем эксплуатировать данный регистр то все пропадет и дорогу назад мы забудем. Следовательно надо делать какую то структурку похожую на стек.

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

Совершенно верно, это по сути быстрые заметки сделанные по ходу первого знакомства. Конечно многое надо доделать и додумать. Но учитывая что сам я в этом деле человек несколько новый, надо с чего то начинать. И в целом что называется сведений "для самых маленьких" на эту тему особенно по отечественным процессорам очень мало на просторах интернета. Так что возможно такой формат кому-то будет по первости понятнее. За советы и информацию спасибо, надеюсь в будущем претворить их в жизнь.

Традиционный вопрос — есть ли GCC toolchain для этого изделия?
Открыл ссылку, почитал… опять все под винду: программаторы, драйвера, какие-то ключи поставляемые отдельно, куча плагинов к Eclipse. Не в ту сторону идет прогресс в нашей стране. В топку этот DSP!

это понятно.

А у меня в топке все процессоры у которых нет среды разработки или toolchain под винду. И при этом еще обязателен симулятор , ну и без замков :)

Так то не обязательно пользоваться отечественной CM-LYNX, можно использовать VisualDSP.

VisualDSP это как раз "не в ту сторону прогресс" :) С CM-LYNX вы будете иметь полноценный double, а не какую-то расширенную точность, полноценную байтовую адресацию, а не байты по 32 бита :). FFT у вас будет более чем в 2 раза быстрее за счет удвоенной SIMD обработки для float. Да и Clang .замечательная штука. Вот только к нему бы еще нормальный бэкенд, :)

Sign up to leave a comment.

Articles