diff --git a/Makefile b/Makefile index 9d298945d..db5ae7e2a 100755 --- a/Makefile +++ b/Makefile @@ -174,6 +174,11 @@ MPY_CFLAGS += -DMICROPY_PY_IMU=1 MPY_MKARGS += MICROPY_PY_IMU=1 endif +ifeq ($(MICROPY_PY_CRC), 1) +MPY_CFLAGS += -DMICROPY_PY_CRC=1 +MPY_MKARGS += MICROPY_PY_CRC=1 +endif + ifeq ($(MICROPY_PY_BTREE), 1) MPY_CFLAGS += -DMICROPY_PY_BTREE=1 MPY_MKARGS += MICROPY_PY_BTREE=1 diff --git a/boards/OPENMV_AE3/omv_boardconfig.mk b/boards/OPENMV_AE3/omv_boardconfig.mk index 1f75f7886..bdcc38435 100755 --- a/boards/OPENMV_AE3/omv_boardconfig.mk +++ b/boards/OPENMV_AE3/omv_boardconfig.mk @@ -20,6 +20,7 @@ MICROPY_PY_TOF = 1 MICROPY_PY_ULAB = 1 MICROPY_PY_DISPLAY = 1 MICROPY_PY_IMU = 1 +MICROPY_PY_CRC = 1 MICROPY_PY_AUDIO = 1 MICROPY_PY_ML = 1 MICROPY_PY_ML_TFLM = 1 diff --git a/boards/OPENMV_N6/omv_boardconfig.mk b/boards/OPENMV_N6/omv_boardconfig.mk index 4ea2df2d5..480ac7631 100755 --- a/boards/OPENMV_N6/omv_boardconfig.mk +++ b/boards/OPENMV_N6/omv_boardconfig.mk @@ -30,6 +30,7 @@ MICROPY_PY_CSI = 1 MICROPY_PY_CSI_NG = 1 MICROPY_PY_FIR = 1 MICROPY_PY_IMU = 1 +MICROPY_PY_CRC = 1 MICROPY_PY_ULAB = 1 MICROPY_PY_DISPLAY = 1 MICROPY_PY_TV = 1 diff --git a/common/omv_crc.c b/common/omv_crc.c index d4dfcf48f..02b3ef312 100644 --- a/common/omv_crc.c +++ b/common/omv_crc.c @@ -23,64 +23,120 @@ */ #include "omv_crc.h" +// CRC16 lookup table for polynomial 0xF94F (HD=6, len=135) static const uint16_t crc16_table[256] = { - 0x0000, 0xBAAD, 0xCFF7, 0x755A, 0x2543, 0x9FEE, 0xEAB4, 0x5019, - 0x4A86, 0xF02B, 0x8571, 0x3FDC, 0x6FC5, 0xD568, 0xA032, 0x1A9F, - 0x950C, 0x2FA1, 0x5AFB, 0xE056, 0xB04F, 0x0AE2, 0x7FB8, 0xC515, - 0xDF8A, 0x6527, 0x107D, 0xAAD0, 0xFAC9, 0x4064, 0x353E, 0x8F93, - 0x90B5, 0x2A18, 0x5F42, 0xE5EF, 0xB5F6, 0x0F5B, 0x7A01, 0xC0AC, - 0xDA33, 0x609E, 0x15C4, 0xAF69, 0xFF70, 0x45DD, 0x3087, 0x8A2A, - 0x05B9, 0xBF14, 0xCA4E, 0x70E3, 0x20FA, 0x9A57, 0xEF0D, 0x55A0, - 0x4F3F, 0xF592, 0x80C8, 0x3A65, 0x6A7C, 0xD0D1, 0xA58B, 0x1F26, - 0x9BC7, 0x216A, 0x5430, 0xEE9D, 0xBE84, 0x0429, 0x7173, 0xCBDE, - 0xD141, 0x6BEC, 0x1EB6, 0xA41B, 0xF402, 0x4EAF, 0x3BF5, 0x8158, - 0x0ECB, 0xB466, 0xC13C, 0x7B91, 0x2B88, 0x9125, 0xE47F, 0x5ED2, - 0x444D, 0xFEE0, 0x8BBA, 0x3117, 0x610E, 0xDBA3, 0xAEF9, 0x1454, - 0x0B72, 0xB1DF, 0xC485, 0x7E28, 0x2E31, 0x949C, 0xE1C6, 0x5B6B, - 0x41F4, 0xFB59, 0x8E03, 0x34AE, 0x64B7, 0xDE1A, 0xAB40, 0x11ED, - 0x9E7E, 0x24D3, 0x5189, 0xEB24, 0xBB3D, 0x0190, 0x74CA, 0xCE67, - 0xD4F8, 0x6E55, 0x1B0F, 0xA1A2, 0xF1BB, 0x4B16, 0x3E4C, 0x84E1, - 0x8D23, 0x378E, 0x42D4, 0xF879, 0xA860, 0x12CD, 0x6797, 0xDD3A, - 0xC7A5, 0x7D08, 0x0852, 0xB2FF, 0xE2E6, 0x584B, 0x2D11, 0x97BC, - 0x182F, 0xA282, 0xD7D8, 0x6D75, 0x3D6C, 0x87C1, 0xF29B, 0x4836, - 0x52A9, 0xE804, 0x9D5E, 0x27F3, 0x77EA, 0xCD47, 0xB81D, 0x02B0, - 0x1D96, 0xA73B, 0xD261, 0x68CC, 0x38D5, 0x8278, 0xF722, 0x4D8F, - 0x5710, 0xEDBD, 0x98E7, 0x224A, 0x7253, 0xC8FE, 0xBDA4, 0x0709, - 0x889A, 0x3237, 0x476D, 0xFDC0, 0xADD9, 0x1774, 0x622E, 0xD883, - 0xC21C, 0x78B1, 0x0DEB, 0xB746, 0xE75F, 0x5DF2, 0x28A8, 0x9205, - 0x16E4, 0xAC49, 0xD913, 0x63BE, 0x33A7, 0x890A, 0xFC50, 0x46FD, - 0x5C62, 0xE6CF, 0x9395, 0x2938, 0x7921, 0xC38C, 0xB6D6, 0x0C7B, - 0x83E8, 0x3945, 0x4C1F, 0xF6B2, 0xA6AB, 0x1C06, 0x695C, 0xD3F1, - 0xC96E, 0x73C3, 0x0699, 0xBC34, 0xEC2D, 0x5680, 0x23DA, 0x9977, - 0x8651, 0x3CFC, 0x49A6, 0xF30B, 0xA312, 0x19BF, 0x6CE5, 0xD648, - 0xCCD7, 0x767A, 0x0320, 0xB98D, 0xE994, 0x5339, 0x2663, 0x9CCE, - 0x135D, 0xA9F0, 0xDCAA, 0x6607, 0x361E, 0x8CB3, 0xF9E9, 0x4344, - 0x59DB, 0xE376, 0x962C, 0x2C81, 0x7C98, 0xC635, 0xB36F, 0x09C2 + 0x0000, 0xF94F, 0x0BD1, 0xF29E, 0x17A2, 0xEEED, 0x1C73, 0xE53C, + 0x2F44, 0xD60B, 0x2495, 0xDDDA, 0x38E6, 0xC1A9, 0x3337, 0xCA78, + 0x5E88, 0xA7C7, 0x5559, 0xAC16, 0x492A, 0xB065, 0x42FB, 0xBBB4, + 0x71CC, 0x8883, 0x7A1D, 0x8352, 0x666E, 0x9F21, 0x6DBF, 0x94F0, + 0xBD10, 0x445F, 0xB6C1, 0x4F8E, 0xAAB2, 0x53FD, 0xA163, 0x582C, + 0x9254, 0x6B1B, 0x9985, 0x60CA, 0x85F6, 0x7CB9, 0x8E27, 0x7768, + 0xE398, 0x1AD7, 0xE849, 0x1106, 0xF43A, 0x0D75, 0xFFEB, 0x06A4, + 0xCCDC, 0x3593, 0xC70D, 0x3E42, 0xDB7E, 0x2231, 0xD0AF, 0x29E0, + 0x836F, 0x7A20, 0x88BE, 0x71F1, 0x94CD, 0x6D82, 0x9F1C, 0x6653, + 0xAC2B, 0x5564, 0xA7FA, 0x5EB5, 0xBB89, 0x42C6, 0xB058, 0x4917, + 0xDDE7, 0x24A8, 0xD636, 0x2F79, 0xCA45, 0x330A, 0xC194, 0x38DB, + 0xF2A3, 0x0BEC, 0xF972, 0x003D, 0xE501, 0x1C4E, 0xEED0, 0x179F, + 0x3E7F, 0xC730, 0x35AE, 0xCCE1, 0x29DD, 0xD092, 0x220C, 0xDB43, + 0x113B, 0xE874, 0x1AEA, 0xE3A5, 0x0699, 0xFFD6, 0x0D48, 0xF407, + 0x60F7, 0x99B8, 0x6B26, 0x9269, 0x7755, 0x8E1A, 0x7C84, 0x85CB, + 0x4FB3, 0xB6FC, 0x4462, 0xBD2D, 0x5811, 0xA15E, 0x53C0, 0xAA8F, + 0xFF91, 0x06DE, 0xF440, 0x0D0F, 0xE833, 0x117C, 0xE3E2, 0x1AAD, + 0xD0D5, 0x299A, 0xDB04, 0x224B, 0xC777, 0x3E38, 0xCCA6, 0x35E9, + 0xA119, 0x5856, 0xAAC8, 0x5387, 0xB6BB, 0x4FF4, 0xBD6A, 0x4425, + 0x8E5D, 0x7712, 0x858C, 0x7CC3, 0x99FF, 0x60B0, 0x922E, 0x6B61, + 0x4281, 0xBBCE, 0x4950, 0xB01F, 0x5523, 0xAC6C, 0x5EF2, 0xA7BD, + 0x6DC5, 0x948A, 0x6614, 0x9F5B, 0x7A67, 0x8328, 0x71B6, 0x88F9, + 0x1C09, 0xE546, 0x17D8, 0xEE97, 0x0BAB, 0xF2E4, 0x007A, 0xF935, + 0x334D, 0xCA02, 0x389C, 0xC1D3, 0x24EF, 0xDDA0, 0x2F3E, 0xD671, + 0x7CFE, 0x85B1, 0x772F, 0x8E60, 0x6B5C, 0x9213, 0x608D, 0x99C2, + 0x53BA, 0xAAF5, 0x586B, 0xA124, 0x4418, 0xBD57, 0x4FC9, 0xB686, + 0x2276, 0xDB39, 0x29A7, 0xD0E8, 0x35D4, 0xCC9B, 0x3E05, 0xC74A, + 0x0D32, 0xF47D, 0x06E3, 0xFFAC, 0x1A90, 0xE3DF, 0x1141, 0xE80E, + 0xC1EE, 0x38A1, 0xCA3F, 0x3370, 0xD64C, 0x2F03, 0xDD9D, 0x24D2, + 0xEEAA, 0x17E5, 0xE57B, 0x1C34, 0xF908, 0x0047, 0xF2D9, 0x0B96, + 0x9F66, 0x6629, 0x94B7, 0x6DF8, 0x88C4, 0x718B, 0x8315, 0x7A5A, + 0xB022, 0x496D, 0xBBF3, 0x42BC, 0xA780, 0x5ECF, 0xAC51, 0x551E, }; -__attribute__((weak)) void omv_crc_init(void) { +// CRC32 lookup table for polynomial 0xFA567D89 +const uint32_t crc32_table[256] = { + 0x00000000, 0xFA567D89, 0x0EFA869B, 0xF4ACFB12, 0x1DF50D36, 0xE7A370BF, 0x130F8BAD, 0xE959F624, + 0x3BEA1A6C, 0xC1BC67E5, 0x35109CF7, 0xCF46E17E, 0x261F175A, 0xDC496AD3, 0x28E591C1, 0xD2B3EC48, + 0x77D434D8, 0x8D824951, 0x792EB243, 0x8378CFCA, 0x6A2139EE, 0x90774467, 0x64DBBF75, 0x9E8DC2FC, + 0x4C3E2EB4, 0xB668533D, 0x42C4A82F, 0xB892D5A6, 0x51CB2382, 0xAB9D5E0B, 0x5F31A519, 0xA567D890, + 0xEFA869B0, 0x15FE1439, 0xE152EF2B, 0x1B0492A2, 0xF25D6486, 0x080B190F, 0xFCA7E21D, 0x06F19F94, + 0xD44273DC, 0x2E140E55, 0xDAB8F547, 0x20EE88CE, 0xC9B77EEA, 0x33E10363, 0xC74DF871, 0x3D1B85F8, + 0x987C5D68, 0x622A20E1, 0x9686DBF3, 0x6CD0A67A, 0x8589505E, 0x7FDF2DD7, 0x8B73D6C5, 0x7125AB4C, + 0xA3964704, 0x59C03A8D, 0xAD6CC19F, 0x573ABC16, 0xBE634A32, 0x443537BB, 0xB099CCA9, 0x4ACFB120, + 0x2506AEE9, 0xDF50D360, 0x2BFC2872, 0xD1AA55FB, 0x38F3A3DF, 0xC2A5DE56, 0x36092544, 0xCC5F58CD, + 0x1EECB485, 0xE4BAC90C, 0x1016321E, 0xEA404F97, 0x0319B9B3, 0xF94FC43A, 0x0DE33F28, 0xF7B542A1, + 0x52D29A31, 0xA884E7B8, 0x5C281CAA, 0xA67E6123, 0x4F279707, 0xB571EA8E, 0x41DD119C, 0xBB8B6C15, + 0x6938805D, 0x936EFDD4, 0x67C206C6, 0x9D947B4F, 0x74CD8D6B, 0x8E9BF0E2, 0x7A370BF0, 0x80617679, + 0xCAAEC759, 0x30F8BAD0, 0xC45441C2, 0x3E023C4B, 0xD75BCA6F, 0x2D0DB7E6, 0xD9A14CF4, 0x23F7317D, + 0xF144DD35, 0x0B12A0BC, 0xFFBE5BAE, 0x05E82627, 0xECB1D003, 0x16E7AD8A, 0xE24B5698, 0x181D2B11, + 0xBD7AF381, 0x472C8E08, 0xB380751A, 0x49D60893, 0xA08FFEB7, 0x5AD9833E, 0xAE75782C, 0x542305A5, + 0x8690E9ED, 0x7CC69464, 0x886A6F76, 0x723C12FF, 0x9B65E4DB, 0x61339952, 0x959F6240, 0x6FC91FC9, + 0x4A0D5DD2, 0xB05B205B, 0x44F7DB49, 0xBEA1A6C0, 0x57F850E4, 0xADAE2D6D, 0x5902D67F, 0xA354ABF6, + 0x71E747BE, 0x8BB13A37, 0x7F1DC125, 0x854BBCAC, 0x6C124A88, 0x96443701, 0x62E8CC13, 0x98BEB19A, + 0x3DD9690A, 0xC78F1483, 0x3323EF91, 0xC9759218, 0x202C643C, 0xDA7A19B5, 0x2ED6E2A7, 0xD4809F2E, + 0x06337366, 0xFC650EEF, 0x08C9F5FD, 0xF29F8874, 0x1BC67E50, 0xE19003D9, 0x153CF8CB, 0xEF6A8542, + 0xA5A53462, 0x5FF349EB, 0xAB5FB2F9, 0x5109CF70, 0xB8503954, 0x420644DD, 0xB6AABFCF, 0x4CFCC246, + 0x9E4F2E0E, 0x64195387, 0x90B5A895, 0x6AE3D51C, 0x83BA2338, 0x79EC5EB1, 0x8D40A5A3, 0x7716D82A, + 0xD27100BA, 0x28277D33, 0xDC8B8621, 0x26DDFBA8, 0xCF840D8C, 0x35D27005, 0xC17E8B17, 0x3B28F69E, + 0xE99B1AD6, 0x13CD675F, 0xE7619C4D, 0x1D37E1C4, 0xF46E17E0, 0x0E386A69, 0xFA94917B, 0x00C2ECF2, + 0x6F0BF33B, 0x955D8EB2, 0x61F175A0, 0x9BA70829, 0x72FEFE0D, 0x88A88384, 0x7C047896, 0x8652051F, + 0x54E1E957, 0xAEB794DE, 0x5A1B6FCC, 0xA04D1245, 0x4914E461, 0xB34299E8, 0x47EE62FA, 0xBDB81F73, + 0x18DFC7E3, 0xE289BA6A, 0x16254178, 0xEC733CF1, 0x052ACAD5, 0xFF7CB75C, 0x0BD04C4E, 0xF18631C7, + 0x2335DD8F, 0xD963A006, 0x2DCF5B14, 0xD799269D, 0x3EC0D0B9, 0xC496AD30, 0x303A5622, 0xCA6C2BAB, + 0x80A39A8B, 0x7AF5E702, 0x8E591C10, 0x740F6199, 0x9D5697BD, 0x6700EA34, 0x93AC1126, 0x69FA6CAF, + 0xBB4980E7, 0x411FFD6E, 0xB5B3067C, 0x4FE57BF5, 0xA6BC8DD1, 0x5CEAF058, 0xA8460B4A, 0x521076C3, + 0xF777AE53, 0x0D21D3DA, 0xF98D28C8, 0x03DB5541, 0xEA82A365, 0x10D4DEEC, 0xE47825FE, 0x1E2E5877, + 0xCC9DB43F, 0x36CBC9B6, 0xC26732A4, 0x38314F2D, 0xD168B909, 0x2B3EC480, 0xDF923F92, 0x25C4421B, +}; -} +__attribute__((weak)) uint16_t omv_crc16_start(const void *buf, size_t len) { + const uint8_t *data = (const uint8_t *) buf; + uint16_t crc = OMV_CRC16_INIT; -__attribute__((weak)) omv_crc_t omv_crc_start(const void *buf, size_t size) { - const uint8_t *data = (const uint8_t *)buf; - omv_crc_t crc = OMV_CRC_INIT; - - for (size_t i = 0; i < size; i++) { + for (size_t i = 0; i < len; i++) { uint8_t index = (crc >> 8) ^ data[i]; crc = (crc << 8) ^ crc16_table[index]; } - + return crc; } -__attribute__((weak)) omv_crc_t omv_crc_update(omv_crc_t crc, const void *buf, size_t size) { - const uint8_t *data = (const uint8_t *)buf; - - for (size_t i = 0; i < size; i++) { +__attribute__((weak)) uint16_t omv_crc16_update(uint16_t crc, const void *buf, size_t len) { + const uint8_t *data = (const uint8_t *) buf; + + for (size_t i = 0; i < len; i++) { uint8_t index = (crc >> 8) ^ data[i]; crc = (crc << 8) ^ crc16_table[index]; } - + + return crc; +} + +__attribute__((weak)) uint32_t omv_crc32_start(const void *buf, size_t len) { + const uint8_t *data = (const uint8_t *) buf; + uint32_t crc = OMV_CRC32_INIT; + + for (size_t i = 0; i < len; i++) { + uint8_t index = (crc >> 24) ^ data[i]; + crc = (crc << 8) ^ crc32_table[index]; + } + + return crc; +} + +__attribute__((weak)) uint32_t omv_crc32_update(uint32_t crc, const void *buf, size_t len) { + const uint8_t *data = (const uint8_t *) buf; + + for (size_t i = 0; i < len; i++) { + uint8_t index = (crc >> 24) ^ data[i]; + crc = (crc << 8) ^ crc32_table[index]; + } + return crc; } diff --git a/common/omv_crc.h b/common/omv_crc.h index 6c56fa991..3febe730c 100644 --- a/common/omv_crc.h +++ b/common/omv_crc.h @@ -27,37 +27,74 @@ #include #include -// 0xBAAD is ironically good - HD=5 up to 108 bits, HD=4 up to 7985 bits -#define OMV_CRC_POLY (0xBAAD) -#define OMV_CRC_INIT (0xFFFF) -#define OMV_CRC_SIZE (16) -typedef uint16_t omv_crc_t; +// CRC size enumeration +typedef enum { + OMV_CRC16 = 16, + OMV_CRC32 = 32 +} omv_crc_size_t; -// Initialize CRC hardware (if available) -void omv_crc_init(void); +// HD=6 up to 114 bits +#define OMV_CRC16_POLY (0xF94F) +#define OMV_CRC16_INIT (0xFFFF) -// Start CRC calculation for buffer data -omv_crc_t omv_crc_start(const void *buf, size_t size); +// HD=6 up to 32738 bits (~4K) +#define OMV_CRC32_POLY (0xFA567D89) +#define OMV_CRC32_INIT (0xFFFFFFFF) -// Update CRC with additional buffer data -omv_crc_t omv_crc_update(omv_crc_t crc, const void *buf, size_t size); +// Default software implementations +uint16_t omv_crc16_start(const void *buf, size_t len); +uint16_t omv_crc16_update(uint16_t crc, const void *buf, size_t len); -// Read CRC value from the last 2 bytes of buffer -static inline omv_crc_t omv_crc_read(const void *buf, size_t size) { - const uint8_t *crc_buf = (const uint8_t *)buf + size - 2; - return *((omv_crc_t*) crc_buf); +uint32_t omv_crc32_start(const void *buf, size_t len); +uint32_t omv_crc32_update(uint32_t crc, const void *buf, size_t len); + +// Public API functions +static inline uint32_t omv_crc_start(omv_crc_size_t size, const void *buf, size_t len) { + if (size == OMV_CRC16) { + return omv_crc16_start(buf, len); + } else if (size == OMV_CRC32) { + return omv_crc32_start(buf, len); + } + return 0; } -// Calculate CRC and write it to the last 2 bytes of buffer -static inline void omv_crc_write(void *buf, size_t size) { - omv_crc_t crc = omv_crc_start(buf, size - 2); - uint8_t *crc_buf = (uint8_t *)buf + size - 2; - crc_buf[0] = crc & 0xFF; - crc_buf[1] = (crc >> 8) & 0xFF; +static inline uint32_t omv_crc_update(omv_crc_size_t size, uint32_t crc, const void *buf, size_t len) { + if (size == OMV_CRC16) { + return omv_crc16_update((uint16_t) crc, buf, len); + } else if (size == OMV_CRC32) { + return omv_crc32_update(crc, buf, len); + } + return crc; +} + +// Read CRC value from the last bytes of buffer +static inline uint32_t omv_crc_read(omv_crc_size_t crc_size, const void *buf, size_t size) { + const uint8_t *crc_buf = (const uint8_t *) buf + size - (crc_size == OMV_CRC16 ? 2 : 4); + if (crc_size == OMV_CRC16) { + return *((uint16_t *) crc_buf); + } else { + return *((uint32_t *) crc_buf); + } +} + +// Calculate CRC and write it to the last bytes of buffer +static inline void omv_crc_write(omv_crc_size_t crc_size, void *buf, size_t size) { + uint32_t crc = omv_crc_start(crc_size, buf, size - (crc_size == OMV_CRC16 ? 2 : 4)); + uint8_t *crc_buf = (uint8_t *) buf + size - (crc_size == OMV_CRC16 ? 2 : 4); + + if (crc_size == OMV_CRC16) { + crc_buf[0] = crc & 0xFF; + crc_buf[1] = (crc >> 8) & 0xFF; + } else { + crc_buf[0] = crc & 0xFF; + crc_buf[1] = (crc >> 8) & 0xFF; + crc_buf[2] = (crc >> 16) & 0xFF; + crc_buf[3] = (crc >> 24) & 0xFF; + } } // Calculate and check if CRC matches the one stored in buffer -static inline bool omv_crc_check(const void *buf, size_t size) { - return omv_crc_read(buf, size) == omv_crc_start(buf, size - 2); +static inline bool omv_crc_check(omv_crc_size_t crc_size, const void *buf, size_t size) { + return omv_crc_read(crc_size, buf, size) == omv_crc_start(crc_size, buf, size - (crc_size == OMV_CRC16 ? 2 : 4)); } #endif // __OMV_CRC_H__ diff --git a/modules/py_crc.c b/modules/py_crc.c new file mode 100644 index 000000000..193ee600b --- /dev/null +++ b/modules/py_crc.c @@ -0,0 +1,97 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2025 OpenMV, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * CRC Python module for testing CRC implementations. + */ + +#if MICROPY_PY_CRC +#include "py/runtime.h" +#include "py/obj.h" +#include "omv_crc.h" + +// CRC16 function +static mp_obj_t py_crc16(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kwargs) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kwargs, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[0].u_obj, &bufinfo, MP_BUFFER_READ); + + uint16_t crc; + if (args[1].u_obj == MP_OBJ_NULL) { + crc = omv_crc16_start(bufinfo.buf, bufinfo.len); + } else { + uint16_t prev_crc = mp_obj_get_int_truncated(args[1].u_obj) & 0xFFFF; + crc = omv_crc16_update(prev_crc, bufinfo.buf, bufinfo.len); + } + return mp_obj_new_int_from_uint(crc); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(py_crc16_obj, 1, py_crc16); + +// CRC32 function +static mp_obj_t py_crc32(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kwargs) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kwargs, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[0].u_obj, &bufinfo, MP_BUFFER_READ); + + uint32_t crc; + if (args[1].u_obj == MP_OBJ_NULL) { + crc = omv_crc32_start(bufinfo.buf, bufinfo.len); + } else { + uint32_t prev_crc = mp_obj_get_int_truncated(args[1].u_obj); + crc = omv_crc32_update(prev_crc, bufinfo.buf, bufinfo.len); + } + return mp_obj_new_int_from_uint(crc); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(py_crc32_obj, 1, py_crc32); + +// Module globals table +static const mp_rom_map_elem_t crc_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_crc) }, + { MP_ROM_QSTR(MP_QSTR_crc16), MP_ROM_PTR(&py_crc16_obj) }, + { MP_ROM_QSTR(MP_QSTR_crc32), MP_ROM_PTR(&py_crc32_obj) }, +}; + +static MP_DEFINE_CONST_DICT(crc_module_globals, crc_module_globals_table); + +// Define module object +const mp_obj_module_t crc_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *) &crc_module_globals, +}; + +// Register the module to make it available in Python +MP_REGISTER_MODULE(MP_QSTR_crc, crc_user_cmodule); +#endif // MICROPY_PY_CRC diff --git a/ports/alif/omv_crc.c b/ports/alif/omv_crc.c index e769ca69c..15f133e0d 100644 --- a/ports/alif/omv_crc.c +++ b/ports/alif/omv_crc.c @@ -25,46 +25,78 @@ #include "global_map.h" #include "omv_crc.h" -#define CRC0 ((CRC_Type *)CRC0_BASE) +// There seems to be a mismatch between how the Alif HW CRC32 algorithm +// works and the other HW CRCs and the software implementation. +// Just leaving this here just in case it works on a different series. +#if 0 +#define CRC0 ((CRC_Type *) CRC0_BASE) -static bool crc_initialized = false; +static bool crc32_initialized = false; +static void crc_calculate(CRC_Type *crc, const void *buf, uint32_t len, uint32_t *value); -void omv_crc_init(void) { - crc_clear_config(CRC0); - crc_enable_16bit(CRC0); - crc_enable_custom_poly(CRC0); - crc_set_custom_poly(CRC0, OMV_CRC_POLY); - crc_set_seed(CRC0, OMV_CRC_INIT); - crc_enable(CRC0); - crc_initialized = true; +static void omv_crc32_init(void) { + CRC0->CRC_CONTROL = CRC_32C | + CRC_CUSTOM_POLY | + CRC_ALGO_32_BIT_SIZE; + CRC0->CRC_SEED = OMV_CRC32_INIT; + CRC0->CRC_POLY_CUSTOM = OMV_CRC32_POLY; + crc32_initialized = true; } -omv_crc_t omv_crc_start(const void *buf, size_t size) { - if (!crc_initialized) { - omv_crc_init(); +uint32_t omv_crc32_start(const void *buf, size_t len) { + uint32_t result = OMV_CRC32_INIT; + + if (!crc32_initialized) { + omv_crc32_init(); } - - if (size == 0) { - return OMV_CRC_INIT; + + if (len == 0) { + return OMV_CRC32_INIT; } - - uint32_t result = 0; - crc_calculate_16bit(CRC0, buf, size, &result); - return (omv_crc_t)result; + + crc_calculate(CRC0, buf, len, &result); + return result; } -omv_crc_t omv_crc_update(omv_crc_t crc, const void *buf, size_t size) { - if (!crc_initialized) { - omv_crc_init(); +uint32_t omv_crc32_update(uint32_t crc, const void *buf, size_t len) { + uint32_t result = crc; + + if (!crc32_initialized) { + omv_crc32_init(); } - if (size == 0) { + if (len == 0) { return crc; } - // Set current CRC value and calculate incrementally - crc_set_seed(CRC0, crc); - uint32_t result = 0; - crc_calculate_16bit(CRC0, buf, size, &result); - return (omv_crc_t)result; + crc_calculate(CRC0, buf, len, &result); + return result; } + +static void crc_calculate(CRC_Type *crc, const void *buf, uint32_t len, uint32_t *value) { + const uint8_t *data = (const uint8_t *) buf; + + if ((len % 4) == 0) { + // Use hardware only if length is multiple of 4 + crc->CRC_SEED = *value; + crc->CRC_CONTROL |= CRC_INIT_BIT; + + // Hardware path - process all data as 32-bit words + const uint32_t *data32 = (const uint32_t *) data; + for (uint32_t i = 0; i < len / 4; i++) { + crc->CRC_DATA_IN_32_0 = data32[i]; + } + *value = crc->CRC_OUT; + } else { + // Software path - use lookup table + uint32_t result = *value; + extern const uint32_t crc32_table[256]; + + for (uint32_t i = 0; i < len; i++) { + uint8_t index = (result >> 24) ^ data[i]; + result = (result << 8) ^ crc32_table[index]; + } + *value = result; + } +} +#endif diff --git a/ports/stm32/omv_crc.c b/ports/stm32/omv_crc.c index 8d6e12751..af96d3972 100644 --- a/ports/stm32/omv_crc.c +++ b/ports/stm32/omv_crc.c @@ -31,43 +31,66 @@ static CRC_HandleTypeDef hcrc = {0}; static bool crc_initialized = false; -void omv_crc_init(void) { +static void omv_crc_init(void) { hcrc.Instance = CRC; + hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES; hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_DISABLE; - hcrc.Init.GeneratingPolynomial = OMV_CRC_POLY; hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_DISABLE; - hcrc.Init.InitValue = OMV_CRC_INIT; - hcrc.Init.CRCLength = CRC_POLYLENGTH_16B; hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_NONE; hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE; - hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES; + hcrc.Init.CRCLength = CRC_POLYLENGTH_32B; + hcrc.Init.InitValue = OMV_CRC32_INIT; + hcrc.Init.GeneratingPolynomial = OMV_CRC32_POLY; HAL_CRC_Init(&hcrc); crc_initialized = true; } -omv_crc_t omv_crc_start(const void *buf, size_t size) { +uint16_t omv_crc16_start(const void *buf, size_t len) { if (!crc_initialized) { omv_crc_init(); } - if (size == 0) { - return OMV_CRC_INIT; + // Set CRC16 polynomial and length directly + WRITE_REG(hcrc.Instance->POL, OMV_CRC16_POLY); + MODIFY_REG(hcrc.Instance->CR, CRC_CR_POLYSIZE, CRC_POLYLENGTH_16B); + + if (len == 0) { + return OMV_CRC16_INIT; } - return (omv_crc_t) HAL_CRC_Calculate(&hcrc, (uint32_t *)buf, size); + return (uint16_t) HAL_CRC_Calculate(&hcrc, (uint32_t *) buf, len); } -omv_crc_t omv_crc_update(omv_crc_t crc, const void *buf, size_t size) { - if (!crc_initialized) { - omv_crc_init(); - } - - if (size == 0) { +uint16_t omv_crc16_update(uint16_t crc, const void *buf, size_t len) { + if (len == 0) { return crc; } - return (omv_crc_t) HAL_CRC_Accumulate(&hcrc, (uint32_t *)buf, size); + return (uint16_t) HAL_CRC_Accumulate(&hcrc, (uint32_t *) buf, len); +} + +uint32_t omv_crc32_start(const void *buf, size_t len) { + if (!crc_initialized) { + omv_crc_init(); + } + + // Set CRC32 polynomial and length directly + WRITE_REG(hcrc.Instance->POL, OMV_CRC32_POLY); + MODIFY_REG(hcrc.Instance->CR, CRC_CR_POLYSIZE, CRC_POLYLENGTH_32B); + + if (len == 0) { + return OMV_CRC32_INIT; + } + + return HAL_CRC_Calculate(&hcrc, (uint32_t *) buf, len); +} + +uint32_t omv_crc32_update(uint32_t crc, const void *buf, size_t len) { + if (len == 0) { + return crc; + } + return HAL_CRC_Accumulate(&hcrc, (uint32_t *) buf, len); } #endif // STM32F7 || STM32H7 || STM32N6 diff --git a/tools/gen_crc_table.py b/tools/gen_crc_table.py new file mode 100755 index 000000000..935196bc6 --- /dev/null +++ b/tools/gen_crc_table.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +# +# This file is part of the OpenMV project. +# Copyright (C) 2025 OpenMV, LLC. +# +# This work is licensed under the MIT license, see the file LICENSE for details. +# +# Generate CRC lookup tables for OpenMV CRC implementation + +import argparse + + +def generate_crc16_table(polynomial): + """Generate CRC16 lookup table""" + table = [] + for i in range(256): + crc = i << 8 + for _ in range(8): + if crc & 0x8000: + crc = (crc << 1) ^ polynomial + else: + crc = crc << 1 + crc &= 0xFFFF + table.append(crc) + return table + + +def generate_crc32_table(polynomial): + """Generate CRC32 lookup table""" + table = [] + for i in range(256): + crc = i << 24 + for _ in range(8): + if crc & 0x80000000: + crc = (crc << 1) ^ polynomial + else: + crc = crc << 1 + crc &= 0xFFFFFFFF + table.append(crc) + return table + + +def format_c_array(table, name, data_type): + """Format array as C code""" + lines = [f"static const {data_type} {name}[256] = {{"] + + for i in range(0, len(table), 8): + row = table[i : i + 8] + if data_type == "uint16_t": + formatted_row = ", ".join(f"0x{val:04X}" for val in row) + else: # uint32_t + formatted_row = ", ".join(f"0x{val:08X}" for val in row) + lines.append(f" {formatted_row},") + + lines.append("};") + return "\n".join(lines) + + +def main(): + parser = argparse.ArgumentParser( + description="Generate CRC lookup tables for OpenMV CRC implementation" + ) + parser.add_argument( + "--crc16-poly", + type=lambda x: int(x, 0), + default=0xF94F, + help="CRC16 polynomial (default: 0xF94F)", + ) + parser.add_argument( + "--crc32-poly", + type=lambda x: int(x, 0), + default=0xFA567D89, + help="CRC32 polynomial (default: 0xFA567D89)", + ) + + args = parser.parse_args() + + # Generate CRC16 table + crc16_table = generate_crc16_table(args.crc16_poly) + + # Generate CRC32 table + crc32_table = generate_crc32_table(args.crc32_poly) + + print(f"// CRC16 lookup table for polynomial 0x{args.crc16_poly:04X}") + print(format_c_array(crc16_table, "crc16_table", "uint16_t")) + print() + print(f"// CRC32 lookup table for polynomial 0x{args.crc32_poly:08X}") + print(format_c_array(crc32_table, "crc32_table", "uint32_t")) + + +if __name__ == "__main__": + main()