CRC32: на STM32 как на компе или на компе как на STM32.

Все знают, что в STM32F1xx, STM32F2xx, STM32F4xx есть аппаратный блок CRC32 с полиномом 0x04C11DB7.
И он, в общем-то, работает. Но только контрольная сумма почему-то не совпадает с таковой, рассчитанной софтварно.
В гугле обычно 2 типа вопросов:
  1. Как хардварно посчитать на STM32 побайтовую CRC
  2. Как посчитать софтово CRC так, чтоб она совпала с хардовой на STM32
Причём, на первый вопрос ответ везде отрицательный. Так ли это? Попробуем разобраться.

Софтварно CRC32 обычно считается побайтово и (как в эзернете) — младшим битом вперёд, сдвиг LSFR — вправо, в сторону младшего бита, поэтому используется реверсированный полином 0xEDB88320.
Регистр данных же в CRC блоке STM32 — 32х-битный и сдвигается в сторону старшего бита.
Чтоб понять, почему так, немного иллюстрации:

  • Во-первых, CRC — побитовая функция. параллельный подсчёт CRC — плюшки из следствий математики двоичных полиномов.
  • порядок загрузки бит в LSFR не должен нарушаться в зависимости от разрядности и остроконечности архитектуры
Смотрим на рисунок: все биты поступают по порядку, цветами я пометил биты, которые соответствуют друг другу для прямого и зеркального полиномов, нумерация байтов совпадает со смещением в памяти.
Т.е., CRC32 на STM32 можно посчитать так же, как и принято в ethernet. Для этого надо реверсировать входные слова и реверсировать в итоге контрольную сумму. Это работает только для длины, кратной 4.
Сперва совтварная реализация.
Инициализируем таблицы остатков для быстрого вычисления CRC:

static uint32_t crc32_table[256];
static uint32_t crc32r_table[256];

#define CRC32_POLY   0x04C11DB7
#define CRC32_POLY_R 0xEDB88320

static void crc32_init(void)
{
	int i, j;
	uint32_t c, cr;
	for (i = 0; i < 256; ++i) {
		cr = i;
		c = i << 24;
		for (j = 8; j > 0; --j) {
			c = c & 0x80000000 ? (c << 1) ^ CRC32_POLY : (c << 1);
			cr = cr & 0x00000001 ? (cr >> 1) ^ CRC32_POLY_R : (cr >> 1);
			}
		crc32_table[i] = c;
		crc32r_table[i] = cr;
		//printf("f[%02X]=%08X\t", i, crc32_table[i]);
		//printf("r[%02X]=%08X\t", i, crc32r_table[i]);
	}
	//printf("\n");
}


Побайтовое вычисление «нормальной CRC»:

uint32_t crc32_byte(uint32_t init_crc, uint8_t *buf, int len)
{
	uint32_t v;
	uint32_t crc;
	crc = ~init_crc;
	while(len > 0) {
		v = *buf++;
		crc = ( crc >> 8 ) ^ crc32r_table[( crc ^ (v ) ) & 0xff];
		len --;
	}
	return ~crc;
}


Вычисление CRC как на CRC блоке STM32:

uint32_t crc32_stm32(uint32_t init_crc, uint32_t *buf, int len)
{
	uint32_t v;
	uint32_t crc;
	crc = ~init_crc;
	while(len >= 4) {
		v = htonl(*buf++);
		crc = ( crc << 8 ) ^ crc32_table[0xFF & ( (crc >> 24) ^ (v ) )];
		crc = ( crc << 8 ) ^ crc32_table[0xFF & ( (crc >> 24) ^ (v >> 8) )];
		crc = ( crc << 8 ) ^ crc32_table[0xFF & ( (crc >> 24) ^ (v >> 16) )];
		crc = ( crc << 8 ) ^ crc32_table[0xFF & ( (crc >> 24) ^ (v >> 24) )];
		len -= 4;
	}
	if(len) {
		switch(len) {
			case 1: v = 0xFF000000 & htonl(*buf++); break;
			case 2: v = 0xFFFF0000 & htonl(*buf++); break;
			case 3: v = 0xFFFFFF00 & htonl(*buf++); break;
		}
		crc = ( crc << 8 ) ^ crc32_table[0xFF & ( (crc >> 24) ^ (v ) )];
		crc = ( crc << 8 ) ^ crc32_table[0xFF & ( (crc >> 24) ^ (v >> 8) )];
		crc = ( crc << 8 ) ^ crc32_table[0xFF & ( (crc >> 24) ^ (v >> 16) )];
		crc = ( crc << 8 ) ^ crc32_table[0xFF & ( (crc >> 24) ^ (v >> 24) )];
	}
	return ~crc;
}

