Обсуждаем контроллеры компании Atmel.
Ответить

Конечный автомат на ассемблере

Чт ноя 24, 2022 19:40:12

Для распознавания вводимых через USART команд хочу замутить конечный автомат. У меги (да и у тиньки тоже) на такой случай даже есть инструкция IJMP. Засовываем в регистр Z адрес процедуры, обрабатывающей данное конкретное состояние, а при приходе события (символа с USART), вызываем эту процедуру. В теле указатель может меняться для вызова процедуры для другого состояния конечного автомата.

Мой вопрос в следующем: как правильно использовать комбо инструкций LDI, ZL, addr_l / LDI ZH, addr_h / RJMP ? Адрес же не нужно умножать на два как при чтении с помощью инструкции LPM ?
Последний раз редактировалось B@R5uk Пт ноя 25, 2022 14:00:00, всего редактировалось 1 раз.

Re: Конечный автомат на ассемблере

Чт ноя 24, 2022 20:18:13

Если прямо-таки так асм хочется, можно для начала посмотреть, что gcc сгенерит на элементарный switch…
Но таки понятней сразу на С писать, т.к. сложный код на асме выложится в тысячи страниц, которые прочесть далеко не так просто, как сишный код!

Re: Конечный автомат на ассемблере

Чт ноя 24, 2022 22:19:25

Адрес же не нужно умножать на два как при чтении с помощью инструкции LMP ?

Нет, не нужно. Но задачу так и не понял… :dont_know:

Re: Конечный автомат на ассемблере

Пт ноя 25, 2022 06:45:04

Адрес же не нужно умножать на два как при чтении с помощью инструкции LMP ?

А что это за инструкция такая - LMP? Что-то я такой не помню.

Re: Конечный автомат на ассемблере

Пт ноя 25, 2022 08:36:14

Код:
.macro LMP
       LPM
.endmacro

:)

Re: Конечный автомат на ассемблере

Пт ноя 25, 2022 10:53:22

Re: Конечный автомат на ассемблере

Пт ноя 25, 2022 14:15:12

Нет, не нужно. Но задачу так и не понял… :dont_know:
Спасибо. Ну, вопрос был не в задаче, а в использовании инструкции. Если задача всё-таки интересна, то я вот такой вот код замутил и отладил:

Для трёх состояний можно, конечно, было бы обойтись и CP / BREQ связкой, но мне захотелось испытать и отладить что-нибудь новенькое. Тем более, что раньше инструкцией IJMP никогда не пользовался.

Добавлено after 10 minutes 10 seconds:
...можно для начала посмотреть, что gcc сгенерит на элементарный switch...
Так я и не научился на си под AVR писать. Пользуюсь AVR Studio 4 и калякаю на асме по мере необходимости. У меня есть большое подозрение, что сишный компилятор выдаст всё таки связку CP / BREQ.


А теперь вопрос на засыпку. Инструкция IJMP использует только регистр Z, так же как и инструкция LPM. Может случиться так, что необходимо будет использовать и то, и другое. С другой стороны не хотелось бы постоянно перекидывать информацию из регистра Z туда-сюда, поэтому адреса было бы не плохо хранить и использовать из других регистров. С чтением программной памяти ничего не поделаешь. А вот переход по динамическому адресу можно замутить с помощью комбо PUSH / RET. Вопрос: что надо в стек совать раньше: младший байт адреса или старший?

Re: Конечный автомат на ассемблере

Пт ноя 25, 2022 14:45:55

B@R5uk, вообще-то испокон веков сначала отправляется младший байт, потом старший.
пора бы давно это простое правило знать. можно сказать, что это закон для работы с различными данными.
это правило всегда используется и при записи данных в ОЗУ и при записи данных в файлы.
например, файлы графических форматов (картинки), музыкальные файлы содержат длину блоков данных, начиная с младшего байта.

Re: Конечный автомат на ассемблере

Пт ноя 25, 2022 14:54:10

Starichok51, а разве не обе формы существуют?

Re: Конечный автомат на ассемблере

Пт ноя 25, 2022 15:04:27

Как и бесконечные войны между остроконечниками и тупоконечниками...)

Re: Конечный автомат на ассемблере

Пт ноя 25, 2022 15:06:11

Martian, существует обе формы. Они даже специальное название имеют.

Starichok51, спасибо. Но как бы я не старался запомнить, к стеку у меня будет всегда подозрение. Потому что вот положили мы младший байт раньше в стек, а в памяти он оказался позже старшего, потому что стек переворачивает очерёдность. Тут сразу становится закономерен вопрос: что важнее, процесс или результат? Какую мнемоничность я бы себе не пытался заучить, всегда останется сомнение из-за этой дуальности. Так что уж не обессудьте за казалось бы глупый вопрос.

Re: Конечный автомат на ассемблере

Пт ноя 25, 2022 17:35:53

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

Re: Конечный автомат на ассемблере

Пт ноя 25, 2022 17:51:42

B@R5uk, стек потому и стек что, что последним положил, то первым вышло.
у меня есть программы, где я из прерывания таймера выхожу в начало цикла через стек.
если конкретно, то в этой программе мне нужно делать работу цикла 1 раз в секунду. соответственно, таймер отмеряет эту секунду.
прерывание таймера у меня ничего специального не делает. в прерывании я загружаю в указатель стека начало стека, потом в стек отправляю адрес начала цикла и выхожу из прерывания.
а в конце цикла у меня стоит такая "заглушка":
Код:
wait_timer:
rjmp wait_timer         ; ожидаем прерывания таймера

