Как стать автором
Обновить

Комментарии 24

Спасибо за статью!
#define dbgG ((dbg_t*)DBG_ADDR) // адрес задан жестко, в настройках линкера эта часть озу убирается из доступной

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

А GDB разве позволяет грузить прошивку? Или вы отдельный протокол для этого будете делать?
Есть возможность загружать прошивки через GDB. IAR к примеру сперва загружает командами записи памяти boot в ОЗУ. Далее его алгоритм загружает куски прошивки в ОЗУ, устанавливает PC на адрес boot и ждет breakpointа окончания операции. В папке st-link utility есть примеры таких бутов с исходниками.
Отдельный протокол нет смысла придумывать, когда он уже есть в openOCD.
Интересно сделано в IAR. Это у него так в доках прям написано? или вы реверсили его?
В папке st-link utility есть примеры

st-link utility это вот эту утилиту имеет ввиду
фото
image

Там в папке ExternalLoader подобное применяется, но я так понял это что бы прошивать внешние чипы памяти
Реверсил обмен в WireShark между IAR и openOCD. Но в моей конфигурации breakpoint окончания операции не срабатывает по какой-то причине.
Что-то похожее видел в PIC24 Programming Manual. Там можно либо вручную программатором писать флеш. Либо зашить загрузчик и работать с ним.

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

Можно вместо брейкпойнта и поллинг окончания операции сделать.

9 — шаг (не получилось реализовать)

А в чем возникла проблема сделать шаг? вроде бы просто поставить брейкпойнт на следующую ассемблерную инструкцию.

Можно вместо брейкпойнта и поллинг окончания операции сделать.

Поллинг будет забирать часть времени, что увеличит время заливки.

А в чем возникла проблема сделать шаг?

Тогда нужно реализовать определение текущей инструкции средствами МК. Ато вдруг это инструкция перехода?
Но есть другой способ: В регистре DEMCR есть бит MON_STEP. Но тут возникли проблемы. Если не выключить прерывания, программа всегда останавливается на первой инструкции любого из наступивших прерываний. А если их выключить, он больше не попадает в DebugMon_Handler
Единственно кажущийся рабочий вариант — эмулировать эту команду на стороне GDB сервера
Ато вдруг это инструкция перехода?

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

Выключать можно не все, а оставить только DebugMon_Handler, разве нет?
А вот если оставлять только DebugMon_Handler включенным, работает. При выходе из обработчика он выполняет одну инструкцию и возвращается обратно.
Т.е. команду пошагового выполнения реализовать удалось

А вот это круто! Спасибо!


Но, как и в прошлый раз, позволю себе немного поворчать:
0хАА 0xFF — Start of frame
0xAA 0x00 — End of frame
0xAA 0xA5 — Interrupt
0xAA 0xAA — Заменяется на 0xAA

На мой взгляд, протокол странный. Никакого контроля ошибок, зато есть escape-кодирование.
Может быть, хотя бы чексумму добавить?
А если использовать CRC, то можно и без escape-кодирования обойтись...

Контрольная сумма — хорошая идея, нужно будет добавить.
Не представляю как можно используя CRC избавиться от escape-кодирования при условии что пакеты переменной длинны
Не представляю как можно используя CRC избавиться от escape-кодирования при условии что пакеты переменной длинны

  1. Делаем пакета вида START, SIZE, DATA....DATA, CRC
  2. Во входном потоке байт ищем START.
  3. Следующий байт (байты) — SIZE.
  4. Набираем SIZE байт
  5. Проверяем CRC, сошлась — хорошо, набираем новый пакет. Не сошлась — ищем в том, что уже собрали еще один START; если нашли — пляшем от него по новой.

UPD: Вроде как это называется https://en.wikipedia.org/wiki/CRC-based_framing

Спасибо! Буду знать.
А как быть с такой ситуацией, что приемник пропустил START байт, а в дата лежит такая последовательность байт «START, SIZE», ведь у нас без escape-кодирования.
Он этот пакет пропустит, дальше отправитель будет дублировать этот пакет и приемник уже ни один пакет принять не сможет.

Если сделать правильно, то будет так:


  • пропущен start, size
  • в середине данных найден фиктивный start, size, пошло накопление пакета
  • пакет набран по фиктивному размеру, crc не сошлось.
  • в уже набранном наборе байт ищется следующий start — он настоящий, синхронизация восстановлена

Т.е. если crc не сходится, не нужно выбрасывать все накопленное.


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

С escape-кодированием проще выходит код как-то)
Ведь в дата может быть:
«START, SIZE,START, SIZE,START, SIZE,START, SIZE...» =)

А несколько стартовых байт, по сути, не спасут от
«START, SIZE,START, SIZE,START, SIZE,START, SIZE...»

Про ограничить размер пакета, что вы имели ввиду?

С эскейп-кодированием нужно эскейп кодирование и контрольная сумма тоже! А так — только контрольная сумма.


Код проще? Спорный вопрос.


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


Так — только фиксированный запас на CRC.


Да и в чем сложность? Crc не сошлось — цикл для поиска старта, сдвиг массива (один вызов std::rotate), запускаем основной автомат набора пакета.


«START, SIZE,START, SIZE,START, SIZE,START, SIZE...»

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


Про ограничить размер пакета, что вы имели ввиду?

Это и имел — сказать, что пакеты могут быть не более Х байт.




Лично мое мнение: эскейп-кодирование — это устаревшая, неудобная и ненужная штука. Огромное количество протоколов (в т.ч. Ethernet и TCP) живет без него и отлично себя чувствует.

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

Можно и на лету эскапировать и не потребуется двойной буфер, если уж память жалко.

Про простоту кода, это мое субъективное мнение, доказать я её не пытаюсь. Однозначно crc нужно всегда это да.

Можно и на лету эскапировать и не потребуется двойной буфер, если уж память жалко.

Чтобы эскапировать на лету, придется руками каждый байт перекладывать в UART, не получится ни выделить работу с UART'ом в отдельный модуль, ни использовать DMA, например.


Можно, конечно, но не очень удобно.

Код проще? Спорный вопрос.

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

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

Да и в чем сложность? Crc не сошлось — цикл для поиска старта, сдвиг массива (один вызов std::rotate), запускаем основной автомат набора пакета.

Сложность в объеме и скорости выполнения кода. На стороне ПК это рабочий вариант, но не в МК.

В некоторых МК у модуля UART есть прерывание IDLE, наступает когда обнаружена пауза в потоке данных. Можно было бы использовать его и не использовать Esc-кодирование, но в STM32F7 нет такого прерывания у UARTа.
Вы читали мой код, в частности функцию tx_cb? Всё кодирование при отправке реализуется средствами конечного автомата, при этом его алгоритм проще некуда. Так-же и при приеме. В буфере лежат только полезные данные.

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


Сложность в объеме и скорости выполнения кода. На стороне ПК это рабочий вариант, но не в МК.

Как вы поняли, что для МК это не рабочий вариант?
Мы ведь с вами говорим про Cortex-M, а не про PIC восьмибитный; тут и памяти вполне достаточно и скорости.


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


А уж STM32F7 тактовые за 200 МГц! Это ж почти Пентиум 2 :)

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

Это довод.

Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории