diff --git a/src/freertos_drivers/ti/CC3200GPIO.hxx b/src/freertos_drivers/ti/CC3200GPIO.hxx index 82c3f6c79..bd416c64e 100644 --- a/src/freertos_drivers/ti/CC3200GPIO.hxx +++ b/src/freertos_drivers/ti/CC3200GPIO.hxx @@ -28,6 +28,19 @@ * * Helper declarations for using GPIO pins on CC3200 MCUs. * + * Note about barriers: + * + * GCC is extremely efficient at optimizing the memory access instructions that + * we use for writing to GPIO output pins. In many cases writing to multiple + * GPIO pins turns into back-to-back Thumb instructions, and the hardware + * peripheral seems to be unable to process bus transactions this fast. We + * therefore add a barrier after each GPIO write. The barrier ensures that the + * execution continues after the GPIO write only once the transaction is + * successfully completed by the bus. We tested that back to back GPIO writes + * are operational. The barrier also ensures correct sequencing against other + * effects of the running program. One GPIO write is about 50 nsec (4 clock + * cycles), the shortest pulse we can generate is 100 nsec. + * * @author Balazs Racz * @date 2 Dec 2014 */ @@ -70,16 +83,22 @@ public: void write(Value new_state) const OVERRIDE { *pin_address() = (new_state ? 0xff : 0); + /// See note at the top of the file about barriers. + __asm__ volatile("dsb" : : : "memory"); } void set() const OVERRIDE { *pin_address() = 0xff; + /// See note at the top of the file about barriers. + __asm__ volatile("dsb" : : : "memory"); } void clr() const OVERRIDE { *pin_address() = 0; + /// See note at the top of the file about barriers. + __asm__ volatile("dsb" : : : "memory"); } Value read() const OVERRIDE @@ -171,6 +190,8 @@ public: volatile uint8_t *ptr = reinterpret_cast( GPIO_BASE + (((unsigned)GPIO_PIN) << 2)); *ptr = value ? 0xff : 0; + /// See note at the top of the file about barriers. + __asm__ volatile("dsb" : : : "memory"); } /// @return current value of the input pin: if true HIGH. static bool __attribute__((always_inline)) get() diff --git a/src/utils/Crc.cxx b/src/utils/Crc.cxx index 10af6db5c..a70179ade 100644 --- a/src/utils/Crc.cxx +++ b/src/utils/Crc.cxx @@ -169,3 +169,51 @@ void crc3_crc16_ibm(const void* data, size_t length_bytes, uint16_t* checksum) { checksum[1] = crc_16_ibm_finish(state2); checksum[2] = crc_16_ibm_finish(state3); } + +// static +uint8_t Crc8DallasMaxim::table256[256] = { + 0x00, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83, + 0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 0x41, + 0x9d, 0xc3, 0x21, 0x7f, 0xfc, 0xa2, 0x40, 0x1e, + 0x5f, 0x01, 0xe3, 0xbd, 0x3e, 0x60, 0x82, 0xdc, + 0x23, 0x7d, 0x9f, 0xc1, 0x42, 0x1c, 0xfe, 0xa0, + 0xe1, 0xbf, 0x5d, 0x03, 0x80, 0xde, 0x3c, 0x62, + 0xbe, 0xe0, 0x02, 0x5c, 0xdf, 0x81, 0x63, 0x3d, + 0x7c, 0x22, 0xc0, 0x9e, 0x1d, 0x43, 0xa1, 0xff, + 0x46, 0x18, 0xfa, 0xa4, 0x27, 0x79, 0x9b, 0xc5, + 0x84, 0xda, 0x38, 0x66, 0xe5, 0xbb, 0x59, 0x07, + 0xdb, 0x85, 0x67, 0x39, 0xba, 0xe4, 0x06, 0x58, + 0x19, 0x47, 0xa5, 0xfb, 0x78, 0x26, 0xc4, 0x9a, + 0x65, 0x3b, 0xd9, 0x87, 0x04, 0x5a, 0xb8, 0xe6, + 0xa7, 0xf9, 0x1b, 0x45, 0xc6, 0x98, 0x7a, 0x24, + 0xf8, 0xa6, 0x44, 0x1a, 0x99, 0xc7, 0x25, 0x7b, + 0x3a, 0x64, 0x86, 0xd8, 0x5b, 0x05, 0xe7, 0xb9, + 0x8c, 0xd2, 0x30, 0x6e, 0xed, 0xb3, 0x51, 0x0f, + 0x4e, 0x10, 0xf2, 0xac, 0x2f, 0x71, 0x93, 0xcd, + 0x11, 0x4f, 0xad, 0xf3, 0x70, 0x2e, 0xcc, 0x92, + 0xd3, 0x8d, 0x6f, 0x31, 0xb2, 0xec, 0x0e, 0x50, + 0xaf, 0xf1, 0x13, 0x4d, 0xce, 0x90, 0x72, 0x2c, + 0x6d, 0x33, 0xd1, 0x8f, 0x0c, 0x52, 0xb0, 0xee, + 0x32, 0x6c, 0x8e, 0xd0, 0x53, 0x0d, 0xef, 0xb1, + 0xf0, 0xae, 0x4c, 0x12, 0x91, 0xcf, 0x2d, 0x73, + 0xca, 0x94, 0x76, 0x28, 0xab, 0xf5, 0x17, 0x49, + 0x08, 0x56, 0xb4, 0xea, 0x69, 0x37, 0xd5, 0x8b, + 0x57, 0x09, 0xeb, 0xb5, 0x36, 0x68, 0x8a, 0xd4, + 0x95, 0xcb, 0x29, 0x77, 0xf4, 0xaa, 0x48, 0x16, + 0xe9, 0xb7, 0x55, 0x0b, 0x88, 0xd6, 0x34, 0x6a, + 0x2b, 0x75, 0x97, 0xc9, 0x4a, 0x14, 0xf6, 0xa8, + 0x74, 0x2a, 0xc8, 0x96, 0x15, 0x4b, 0xa9, 0xf7, + 0xb6, 0xe8, 0x0a, 0x54, 0xd7, 0x89, 0x6b, 0x35, +}; + +// static +uint8_t Crc8DallasMaxim::tableLo16[16] = { + 0x00, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83, + 0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 0x41, +}; + +// static +uint8_t Crc8DallasMaxim::tableHi16[16] = { + 0x00, 0x9d, 0x23, 0xbe, 0x46, 0xdb, 0x65, 0xf8, + 0x8c, 0x11, 0xaf, 0x32, 0xca, 0x57, 0xe9, 0x74 +}; diff --git a/src/utils/Crc.cxxtest b/src/utils/Crc.cxxtest index 3946b204a..2374ecae9 100644 --- a/src/utils/Crc.cxxtest +++ b/src/utils/Crc.cxxtest @@ -1,6 +1,8 @@ -#include "utils/test_main.hxx" #include "utils/Crc.hxx" +#include +#include "utils/test_main.hxx" + extern uint8_t reverse(uint8_t data); @@ -30,11 +32,76 @@ TEST(CrcIbmTest, Example3) { EXPECT_EQ("0459", actual); } +TEST(Crc3Test, Example) +{ + uint16_t data[3]; + crc3_crc16_ibm("12345678", 8, data); + EXPECT_EQ(0x3c9d, data[0]); + EXPECT_EQ(0x75a8, data[1]); + EXPECT_EQ(0x0459, data[2]); +} + +TEST(Crc8Test, Example) +{ + // This test vector comes from the RCN-218 document by RailCommunity. It is + // the same as an example message from the BiDiB examples page: + // http://www.bidib.org/support/example1_e.html + static const uint8_t sample_message[] = { + 0x0B, 0x0A, 0x00, 0x00, 0x8E, 0x40, 0x00, 0x0D, 0x67, 0x00, 0x01, 0x00}; + + Crc8DallasMaxim c0; + Crc8DallasMaxim c16; + Crc8DallasMaxim c256; + + for (unsigned i = 0; i < ARRAYSIZE(sample_message); i++) { + c0.update0(sample_message[i]); + c16.update16(sample_message[i]); + c256.update256(sample_message[i]); + + EXPECT_EQ(c0.get(), c16.get()); + EXPECT_EQ(c0.get(), c256.get()); + } + EXPECT_EQ(0x4Cu, c0.get()); + + EXPECT_TRUE(c0.check_ok(0x4C)); + EXPECT_TRUE(c16.check_ok(0x4C)); + EXPECT_TRUE(c256.check_ok(0x4C)); + + // Consumes the CRC byte + c0.update0(0x4C); + c16.update16(0x4C); + c256.update256(0x4C); + // The message should check to zero now. + EXPECT_TRUE(c0.check_ok()); + EXPECT_TRUE(c16.check_ok()); + EXPECT_TRUE(c256.check_ok()); +} + +TEST(Crc8Test, Fuzz) +{ + // In the fuzz test we test the three different implementations of CRC8 + // against each other on random data. + unsigned int seed = 42; + + Crc8DallasMaxim c0; + Crc8DallasMaxim c16; + Crc8DallasMaxim c256; + + for (unsigned i = 0; i < 100000; i++) + { + int r1 = rand_r(&seed); + if (r1 % 100 == 0) + { + c0.init(); + c16.init(); + c256.init(); + } + uint8_t m = rand_r(&seed) & 0xFF; + c0.update0(m); + c16.update16(m); + c256.update256(m); -TEST(Crc3Test, Example) { - uint16_t data[3]; - crc3_crc16_ibm("12345678", 8, data); - EXPECT_EQ(0x3c9d, data[0]); - EXPECT_EQ(0x75a8, data[1]); - EXPECT_EQ(0x0459, data[2]); + EXPECT_EQ(c0.get(), c16.get()); + EXPECT_EQ(c0.get(), c256.get()); + } } diff --git a/src/utils/Crc.hxx b/src/utils/Crc.hxx index cb5f3402f..a3069935d 100644 --- a/src/utils/Crc.hxx +++ b/src/utils/Crc.hxx @@ -59,3 +59,130 @@ uint16_t crc_16_ibm(const void* data, size_t length_bytes); * @param checksum is the output buffer where to store the 48-bit checksum. */ void crc3_crc16_ibm(const void* data, size_t length_bytes, uint16_t* checksum); + + +/// Helper class for computing CRC-8 according to Dallas/Maxim specification +/// for 1-wire protocol. This specification is used for BiDiB, RCN-218 and +/// S-9.2.1.1. +/// +/// This class can incrementally compute CRC byte by byte. There are three +/// implementations available, with different code space requirements. +class Crc8DallasMaxim { +public: + Crc8DallasMaxim() + : state_(0) + { + } + + /// Re-sets the state machine for checksumming a new message. + void init() + { + state_ = 0; + } + + /// @return the checksum of the currently consumed message. + uint8_t get() + { + return state_; + } + + /// Checks that the message has a correct CRC. This function assumes that + /// the CRC byte has already been consumed. + /// @return true if the message checksum is correct. + bool check_ok() + { + return (state_ == 0); + } + + /// Checks that the message has a correct CRC. This function assumes that + /// the CRC byte was not part of the message. + /// @param checksum_byte the CRC8 byte of the received message. + /// @return true if the message checksum is correct. + bool check_ok(uint8_t checksum_byte) + { + return (state_ == checksum_byte); + } + + /// Processes one byte of the incoming message. No lookup table will be + /// used. + /// @param message_byte next byte in the message. + void update0(uint8_t message_byte) + { + uint8_t data = state_ ^ message_byte; + uint8_t crc = 0; + if (data & 1) + { + crc ^= 0x5e; + } + if (data & 2) + { + crc ^= 0xbc; + } + if (data & 4) + { + crc ^= 0x61; + } + if (data & 8) + { + crc ^= 0xc2; + } + if (data & 0x10) + { + crc ^= 0x9d; + } + if (data & 0x20) + { + crc ^= 0x23; + } + if (data & 0x40) + { + crc ^= 0x46; + } + if (data & 0x80) + { + crc ^= 0x8c; + } + state_ = crc; + } + + /// Processes one byte of the incoming message. A small lookup table will be + /// used. + /// @param message_byte next byte in the message. + void update16(uint8_t message_byte) + { + uint8_t data = state_ ^ message_byte; + state_ = tableLo16[data & 0xf] ^ tableHi16[data >> 4]; + } + + /// Processes one byte of the incoming message. A 256-byte lookup table + /// will be used. + /// @param message_byte next byte in the message. + void update256(uint8_t message_byte) + { + state_ = table256[state_ ^ message_byte]; + } + +#ifdef CRC8_TABLE_SIZE + /// Processes one byte of the incoming message. + /// @param message_byte next byte in the message. + void update(uint8_t message_byte) + { + update##CRC8_TABLE_SIZE(message_byte); + } +#endif + +private: + // Of the static tables here only those will be linked into a binary which + // have been used there. + + /// 256-entry lookup table for the update256 function. + static uint8_t table256[256]; + + /// 16-entry lookup table for the update16 function. + static uint8_t tableHi16[16]; + /// 16-entry lookup table for the update16 function. + static uint8_t tableLo16[16]; + + /// Current value of the state register for the CRC computation. + uint8_t state_; +};