1

Topic: ARM

Держим в уме, что у нас на самом деле 4 ядра в Odroid-X2

Exynos4412 Prime 1.7GHz  ARM Cortex-A9 Quad Core

2

Re: ARM

Вначале приведем в соответствие со стандартным ARM

#define IP r10

r12 – это тот же самый ip (Intra-Procedure-call scratch register), используется компиляторами C для доступа к параметрам в стеке в случае подвызовов, слинкованных библиотек или как локальная переменная. То есть не является сохраняемым во время вызовов.

Попробуем его поставить r12 вместо регистра общего назначения r10. С этим простым кодом работают тесты, но не работает диалоговый вывод.  Возможно, такого рода внесенная ошибка может быть использована совмесно с патчем памяти во время выполнения программы.

@ IP (r10) points to the next forth word that will be executed

Держим в уме, что у нас на самом деле 4 ядра в Odroid-X2

Exynos4412 Prime 1.7GHz  ARM Cortex-A9 Quad Core

А значит куча переферии, регистры управления которой располагаются прямо в памяти. И некоторые из подключаемых устройств могут делать запись чужих участков памяти непосредственно. :suss:

(Кроме того, там есть ошибки в процессоре которые опубликованы только в версиях документации для внутреннего использования доверенных специалистов корпорации не ниже 4 уровня. В частности поток инструкций к графическому сопроцессору может приводить к перегреву SoC и зависанию системы. Поэтому требуется особая последовательность инициализации. И эти спеки не будут опубликовыны в открытом доступе.)

Поэтому эти 2000 строк кода дают только логические операции и работу со строками через стандартные функции

которые линкуются статически с linux-libc-dev:armhf

/usr/include/arm-linux-gnueabihf/asm/unistd.h

Была одна душещипательная история, как несколько китайцев и англосаксов в течении 3 лет пытались методом перебора нащупать и определить системные библиотеки MTK


https://geektimes.ru/post/275142/

что только они не делали, какие только отладчики не вставляли в систему. Но разобрались с ошибками в менее чем 50% устройства простых наручных часов. В итоге пришли русские - стырили доки прямо с компьютера производителя (или взяли из почти свободного доступа стыренные бразильцами), опубликовали и  дали этим англосаксам поиграться.


этот случай можно рассматривать как пример бессилия ИИ (или да же полного перебора) в ситуации когда отсутствует описание более высокого уровня.

http://shervinemami.info/armAssembly.html

При использовании всего потенциала векторного исчисления на уровне ASM кода можно добиться увеличение скорости в 30 раз!

Внеочередной, спекулятивный, суперскалярный, с динамическим предсказанием ветвлений вычислительный конвейер. Декодер обрабатывает 2 команды за такт, буфер для перераспределения инструкций ёмкостью 32-40 команд, глубина целочисленного конвейера — 8 ступеней. Ядро обрабатывает до четырёх микроопераций за такт.

3

Re: ARM

В современных АРМв7 есть конвейер (кажется 5 уровней), есть автоматическое ветвление (кажется 3), есть предсказание, есть вращение. Сброс или очистку конвейера надо делать каждый раз при переходе между Тумб и АРМ асемблером. Достигается это добавлением посфикса x к команде ветвления. Времени не занимает, так как на ~18 логических регистров есть около 56 физических. Поэтому пересылок не требуется.

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

Все что требуется это считать или записать в правильном порядке регистры переферии SoC. По сути операционной системы с старом смысле не нужно. Нужны только библиотеки управления переферией.

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

4

Re: ARM

Выполнение происходит не на вершине конвейра, поэтому на переходе нужно две ветви где уже выбраны инструкции, декодированы, регистры назначены, всякое прочее. Если конвейер одинарный линейный то при переходе это всё выбрасывается. Условные инструкции в ARM нужны чтобы пропустить несколько действий не нужной ветви не нарушая всего процесса.

5

Re: ARM

Exinox 4412 - 4 ядра.

Систолическая структура получается на входе 8 на 8 узлов (всего 64)
А в кеше каждого ядра 32 - 40 команд на предвыборке (тупо местами переставляет блоки команд).

Декодер обрабатывает 2 команды за такт, буфер для перераспределения инструкций ёмкостью 32-40 команд, глубина целочисленного конвейера — 8 ступеней. Ядро обрабатывает до четырёх микроопераций за такт.
Целочисленный регистровый файл с переименованием, 32 архитектурных регистра проецируются на 56 физических.[3]

6

Re: ARM

http://coollib.com/i/63/182463/i676698a9-osprey.jpg

http://intelag.erp2crm.ru/file/pub/CMig … 7tRfBMVJeL

На картинке двухядерный, а мы о 4-х ядерном говорим.

Зеленые квадратики это и есть операции с командами - BTAC (Branch Target Address Cashe). Рядом четко видны по 8 зеленых прямоугольников в каждом углу ядра. это и есть те самые конвееры глубиной 8. Хорошо видно что их всего по 2 группы на ядро. Всего у нас 4 ядра, всего 64 Pipelines.

I-Data - кеш инструкций
D-Data - кеш данных
TLB (Translation Lookaside Buffer) - кеш адресации страниц
Микро адресация по числу потоков в ядре 8+8 (кеш данных и инструкций), макротаблица по общему числу потоков в процессоре 64

https://habrastorage.org/storage3/207/b66/53b/207b6653b7cd656e7c5028500b94b44b.jpg

Поток команд (в основном начинают выполнятся до того момента как реально нужен результат).

1 - получить
2 - декодировать
3 - выполнить

PTM (Program Trace Macrocell) - часть которая требуется для трасировки процессора ARM дибагером DS-5 без изменения потока команд и флагов.
PL310 - Контроллер кеша второго уровня с предвыборкой на 35-40 команд.
VFP - операции с плавающей точной (нет штатно в АРМ)
NEON - векторная графика, часто 3D

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

7

Re: ARM

