[util] Add generic ecc encode function for re-use

Signed-off-by: Timothy Chen <timothytim@google.com>

[util] secded_gen fixes

Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/util/design/secded_gen.py b/util/design/secded_gen.py
index f216565..93e2429 100755
--- a/util/design/secded_gen.py
+++ b/util/design/secded_gen.py
@@ -18,6 +18,8 @@
 import random
 import hjson
 import subprocess
+from typing import Tuple
+from pathlib import Path
 
 COPYRIGHT = """// Copyright lowRISC contributors.
 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
@@ -76,6 +78,9 @@
 # secded configurations
 SECDED_CFG_FILE = "util/design/data/secded_cfg.hjson"
 
+PROJ_ROOT = Path(__file__).parent.joinpath('../../')
+SECDED_CFG_PATH = Path(PROJ_ROOT).joinpath(SECDED_CFG_FILE)
+
 # The seed we use to initialise the PRNG when running the randomised algorithm
 # to choose constants for Hsiao codes.
 _RND_SEED = 123
@@ -317,6 +322,53 @@
     return error
 
 
+def ecc_encode(codetype: str, k: int, dataword: int) -> Tuple[int, int]:
+    log.info(f"Encoding ECC for {hex(dataword)}")
+
+    # first check to see if bit width is supported among configuration
+    with open(SECDED_CFG_PATH, 'r') as infile:
+        config = hjson.load(infile)
+
+    codes = None
+    bitmasks = None
+    m = None
+    for cfg in config['cfgs']:
+        if cfg['k'] == k and cfg['code_type'] == codetype:
+            m = cfg['m']
+            codes = gen_code(codetype, k, m)
+            bitmasks = calc_bitmasks(k, m, codes, False)
+            break
+
+    # error if k not supported
+    if not m:
+        raise Exception(f'ECC for length {k} of type {codetype} unsupported')
+
+    # represent supplied dataword as a binary string
+    word_bin = format(dataword, '0' + str(k) + 'b')
+
+    codeword = word_bin
+    for j, mask in enumerate(bitmasks):
+        bit = 0
+        log.debug(f'codword: {codeword}')
+        log.debug(f'mask: {hex(mask)}')
+        mask = (format(mask, '0' + str(k + m) + 'b'))
+
+        # reverse codeword for index selection
+        # This is because the LSB is the farthest entry in the string
+        codeword_rev = codeword[::-1]
+        for idx, f in enumerate(mask[::-1]):
+            if int(f):
+                log.debug(f'xoring position {idx} value {codeword_rev[idx]}')
+                bit ^= int(codeword_rev[idx])
+
+        codeword = str(bit) + codeword
+
+    # Debug printouts
+    log.debug(f'original hex: {hex(dataword)}')
+    log.debug(f'codeword hex: {hex(int(codeword,2))}')
+    return int(codeword, 2), m
+
+
 def gen_code(codetype, k, m):
     # The hsiao_code generator uses (pseudo)random values to pick good ECC
     # constants. Rather than exposing the seed, we pick a fixed one here to