Тут я применил htonl чтоб байты в слове были в определённом порядке независимо от LE/BE: сначала в LSFR заправляется байт, который лежит в памяти по смещению 3 (как на рисунке). Ещё, остаток сообщения, не влезающий в 4х-байтовое слово дополняется нулями справа и CRC досчитвается дальше.
Можно писать конструкцию типа такой (вычислять CRC кусками):

printf("crc32_byte   = %08X\n", crc32_byte(crc32_byte(0, "12345", 5), "6789", 4));
printf("crc32_stm32   = %08X\n", crc32_stm32(crc32_stm32(0, "1234", 4), "56789", 5));


Вот то, что получается:
crc32_byte(«123456789») = CBF43926
crc32_stm32(«123456789») = 500E6FA8
crc32_byte(«12345678») = 9AE0DAAF
crc32_stm32(«12345678») = 0103AB06

Теперь код для STM32:
Сперва чисто хардварная CRC (ей соответствует софтовая crc32_stm32):

    uint32_t crc32_native(char *bfr, int len, int clear) {
	int l;
	uint32_t *p, x, crc;
	l = len / 4;
	p = (uint32_t*)bfr;
	x = p[l];
	if(clear) CRC_ResetDR();
	while(l--) {
		crc = CRC_CalcCRC(*p++);
	}
	switch(len & 3) {
		case 1: crc = CRC_CalcCRC(x & 0x000000FF); break;
		case 2: crc = CRC_CalcCRC(x & 0x0000FFFF); break;
		case 3: crc = CRC_CalcCRC(x & 0x00FFFFFF); break;
	}
	return 0xFFFFFFFF ^ crc;
}


Далее сделаем «как на софте», или «как в ethernet». Благо, в ARM есть инструкция для реверсирования бит.
Но это не всё. Ведь если пораскинуть мозгами, то можно добавить вкусную плюшку — сосчитать-таки побайтовую CRC на аппаратном блоке.
Нужно просто вычислить полином-остаток от оставшегося куска и добавить его к уже посчитанной пословно CRC. Остаток — это по сути та же CRC, но с начальным состоянием LSFR=0 (см инициализацию таблицы остатков). Но вот беда — CRC_ResetDR может установить регистр CRC только в 0xFFFFFFFF. Слава яйцам, что нам надо именно 0, а не что-то ещё. Одно из свойств CRC состоит в том, что если к сообщению приписать его CRC, то CRC нового сообщения будет равна 0. Другими словами, если мы подадим в регистр CRC то, что мы из него считали, то в результате получится 0. Теперь осталось заправить в регистр кусочек из одного, двух или трёх оставшихся байт — et voila, забираем наш полином-остаток и добавляем его к CRC.
Ниже код:


uint32_t reverse_32(uint32_t data)
{
	asm("rbit r0,r0");
	return data;
};

uint32_t crc32_ether(char *buf, int len, int clear)
{
	uint32_t *p = (uint32_t*) buf;
	uint32_t crc, crc_reg;
	if(clear) CRC_ResetDR();
        if(len >=4) {
        	while(len >= 4) {
	        	crc_reg = CRC_CalcCRC(reverse_32(*p++));
		        len -= 4;
                }
        } else {
                crc = 0xFFFFFFFF;
                crc_reg = CRC_CalcCRC(0xEBABAB);
        }
	crc = reverse_32(crc_reg);
	if(len) {
		CRC_CalcCRC(crc_reg);
		switch(len) {
			case 1:
			crc_reg = CRC_CalcCRC(reverse_32((*p & 0xFF) ^ crc) >> 24);
			crc = ( crc >> 8 ) ^ reverse_32(crc_reg);
			break;
			case 2:
			crc_reg = CRC_CalcCRC(reverse_32((*p & 0xFFFF) ^ crc) >> 16);
			crc = ( crc >> 16 ) ^ reverse_32(crc_reg);
			break;
			case 3:
			crc_reg = CRC_CalcCRC(reverse_32((*p & 0xFFFFFF) ^ crc) >> 8);
			crc = ( crc >> 24 ) ^ reverse_32(crc_reg);
			break;
		}
	}
	return ~crc;
}


