Liv писал(а):... табличное вычисление CRC не делал. Хотя в будущей реализации для AVR планирую добавить.
И здесь здравствуйте!
Может, ну его, табличное? Для AVR, по крайней мере. У них же нет возможности за один мах зачитать слово из таблицы, в результате табличный 16-битный CRC в зависимости от реализации (на асме с выровненными по границе 256 байт полутаблицами или на С «ну как легло») займёт грубо 15+-3 цикла, а быстрый
нетабличный метод — где-то от 20 до 25. Разница ощутимая, но что одно, что другое гораздо быстрее цикла по битам. И разница в десяток тактов на байт частично теряется во всех остальных затратах на байт — начиная от разбора SLIP и заканчивая push/pop в обработчике прерывания.
Я давно для себя решил, что «побитный» метод не имеет никаких преимуществ — длина кода не настолько меньше «быстрого нетабличного», чтобы это что-то весило, а для ухода на табличный нужно ну очень за скоростью гнаться.
«Быстрый нетабличный» для выглядит приблизительно так (выписано с оглядкой на 8-битник, для 16-битника выглядит ещё красивее, CRC_SPACE для 51-ых могло оказатья idata/xdata):
Код: Выделить всё
uint16_t calc_crc(void CRC_SPACE * array, CRC_LEN len)
{
uint8_t carry;
uint8_t CRC_SPACE *ptr = (uint8_t CRC_SPACE *) array;
uint8_t crcbuf_lo = 0;
uint8_t crcbuf_hi = 0;
while(len--) {
carry = crcbuf_hi ^ *ptr++;
carry = carry ^ (uint8_t) (carry >> 4);
crcbuf_hi = crcbuf_lo ^ (uint8_t) (carry << 4) ^ (uint8_t) (carry >> 3);
crcbuf_lo = carry ^ (uint8_t) (carry << 5);
}
return ((uint16_t) crcbuf_hi << 8) | crcbuf_lo;
}
Конкретный вид кода (и время тела цила) зависят от полинома.
Хорошо это дело опиcано было у ... Альтеры в аппноте по вычислению CRC — с картинками, пояснющими как из заданного полинома получить вот такой код, только там он на AHDL был. Могу поискать, это апнота времён MAX+II 7.x.
В avr-libc в <util/crc16.h> есть inline-функции c asm-ядром для нескольких полиномов.
Liv писал(а):При наличии уникального маркера начала пакета не видно необходимости в кольцевом буфере. Начало пакета всегда может помещаться в начало буфера.
+1
Liv писал(а):Анализ символа начала пакета - минимальная работа, вполне можно делать и в прерывании. Конец пакета тоже несложно определить в прерывании, и только после этого дать знать о приеме пакета процессу разбора. Хотя, конечно, можно и всю работу поручить процессу.
С выносом из прерывания, на мой взгляд, тоже без фанатизма надо. Разбор SLIP-овской подстановки байтов вполне может сидеть в прерывании, использование не кольцевого буфера, а буфера пакета этому способствует. А уже потом отдавать наверх, пусть там CRC сверяют, то-сё.
В случае с вытесняющей ОС так и вообще — тот процесс, которому делегируется работа, для всех менее приоритетных процессов неотличим от прерывания — так зачем выносить в процесс? Стоит сокращать суммарное время работы прерывания и процесса. Переключать процессы по каждому байту — это на одни переключения контекста потратить больше времени, чем нужно для разбора SLIP и даже подсчёта CRC.
Если есть принципиально *) более приоритетные процессы, тот тут стоит думать. Но я склонен часть протокола уносить в прерывания, процессу отдавать (у процесса брать) пакет, а более мелкую работу делать в прерываниях.
*) «принципиально» — это не просто формально больше приоритет, в случае scmRTOS только потому, что у неё нельзя создать две задачи одинакового приоритета, а действительно она должна отпихивать в сторон все эти протоколы и делать своё дело. Но и тогда нет смысла выносить из прерываия в коммуникационный процесс то, что занимает время, сравнимое со временем переключения.
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.