При таком конвейере дешевле выполнить переход.
Но сейчас обычно конвейеры длиннее. Нужно например извлечь из памяти аргументы, это относительно долго и как раз делается перед выполнением операции.

8

Re: ARM

Там 2 уровня кеша L1, L2. В одном 35-40 команд тасуются. И уже после предсказания загоняются в конвеер в районе кеша 1 уровня инструкций и данных.

9

Re: ARM

https://sourceware.org/binutils/docs/as … on_002dSet

Существует в АРМ несколько типов мемонического (человеческого) кода.
Но в последнее время приняли унифицированный (общий стандарт) который выставляется следующей директивой компилятору:

".syntax unified", ".code 16, .thumb, .arm"

после чего легко можно употреблять команды ARMv7 типа а не команды совместимости со старыми АРМ процессорами.

Какой тип инструкций установлен показывает бит T, регистра CPSR (Курент програм статус регистрер) 0 это АРМ, 1 это Тхумб.

Как мы помним есть еще конвейеры глубиной в 8 команд, которые очищаются автоматически при использовании следующих команд:для перехода BX (Бранчь виз Ексчендж) и BLX (Бранчь анд Линк виз Ексчендж), возвращение BX LR. Или другой способ через обмен на стеке LDR для PC и POP/LDM для PC. Более того первый бит адреса этих команд содержит информацию в какой режим надо переключится в 0 АРМ или в 1 Тхумб.

Получается, что для создания компилятора на современных микропроцессорах (а значит и операционной системы путем расширения словаря на основе ассемблерных примитивов кода), практически не требуется язык высокого уровня. Дальше мы увидим, что да же трансляция слов в чистый вызов ассемблерных команд не нуждается в дополнительных структурах. Все уже реализовано в железе.

При прочтении по диагонали Руководства программиста процессоров серии Кортекс-А, я понял, что многие структуры заложенные в старых языках типа Форт, сделаны в современных процессорах в железе.

Так например Кеш 2 уровня L2 содержит стек команд и стек данных вплоть до 16 команд и систему переключения страниц памяти контекста команд с сокращениями адреса.

Получается вполне логично использовать эти железные возможности непосредственно в трансляторе шитого (адоптированной таблице ссылок на подпрограммы в машинных кодах) кода.

https://marsohod.org/images/stories/c3/arm/arm_set_dp.png

PLD - preload data chache
PLI - preload instruction cashe

Preload Engine FIFO flush

MCR p15, 0, <Rt>, c11, c2, 1

pause channel
~ c3, 0
resume ~
~ c3, 1
kill ~
~ c3, 2

PLE Program New Channel

MCRR p15, 0, <Rt>, <Rt2> c11 ;PLE Program New Channel


Вообщем складывается впечатление, что нам под видом новых чипов впаривают старые разработки СССР и Американских хакеров MIT и других. Изменения касаются в основном технологии производства, но не сути устройства.

10

Re: ARM

Да ничего такого особенного в нуле нет. Если 3 лучше 2 по количеству значений то можно сравниваемые числа просто вычесть и использовать результат как индекс в таблице переходов, будет не 3, а все 2^64.

Конвейеры тоже продублируют, если уже не сделали. И будет много источников инструкций и много их исполнителей. Проблемы сбрасывать конвейер при переходе не будет. Просто исполнитель перейдёт под другого начальника без всякого перерыва.

11

Re: ARM

Смотрим на режимы работы этой микстуры АРМ.
Следуем бинарному коду:

10000  - USER - ядро линукс в режиме пользователя
10001  - FIQ - быстрые прерывания (было да же в первых компиляторах)
10010  - IRG - когда поняли что недовыполненная команда может привести к сбоям, добавили этот режим (вообще то что то подобное было да же в ДССП - РАЯ).
10011  - SVC - режим суперпользователя или драйверов во время загрузки
10100  - ВОТ ЭТОГО РЕЖИМА НЕТ В ОТКРЫТОЙ ДОКУМЕНТАЦИИ!
10101  -
10110  -
10111  - ABT - Аборт или защита от хакеров при пересечении границы выделенной памяти
11000  -
11001  -
11010  -
11011  - UND - Режим выполнения сверху вниз, когда некоторые команды (например сопроцессора или микрокода еще не определены).
11100  - СНОВА НЕТ!
11101  -
11110  -
11111  - SYS - режим защиты от Джейблока для будущих ArchARM 64 процессоров с прошитыми ключами за которые все население Земли (Ерёмы) будет платить ренту Японцам каждый месяц.

Есть еще два режима:
MON (Монитор), что как бы намекает, что устройство будет пытаться передать данные в центр, в случае Джейблока. Поэтому манипуляции желательно производить на устройстве не подключённому к интернету и вообще в клетке Фарадея с приложенным потенциалом.

Есть облегченная версия этого режима для ARMv7 32 бита - HYP. Но что то мне подсказывает что и на ArchARM 64 этот режим так же будет на уровне микрокода (просто он не будет не как себя проявлять). Особенно если окажется, что вдруг кто то из Японцев сделал АРМ чип с энергонезависимым встроенным источником питания, либо с неотключаемым питанием сопроцессора или моста или ЧИСТО для ВатчьДога - все эти приколы мы знаем ... и не ведемся.


По поводу последнего режима такая же идея была по поводу SD дисков у SONY, но не срослось. Спасибо Китайцам. Берем попкорн и следим за развитием событий.

Пропущенные режимы, это те же самые режимы что выше но с установленным битом защиты (слева на право)

1 бит не нарушать границы памяти данных
2 бит не нарушать границы адресов команд
3 бит не подменять системные таблицы вызовов и адресов во время работы бутлоадера

4 и 5 биты  это [1:0] биты регистра R15 (PC - GNU - Програм Каунтер).
https://marsohod.org/images/stories/c3/arm/arm_regs_r15.png
Маски отменяют реакцию на прерывания.

Кортекс-А (АРМв7) отличается от старых АРМ тем, что в режиме простого пользователя имеет один регистр доступный только для чтения CPSR (Курент Програм Статус Регистр).
А в остальных режимах (привилегированных) этот же регистр SPSR (Сайвед Програм Статут Регистр) доступен для записи.

Таким образом в Кортекс-А добавлены еще 3 бита в этот регистр (либо они берутся из специального места CPSR\SPSR - как я правильно предположил их добавили в биты M[4:2]). Так же из нововведений Стикли бит - бит наследования прав стоит на месте бывшей маски прерываний [27]. Таким образом количество суфиксов условных возрасло до 5 (думаю что лучше их изображать приставочными словами - потому что они находятся в начале слова).
А среди суфиксов добавились E[9] - бит порядка, A[8] - маска аборта памяти, 27,26 маски передвинулись на I,F[7,6] места.
На пятом месте T [5] стоит тот самый бит переключения в укороченный Тхумб код.
Так же зарегистрированы SIMD - GE[19:16] - для векторов.

IT[26:25] Условные приставки для Thumb-2
J[24] - бит установки в Джава режим (который нужен для всяких Питонов, Си-шарпов и Джава машины и вообще ГИТ компиляции - вот тут нас и постараются поиметь прямо через JIT компиляцию всяких видеокарт) Jazelle.

4 бита зарезервированы [23:20] - СКОРЕЕ всего они описаны во внутренней документации и связаны с дополнительными командами виртуализации (отправить, послать, считать, записать и т.д.)

Если кому нужна закрытая документация обращайтесь (пошлю на ваш личный почтовый ящик), но она закодирована. Пожалуй, надо быть Дмитрием Скляровым, чтоб ее прочитать.

P.S. Могу предположить что тонкий поток радиактивных частиц (типа йонов) в нужную область микропроцессора, может поменять состояние и дать доступ хакерам к будущим чипам. Правда, вероятно придется спилить защитный корпус.
Будем считать что их план не удался.

12

Re: ARM

Сейчас обсудим различия в коде примитивов Форта для старых АРМ
https://github.com/M2IHP13-admin/JonesF … nesforth.S

И для новых ARMv7

https://github.com/0ctobyte/arm4th/blob/master/arm4th.s

Принципиальная разница начинается тут:

.text
.code 32