Вот, как-то так, думаю, многим пригодится.

P.S. бонус:

uint8_t pkt_alt[] = {
	0x00, 0x10, 0xA4, 0x7B, 0xEA, 0x80, 0x00, 0x12,
	0x34, 0x56, 0x78, 0x90, 0x08, 0x00, 0x45, 0x00,
	0x00, 0x2E, 0xB3, 0xFE, 0x00, 0x00, 0x80, 0x11,
	0x05, 0x40, 0xC0, 0xA8, 0x00, 0x2C, 0xC0, 0xA8,
	0x00, 0x04, 0x04, 0x00, 0x04, 0x00, 0x00, 0x1A,
	0x2D, 0xE8, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
	0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
	0x0E, 0x0F, 0x10, 0x11, 0xB3, 0x31, 0x88, 0x1B
};

uint8_t pkt_alt_d[] = {
	0x00, 0xC0, 0x02, 0x37, 0x57, 0x28, 0x00, 0x10,
	0xA4, 0x7B, 0xEA, 0x80, 0x08, 0x00, 0x45, 0x00,
	0x00, 0x3C, 0x02, 0x24, 0x00, 0x00, 0x80, 0x01,
	0xB7, 0x47, 0xC0, 0xA8, 0x00, 0x04, 0xC0, 0xA8,
	0x00, 0x01, 0x08, 0x00, 0x42, 0x5C, 0x02, 0x00,
	0x09, 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
	0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
	0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
	0x77, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
	0x68, 0x69, 0x62, 0x31, 0xC5, 0x4E
};

uint8_t pkt_xil[] = {
	0x00, 0x0A, 0xE6, 0xF0, 0x05, 0xA3, 0x00, 0x12,
	0x34, 0x56, 0x78, 0x90, 0x08, 0x00, 0x45, 0x00,
	0x00, 0x30, 0xB3, 0xFE, 0x00, 0x00, 0x80, 0x11,
	0x72, 0xBA, 0x0A, 0x00, 0x00, 0x03, 0x0A, 0x00,
	0x00, 0x02, 0x04, 0x00, 0x04, 0x00, 0x00, 0x1C,
	0x89, 0x4D, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
	0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
	0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13,
	0x7A, 0xD5, 0x6B, 0xB3
};

это, если кому надо, реальные ethernet-фреймы с CRC32 FCS
  • +13
  • 09 октября 2013, 00:43
  • scaldov

Комментарии (46)

RSS свернуть / развернуть
раскрыть комментарий
-10
да конечно, я знаю правила. просто про постоянно забываю.

а вот вам, думаю, надо освежить свою память, т.к., например, оскорблять участников форума — запрещено.
+4
забываю про КАТ (слово cut куда-то похерилось)
0
в общем-то да, погорячился немного. признаю. но вы бы знали, как периодически появляющиеся безкатовые поств достали…
0
да я реально забыл вставить cut
0
а вот вам, думаю, надо освежить свою память, т.к., например, оскорблять участников форума — запрещено
Банить таких надо вообще с сайта, если участников оскорбляют.
+1
Та похуй на статью, главное — кат, конечно-же!
+2
разумеется. иногда желание читать отпадает, после того как вижу что статья весь фид засрала.
-1
именно
-4
раскрыть комментарий
-5
Так им всем! Будут знать :)
0
Плюсик =) Помню как постигал эти реверсы в СТМке, пытаясь заставить CRC совпадать с расчетом на компе :D
0
  • avatar
  • N1X
  • 09 октября 2013, 11:05
А кроме этернета, контрольная сумма с таким полиномом еще в каком-то протоколе используется?
0
V.42, MPEG-2, PNG, разные архиваторы, chksum, образы виртуалбокса, да где угодно. Подозреваю, что если надо что-то защитить от помех при хранении/передаче, то именно этот алгоритм кочует в свежий исходник с помощью копипасты.
0
У вас в crc32_ether ошибка.
Если длинна буфера будет кратна 4 то тогда у вас будет возвращатся 0.
Так-как так-как переменная crc в while не изменяется.
И еще зачем вам дополнительная переменная p(uint32_t *p = (uint32_t*) buf;)
0
верно, забыл перепастить из финального исходника.
надо поднять crc = reverse_32(crc_reg); на скобку выше (поправил в статье).

так buf это char*, а мы тащим словами. поэтому.
кстати, компиляторы идеально утилизируют регистры. так что, с лишними переменными щас можно не париться. для этого используются RTL-паттерны.
0
Да понятно что компилятор их соптимизирует.
Кстати вопрос
if(clear) CRC_ResetDR();
Это кокраз и есть запись в CRC_DR 0xFFFFFFFF?
Да и спасибо за статью.
0
да, это из STM32f1xx-STL, эта ф-я записывает бит0 в регистре CRC->CR, и блок CRC инициализирует CRC->DATA значением 0xFFFFFFFF
0
CRC->DATA нельзя задать произвольным образом. на запись он принимает данные, а на чтение — выдаёт текущую CRC

в connectivity line таких проблем нету, там и направление сдвига задаётся, и образующий полином.
0
Я к тому где используется возможность сбросить CRC->DR в 0 путем записи туда текущего значения crc?
0
аа, да вот же:
CRC_CalcCRC(crc_reg);
0
кстати, как перенсти статью в более другой блог?
0
Открыть для редактирования и сохранить, но выбрать немножко другой блог.
0
+1
До хардварного расчета пока еще ручки не добирались, все как-то софтово считается, ибо копипастится их разных других мест, где раньше аппаратного CRC никак небыло. Прозреваю, что потратил бы какое-то количество времени, чтобы вкурить этот момент при необходимости (а она, как водится, рано или поздно появится), так что спасибо за аттеншн.
0

public class CRC32
{
	static UInt32[] Crc32Table = new UInt32[] {
		0x00000000,0x04C11DB7,0x09823B6E,0x0D4326D9,
		0x130476DC,0x17C56B6B,0x1A864DB2,0x1E475005,
		0x2608EDB8,0x22C9F00F,0x2F8AD6D6,0x2B4BCB61,
		0x350C9B64,0x31CD86D3,0x3C8EA00A,0x384FBDBD
	};

	static UInt32 DR;
	public static void Reset()
	{
		DR = 0xFFFFFFFF;
	}

	public static void Write(UInt32 data)
	{
		DR = DR ^ data;
		DR = (DR << 4) ^ Crc32Table[DR >> 28];
		DR = (DR << 4) ^ Crc32Table[DR >> 28];
		DR = (DR << 4) ^ Crc32Table[DR >> 28];
		DR = (DR << 4) ^ Crc32Table[DR >> 28];
		DR = (DR << 4) ^ Crc32Table[DR >> 28];
		DR = (DR << 4) ^ Crc32Table[DR >> 28];
		DR = (DR << 4) ^ Crc32Table[DR >> 28];
		DR = (DR << 4) ^ Crc32Table[DR >> 28];
	}
	public static UInt32 Read()
	{
		return DR;
	}
}
0
  • avatar
  • x893
  • 12 октября 2013, 23:49
немного поясню за коментатора:
это таблица остатков, которые равны CRC32(x), x = 0...15
16 значений память конечно экономят, но операций больше, чем при 256.
0
Забавно, пару дней назад долго разбирался с этим вопросом :)

