summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/util.c53
-rw-r--r--include/util.h31
-rw-r--r--test/utils.c25
3 files changed, 109 insertions, 0 deletions
diff --git a/common/util.c b/common/util.c
index d60fcd95d5..8e965273a5 100644
--- a/common/util.c
+++ b/common/util.c
@@ -699,3 +699,56 @@ void wait_for_ready(volatile uint32_t *reg, uint32_t enable, uint32_t ready)
while (!(*reg & ready))
;
}
+
+int binary_first_base3_from_bits(int *bits, int nbits)
+{
+ int binary_below = 0;
+ int has_z = 0;
+ int base3 = 0;
+ int i;
+
+ /* Loop through every ternary digit, from MSB to LSB. */
+ for (i = nbits - 1; i >= 0; i--) {
+ /*
+ * We keep track of the normal ternary result and whether
+ * we found any bit that was a Z. We also determine the
+ * amount of numbers that can be represented with only binary
+ * digits (no Z) whose value in the normal ternary system
+ * is lower than the one we are parsing. Counting from the left,
+ * we add 2^i for any '1' digit to account for the binary
+ * numbers whose values would be below it if all following
+ * digits we parsed would be '0'. As soon as we find a '2' digit
+ * we can total the remaining binary numbers below as 2^(i+1)
+ * because we know that all binary representations counting only
+ * this and following digits must have values below our number
+ * (since 1xxx is always smaller than 2xxx).
+ *
+ * Example: 1 0 2 1 (counting from the left / most significant)
+ * '1' at 3^3: Add 2^3 = 8 to account for binaries 0000-0111
+ * '0' at 3^2: Ignore (not all binaries 1000-1100 are below us)
+ * '2' at 3^1: Add 2^(1+1) = 4 to account for binaries 1000-1011
+ * Stop adding for lower digits (3^0), all already accounted
+ * now. We know that there can be no binary numbers 1020-102X.
+ */
+ base3 = (base3 * 3) + bits[i];
+
+ if (!has_z) {
+ switch (bits[i]) {
+ case 0: /* Ignore '0' digits. */
+ break;
+ case 1: /* Account for binaries 0 to 2^i - 1. */
+ binary_below += 1 << i;
+ break;
+ case 2: /* Account for binaries 0 to 2^(i+1) - 1. */
+ binary_below += 1 << (i + 1);
+ has_z = 1;
+ }
+ }
+ }
+
+ if (has_z)
+ return base3 + (1 << nbits) - binary_below;
+
+ /* binary_below is normal binary system value if !has_z. */
+ return binary_below;
+}
diff --git a/include/util.h b/include/util.h
index a25bbbd77a..21603484bb 100644
--- a/include/util.h
+++ b/include/util.h
@@ -328,6 +328,37 @@ static inline uint64_t mulaa32(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
*/
void wait_for_ready(volatile uint32_t *reg, uint32_t enable, uint32_t ready);
+/**
+ * Convert the ternary bit array (each element is either 0, 1, or 2) to a
+ * non-standard ternary number system where the first 2^n natural numbers are
+ * represented as they would be in a binary system (without any Z digits) and
+ * the following 3^n-2^n numbers use the remaining ternary representations in
+ * the normal ternary system order (skipping the values that were already used
+ * up).
+ *
+ * This function is useful for converting BOARd ID, which is initially used a
+ * binary and later decided to switch to tri-state after some revisions have
+ * already been built.
+ *
+ * Example: For nbits = 2 we get the following representation:
+ *
+ * Number X1 X0
+ * 0 0 0
+ * 1 0 1
+ * 2 1 0
+ * 3 1 1 // Start counting ternaries back at 0 after this
+ * 4 0 2 // Skipping 00 and 01 which are already used up
+ * 5 1 2 // Skipping 10 and 11 which are already used up
+ * 6 2 0
+ * 7 2 1
+ * 8 2 2
+ *
+ * @param bits Array of ternary bits (LSB first).
+ * @param nbits Total number of bits.
+ * @return Number in the binary-first ternary number system.
+ */
+int binary_first_base3_from_bits(int *bits, int nbits);
+
#ifdef __cplusplus
}
#endif
diff --git a/test/utils.c b/test/utils.c
index 82316a53cc..9402e92740 100644
--- a/test/utils.c
+++ b/test/utils.c
@@ -481,6 +481,30 @@ test_static int test_alignment_log2(void)
return EC_SUCCESS;
}
+test_static int test_binary_first_base3_from_bits(void)
+{
+ int n0[] = {0, 0, 0}; /* LSB first */
+ int n7[] = {1, 1, 1};
+ int n8[] = {2, 0, 0};
+ int n9[] = {2, 1, 0};
+ int n10[] = {0, 2, 0};
+ int n11[] = {1, 2, 0};
+ int n18[] = {0, 0, 2};
+ int n26[] = {2, 2, 2};
+ int n38[] = {1, 2, 0, 1};
+
+ TEST_EQ(binary_first_base3_from_bits(n0, ARRAY_SIZE(n0)), 0, "%d");
+ TEST_EQ(binary_first_base3_from_bits(n7, ARRAY_SIZE(n7)), 7, "%d");
+ TEST_EQ(binary_first_base3_from_bits(n8, ARRAY_SIZE(n8)), 8, "%d");
+ TEST_EQ(binary_first_base3_from_bits(n9, ARRAY_SIZE(n9)), 9, "%d");
+ TEST_EQ(binary_first_base3_from_bits(n10, ARRAY_SIZE(n10)), 10, "%d");
+ TEST_EQ(binary_first_base3_from_bits(n11, ARRAY_SIZE(n11)), 11, "%d");
+ TEST_EQ(binary_first_base3_from_bits(n18, ARRAY_SIZE(n18)), 18, "%d");
+ TEST_EQ(binary_first_base3_from_bits(n26, ARRAY_SIZE(n26)), 26, "%d");
+ TEST_EQ(binary_first_base3_from_bits(n38, ARRAY_SIZE(n38)), 38, "%d");
+ return EC_SUCCESS;
+}
+
void run_test(int argc, char **argv)
{
test_reset();
@@ -502,6 +526,7 @@ void run_test(int argc, char **argv)
RUN_TEST(test_is_aligned);
RUN_TEST(test_safe_memcmp);
RUN_TEST(test_alignment_log2);
+ RUN_TEST(test_binary_first_base3_from_bits);
test_print_result();
}