то есть, топчемся на месте и ждем прерывания таймера.

Re: Конечный автомат на ассемблере

Пт ноя 25, 2022 18:27:39

Тоже подобное делал. Пока не понял, что в этом нет необходимости. Конечный автомат сам себе и диспетчер и так далее.

Re: Конечный автомат на ассемблере

Пт ноя 25, 2022 19:45:59

Если задача всё-таки интересна, то я вот такой вот код замутил и отладил

Задача интересная, но только лишь при описании её просто словами, а не кодом, так как большинство инструкций ассемблера к своему стыду я не знаю. :oops: Если у Вас всё получилось как задумано, то я очень рад что хоть чем-то смог помочь. :)

Добавлено after 8 minutes 57 seconds:
а разве не обе формы существуют?

Существуют обе, но если отдать компилятору решать данную задачу "самостоятельно", то он поступит стандартно, вначале младший затем старший… но можно принудительно поступить иначе, если это кому то так необходимо… :)

Re: Конечный автомат на ассемблере

Пт ноя 25, 2022 22:58:08

IJMP/ICALL с передачей двух байт адреса начала подпрограммы обработки в теле команды.
Для загрузки Zh:Zl используем адреса, взятые из листинга основной программы.
8)
В случае необходимости сверхоперативного обмена данными (помимо пересылок через стек) используем для хранения любую регистровую пару рон созу и команду MOVW rd,rr.
К примеру
MOVW Zl,R0 ; она же в полной форме MOVW Zh:Zl,R1:R0
:wink:

Re: Конечный автомат на ассемблере

Сб ноя 26, 2022 08:51:09

Во дичь-то! А переход в узел КА через таблицу адресов слабО сделать? И перед началом анализа привести все символы к одному регистру.

Re: Конечный автомат на ассемблере

Сб ноя 26, 2022 08:57:05

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

Re: Конечный автомат на ассемблере

Сб ноя 26, 2022 10:40:22

Во дичь-то! А переход в узел КА через таблицу адресов слабО сделать? И перед началом анализа привести все символы к одному регистру.

В самом начале топикстартер указал на получение команд по UART.
Смысл в дополнительной таблице векторов? Лишний расход ПЗУ?
Другое дело как-то через ОЗУ "подменные" вектора ставить - но то более "многофункциональное устройство с субменюшками" нежели простая дистанционка.
В случае получения команд без контроля достоверности (несинхронный канал при отсутствии контроля целостности данных) посылать ОДИН байт весьма рискованное дело.
А синхронная пересылка это уже лишние проводки (то же SPI как минимум две витые пары и уж никак не ИК или радиоканал).
При пересылке пакета (хотя бы подобие микролан далласа) смысл гнать единичный байт пропадает - достаточно использовать полный адрес обработчика команды, корректируя при необходимости содержимое программы передатчика дистанционки под имеющийся/обновляемый набор команд внешнего устройства.
8)

Re: Конечный автомат на ассемблере

Сб ноя 26, 2022 10:52:02

Моя любимая тема.
Проворачивал косвенные вызовы на ассемблере AVR.
Нужно завести сначала массив указателей на вызываемые подпрограммы - обработчики состояний автомата.
Код:
;---------- Таблица переходов
STATES:
.dw STATE_0, STATE_1, STATE_2

STATE_0:
ldi temp0, 0x01 ;Переключаем на следующее состояние
ret

STATE_1:
ldi temp0, 0x02 ;Переключаем на следующее состояние
ret

STATE_2:
ldi temp0, 0x00 ;Переключаем на начальное состояние
ret


Далее сам автомат, который может вызываться в основном цикле.
Стоит заметить, что я не привожу тут загрузку и сохранение текущего состояния в ОЗУ.
Код:
; Много телодвижений связано с тем, что в конкретном МК LPM и IJMP или ICALL работают только с парой Z
; Сначала грузим номер текущего состояния из ОЗУ в регистры temp0 и temp1
; За тем выполняем код ниже:
ldi           ZL,               Low(STATES*2) ; Грузим начальный адрес массива указателей на процедуры
ldi           ZH,               High(STATES*2)
lsl           temp0 ; В регистрах temp0 и temp1 хранится номер текущего состояния. temp0 - младший байт
clr           temp1 ; , temp1 - старший. Но тут он не используется, так как состояний не так много было
add           ZL,               temp0 ;Смещаемся по массиву для выбора нужного указателя
adc           ZH,               temp1
lpm           temp2,              Z ; Грузим младший байт адреса
adiw          Z,                0x01 ; Смещаемся к старшему байту адрса
lpm           temp3,              Z ; Грузим старший байт адреса
mov           ZL,               temp2 ; Заносим байты алреса процедуры в Z
mov           ZH,               temp3
icall ; Вызываем процедуру
; Тут можем сохранить номер состояния в ОЗУ

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

Смысл в дополнительной таблице векторов? Лишний расход ПЗУ?

Пара инструкций сравнение + переход по условию занимают четыре байта. А если процедура далеко, то и все шесть. Ну и проход по простыне условий затрачивает лишние такты.
А вот один адрес в массиве занимает только два байта, что при большом количестве состояний нехило сэкономит ПЗУ и увеличит быстродействие. Быстродействие вообще почти мгновенное: Вычислил смещение, загрузил адрес, вызвал процедуру по этому адресу.
В жизни был проект, где был КА на 45 состояний (обработчик сложного протокола обмена) И там такой метод дал много выйгрыша.
Последний раз редактировалось DX168B Сб ноя 26, 2022 11:25:25, всего редактировалось 1 раз.
Ответить