# Minimal code for coldbooting Forth on an ARMv7 machine
.global _start
.balign 4
_start:
#if BBB
  # For the BeagleBoneBlack disable the WDT
  bl      _disable_wdt_

  # Enable the vfpu
  bl      _enable_vfpu_

  # enable the UART FIFO on the BeagleBone
  movw    r0, #0x1
  movw    r1, #0x9000
  movt    r1, #0x44e0
  str     r0, [r1, #0x8]
#endif
  
  # Set origin
  movw    org, #0x0
  movt    org, #0x8001

  # Setup the stack pointer and return stack
  mov     rp, org
  sub     sp, rp, #0x400

  # tos magic value
  movw    r0, #0xbeef
  movt    r0, #0xdead
  mov     tos, r0

  # Finally set the ip to cold
  ldr     ip, =cold
  add     ip, ip, org

  # Go to init!
  next

.balign 4
.ltorg

#if BBB
.balign 4
_enable_vfpu_:
  # CPACR: Allow full (PL0 & PL1) access to coprocessors 10 & 11 (VFPU) 
  mrc     p15, 0, r2, c1, c0, 2
  mov     r3, #0xf
  orr     r2, r2, r3, lsl #20
  mcr     p15, 0, r2, c1, c0, 2
  isb

  # Enable the VFPU
  vmrs    r2, fpexc
  mov     r3, #1
  orr     r2, r2, r3, lsl #30
  vmsr    fpexc, r2

  bx      lr

# Disable the watchdog timer
.balign 4
_disable_wdt_:
  movw    r0, #0x5000
  movt    r0, #0x44e3
  movw    r2, #0xaaaa
wdt_wpsr_write:
  # Offset to WSPR register (watchdog timer start/stop register) 
  add     r1, r0, #0x48
  str     r2, [r1]
  # Offset to WWPS register (watchdog timer write posting bits register)
  add     r1, r0, #0x34
wdt_wwps_poll:
  ldr     r3, [r1]
  # Check if write is pending
  tst     r3, #0x10
  bne     wdt_wwps_poll
  movw    r3, #0x5555
  teq     r2, r3
  beq     wdt_done
  movw    r2, #0x5555
  b       wdt_wpsr_write
wdt_done:
  bx      lr
#endif

Вообщем не чего особенного, как в любом Ардвино все те же самые действия.
Ватчьдог, УАРТ для терминального вывода, подключение сопроцессора с плавающей точкой.

  # CPACR: Allow full (PL0 & PL1) access to coprocessors 10 & 11 (VFPU)
  mrc     p15, 0, r2, c1, c0, 2

Вот это та самая команда, которая дает доступ чтения в режимах USER и SYS (Но не Монитор и Гипервизор) к CPACR (Копроцессор Аксес Контрол Регистр).

И записи:
  mcr     p15, 0, r2, c1, c0, 2

[23:22] биты отвечают за копроцессор 11 (cp11), а [21:20] биты за копроцессор 10 (cp10)

13

Re: ARM

Для примера сравним операцию для старого АРМ двух чисел из стека

@ + ( a b | a+b)

defcode "+",1,,ADD
        pop r0
        pop r1
        add r0,r0,r1
        push r0
        NEXT

И для ARMv7

# Add
# ( n0 n1 -- n2 )
defcode "+",plus
  pop     r0, sp
  add     tos, tos, r0
  NEXT

Разница в параметрах макроса defcode не существенна, просто новый макрос самостоятельно вычисляет длину имени и проставляет флаги компиляции на месте другим способом (через указание переменных defvar и запуск ассемблерных примитивов через директиву .int).

Макросы pop по сути не отличаются. Только для ARMv7 существует больше режимов работы и регистр указателя стека SP или DSP (Дата стек поинтер) реализован с параметром sp. В случае режима USR он так и называется sp. В других режимах это другой физический регистр и его название отличается дополнением через подчеркивание по названию режима (например r13_svc, r13_irq, ...).

Так как у нас есть кеш L1 и L2, а так же устройство отображения MMU (Мемори Менеджмент Юнит) частей памяти внутрь процессора, то мы можем работать не с регистрами общего назначения как в более ранних АРМ, а на прямую с участком памяти.

Так как в первом коде используется общий регистр r0 для манипуляции стеком, то его приходится восстанавливать макросом push r0.

В случае ARMv7 tos (Топ оф зе Стек) является регистром r9, который в нашем случае установлен сразу после исполняемого кода (на самое дно стека было помещено число 0xdeadbeef). Стек растет в сторону уменьшения адреса. А стек сохраняется в регистре tos (r9) поэтому возвращать r0 в стек не требуется.

14

Re: ARM

Две выдержки из кода выше завязаны своим содержанием на макросы NEXT, pop, push.

То есть если ИИ будет сравнивать два этих куска без спеков процессора или хотя бы без рассмотрения реализации входящих в них макросов. То заметит отсутствие одной команды (потому что для ARMv7 кода в стек элемент из вершины возвращается позже из регистра r9 (tos). Так же будет заметно что r1, r0 имеют другие значения.

Вижу две возможности.
1 перебрать все возможности конфигурации процессоров и оставить только те после которых компилятор или выполняемая программа не вылетит с ошибкой.

2 сделать больше настроек для макросов, чтобы все макросы были однотипными и отличались только параметрами.

Так же хочу заметить что в АРМ имется минимум 8 регистров общего назначения и еще 7 в зависимости от контекста. Не плохо было бы раскрыть потенциальные возможности процессора.

Потому что старый Форт (РАЯ) был сделан в расчёте на 2 основных регистра S и T (это следует из описания компьютера Сетунь.

http://forum.ksri.info/viewtopic.php?pid=10816#p10816

15

Re: ARM

Вначале о регистрах:

Старый ARM

@ Reserve three special registers:
@ DSP (r13) points to the top of the data stack
@ RSP (r11) points to the top of the return stack
@ IP (r10) points to the next forth word that will be executed

        #define DSP r13
        #define RSP r11
        #define IP r10

ARMv7 Cortex-A9

# tos = top of stack
# org = origin == start address of binary image, 
#                      used to offset ip tokens
# rp = return stack pointer
# ip = "interpreter" pointer (r12)
tos .req r9
org .req r10
rp  .req r11

https://sourceware.org/binutils/docs-2. … tives.html

Деректива ".req" просто создает алиас для имени регистра. Далее с алиасом можно работать как с переменной. Как видим разница в регистрах объясняется тем что задействован дополнительный регистр для хранения вершины стека данных (tos), который мы не возвращаем в стек.

Так же адрес следующей вычисляемой команды в коде для ARMv7 вычисляется смещением.

# Inline NEXT macro
.macro next
  ldr   r0, [ip], #4
  add   r0, r0, org
  bx    r0
.endm

А в первом коде ARM сшивка кода происходит через разименование указателей на указатель.

@ Implement NEXT, which:
@   1. finds the address of the forth word to execute by
@      dereferencing the IP
@   2. increment IP
@   3. executes the forth word

        .macro NEXT
        ldr r0, [IP], #4
        ldr r1, [r0]
        bx r1
        .endm

Между интерпретацией и компиляцией есть целых 4 варианта сшивки кода. Думаю что нужно выбрать наиболее продвинутый, который позволит сделать вызов ассемблерных команд прямо из JIT (Джаст ин Тайм) компилятора (считай виртуальной машины каждого слова) Форта.

Кроме того, ранние версии Форта были написаны для компьютера с меньшим количеством регистров. Команды в тех процессорах были не сложные. Поэтому можно для нового процессора Кортекс-А9 на первом этапе добавить отдельные слова соответствующие префиксам и суфиксам оригинального асемблера Кортекс-А9. А на следующем этапе связать наиболее популярные группы (приставок, слов и суфиксов) в отдельные слова.

https://github.com/ingeniarius/DSSP-32/ … ommand-set

Особенно полезны будут следующие префиксы: IF-, IF0, IF+, >, <, =, NEG, SEG, LO, HI, SETLO, SGX, SRCHB, DEFINE?.

Среди суфиксов должны быть set - для установки флагов,ldr/str - для направлении записи, бит B[22] - слово или байт, I[25] - смещение, U[23] - направление роста стека. P[24] - преиндексный и постиндексный доступ (!), W[21] - направление инкрементации указателя (++, --, 1+, 1-, 2+, 2-, 3+, 3-, 4+, 4-, !0, !1, !1+, !1-). Хотя установку флагов I, P, U, B, W, L можно отнести к приставкам как и биты [27:26] которые определяют тип команды (REGOP, MULT, SWAP, TRANS, MTRANS, BRANCH, CODTRANS или CP, COREGOP, CORTRANS, SWI). 

Простая команда push {r0-r3} - сохранить на стеке список регистров.
На самом деле будет выполнена - stmfd sp!,{r0-r3}.

16

Re: ARM

Шитый код бывает:
1. подпрограмный (надо на этапе линковки подставлять реальные адреса подпрограмм)
2. Прямой шитый код (Реализация макроса NEXT с переходом по адресу в регистре, так же во время JIT компиляции слова Форт происходит выполнение DOCOL (: - символ начала определения слова Форта).
3. Косвенный шитый код

Пример для старого ARM

DOCOL:
        PUSHRSP IP
        add IP, r0, #4
        NEXT


@ : ( -- ) Define a new forth word

defword ":",1,,COLON
        .int WORD                       @ Get the name of the new word
        .int CREATE                     @ CREATE the dictionary entry / header
        .int LIT, DOCOL, COMMA          @ Append DOCOL  (the codeword).
        .int LATEST, FETCH, HIDDEN      @ Make the word hidden
                                        @ (see below for definition).
        .int RBRAC                      @ Go into compile mode.
        .int EXIT                       @ Return from the function.

defword ";",1,F_IMMED,SEMICOLON
        .int LIT, EXIT, COMMA           @ Append EXIT (so the word will return).
        .int LATEST, FETCH, HIDDEN      @ Toggle hidden flag -- unhide the word
                                        @ (see below for definition).
        .int LBRAC                      @ Go back to IMMEDIATE mode.
        .int EXIT                       @ Return from the function.

Как видим адрес хранится в ячейке (машинном слове в регистре IP - в данном случае этоо регистр r10, а не настоящий ip (r12), который видимо задействован при обработке работы командной строки через стандартную библиотеку Си, которая линкуются статически с linux-libc-dev:armhf /usr/include/arm-linux-gnueabihf/asm/unistd.h ).

4. Свернутый шитый код бывает прямой и косвенный, но для получения физического адреса нужно выполнить преобразование из Виртуального адреса путем сверточной функции. На для Кортекс-А9 устройство управления памятью (страницами) MMU позволяет это сделать.

Для повышения производительности микропроцессора можно выбрать группу примитивов в операционных кодах, разместить ее в памяти и делать адресацию прямо из слов Форта по индексам в режиме немедленной (JIT) компиляции. Сигналом начала компиляции слова служит завершающий символ ";"

Пример для кода ARMv7 процессора Cortex-A9 (В данном случае A8, но суть та же)

# Prologue to every high-level Forth word
# ( -- ) ( R: -- addr )
defcode "enter",enter
  push    ip, rp
  mov     ip, lr
  next


# Store execution token of forth word in current location
.macro _xt label
.int \label
.endm

# Create a new colon definition for a name
# ( "<spaces>name" -- )
defword ":",colon
  _xt create
  _xt lit
  _xt enter+ORIGIN
  _xt here
  _xt minus
  _xt lit
  _xt 8
  _xt minus
  _xt lit
  _xt 2
  _xt rshift
  _xt lit
  _xt 0x00ffffff
  _xt and
  _xt lit
  _xt 0xeb000000
  _xt or
  _xt comma
  _xt latest
  _xt fetch
  _xt hidden
  _xt right_bracket
  _xt exit

# End a colon definition
defword ";",semicolon,F_IMMED
  _xt lit
  _xt exit
  _xt comma
  _xt latest
  _xt fetch
  _xt unhidden
  _xt left_bracket
  _xt exit

http://cs.mtu.edu/~mmkoksal/blog/?x=ent … 116-130037

Макрос _xt сделан для инструментирования и отладки, по сути это просто директива ".int". Деректива ".int" подставляет результат выполнения вырожения, полученного во время компиляции. По сути мы получаем таблицу сшитых адресов кода. Аналогично можно сделать словарные статьи для АСМ команд. Причем их можно получить автоматически путем компиляции спеков самого процессора (или воспользоваться выжимкой из HDL описания процессора). IMHO

17

Re: ARM

Рассмотрим поближе ИИ образца 1968 года. этот код просматривал доступные слова (за которыми стояли усочки машинного кода по работе с двумя, тремя стеками).

@ FIND ( addr length -- dictionary_address )
@ Tries to find a word in the dictionary and returns its address.
@ If the word is not found, NULL is returned.

defcode "FIND",4,,FIND
    pop r1 @length
    pop r0 @addr
        bl _FIND
        push r0
        NEXT

_FIND:
        stmfd   sp!, {r5,r6,r8,r9}      @ save callee save registers
        ldr r2, =var_LATEST
        ldr r3, [r2]                    @ get the last defined word address
1:
        cmp r3, #0                      @ did we check all the words ?
        beq 4f                          @ then exit

        ldrb r2, [r3, #4]               @ read the length field
        and r2, r2, #(F_HIDDEN|F_LENMASK) @ keep only length + hidden bits
        cmp r2, r1                      @ do the lengths match ?
                                        @ (note that if a word is hidden,
                                        @  the test will be always negative)
        bne 3f                          @ branch if they do not match
                                        @ Now we compare strings characters
        mov r5, r0                      @ r5 contains searched string
        mov r6, r3                      @ r6 contains dict string
        add r6, r6, #5                  @ (we skip link and length fields)
                                        @ r2 contains the length

2:
        ldrb r8, [r5], #1               @ compare character per character
        ldrb r9, [r6], #1
        cmp r8,r9
        bne 3f                          @ if they do not match, branch to 3
        subs r2,r2,#1                   @ decrement length
        bne 2b                          @ loop

                                        @ here, strings are equal
        b 4f                            @ branch to 4

3:
        ldr r3, [r3]                    @ Mismatch, follow link to the next
        b 1b                            @ dictionary word
4:
        mov r0, r3                      @ move result to r0
        ldmfd   sp!, {r5,r6,r8,r9}      @ restore callee save registers
        bx lr

В качестве локальных переменных этой процедуры используются регистры r4, r5.

По метке 2: происходит сравнение строк (которые являются словом и претендентом на аналогичное слово из библиотеки) посимвольно.
По метке 1: происходит сравнение длинны слов.
#5 - это пропуск заголовка (сслыки и длинны слова) в структуре связанной с искомым словом.

@ INTERPRET, reads a word from stdin and executes or compiles it

defcode "INTERPRET",9,,INTERPRET
    @ No need to backup callee save registers here, since
    @ we are the top level routine

        mov r8, #0                      @ interpret_is_lit = 0

        bl _WORD                        @ read a word from stdin
        mov r4, r0                      @ store it in r4,r5
        mov r5, r1

        bl _FIND                        @ find its dictionary entry
        cmp r0, #0                      @ if not found go to 1
        beq 1f

    @ Here the entry is found
        ldrb r6, [r0, #4]               @ read length and flags field
        bl _TCFA                        @ find code field address
        tst r6, #F_IMMED                @ if the word is immediate
        bne 4f                          @ branch to 6 (execute)
        b 2f                            @ otherwise, branch to 2

1:  @ Not found in dictionary
        mov r8, #1                      @ interpret_is_lit = 1
        mov r0, r4                      @ restore word
        mov r1, r5
        bl _NUMBER                      @ convert it to number
        cmp r1, #0                      @ if errors were found
        bne 6f                          @ then fail

    @ it's a literal
        mov r6, r0                      @ keep the parsed number if r6
        ldr r0, =LIT                    @ we will compile a LIT codeword

2:  @ Compiling or Executing
        ldr r1, =var_STATE              @ Are we compiling or executing ?
        ldr r1, [r1]
        cmp r1, #0
        beq 4f                          @ Go to 4 if in interpret mode

    @ Here in compile mode

        bl _COMMA                       @ Call comma to compile the codeword
        cmp r8,#1                       @ If it's a literal, we have to compile
        moveq r0,r6                     @ the integer ...
        bleq _COMMA                     @ .. too
        NEXT

4:  @ Executing
        cmp r8,#1                       @ if it's a literal, branch to 5
        beq 5f

                                        @ not a literal, execute now
        ldr r1, [r0]                    @ (it's important here that
        bx r1                           @  IP address in r0, since DOCOL
                                        @  assummes it)

5:  @ Push literal on the stack
        push r6
        NEXT

6:  @ Parse error
        mov r0, #stderr                 @ Write an error message
        ldr r1, =errmsg
        mov r2, #(errmsgend-errmsg)
        ldr r7, =__NR_write
        swi 0

        mov r0, #stderr                 @ with the word that could not be parsed
        mov r1, r4
        mov r2, r5
        ldr r7, =__NR_write
        swi 0

        mov r0, #stderr
        ldr r1, =errmsg2
        mov r2, #(errmsg2end-errmsg2)
        ldr r7, =__NR_write
        swi 0

        NEXT

        .section .rodata
errmsg: .ascii "PARSE ERROR<"
errmsgend:

errmsg2: .ascii ">\n"
errmsg2end:

Смотрим в словарь. Если не нашли такой цифры то по метке 1: , значит это литерал может оказаться (просто числом). В ДССП в этом месте допускают что это неопределенное слово, которое могут доопределить при следующей итерации. В простом Форте просто выдают ошибку, а не список неопределенных слов в качестве результата.

При компиляции слова программы Форт - помещаем результат в словарь (вторая часть под меткой 2:)

По метке 4: можно прочитать решение ИНТЕРПРЕТАТОРА либо на выполнение команды с передачей управления на примитив асм кода данного слова, либо если это число - просто положить его на стек (метка 5:) и продолжить выполнение следующего слова.

Ниже мы конечно разберем цикл создания и сшивки кода в библиотеке Форта. Но в целом можно однозначно заключить, что в 1968 году ИИ как раз и являлся обычный компилятор типа Forth, Lisp, Фортран, а позже C.

Интересно добавить что вплоть до перестройки существовали "Объектно ориентированные" или LISP ориентированные или да же Фортран-ориентированные версии компиляторов Форт. Отличающиеся дополнительными стеками для локальных переменных или дополнительными списками (как в LISP или STDlib из С).

18

Re: ARM

/*
    Related to >CFA is >DFA which takes a dictionary entry address as returned by FIND and
    returns a pointer to the first data field.

    FIND returns a pointer to this
    |                >CFA converts it to a pointer to this
    |                       |
    |                       |    >DFA converts it to a pointer to this
    |                       |         |
    V                       V         V
    +---------+---+---+---+---+---+---+---+---+------------+------------+------------+------------+
    | LINK    | 6 | D | O | U | B | L | E | 0 | DOCOL      | DUP        | +          | EXIT       |
    +---------+---+---+---+---+---+---+---+---+------------+------------+------------+------------+
                           codeword

    (Note to those following the source of FIG-FORTH / ciforth: My >DFA definition is
    different from theirs, because they have an extra indirection).

    You can see that >DFA is easily defined in FORTH just by adding 4 to the result of >CFA.
*/

Вот таким способом можно сделать "выполняемый" гипертекст. Который будет работать на самых простых калькуляторах с 2 регистрами и общей памятью и основными операциями.

И продать такое устройство по цене АйФона (потому что оно военное например и брендовое или специальная кредитка с жутко навороченной защитой типа калькулятор с кнопками). Соответственно разницу в цене заплатят военные, а покроют за счет побежденных в том числе и в экономической войне.

Выше на рисунке структура типичного слова в библиотеке Форта с 3 указателями.
А слово это запросто может быть договором об аренде жилья или договором заверенным цифровым ИИ нотариусом (компилятором Форта с прерываниями по таймерам) и цифровыми подписями участников договора.

Кстати АйФон это то же компилятор Форта только выполненый в "железе" на полузаказной микросхеме с дешевыми добавками из китайского ширпотреба, типа экрана с тачем и батарейкой. Рыночная стоимость изначально завышена в 10 раз. На вторичном рынке за счет продажи комплектующих искусственно завышена в 4-5 раз.

Позже мы разберем как управлять через MMU (Мемори Менеджмент Юнит) типичным железным компилятором типа ARMv7 или что да же ближе к истине - мотороловским эпловским чипом.

19

Re: ARM

ARMv7

const_bl - Определение символа пробела (потребуется как терминал для парсинга ниже)

# ASCII space character
# ( -- char )
defconst "bl",bl,0x20
# TODO
# ( c-addr u -- )
defcode "interpret",interpret
  bl      _interpret_
  next

defcode "_interpret_",_interpret_
  push    lr, rp

  ldr     r0, var_state
  cmp     r0, #0
  bne     _interpret__compile

  ldr     r0, const_bl
  bl      _word_
  bl      _find_
  cmp     r1, #0
  strne   tos, [sp, #-4]!
  movne   tos, r0
  blne    execute
  
  bl      _question_number_
  cmp     r1, #0
  strne   tos, [sp, #-4]!
  movne   tos, r0
  b       _interpret__exit

_interpret__compile:
  ldr     r0, const_bl
  bl      _word_
  bl      _find_
  cmp     r1, #1  // Is immediate
  streq   tos, [sp, #-4]!
  moveq   tos, r0
  bleq    execute
  cmp     r1, #-1 // Check if not immediate
  subeq   r0, r0, org // adjust execution token
  bleq    _comma_
  
  bl      _question_number_
  cmp     r1, #0
  blne    _literal_

_interpret__exit:
  pop     lr, rp
  bx      lr

Разница в том что в этом коде для Кортекс-А9 сслыка на буфер со словом из словаяря и его длинну передается через стек. И можно обойтись без локальных меток связанных с логикой разбора. Так как в АРМв7 есть условные суфиксы.


ldr r0, const_bl
bl _word_
bl _find_
cmp r1, #0
strne tos, [sp, #-4]!
movne tos, r0
blne execute

Вот этот кусок кода дублируется полностью, видимо код делался в спешке, либо были использованы диасемблированные куски (полученные с помощью компилятора Си) из другого проекта.

На практике в языках программирования не принято употреблять все спряжения и склонения (все флаги которые я выше перечислял), видимо надо подумать о балансе морфологии слова и удобства написания слов. Так например в ДССП все слова в 2-3 символа, редко когда длиннее. Вероятно это связано с ограничениями по памяти для первых компьюторов, экономией бумаги на принторе и количеством символов умещающихся на экран терминала или электронной пишущей машинки.

20

Re: ARM

На основе Фортовских словарных статей (на ASCII-art картинке выше) можно организовать целую онтологию. Или даже несколько онтологий (словари можно менять).

Давайте рассмотрим поближе состав словарной статьи:
поле связи - просто указатель динамического списка словарных статей
поле имени - слово, которое используется в онтологии (31 символ)
поле кода - это шитый код или просто вектор адресов других подслов
поле параметров - для ассемблерных примитивов это просто операционные коды

Поле имени - NFA (Наме филд адерс) состоит из счетчика букв образующих имя и 3 флагов в одном байте и самой строки. Важнейший флаг это - признак немедленного исполнения.

Поле связи - LFA (Линк филд адрес)2 байта (изначально) указывает на адрес предыдущей словарной статьи.

Поле кода - CFA (Коде филд адрес) для встроенных слов присутствует встроенный интерпретатор (DOCOL, EXIT).
Так же это поле может ссылаться на переменные, которые могут быть инициализированы (другими словами, иметь собственный конструктор)

ARMv7

# Define code words
.macro defcode name,label,flags=0
.balign 4
.global name_\label
name_\label:
.int link                    // link pointer
.set link,name_\label+ORIGIN // set link pointer to this word
.byte \flags                 // 1 byte for flags
.byte (12346f - 12345f)      // 1 byte for length
12345:
.ascii "\name"               // name of the word
12346:
.balign 4
.global \label               // DTC Forth doesn't need a codeword here
\label:
.endm

Поле параметров - PFA (Параметр филд адресс)

# Define a variable
.macro defvar name,label,initial=0,flags=0
defcode \name,\label,\flags
  push  tos, sp
  add   tos, pc, #0x8
  next
.balign 4
var_\label:
.int \initial
.endm

Приведен код выше, чтобы наглядно можно было представить какие ссылки на параметры может иметь Форт и каким путем Форт может самомомодифицироваться (понятно что по программе написанной человеком заранее, а не по программе, которую компилятор сгенерировал из датчика случайных чисел или на основе состояния памяти, как некоторые люди на этом форуме считают).

Еще раз повторю - все поисковые эвристики в современном компьютере закладываются заранее. За 32 года практики с компьютером не видел код, который родился самостоятельно.

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

Для отслеживания процесса компиляции или интерпретации ("понимания" в терминах этого форума) служит стек возврата. Так же есть флаг HIDDEN (скрытый) для установки маркера и предупреждения зацикливания во время компиляции рекурсии. Таким образом есть средства для активного (с выполнением) кода обхода всего дерева программы.

Из опыта других языков, понятно, что добавления дополнительного стека локальных переменных или дополнительных списков, в некотором случае, позволяет упростить код высокого уровня абстракции на языке Форт.

Выше я показывал множество дополнительных флагов, которые используют современные процессоры в своих инструкциях и для управления (разбора) в процессе выполнения кода. Можно попробовать сохранять эти признаки на дополнительном стеке (хотя можно просто привязать структуры к железным регистрам состояний типа CPSR/SPSR) и добавив в интерпретатор анализ этих флагов с целью дополнить управление содержанием поля параметров или просто рассширения встроенного асм.

21

Re: ARM

VFPU-Divide

https://github.com/Ignat99/JonesForth-a … /cortex-a9

Для не слишком сложных вещей, обычный родной ассемблер не чем не хуже какого-нибдь СИ, и уж тем более лучше Java, потому что ассемблер генерирует родной для Кортекса-А9 код.

На самом деле символ "/" определен силами самого форта (который уже манипулирует подветвями с метками как примитивами работы со стеком) в другом файле - jonesforth.f. Поэтому я использовал "%" для деления с плавающей точной. Надо вывести сопроцессор в Форте на чистую воду.

Возможно, под Линукс должны быть какие то специальные права, чтоб была возможность управлять сопроцессором. Потому что регистр состояния SPSR в режиме USR не доступен для записи (только чтение из CPSR).

1/ Из замеченных проблем при переносе кусков кода, надо дать названия регисторов внутри кода в соответствии с конфигурации в начаеле файла.

2/ Далее надо подправить параметры в макросах, либо заменить сами макросы.

3/ Настроить или дать доступ к управлению подпроцессорами и другими устройствами.
Возможно это проще сделать при загрузке с СД карты, потому что тогда процессор работает в режиме SYS.

Для создания ИИ по переносу кода можно:

a/ Написать эвристику или регулярное выражение на скриптовом языке. К которому добавить список синонимов и возможных их границ применения в Пролог подобном синтаксисе.

b/ Либо соорудить что то похожее средствами самого Форта.

Можно да же накапливать опыт удачной подстановки (в случае прохождения формальных тестов) с тем чтоб улучшить вероятность выбора для данной конкретной системы.
Потому что для системы с другой историей список синонимов будет совершенно другой.

22

Re: ARM

С помощю SIMD (Адвансед Сингле Инструкшен Мультипле Дата) на ARMv7 можно складывать аппаратно вектора. 32 регистра с 64 битами или 16 регистров с 128 битами.

i0-i127 - 8 bit
s0-s63 - 16 bit
d0-d31 - 64 bit (32 всего)
q0-q15 - 128 bit (16  всего)

Например Векторное деление:

vdiv.f64 d0, d0, d1

Взять два 32 битных значения d0, d1, поделить и результат положить в d0 в формате флоат f64.

Векторное конвертирование:

vmov s0, r0
vcvt.f64.u32 d0, s0

Из интеджер s0 в число с плавающей точкой d0.

Перед передачей обратно в Форт процессор из неон сопроцессора (vmov r0, s0) мы делаем обратную конвертацию.

vcvt.u32.f64 s0, d0
vmov r0, s0

У меня на 4 ядрах Одроид Икс2 Неон поддерживается:

cat /proc/cpuinfo | grep Features 
Features    : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls 
Features    : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls 
Features    : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls 
Features    : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls

Для управления сопроцессором нужно работать в привелигированном режиме доступа.

23

Re: ARM

Автор: *

А может посчитать количество единиц в 64битном слове за одно действие? Или сохранить 50 бит в массив бит с побитовой адресацией.

BFI (Бит филед Инсерт)
BFC (Бит Филед Клеар)
SBFX и UBFX (Сигнед и Унсигнед бит филед экстракт)
В Кортекс-А9 может не только скопировать нужные соседние  биты, но и дополнить нулями или единицами до 32 бит за 1 раз но в 64-бит или да же более крупном слове.

https://developer.arm.com/products/proc … x-and-ubfx

RBIT - (Реверсе БИТ ) изменить порядок бит в регистре на противоположный.


Так же есть

https://en.wikipedia.org/wiki/Saturation_arithmetic

CLZ (Каунтинг лендинг зерос) - вернет количество нулей с краю.

USAT и SSAT - устанавливают биты (степени 2)
USAT16 и SSAT16 - можно две гурппы битов упаковать в 1 регистр.

В сопроцессоре с векторой арифметикой етсь возможность делать операции сразу с 4 32 битными регистрами за одну команду. Сатуратинг там то же есть. Так что можно не 64 , а целых 128 бит за 1 команду подвигать.

VBIT, VCLZ, VQRSHL ... много там команд.
https://developer.arm.com/products/proc … st/preface

Многие Райсбери это умеют.  Но надо делать настройки доступа к сопроцессору в привелигированном режиме работы. В случае ядра Линукс это специальный дарайвер VFPU-NEON.

Хотя как я говорил выше, ОС не нужа чтоб использовать терминал или встроенный редактор. Да же не нужна для подключению к интернету, если на ПЛИС загрузили ядро, которое в железе пакеты собирает (бывает и такое).

Массив ваших бит можно подгрузить прямо в кеш.

https://developer.arm.com/products/proc … st/preface

24

Re: ARM

Массив ваших бит можно подгрузить прямо в кеш. В L1 от 16 до 32К, В L2 от 256к до 1 Мегабайта обычно. В любом Райсбери Пай это так.

(Я предпочитаю четырехядерный Одроид-X2 с возможностю обработки до четырех! 128 битных векторов за 1 такт, либо нежмитесь и отведите по 1 байту, тогда можно будет состояние нейронов сохранять причем в логарифмических единицах - Децибелах)

В Сопроцессоре есть операции побайтовые с Ограничителльной арифметикой или да же побитовые. Сделайте пряом там нейросетку в кеше L1 и VFPU сопроцессоре.

Минимальная подгружаемая ЛИНИЯ (кусок который можно докинуть) кеша в Кортекс-А9 есть 8 байт. В Кортекс-А15 это 16 байт. То есть на эти величины надо орентироваться как на размер подачи от сенсора.

При прямом отображении памяти [5:4] биты адреса лучше отвести под номер нейрона (тогда в любой момент времени он будет находиться в том же самом месте кеша). Тогда каждое новое слово попавшее в кеш будет записываться на то же самое место и будет означать переход к следующему моменту времени.

При использовании политики замещения Round-robin в ассоциативном кеше, можно считать номер пути (way) индексом по времени. Пример для 32 К кеша L1, 4 way (4 временные метки), линия данных будет в 32 байта (8 слов по 4 байта). Всего 256 линий.

25

Re: ARM

Вот ещё некий Bit Banding, можно работать с битами через байтовую адресацию
https://blablacode.ru/mikrokontrollery/455
Похоже ничего особенного в ARM нет, просто слегка своеобразный RISC процессор.
Хотелось бы получше поддержку обработки бит, как для шифрования или архивации. А то доросли до 64 и больше разрядности, теперь приходится их ворочать.

Про кэш тоже, процессор уже мегабайтами пользуется, а программисту и 16 штук не дают. Удобно было бы за одно действие находить в 16-байтовом массиве нужное значение. Чтобы не строить всякие мелкие хэши и деревья.

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