Добавлю лишь, что очень крутой модуль CRC в серии STM32F3 — там можно настраивать любой полином, автоматом реверсировать биты, причем разными способами и считать для 7, 8, 16 или 32 битного полинома.
+1
Непередаваемое удовольствие читать статьи, из которых узнаешь что-то новое, что не-сумел/успел/нашел/понял самостоятельно. Автору спасибо!
0
  • avatar
  • Flash
  • 13 октября 2013, 22:10
Проблемы несовпадения CRC при использовании аппаратного CRC-юнита описаны на сайте ST и на electronix

Писал в своё время


inline unsigned int revbit(unsigned int Data)
{
	asm("rbit r0,r0");	//Аргумент передаётся через r0. Над ним и выполнить команду rbit
	return Data;
}

unsigned int CalcCRC32(unsigned char *Buf, unsigned int Len)
{
	unsigned int i;
	unsigned int Temp;

	RCC->AHBENR |= RCC_AHBENR_CRCEN;		//Разрешить тактирование CRC-юнита

	CRC->CR = 1;
	asm("nop");		//Аппаратная готовность за 4 такта, жду...
	asm("nop");
	asm("nop");

	// Аппаратный CRC-расчёт работает с 32-битными словами. Т.е. сразу по 4 байта из входной последовательности
	i = Len >> 2;
	while(i--)
	{
		Temp = *((unsigned int*)Buf);
		Temp = revbit(Temp);		//Переставить биты во входных данных
		CRC->DR = Temp;

		Buf += 4;
	}
	Temp = CRC->DR;
	Temp = revbit(Temp);			//Переставить биты в выходных данных
	
	// Обработать оставшиеся байты (классическим не аппаратным методом), если их число не было кратно 4
	i = Len & 3;
	while(i--)
	{
		Temp ^= (unsigned int)*Buf++;
		
		for(int j=0; j<8; j++)
			if (Temp & 1)
				Temp = (Temp >> 1) ^ 0xEDB88320;
			else
				Temp >>= 1;
	}

	Temp ^= 0xFFFFFFFFul;
	return Temp;
}
0
да, но в этой статье оставшиеся байты обрабатываются аппаратным методом.
плюс, всё собрано в одном месте.
0
И правда =). Читал по диагонали.
0
Кстати да, запостил на ST.
0
Спасибо за статью. Не один день убил разбираясь почему crc не совпадают. Чаниковский вопрос — keil Не принимает
asm(«rbit r0,r0»); И в гугле ответ как-то не нашел-(
0
спасибо большое!
0
А вот
crc_reg
не определён при
len < 4
и результат непредсказуем. Вы хоть тестировали код?
0
вы его даже не запускали, даю 146%.
а я тестировал конешно, но на последовательностях >=4, он уж 2 года работает во многих местах.

Вобщем, пропатчил, пользуйтесь наздоровье :)
проверка
на длине 3 CRC(«012»)=D5A06AB0
на длине 2 CRC(«01»)=CF412436
на длине 1 CRC(«0»)=F4DBDF21
0
Запускал, и даже переделал с претензией на большую скорость
uint32_t HW_CRC32(const uint8_t* pData, size_t count, uint32_t init) {
	uint32_t crc;
	uint32_t *p32 = (uint32_t*) pData;
	size_t count32 = count >> 2;
	if (0xFFFFFFFF == init)
		CRC->CR |= CRC_CR_RESET;

	while (count32--) {
		CRC->DR = __RBIT(*p32++);
	}

	crc = __RBIT(CRC->DR);
	count = count % 4;
	if (count) {
		CRC->DR = __RBIT(crc);
		switch (count) {
		case 1:
			CRC->DR = __RBIT((*p32 & 0x000000FF) ^ crc) >> 24;
			crc = (crc >> 8) ^ __RBIT(CRC->DR);
			break;
		case 2:
			CRC->DR = (__RBIT((*p32 & 0x0000FFFF) ^ crc) >> 16);
			crc = (crc >> 16) ^ __RBIT(CRC->DR);
			break;
		case 3:
			CRC->DR = __RBIT((*p32 & 0x00FFFFFF) ^ crc) >> 8;
			crc = (crc >> 24) ^ __RBIT(CRC->DR);
			break;
		}
	}
	return ~crc;
}
Сброс делается при
init = 0xFFFFFFFF
для совместимости с софтовой версией
0
а откуда большая :)? типа сразу регистры?
компилер нормально компилит, там и так сразу регистры.
0
Во-первых, ясное дело, что компилятор оперирует регистрами и прочими низкоуровневыми сущностями.
Во-вторых, я избавился от вызова функции
reverse_32
применив
__RBIT
.
В третьих, imho,
CRC_CalcCRC
делает лишние телодвижения, которые компилятор не будет игнорировать, а эта функция у Вас в цикле вызывается.
В четвёртых, счётчик цикла у меня изменяется простым декрементом.
В пятых, цикл у меня короче, а значит и быстрее.
Вывод, на компилятор надейся, а сам не плошай.
+1
_RBIT это тоже самое что и

