summaryrefslogtreecommitdiffstats
path: root/src/occ_gpe0/firdata/ecc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/occ_gpe0/firdata/ecc.c')
-rw-r--r--src/occ_gpe0/firdata/ecc.c272
1 files changed, 272 insertions, 0 deletions
diff --git a/src/occ_gpe0/firdata/ecc.c b/src/occ_gpe0/firdata/ecc.c
new file mode 100644
index 0000000..6696678
--- /dev/null
+++ b/src/occ_gpe0/firdata/ecc.c
@@ -0,0 +1,272 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/occ/firdata/ecc.C $ */
+/* */
+/* OpenPOWER OnChipController Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2015 */
+/* [+] International Business Machines Corp. */
+/* */
+/* */
+/* Licensed under the Apache License, Version 2.0 (the "License"); */
+/* you may not use this file except in compliance with the License. */
+/* You may obtain a copy of the License at */
+/* */
+/* http://www.apache.org/licenses/LICENSE-2.0 */
+/* */
+/* Unless required by applicable law or agreed to in writing, software */
+/* distributed under the License is distributed on an "AS IS" BASIS, */
+/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
+/* implied. See the License for the specific language governing */
+/* permissions and limitations under the License. */
+/* */
+/* IBM_PROLOG_END_TAG */
+
+/*#include <stdio.h> */
+#include <endian.h>
+#include <assert.h>
+
+#include <native.h>
+#include <ecc.h>
+
+/** Matrix used for ECC calculation.
+ *
+ * Each row of this is the set of data word bits that are used for
+ * the calculation of the corresponding ECC bit. The parity of the
+ * bitset is the value of the ECC bit.
+ *
+ * ie. ECC[n] = eccMatrix[n] & data
+ *
+ * Note: To make the math easier (and less shifts in resulting code),
+ * row0 = ECC7. HW numbering is MSB, order here is LSB.
+ *
+ * These values come from the HW design of the ECC algorithm.
+ */
+static uint64_t eccMatrix[] = {
+ /*0000000000000000111010000100001000111100000011111001100111111111 */
+ 0x0000e8423c0f99ff,
+ /*0000000011101000010000100011110000001111100110011111111100000000 */
+ 0x00e8423c0f99ff00,
+ /*1110100001000010001111000000111110011001111111110000000000000000 */
+ 0xe8423c0f99ff0000,
+ /*0100001000111100000011111001100111111111000000000000000011101000 */
+ 0x423c0f99ff0000e8,
+ /*0011110000001111100110011111111100000000000000001110100001000010 */
+ 0x3c0f99ff0000e842,
+ /*0000111110011001111111110000000000000000111010000100001000111100 */
+ 0x0f99ff0000e8423c,
+ /*1001100111111111000000000000000011101000010000100011110000001111 */
+ 0x99ff0000e8423c0f,
+ /*1111111100000000000000001110100001000010001111000000111110011001 */
+ 0xff0000e8423c0f99
+};
+
+/** Syndrome calculation matrix.
+ *
+ * Maps syndrome to flipped bit.
+ *
+ * To perform ECC correction, this matrix is a look-up of the bit
+ * that is bad based on the binary difference of the good and bad
+ * ECC. This difference is called the "syndrome".
+ *
+ * When a particular bit is on in the data, it cause a column from
+ * eccMatrix being XOR'd into the ECC field. This column is the
+ * "effect" of each bit. If a bit is flipped in the data then its
+ * "effect" is missing from the ECC. You can calculate ECC on unknown
+ * quality data and compare the ECC field between the calculated
+ * value and the stored value. If the difference is zero, then the
+ * data is clean. If the difference is non-zero, you look up the
+ * difference in the syndrome table to identify the "effect" that
+ * is missing, which is the bit that is flipped.
+ *
+ * Notice that ECC bit flips are recorded by a single "effect"
+ * bit (ie. 0x1, 0x2, 0x4, 0x8 ...) and double bit flips are identified
+ * by the UE status in the table.
+ *
+ * Bits are in MSB order.
+ */
+static uint8_t syndromeMatrix[] = {
+ ECC_GD, ECC_E7, ECC_E6, ECC_UE, ECC_E5, ECC_UE, ECC_UE, 47,
+ ECC_E4, ECC_UE, ECC_UE, 37, ECC_UE, 35, 39, ECC_UE,
+ ECC_E3, ECC_UE, ECC_UE, 48, ECC_UE, 30, 29, ECC_UE,
+ ECC_UE, 57, 27, ECC_UE, 31, ECC_UE, ECC_UE, ECC_UE,
+ ECC_E2, ECC_UE, ECC_UE, 17, ECC_UE, 18, 40, ECC_UE,
+ ECC_UE, 58, 22, ECC_UE, 21, ECC_UE, ECC_UE, ECC_UE,
+ ECC_UE, 16, 49, ECC_UE, 19, ECC_UE, ECC_UE, ECC_UE,
+ 23, ECC_UE, ECC_UE, ECC_UE, ECC_UE, 20, ECC_UE, ECC_UE,
+ ECC_E1, ECC_UE, ECC_UE, 51, ECC_UE, 46, 9, ECC_UE,
+ ECC_UE, 34, 10, ECC_UE, 32, ECC_UE, ECC_UE, 36,
+ ECC_UE, 62, 50, ECC_UE, 14, ECC_UE, ECC_UE, ECC_UE,
+ 13, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE,
+ ECC_UE, 61, 8, ECC_UE, 41, ECC_UE, ECC_UE, ECC_UE,
+ 11, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE,
+ 15, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE,
+ ECC_UE, ECC_UE, 12, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE,
+ ECC_E0, ECC_UE, ECC_UE, 55, ECC_UE, 45, 43, ECC_UE,
+ ECC_UE, 56, 38, ECC_UE, 1, ECC_UE, ECC_UE, ECC_UE,
+ ECC_UE, 25, 26, ECC_UE, 2, ECC_UE, ECC_UE, ECC_UE,
+ 24, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, 28, ECC_UE,
+ ECC_UE, 59, 54, ECC_UE, 42, ECC_UE, ECC_UE, 44,
+ 6, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE,
+ 5, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE,
+ ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE,
+ ECC_UE, 63, 53, ECC_UE, 0, ECC_UE, ECC_UE, ECC_UE,
+ 33, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE,
+ 3, ECC_UE, ECC_UE, 52, ECC_UE, ECC_UE, ECC_UE, ECC_UE,
+ ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE,
+ 7, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE,
+ ECC_UE, 60, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE,
+ ECC_UE, ECC_UE, ECC_UE, ECC_UE, 4, ECC_UE, ECC_UE, ECC_UE,
+ ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE,
+};
+
+/** Returns the parity of x, i.e. the number of 1-bits in x modulo 2.
+ * Replacement for __builtin_parityl
+ */
+uint8_t parity_check( uint64_t i_data )
+{
+ int ones = 0;
+ size_t x;
+ for( x=0; x<(sizeof(i_data)*8); x++ )
+ {
+ if( i_data & (0x8000000000000000ull >> x) )
+ {
+ ones++;
+ }
+ }
+ return ones%2;
+}
+
+/** Create the ECC field corresponding to a 8-byte data field
+ *
+ * @param[in] i_data - The 8 byte data to generate ECC for.
+ * @return The 1 byte ECC corresponding to the data.
+ */
+uint8_t generateECC(uint64_t i_data)
+{
+ uint8_t result = 0;
+
+ int i = 0;
+ for (i = 0; i < 8; i++)
+ {
+ result |= (parity_check(eccMatrix[i] & i_data) << i);
+ }
+
+ return result;
+}
+
+/** Verify the data and ECC match or indicate how they are wrong.
+ *
+ * @param[in] i_data - The data to check ECC on.
+ * @param[in] i_ecc - The [supposed] ECC for the data.
+ *
+ * @return eccBitfield or 0-64.
+ *
+ * @retval GD - Indicates the data is good (matches ECC).
+ * @retval UE - Indicates the data is uncorrectable.
+ * @retval all others - Indication of which bit is incorrect.
+ */
+uint8_t verifyECC(uint64_t i_data, uint8_t i_ecc)
+{
+ return syndromeMatrix[generateECC(i_data) ^ i_ecc];
+}
+
+/** Correct the data and/or ECC.
+ *
+ * @param[in,out] io_data - Data to check / correct.
+ * @param[in,out] io_ecc - ECC to check / correct.
+ *
+ * @return eccBitfield or 0-64.
+ *
+ * @retval GD - Data is good.
+ * @retval UE - Data is uncorrectable.
+ * @retval all others - which bit was corrected.
+ */
+uint8_t correctECC(uint64_t* io_data, uint8_t* io_ecc)
+{
+ uint8_t badBit = verifyECC(*io_data, *io_ecc);
+
+ if ((badBit != ECC_GD) && (badBit != ECC_UE)) /* Good is done, UE is hopeless. */
+ {
+ /* Determine if the ECC or data part is bad, do bit flip. */
+ if (badBit >= ECC_E7)
+ {
+ *io_ecc ^= (1 << (badBit - ECC_E7));
+ }
+ else
+ {
+ *io_data ^= (1ul << (63 - badBit));
+ }
+ }
+ return badBit;
+}
+
+void injectECC(const uint8_t* i_src,
+ size_t i_srcSz,
+ uint8_t* o_dst)
+{
+ assert(0 == (i_srcSz % sizeof(uint64_t)));
+
+ size_t i = 0;
+ size_t o = 0;
+ for(i = 0, o = 0;
+ i < i_srcSz;
+ i += sizeof(uint64_t), o += sizeof(uint64_t) + sizeof(uint8_t))
+ {
+ /* Read data word, copy to destination. */
+ uint64_t data = *((const uint64_t*)(&i_src[i]));
+ *((uint64_t*)(&o_dst[o])) = data;
+ data = be64toh(data);
+
+ /* Calculate ECC, copy to destination. */
+ uint8_t ecc = generateECC(data);
+ o_dst[o + sizeof(uint64_t)] = ecc;
+ }
+}
+
+eccStatus removeECC(uint8_t* io_src,
+ uint8_t* o_dst, size_t i_dstSz)
+{
+ assert(0 == (i_dstSz % sizeof(uint64_t)));
+
+ eccStatus rc = ECC_CLEAN;
+
+ size_t i = 0, o = 0;
+ for(i = 0, o = 0;
+ o < i_dstSz;
+ i += sizeof(uint64_t) + sizeof(uint8_t), o += sizeof(uint64_t))
+ {
+ /* Read data and ECC parts. */
+ uint64_t data = *((uint64_t*)(&io_src[i]));
+ data = be64toh(data);
+ uint8_t ecc = io_src[i + sizeof(uint64_t)];
+
+ /* Calculate failing bit and fix data. */
+ uint8_t badBit = correctECC(&data, &ecc);
+
+ /* Return data to big endian. */
+ data = htobe64(data);
+
+ /* Perform correction and status update. */
+ if (badBit == ECC_UE)
+ {
+ rc = ECC_UNCORRECTABLE;
+ }
+ else if (badBit != ECC_GD)
+ {
+ if (rc != ECC_UNCORRECTABLE)
+ {
+ rc = ECC_CORRECTED;
+ }
+ *((uint64_t*)(&io_src[i])) = data;
+ io_src[i + sizeof(uint64_t)] = ecc;
+ }
+
+ /* Copy fixed data to destination buffer. */
+ *((uint64_t*)(&o_dst[o])) = data;
+ }
+
+ return rc;
+}
+
OpenPOWER on IntegriCloud