|  | #include "ecc.h" | 
|  | #include <cassert> | 
|  | #include <iostream> | 
|  |  | 
|  | const std::unordered_map<std::string, size_t> ECC::ecc_word_per_architecture = { | 
|  | {"Series7", 50}, | 
|  | {"UltraScale", 60}, | 
|  | {"UltraScalePlus", 45}}; | 
|  |  | 
|  | uint32_t ECC::GetSeries7WordEcc(uint32_t idx, | 
|  | uint32_t data, | 
|  | uint32_t ecc) const { | 
|  | uint32_t val = idx * 32;  // bit offset | 
|  |  | 
|  | if (idx > 0x25)  // avoid 0x800 | 
|  | val += 0x1360; | 
|  | else if (idx > 0x6)  // avoid 0x400 | 
|  | val += 0x1340; | 
|  | else  // avoid lower | 
|  | val += 0x1320; | 
|  |  | 
|  | if (idx == 0x32)  // mask ECC | 
|  | data &= 0xFFFFE000; | 
|  |  | 
|  | for (int i = 0; i < 32; i++) { | 
|  | if (data & 1) | 
|  | ecc ^= val + i; | 
|  |  | 
|  | data >>= 1; | 
|  | } | 
|  |  | 
|  | if (idx == 0x64) {  // last index | 
|  | uint32_t v = ecc & 0xFFF; | 
|  | v ^= v >> 8; | 
|  | v ^= v >> 4; | 
|  | v ^= v >> 2; | 
|  | v ^= v >> 1; | 
|  | ecc ^= (v & 1) << 12;  // parity | 
|  | } | 
|  |  | 
|  | return ecc; | 
|  | } | 
|  |  | 
|  | uint64_t ECC::GetUSEccFrameOffset(int word, int bit) const { | 
|  | int nib = bit / 4; | 
|  | int nibbit = bit % 4; | 
|  | // ECC offset is expanded to 1 bit per nibble, | 
|  | // and then shifted based on the bit index in nibble | 
|  | // e.g. word 3, bit 9 | 
|  | // offset: 0b10100110010 - concatenate (3 + (255 - last_frame_index(e.g. | 
|  | // 92 for US+)) [frame offset] and 9/4 [nibble offset] becomes: | 
|  | // 0x10100110010 shifted by bit in nibble (9%4): 0x20200220020 | 
|  | uint32_t offset = (word + (255 - (frame_words_ - 1))) << 3 | nib; | 
|  | uint64_t exp_offset = 0; | 
|  | // Odd parity | 
|  | offset ^= (1 << 11); | 
|  | for (int i = 0; i < 11; i++) | 
|  | if (offset & (1 << i)) | 
|  | offset ^= (1 << 11); | 
|  | // Expansion | 
|  | for (int i = 0; i < 12; i++) | 
|  | if (offset & (1 << i)) | 
|  | exp_offset |= (1ULL << (4 * i)); | 
|  | return exp_offset << nibbit; | 
|  | }; | 
|  |  | 
|  | uint64_t ECC::GetUSWordEcc(uint32_t idx, uint32_t data, uint64_t ecc) const { | 
|  | if (idx == ecc_word_) { | 
|  | data = 0x0; | 
|  | } | 
|  | if (idx == ecc_word_ + 1) { | 
|  | data &= 0xffff0000; | 
|  | } | 
|  | for (int i = 0; i < 32; i++) { | 
|  | if (data & 1) { | 
|  | ecc ^= GetUSEccFrameOffset(idx, i); | 
|  | } | 
|  | data >>= 1; | 
|  | } | 
|  | return ecc; | 
|  | } | 
|  |  | 
|  | uint64_t ECC::CalculateECC(const std::vector<uint32_t>& data) const { | 
|  | uint64_t ecc = 0; | 
|  | for (size_t w = 0; w < data.size(); ++w) { | 
|  | const uint32_t& word = data.at(w); | 
|  | if (architecture_ == "Series7") { | 
|  | ecc = GetSeries7WordEcc(w, word, ecc); | 
|  | } else { | 
|  | ecc = GetUSWordEcc(w, word, ecc); | 
|  | } | 
|  | } | 
|  | return ecc; | 
|  | } | 
|  |  | 
|  | void ECC::UpdateFrameECC(std::vector<uint32_t>& data) const { | 
|  | assert(data.size() >= ecc_word_); | 
|  | uint64_t ecc(CalculateECC(data)); | 
|  | if (architecture_ == "Series7") { | 
|  | data[ecc_word_] &= 0xffffe000; | 
|  | data[ecc_word_] |= ecc & 0x1fff; | 
|  | } else { | 
|  | data[ecc_word_] = ecc; | 
|  | data[ecc_word_ + 1] &= 0xffff0000; | 
|  | data[ecc_word_ + 1] |= (ecc >> 32) & 0xffff; | 
|  | } | 
|  | } |