uint32_t reverse_32(uint32_t data)
{
        asm("rbit r0,r0");
        return data;
};
0
цикл короче != быстрее
декремент или нет — всё равно. компилятор это норм оптимизирует. откройте получившийся код.
единственное что оптимизируется — это вызов
<br />
uint32_t CRC_CalcCRC(uint32_t Data)
{
  CRC->DR = Data;
  
  return (CRC->DR);
}


в вашем случае она инлайнится, в моём — нет. но по сравнению со скоростью вычисления в блоке CRC это неактуально
0
цикл короче != быстрее
У Вас что-то с мировоззрением. Разжёвываю, чем меньше команд выполняется в цикле, тем быстрее он заканчивается, и это постулат, который и ежу понятен.
Я вот не поленился и посчитал при дефолтных настройках компилятора у Вас цикл в 17 команд(вызовы учёл)
41          uint32_t CRC_CalcCRC(uint32_t Data)
     42          {
   \                     _Z11CRC_CalcCRCj:
   \   00000000   0x0001             MOVS     R1,R0
     43            CRC->DR = Data;
   \   00000002   0x....             LDR.N    R0,??DataTable3_1  ;; 0x40023000
   \   00000004   0x6001             STR      R1,[R0, #+0]
     44            
     45            return (CRC->DR);
   \   00000006   0x....             LDR.N    R0,??DataTable3_1  ;; 0x40023000
   \   00000008   0x6800             LDR      R0,[R0, #+0]
   \   0000000A   0x4770             BX       LR               ;; return
     46          }
...
53          uint32_t reverse_32(uint32_t data)
     54          {
     55                  asm("rbit r0,r0");
   \                     _Z10reverse_32j:
   \   00000000   0xFA90 0xF0A0      rbit r0,r0
     56                  return data;
   \   00000004   0x4770             BX       LR               ;; return
     57          };
65                          while(len >= 4) {
   \                     ??crc32_ether_2:
   \   0000001A   0xF1B9 0x0F04      CMP      R9,#+4
   \   0000001E   0xDB12             BLT.N    ??crc32_ether_3
     66                                  crc_reg = CRC_CalcCRC(reverse_32(*p++));
   \   00000020   0xF8D8 0x0000      LDR      R0,[R8, #+0]
   \   00000024   0x.... 0x....      BL       _Z10reverse_32j
   \   00000028   0xF118 0x0804      ADDS     R8,R8,#+4
   \   0000002C   0x.... 0x....      BL       _Z11CRC_CalcCRCj
   \   00000030   0x0007             MOVS     R7,R0
     67                                  len -= 4;
   \   00000032   0xF1B9 0x0904      SUBS     R9,R9,#+4
   \   00000036   0xE7F0             B.N      ??crc32_ether_2
     68                          }
, а у меня 10
15          	while (count32--) {
   \                     ??HW_CRC32_0:
   \   0000001A   0x0028             MOVS     R0,R5
   \   0000001C   0x1E45             SUBS     R5,R0,#+1
   \   0000001E   0x2800             CMP      R0,#+0
   \   00000020   0xD006             BEQ.N    ??HW_CRC32_1
     16          		CRC->DR = __RBIT(*p32++);
   \   00000022   0x6820             LDR      R0,[R4, #+0]
   \   00000024   0xFA90 0xF0A0      RBIT     R0,R0
   \   00000028   0x1D24             ADDS     R4,R4,#+4
   \   0000002A   0x....             LDR.N    R7,??DataTable3_1  ;; 0x40023000
   \   0000002C   0x6038             STR      R0,[R7, #+0]
   \   0000002E   0xE7F4             B.N      ??HW_CRC32_0
     17          	}
Предлагаю закончить холивар, всё-равно никому он не интересен.
0
Весьма приглянулся этот код, что позволил себе немного шлифануть его еще:

uint32_t crc32_zlib(const uint32_t *data, size_t cnt) {
 
  uint32_t i;
   
  CRC->CR = CRC_CR_RESET;
   
  for (i = 0; i < (cnt / 4); i++) CRC->DR = __RBIT(data[i]);
   
  uint32_t result = __RBIT(CRC->DR);
  cnt = (cnt % 4) * 8;
  if (cnt) {
    CRC->DR = CRC->DR;
    CRC->DR = __RBIT((data[i] & (0xFFFFFFFF >> (32 - cnt))) ^ result) >> (32 - cnt);
    result = (result >> cnt) ^ __RBIT(CRC->DR);
  }
  return ~result;
}

Чтобы избежать проблем с выравниванием, входной параметр задан, как указатель на 32-битное целое. Однако, при вызове можно передавать хоть строку:

crc32_zlib("just a test", 11)
0
кстати вот если надо кому online CRC32
0
Очень полезная статья. Спасибо большое.
Но для того, чтобы заставить работать под Atollic True Studio, код пришлось поправить(возможно, из за отключенной оптимизации).
Если конкретно, то ф-ция reverse_32(uint32_t data) в том виде, как в статье, у меня скомпилилась так:

.........
08001920:   mov     r0, r3
08001922:   adds    r7, #32
08001924:   mov     sp, r7
08001926:   pop     {r7, pc}
155       {
          reverse_32:
08001928:   push    {r7}
0800192a:   sub     sp, #12
0800192c:   add     r7, sp, #0
0800192e:   str     r0, [r7, #4]
156       	asm volatile("rbit r0,r0""\n\t");
08001930:   rbit    r0, r0
157       	return data;
08001934:   ldr     r3, [r7, #4]
159       };
08001936:   mov     r0, r3
08001938:   adds    r7, #12
0800193a:   mov     sp, r7
0800193c:   pop     {r7}
0800193e:   bx      lr
...............

Содержимое перевернутого r0 никуда не сохранялось, а потом затиралось содержимым r3 и поэтому переменная data выходила нетронутой :)
Если использовать cmsis, то переписать ф-цию можно так:

uint32_t reverse_32(uint32_t data)
{
    return(__RBIT(data));
};

И скомпилированный код стал выглядеть так:

mov     r0, r3
08001922:   adds    r7, #32
08001924:   mov     sp, r7
08001926:   pop     {r7, pc}
155       {
          reverse_32:
08001928:   push    {r7}
0800192a:   sub     sp, #20
0800192c:   add     r7, sp, #0
0800192e:   str     r0, [r7, #4]
08001930:   ldr     r3, [r7, #4]
08001932:   str     r3, [r7, #12]
 531         __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) );
08001934:   ldr     r3, [r7, #12]
08001936:   rbit    r3, r3
0800193a:   str     r3, [r7, #8]
 544        return(result);
0800193c:   ldr     r3, [r7, #8]
158       	return(__RBIT(data));
0800193e:   nop     
159       };
08001940:   mov     r0, r3
08001942:   adds    r7, #20
08001944:   mov     sp, r7
08001946:   pop     {r7}
08001948:   bx      lr
0800194a:   movs    r0, r0
......

Оптимизация, повторюсь, отключена :), но даже так видно, что перевернутое содержимое регистра r3 сохраняется и в стек и в r0 и, конечно же, ф-ция расчета crc32_ether начинает работать правильно.
Вывод: прямое использование конструкций asm volatile(… чревато и требует контроля после каждой компиляции. Поэтому, по возможности, желательно использовать макросы обертки, вроде __RBIT(uint32_t).
Еще раз, статья золотая, спасибо.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.