lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # Copyright lowRISC contributors. |
| 3 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 4 | # SPDX-License-Identifier: Apache-2.0 |
| 5 | r"""SECDED encoder/decoder generator |
| 6 | |
| 7 | Current version doesn't optimize Fan-In. It uses Hsiao code (modified version |
| 8 | of Hamming code + parity). Please refer https://arxiv.org/pdf/0803.1217.pdf |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 9 | |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 10 | For some further background and info on the differences between Hamming and |
| 11 | Hsiao SECDED codes, refer to https://ieeexplore.ieee.org/document/8110065.g |
| 12 | """ |
Eunchan Kim | e19e99f | 2020-03-06 14:44:21 -0800 | [diff] [blame] | 13 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 14 | import argparse |
| 15 | import itertools |
| 16 | import logging as log |
| 17 | import math |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 18 | import random |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 19 | import hjson |
Greg Chadwick | c13cc28 | 2021-05-15 17:09:52 +0100 | [diff] [blame] | 20 | import subprocess |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 21 | |
| 22 | COPYRIGHT = """// Copyright lowRISC contributors. |
| 23 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 24 | // SPDX-License-Identifier: Apache-2.0 |
| 25 | // |
| 26 | """ |
Greg Chadwick | c13cc28 | 2021-05-15 17:09:52 +0100 | [diff] [blame] | 27 | |
| 28 | C_SRC_TOP = """#include <stdbool.h> |
| 29 | #include <stdint.h> |
| 30 | |
| 31 | #include "secded_enc.h" |
| 32 | |
| 33 | // Calculates even parity for a 64-bit word |
| 34 | static uint8_t calc_parity(uint64_t word) { |
| 35 | bool parity = false; |
| 36 | |
| 37 | while (word) { |
| 38 | if (word & 1) { |
| 39 | parity = !parity; |
| 40 | } |
| 41 | |
| 42 | word >>= 1; |
| 43 | } |
| 44 | |
| 45 | return parity; |
| 46 | } |
| 47 | """ |
| 48 | |
| 49 | C_H_TOP = """ |
| 50 | #ifndef OPENTITAN_HW_IP_PRIM_DV_PRIM_SECDED_SECDED_ENC_H_ |
| 51 | #define OPENTITAN_HW_IP_PRIM_DV_PRIM_SECDED_SECDED_ENC_H_ |
| 52 | |
| 53 | #include <stdint.h> |
| 54 | |
| 55 | #ifdef __cplusplus |
| 56 | extern "C" { |
| 57 | #endif // __cplusplus |
| 58 | |
| 59 | // Integrity encode functions for varying bit widths matching the functionality |
| 60 | // of the RTL modules of the same name. Each takes an array of bytes in |
| 61 | // little-endian order and returns the calculated integrity bits. |
| 62 | |
| 63 | """ |
| 64 | |
| 65 | C_H_FOOT = """ |
| 66 | #ifdef __cplusplus |
| 67 | } // extern "C" |
| 68 | #endif // __cplusplus |
| 69 | |
| 70 | #endif // OPENTITAN_HW_IP_PRIM_DV_PRIM_SECDED_SECDED_ENC_H_ |
| 71 | """ |
| 72 | |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 73 | CODE_OPTIONS = {'hsiao': '', 'hamming': '_hamming'} |
Udi Jonnalagadda | 2f5e5cf | 2021-03-08 11:51:17 -0800 | [diff] [blame] | 74 | PRINT_OPTIONS = {"logic": "assign ", "function": " "} |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 75 | |
| 76 | # secded configurations |
| 77 | SECDED_CFG_FILE = "util/design/data/secded_cfg.hjson" |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 78 | |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 79 | # The seed we use to initialise the PRNG when running the randomised algorithm |
| 80 | # to choose constants for Hsiao codes. |
| 81 | _RND_SEED = 123 |
| 82 | |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 83 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 84 | def min_paritysize(k): |
| 85 | # SECDED --> Hamming distance 'd': 4 |
| 86 | # 2^(m-1) should cover (m+k) |
| 87 | for m in range(2, 10): |
| 88 | if 2**m >= (k + m + 1): |
| 89 | return m + 1 |
| 90 | return -1 |
| 91 | |
| 92 | |
| 93 | def ideal_fanin(k, m): |
| 94 | """Compute Ideal Max Fanin of any bit in the ecc codes.""" |
| 95 | fanin = 0 |
| 96 | needed = k |
| 97 | for select in range(3, m + 1, 2): |
| 98 | combinations = list(itertools.combinations(range(m), select)) |
| 99 | if len(combinations) <= needed: |
| 100 | fanin += int(math.ceil(float(len(combinations) * select) / m)) |
| 101 | needed -= len(combinations) |
| 102 | else: |
| 103 | fanin += int(math.ceil(float(needed * select) / m)) |
| 104 | needed = 0 |
| 105 | if not needed: |
| 106 | break |
| 107 | return fanin |
| 108 | |
| 109 | |
Eunchan Kim | e19e99f | 2020-03-06 14:44:21 -0800 | [diff] [blame] | 110 | def calc_fanin(width, codes): |
| 111 | """Sum the ones in a column""" |
| 112 | fanins = [0] * width |
| 113 | log.info("Calc Code: {}".format(codes)) |
| 114 | for i in codes: |
| 115 | for e in i: |
| 116 | fanins[e] += 1 |
| 117 | |
| 118 | return fanins |
| 119 | |
| 120 | |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 121 | def calc_bitmasks(k, m, codes, dec): |
| 122 | # Transform fanin indices into bitmask. |
| 123 | fanin_masks = [0] * m |
| 124 | for i, c in enumerate(codes): |
| 125 | for j in c: |
| 126 | fanin_masks[j] += 1 << i |
| 127 | # For decode ops, include ECC bit position. |
| 128 | if dec: |
| 129 | for j in range(m): |
| 130 | fanin_masks[j] += 1 << (k + j) |
| 131 | return fanin_masks |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 132 | |
| 133 | |
Udi Jonnalagadda | 18c5c43 | 2021-05-05 10:04:49 -0700 | [diff] [blame] | 134 | def print_secded_enum_and_util_fns(cfgs): |
| 135 | enum_vals = [" SecdedNone"] |
| 136 | parity_width_vals = [] |
| 137 | data_width_vals = [] |
| 138 | for cfg in cfgs: |
| 139 | k = cfg['k'] |
| 140 | m = cfg['m'] |
| 141 | n = k + m |
| 142 | suffix = CODE_OPTIONS[cfg['code_type']] |
| 143 | formatted_suffix = suffix.replace('_', '').capitalize() |
| 144 | |
| 145 | enum_name = " Secded%s_%s_%s" % (formatted_suffix, n, k) |
| 146 | enum_vals.append(enum_name) |
| 147 | |
| 148 | parity_width = " %s: return %s;" % (enum_name, m) |
| 149 | parity_width_vals.append(parity_width) |
| 150 | |
| 151 | data_width = " %s: return %s;" % (enum_name, k) |
| 152 | data_width_vals.append(data_width) |
| 153 | |
| 154 | enum_str = ",\n".join(enum_vals) |
| 155 | parity_width_fn_str = "\n".join(parity_width_vals) |
| 156 | data_width_fn_str = "\n".join(data_width_vals) |
| 157 | |
| 158 | enum_str = ''' |
| 159 | typedef enum int {{ |
| 160 | {} |
| 161 | }} prim_secded_e; |
| 162 | |
| 163 | function automatic int get_ecc_data_width(prim_secded_e ecc_type); |
| 164 | case (ecc_type) |
| 165 | {} |
| 166 | // Return a non-zero width to avoid VCS compile issues |
| 167 | default: return 32; |
| 168 | endcase |
| 169 | endfunction |
| 170 | |
| 171 | function automatic int get_ecc_parity_width(prim_secded_e ecc_type); |
| 172 | case (ecc_type) |
| 173 | {} |
| 174 | default: return 0; |
| 175 | endcase |
| 176 | endfunction |
| 177 | '''.format(enum_str, data_width_fn_str, parity_width_fn_str) |
| 178 | |
| 179 | return enum_str |
| 180 | |
| 181 | |
Udi Jonnalagadda | c454740 | 2021-03-08 16:54:46 -0800 | [diff] [blame] | 182 | def print_pkg_types(n, k, m, codes, suffix, codetype): |
| 183 | typename = "secded%s_%d_%d_t" % (suffix, n, k) |
| 184 | |
| 185 | typestr = ''' |
| 186 | typedef struct packed {{ |
| 187 | logic [{}:0] data; |
| 188 | logic [{}:0] syndrome; |
| 189 | logic [1:0] err; |
| 190 | }} {}; |
| 191 | '''.format((k - 1), (m - 1), typename) |
| 192 | |
| 193 | return typestr |
| 194 | |
| 195 | |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 196 | def print_fn(n, k, m, codes, suffix, codetype): |
| 197 | enc_out = print_enc(n, k, m, codes) |
Udi Jonnalagadda | 2f5e5cf | 2021-03-08 11:51:17 -0800 | [diff] [blame] | 198 | dec_out = print_dec(n, k, m, codes, codetype, "function") |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 199 | |
Udi Jonnalagadda | 18c5c43 | 2021-05-05 10:04:49 -0700 | [diff] [blame] | 200 | typename = "secded_%d_%d_t" % (n, k) |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 201 | module_name = "prim_secded%s_%d_%d" % (suffix, n, k) |
| 202 | |
| 203 | outstr = ''' |
Philipp Wagner | 19044ed | 2021-05-10 17:23:13 +0100 | [diff] [blame] | 204 | function automatic logic [{}:0] {}_enc (logic [{}:0] data_i); |
| 205 | logic [{}:0] data_o; |
| 206 | {} return data_o; |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 207 | endfunction |
Udi Jonnalagadda | 2f5e5cf | 2021-03-08 11:51:17 -0800 | [diff] [blame] | 208 | |
Philipp Wagner | 19044ed | 2021-05-10 17:23:13 +0100 | [diff] [blame] | 209 | function automatic {} {}_dec (logic [{}:0] data_i); |
| 210 | logic [{}:0] data_o; |
Udi Jonnalagadda | c454740 | 2021-03-08 16:54:46 -0800 | [diff] [blame] | 211 | logic [{}:0] syndrome_o; |
| 212 | logic [1:0] err_o; |
| 213 | |
| 214 | {} dec; |
| 215 | |
Udi Jonnalagadda | 2f5e5cf | 2021-03-08 11:51:17 -0800 | [diff] [blame] | 216 | {} |
Philipp Wagner | 19044ed | 2021-05-10 17:23:13 +0100 | [diff] [blame] | 217 | dec.data = data_o; |
Udi Jonnalagadda | c454740 | 2021-03-08 16:54:46 -0800 | [diff] [blame] | 218 | dec.syndrome = syndrome_o; |
| 219 | dec.err = err_o; |
| 220 | return dec; |
| 221 | |
Udi Jonnalagadda | 2f5e5cf | 2021-03-08 11:51:17 -0800 | [diff] [blame] | 222 | endfunction |
| 223 | '''.format((n - 1), module_name, (k - 1), (n - 1), enc_out, |
Udi Jonnalagadda | c454740 | 2021-03-08 16:54:46 -0800 | [diff] [blame] | 224 | typename, module_name, (n - 1), (k - 1), (m - 1), typename, dec_out) |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 225 | |
| 226 | return outstr |
| 227 | |
| 228 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 229 | def print_enc(n, k, m, codes): |
Philipp Wagner | 19044ed | 2021-05-10 17:23:13 +0100 | [diff] [blame] | 230 | outstr = " data_o = {}'(data_i);\n".format(n) |
| 231 | format_str = " data_o[{}] = ^(data_o & " + str(n) + "'h{:0" + str( |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 232 | (n + 3) // 4) + "X});\n" |
| 233 | # Print parity computation |
| 234 | for j, mask in enumerate(calc_bitmasks(k, m, codes, False)): |
| 235 | outstr += format_str.format(j + k, mask) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 236 | return outstr |
| 237 | |
| 238 | |
| 239 | def calc_syndrome(code): |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 240 | log.info("in syndrome {}".format(code)) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 241 | return sum(map((lambda x: 2**x), code)) |
| 242 | |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 243 | |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 244 | def print_dec(n, k, m, codes, codetype, print_type="logic"): |
| 245 | |
| 246 | preamble = PRINT_OPTIONS[print_type] |
| 247 | |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 248 | outstr = "" |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 249 | if codetype == "hsiao": |
Udi Jonnalagadda | c454740 | 2021-03-08 16:54:46 -0800 | [diff] [blame] | 250 | outstr += " {}logic single_error;\n".format( |
| 251 | preamble if print_type == "function" else "") |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 252 | |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 253 | outstr += "\n" |
Udi Jonnalagadda | 2f5e5cf | 2021-03-08 11:51:17 -0800 | [diff] [blame] | 254 | outstr += " {}// Syndrome calculation\n".format( |
| 255 | preamble if print_type == "function" else "") |
Philipp Wagner | 19044ed | 2021-05-10 17:23:13 +0100 | [diff] [blame] | 256 | format_str = " {}".format(preamble) + "syndrome_o[{}] = ^(data_i & " \ |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 257 | + str(n) + "'h{:0" + str((n + 3) // 4) + "X});\n" |
| 258 | |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 259 | # Print syndrome computation |
| 260 | for j, mask in enumerate(calc_bitmasks(k, m, codes, True)): |
| 261 | outstr += format_str.format(j, mask) |
| 262 | outstr += "\n" |
Udi Jonnalagadda | 2f5e5cf | 2021-03-08 11:51:17 -0800 | [diff] [blame] | 263 | outstr += " {}// Corrected output calculation\n".format( |
| 264 | preamble if print_type == "function" else "") |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 265 | for i in range(k): |
Udi Jonnalagadda | 18c5c43 | 2021-05-05 10:04:49 -0700 | [diff] [blame] | 266 | outstr += " {}".format(preamble) |
| 267 | outstr += "data_o[%d] = (syndrome_o == %d'h%x) ^ data_i[%d];\n" % ( |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 268 | i, m, calc_syndrome(codes[i]), i) |
| 269 | outstr += "\n" |
Udi Jonnalagadda | 2f5e5cf | 2021-03-08 11:51:17 -0800 | [diff] [blame] | 270 | outstr += " {}// err_o calc. bit0: single error, bit1: double error\n".format( |
| 271 | preamble if print_type == "function" else "") |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 272 | # The Hsiao and Hamming syndromes are interpreted slightly differently. |
| 273 | if codetype == "hamming": |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 274 | outstr += " {}".format(preamble) + "err_o[0] = syndrome_o[%d];\n" % (m - 1) |
| 275 | outstr += " {}".format(preamble) + "err_o[1] = |syndrome_o[%d:0] & ~syndrome_o[%d];\n" % ( |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 276 | m - 2, m - 1) |
| 277 | else: |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 278 | outstr += " {}".format(preamble) + "single_error = ^syndrome_o;\n" |
| 279 | outstr += " {}".format(preamble) + "err_o[0] = single_error;\n" |
| 280 | outstr += " {}".format(preamble) + "err_o[1] = ~single_error & (|syndrome_o);\n" |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 281 | return outstr |
| 282 | |
| 283 | |
Timothy Chen | eb4b0d3 | 2020-12-28 12:32:53 -0800 | [diff] [blame] | 284 | # return whether an integer is a power of 2 |
| 285 | def is_pow2(n): |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 286 | return (n & (n - 1) == 0) and n != 0 |
| 287 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 288 | |
Timothy Chen | eb4b0d3 | 2020-12-28 12:32:53 -0800 | [diff] [blame] | 289 | def is_odd(n): |
| 290 | return (n % 2) > 0 |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 291 | |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 292 | |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 293 | def verify(cfgs): |
| 294 | error = 0 |
| 295 | for cfg in cfgs['cfgs']: |
| 296 | if (cfg['k'] <= 1 or cfg['k'] > 120): |
| 297 | error += 1 |
| 298 | log.error("Current tool doesn't support the value k (%d)", cfg['k']) |
| 299 | |
| 300 | if (cfg['m'] <= 1 or cfg['m'] > 20): |
| 301 | error += 1 |
| 302 | log.error("Current tool doesn't support the value m (%d)", cfg['m']) |
| 303 | |
| 304 | # Calculate 'm' (parity size) |
| 305 | min_m = min_paritysize(cfg['k']) |
| 306 | if (cfg['m'] < min_m): |
| 307 | error += 1 |
| 308 | log.error("given \'m\' argument is smaller than minimum requirement " + |
| 309 | "using calculated minimum (%d)", min_m) |
| 310 | |
| 311 | # Error check code selection |
| 312 | if (cfg['code_type'] not in CODE_OPTIONS): |
| 313 | error += 1 |
| 314 | log.error("Invalid code {} selected, use one of {}".format( |
| 315 | cfg['code_type'], CODE_OPTIONS)) |
| 316 | |
| 317 | return error |
| 318 | |
| 319 | |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 320 | def gen_code(codetype, k, m): |
| 321 | # The hsiao_code generator uses (pseudo)random values to pick good ECC |
| 322 | # constants. Rather than exposing the seed, we pick a fixed one here to |
| 323 | # ensure everything stays stable in future. |
| 324 | old_rnd_state = random.getstate() |
| 325 | random.seed(_RND_SEED) |
| 326 | try: |
| 327 | return globals()["_{}_code".format(codetype)](k, m) |
| 328 | finally: |
| 329 | random.setstate(old_rnd_state) |
| 330 | |
| 331 | |
| 332 | def generate(cfgs, args): |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 333 | pkg_out_str = "" |
Udi Jonnalagadda | c454740 | 2021-03-08 16:54:46 -0800 | [diff] [blame] | 334 | pkg_type_str = "" |
Greg Chadwick | c13cc28 | 2021-05-15 17:09:52 +0100 | [diff] [blame] | 335 | |
| 336 | c_src_filename = args.c_outdir + "/" + "secded_enc.c" |
| 337 | c_h_filename = args.c_outdir + "/" + "secded_enc.h" |
| 338 | |
| 339 | with open(c_src_filename, "w") as f: |
| 340 | f.write(COPYRIGHT) |
| 341 | f.write("// SECDED encode code generated by\n") |
| 342 | f.write(f"// util/design/secded_gen.py from {SECDED_CFG_FILE}\n\n") |
| 343 | f.write(C_SRC_TOP) |
| 344 | |
| 345 | with open(c_h_filename, "w") as f: |
| 346 | f.write(COPYRIGHT) |
| 347 | f.write("// SECDED encode code generated by\n") |
| 348 | f.write(f"// util/design/secded_gen.py from {SECDED_CFG_FILE}\n") |
| 349 | f.write(C_H_TOP) |
| 350 | |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 351 | for cfg in cfgs['cfgs']: |
| 352 | log.debug("Working on {}".format(cfg)) |
| 353 | k = cfg['k'] |
| 354 | m = cfg['m'] |
| 355 | n = k + m |
| 356 | codetype = cfg['code_type'] |
| 357 | suffix = CODE_OPTIONS[codetype] |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 358 | codes = gen_code(codetype, k, m) |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 359 | |
| 360 | # write out rtl files |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 361 | write_enc_dec_files(n, k, m, codes, suffix, args.outdir, codetype) |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 362 | |
Greg Chadwick | c13cc28 | 2021-05-15 17:09:52 +0100 | [diff] [blame] | 363 | # write out C files, only hsiao codes are supported |
| 364 | if codetype == "hsiao": |
| 365 | write_c_files(n, k, m, codes, suffix, c_src_filename, c_h_filename) |
| 366 | |
Udi Jonnalagadda | c454740 | 2021-03-08 16:54:46 -0800 | [diff] [blame] | 367 | # write out package typedefs |
| 368 | pkg_type_str += print_pkg_types(n, k, m, codes, suffix, codetype) |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 369 | # print out functions |
| 370 | pkg_out_str += print_fn(n, k, m, codes, suffix, codetype) |
| 371 | |
| 372 | if not args.no_fpv: |
Cindy Chen | 2f3bba8 | 2021-05-24 16:35:41 -0700 | [diff] [blame] | 373 | write_fpv_files(n, k, m, codes, suffix, args.fpv_outdir) |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 374 | |
Greg Chadwick | c13cc28 | 2021-05-15 17:09:52 +0100 | [diff] [blame] | 375 | with open(c_h_filename, "a") as f: |
| 376 | f.write(C_H_FOOT) |
| 377 | |
| 378 | format_c_files(c_src_filename, c_h_filename) |
| 379 | |
Udi Jonnalagadda | 18c5c43 | 2021-05-05 10:04:49 -0700 | [diff] [blame] | 380 | # create enum of various ECC types - useful for DV purposes in mem_bkdr_if |
| 381 | enum_str = print_secded_enum_and_util_fns(cfgs['cfgs']) |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 382 | # write out package file |
Udi Jonnalagadda | 18c5c43 | 2021-05-05 10:04:49 -0700 | [diff] [blame] | 383 | full_pkg_str = enum_str + pkg_type_str + pkg_out_str |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 384 | write_pkg_file(args.outdir, full_pkg_str) |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 385 | |
| 386 | |
Timothy Chen | eb4b0d3 | 2020-12-28 12:32:53 -0800 | [diff] [blame] | 387 | # k = data bits |
| 388 | # m = parity bits |
| 389 | # generate hsiao code |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 390 | def _hsiao_code(k, m): |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 391 | # using itertools combinations, generate odd number of 1 in a row |
| 392 | |
| 393 | required_row = k # k rows are needed, decreasing everytime when it acquite |
| 394 | |
| 395 | fanin_ideal = ideal_fanin(k, m) |
| 396 | log.info("Ideal Fan-In value: %d" % fanin_ideal) |
| 397 | |
Eunchan Kim | e19e99f | 2020-03-06 14:44:21 -0800 | [diff] [blame] | 398 | # Each entry represents a row in below parity matrix |
| 399 | # Entry is tuple and the value inside is the position of ones |
| 400 | # e.g. (0,1,2) in m:=7 |
| 401 | # row -> [1 1 1 0 0 0 0] |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 402 | codes = [] |
| 403 | |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 404 | # Find code matrix ======================================================= |
Eunchan Kim | e19e99f | 2020-03-06 14:44:21 -0800 | [diff] [blame] | 405 | # This is main part to find the parity matrix. |
Eunchan Kim | 920b48f | 2020-03-12 09:40:57 -0700 | [diff] [blame] | 406 | # For example, find SECDED for 4bit message is to find 4x4 matrix as below |
| 407 | # | 1 0 0 0 x x x x | |
| 408 | # | 0 1 0 0 x x x x | |
| 409 | # | 0 0 1 0 x x x x | |
| 410 | # | 0 0 0 1 x x x x | |
Eunchan Kim | e19e99f | 2020-03-06 14:44:21 -0800 | [diff] [blame] | 411 | # Then message _k_ X matrix_code ==> original message with parity |
| 412 | # |
| 413 | # Make a row to have even number of 1 including the I matrix. |
| 414 | # This helps to calculate the syndrom at the decoding stage. |
| 415 | # To reduce the max fan-in, Starting with smallest number 3. |
| 416 | # the number means the number of one in a row. |
| 417 | # Small number of ones means smaller fan-in overall. |
| 418 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 419 | for step in range(3, m + 1, 2): |
| 420 | # starting from 3 as I matrix represents data |
| 421 | # Increased by 2 as number of 1 should be even in a row (odd excluding I) |
| 422 | |
Eunchan Kim | e19e99f | 2020-03-06 14:44:21 -0800 | [diff] [blame] | 423 | # get the list of combinations [0, .., m-1] with `step` |
| 424 | # e.g. step := 3 ==> [(0,1,2), (0,1,3), ... ] |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 425 | candidate = list(itertools.combinations(range(m), step)) |
| 426 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 427 | if len(candidate) <= required_row: |
| 428 | # we need more round use all of them |
| 429 | codes.extend(candidate) |
| 430 | required_row -= len(candidate) |
| 431 | else: |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 432 | # Find optimized fan-in ========================================== |
Eunchan Kim | e19e99f | 2020-03-06 14:44:21 -0800 | [diff] [blame] | 433 | |
| 434 | # Calculate each row fan-in with current |
| 435 | fanins = calc_fanin(m, codes) |
| 436 | while required_row != 0: |
| 437 | # Let's shuffle |
| 438 | # Shuffling makes the sequence randomized --> it reduces the |
| 439 | # fanin as the code takes randomly at the end of the round |
| 440 | |
| 441 | # TODO: There should be a clever way to find the subset without |
| 442 | # random retrying. |
| 443 | # Suggested this algorithm |
| 444 | # https://en.wikipedia.org/wiki/Assignment_problem |
| 445 | random.shuffle(candidate) |
| 446 | |
| 447 | # Take a subset |
| 448 | subset = candidate[0:required_row] |
| 449 | |
| 450 | subset_fanins = calc_fanin(m, subset) |
| 451 | # Check if it exceeds Ideal Fan-In |
| 452 | ideal = True |
| 453 | for i in range(m): |
| 454 | if fanins[i] + subset_fanins[i] > fanin_ideal: |
| 455 | # Exceeded. Retry |
| 456 | ideal = False |
| 457 | break |
| 458 | |
| 459 | if ideal: |
| 460 | required_row = 0 |
| 461 | |
| 462 | # Append to the code matrix |
| 463 | codes.extend(subset) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 464 | |
| 465 | if required_row == 0: |
| 466 | # Found everything! |
| 467 | break |
| 468 | |
Timothy Chen | eb4b0d3 | 2020-12-28 12:32:53 -0800 | [diff] [blame] | 469 | log.info("Hsiao codes {}".format(codes)) |
| 470 | return codes |
| 471 | |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 472 | |
Timothy Chen | eb4b0d3 | 2020-12-28 12:32:53 -0800 | [diff] [blame] | 473 | # n = total bits |
| 474 | # k = data bits |
| 475 | # m = parity bits |
| 476 | # generate hamming code |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 477 | def _hamming_code(k, m): |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 478 | |
| 479 | n = k + m |
Timothy Chen | eb4b0d3 | 2020-12-28 12:32:53 -0800 | [diff] [blame] | 480 | |
| 481 | # construct a list of code tuples. |
| 482 | # Tuple corresponds to each bit position and shows which parity bit it participates in |
| 483 | # Only the data bits are shown, the parity bits are not. |
| 484 | codes = [] |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 485 | for pos in range(1, n + 1): |
Timothy Chen | eb4b0d3 | 2020-12-28 12:32:53 -0800 | [diff] [blame] | 486 | # this is a valid parity bit position or the final parity bit |
| 487 | if (is_pow2(pos) or pos == n): |
| 488 | continue |
| 489 | else: |
| 490 | code = () |
| 491 | for p in range(m): |
| 492 | |
| 493 | # this is the starting parity position |
| 494 | parity_pos = 2**p |
| 495 | |
| 496 | # back-track to the closest parity bit multiple and see if it is even or odd |
| 497 | # If even, we are in the skip phase, do not include |
| 498 | # If odd, we are in the include phase |
| 499 | parity_chk = int((pos - (pos % parity_pos)) / parity_pos) |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 500 | log.debug("At position {} parity value {}, {}".format( |
| 501 | pos, parity_pos, parity_chk)) |
Timothy Chen | eb4b0d3 | 2020-12-28 12:32:53 -0800 | [diff] [blame] | 502 | |
| 503 | # valid for inclusion or final parity bit that includes everything |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 504 | if is_odd(parity_chk) or p == m - 1: |
| 505 | code = code + (p, ) |
Timothy Chen | eb4b0d3 | 2020-12-28 12:32:53 -0800 | [diff] [blame] | 506 | log.info("add {} to tuple {}".format(p, code)) |
| 507 | |
| 508 | codes.append(code) |
| 509 | |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 510 | # final parity bit includes all ECC bits |
| 511 | for p in range(m - 1): |
| 512 | codes.append((m - 1, )) |
| 513 | |
Timothy Chen | eb4b0d3 | 2020-12-28 12:32:53 -0800 | [diff] [blame] | 514 | log.info("Hamming codes {}".format(codes)) |
| 515 | return codes |
| 516 | |
| 517 | |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 518 | def write_pkg_file(outdir, pkg_str): |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 519 | with open(outdir + "/" + "prim_secded_pkg.sv", "w") as f: |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 520 | outstr = '''{}// SECDED package generated by |
| 521 | // util/design/secded_gen.py from {} |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 522 | |
| 523 | package prim_secded_pkg; |
| 524 | {} |
| 525 | |
| 526 | endpackage |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 527 | '''.format(COPYRIGHT, SECDED_CFG_FILE, pkg_str) |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 528 | f.write(outstr) |
| 529 | |
| 530 | |
Greg Chadwick | c13cc28 | 2021-05-15 17:09:52 +0100 | [diff] [blame] | 531 | def bytes_to_c_type(num_bytes): |
| 532 | if num_bytes == 1: |
| 533 | return 'uint8_t' |
| 534 | elif num_bytes <= 2: |
| 535 | return 'uint16_t' |
| 536 | elif num_bytes <= 4: |
| 537 | return 'uint32_t' |
| 538 | elif num_bytes <= 8: |
| 539 | return 'uint64_t' |
| 540 | |
| 541 | return None |
| 542 | |
| 543 | |
| 544 | def write_c_files(n, k, m, codes, suffix, c_src_filename, c_h_filename): |
| 545 | in_bytes = math.ceil(k / 8) |
| 546 | out_bytes = math.ceil(m / 8) |
| 547 | |
| 548 | if (k > 64): |
| 549 | log.warning(f"Cannot generate C encoder for k = {k}." |
| 550 | " The tool has no support for k > 64 for C encoder " |
| 551 | "generation") |
| 552 | return |
| 553 | |
| 554 | in_type = bytes_to_c_type(in_bytes) |
| 555 | out_type = bytes_to_c_type(out_bytes) |
| 556 | |
| 557 | assert in_type |
| 558 | assert out_type |
| 559 | |
| 560 | with open(c_src_filename, "a") as f: |
| 561 | # Write out function prototype in src |
| 562 | f.write(f"\n{out_type} enc_secded_{n}_{k}{suffix}" |
| 563 | f"(const uint8_t bytes[{in_bytes}]) {{\n") |
| 564 | |
| 565 | # Form a single word from the incoming byte data |
| 566 | f.write(f"{in_type} word = ") |
| 567 | f.write(" | ".join( |
| 568 | [f"(({in_type})bytes[{i}] << {i*8})" for i in range(in_bytes)])) |
| 569 | f.write(";\n\n") |
| 570 | |
| 571 | # AND the word with the codes, calculating parity of each and combine |
| 572 | # into a single word of integrity bits |
| 573 | f.write("return ") |
| 574 | parity_bit_masks = enumerate(calc_bitmasks(k, m, codes, False)) |
| 575 | f.write(" | ".join( |
| 576 | [f"(calc_parity(word & 0x{mask:x}) << {par_bit})" for par_bit, |
| 577 | mask in parity_bit_masks])) |
| 578 | |
| 579 | f.write(";\n}\n") |
| 580 | |
| 581 | with open(c_h_filename, "a") as f: |
| 582 | # Write out function declaration in header |
| 583 | f.write(f"{out_type} enc_secded_{n}_{k}{suffix}" |
| 584 | f"(const uint8_t bytes[{in_bytes}]);\n") |
| 585 | |
| 586 | |
| 587 | def format_c_files(c_src_filename, c_h_filename): |
| 588 | try: |
| 589 | # Call clang-format to in-place format generated C code. If there are |
| 590 | # any issues log a warning. |
| 591 | result = subprocess.run(['clang-format', '-i', c_src_filename, |
| 592 | c_h_filename], stderr=subprocess.PIPE, |
| 593 | universal_newlines=True) |
| 594 | result.check_returncode() |
| 595 | except Exception as e: |
| 596 | stderr = '' |
| 597 | if result: |
| 598 | stderr = '\n' + result.stderr |
| 599 | |
| 600 | log.warning(f"Could not format generated C source: {e}{stderr}") |
| 601 | |
| 602 | |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 603 | def write_enc_dec_files(n, k, m, codes, suffix, outdir, codetype): |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 604 | enc_out = print_enc(n, k, m, codes) |
Timothy Chen | eb4b0d3 | 2020-12-28 12:32:53 -0800 | [diff] [blame] | 605 | |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 606 | module_name = "prim_secded%s_%d_%d" % (suffix, n, k) |
| 607 | |
| 608 | with open(outdir + "/" + module_name + "_enc.sv", "w") as f: |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 609 | outstr = '''{}// SECDED encoder generated by util/design/secded_gen.py |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 610 | |
| 611 | module {}_enc ( |
Philipp Wagner | 19044ed | 2021-05-10 17:23:13 +0100 | [diff] [blame] | 612 | input [{}:0] data_i, |
| 613 | output logic [{}:0] data_o |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 614 | ); |
| 615 | |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 616 | always_comb begin : p_encode |
| 617 | {} end |
| 618 | |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 619 | endmodule : {}_enc |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 620 | '''.format(COPYRIGHT, module_name, (k - 1), (n - 1), enc_out, module_name) |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 621 | f.write(outstr) |
| 622 | |
| 623 | dec_out = print_dec(n, k, m, codes, codetype) |
| 624 | |
| 625 | with open(outdir + "/" + module_name + "_dec.sv", "w") as f: |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 626 | outstr = '''{}// SECDED decoder generated by util/design/secded_gen.py |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 627 | |
| 628 | module {}_dec ( |
Philipp Wagner | 19044ed | 2021-05-10 17:23:13 +0100 | [diff] [blame] | 629 | input [{}:0] data_i, |
| 630 | output logic [{}:0] data_o, |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 631 | output logic [{}:0] syndrome_o, |
| 632 | output logic [1:0] err_o |
| 633 | ); |
| 634 | |
| 635 | {} |
| 636 | endmodule : {}_dec |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 637 | '''.format(COPYRIGHT, module_name, (n - 1), (k - 1), (m - 1), |
| 638 | dec_out, module_name) |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 639 | f.write(outstr) |
| 640 | |
| 641 | |
| 642 | def write_fpv_files(n, k, m, codes, suffix, outdir): |
| 643 | module_name = "prim_secded%s_%d_%d" % (suffix, n, k) |
| 644 | |
| 645 | with open(outdir + "/tb/" + module_name + "_fpv.sv", "w") as f: |
| 646 | outstr = '''{}// SECDED FPV testbench generated by util/design/secded_gen.py |
| 647 | |
| 648 | module {}_fpv ( |
| 649 | input clk_i, |
| 650 | input rst_ni, |
Philipp Wagner | 19044ed | 2021-05-10 17:23:13 +0100 | [diff] [blame] | 651 | input [{}:0] data_i, |
| 652 | output logic [{}:0] data_o, |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 653 | output logic [{}:0] syndrome_o, |
| 654 | output logic [1:0] err_o, |
| 655 | input [{}:0] error_inject_i |
| 656 | ); |
| 657 | |
| 658 | logic [{}:0] data_enc; |
| 659 | |
| 660 | {}_enc {}_enc ( |
Philipp Wagner | 19044ed | 2021-05-10 17:23:13 +0100 | [diff] [blame] | 661 | .data_i, |
| 662 | .data_o(data_enc) |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 663 | ); |
| 664 | |
| 665 | {}_dec {}_dec ( |
Philipp Wagner | 19044ed | 2021-05-10 17:23:13 +0100 | [diff] [blame] | 666 | .data_i(data_enc ^ error_inject_i), |
| 667 | .data_o, |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 668 | .syndrome_o, |
| 669 | .err_o |
| 670 | ); |
| 671 | |
| 672 | endmodule : {}_fpv |
| 673 | '''.format(COPYRIGHT, module_name, (k - 1), (k - 1), (m - 1), (n - 1), (n - 1), |
| 674 | module_name, module_name, module_name, module_name, module_name) |
| 675 | f.write(outstr) |
| 676 | |
| 677 | with open(outdir + "/vip/" + module_name + "_assert_fpv.sv", "w") as f: |
| 678 | outstr = '''{}// SECDED FPV assertion file generated by util/design/secded_gen.py |
| 679 | |
| 680 | module {}_assert_fpv ( |
| 681 | input clk_i, |
| 682 | input rst_ni, |
Philipp Wagner | 19044ed | 2021-05-10 17:23:13 +0100 | [diff] [blame] | 683 | input [{}:0] data_i, |
| 684 | input [{}:0] data_o, |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 685 | input [{}:0] syndrome_o, |
| 686 | input [1:0] err_o, |
| 687 | input [{}:0] error_inject_i |
| 688 | ); |
| 689 | |
| 690 | // Inject a maximum of two errors simultaneously. |
| 691 | `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) |
| 692 | // This bounds the input data state space to make sure the solver converges. |
Philipp Wagner | 19044ed | 2021-05-10 17:23:13 +0100 | [diff] [blame] | 693 | `ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i)) |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 694 | // Single bit error detection |
| 695 | `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) |
| 696 | `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) |
| 697 | // Double bit error detection |
| 698 | `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) |
| 699 | `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) |
| 700 | // Single bit error correction (implicitly tests the syndrome output) |
Cindy Chen | 2f3bba8 | 2021-05-24 16:35:41 -0700 | [diff] [blame] | 701 | `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 702 | // Basic syndrome check |
| 703 | `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) |
| 704 | `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) |
| 705 | |
| 706 | endmodule : {}_assert_fpv |
| 707 | '''.format(COPYRIGHT, module_name, (k - 1), (k - 1), (m - 1), (n - 1), |
| 708 | module_name) |
| 709 | f.write(outstr) |
| 710 | |
| 711 | with open(outdir + "/tb/" + module_name + "_bind_fpv.sv", "w") as f: |
| 712 | outstr = '''{}// SECDED FPV bind file generated by util/design/secded_gen.py |
| 713 | |
| 714 | module {}_bind_fpv; |
| 715 | |
| 716 | bind {}_fpv |
| 717 | {}_assert_fpv {}_assert_fpv ( |
| 718 | .clk_i, |
| 719 | .rst_ni, |
Philipp Wagner | 19044ed | 2021-05-10 17:23:13 +0100 | [diff] [blame] | 720 | .data_i, |
| 721 | .data_o, |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 722 | .syndrome_o, |
| 723 | .err_o, |
| 724 | .error_inject_i |
| 725 | ); |
| 726 | |
| 727 | endmodule : {}_bind_fpv |
| 728 | '''.format(COPYRIGHT, module_name, module_name, module_name, module_name, |
| 729 | module_name) |
| 730 | f.write(outstr) |
| 731 | |
| 732 | with open(outdir + "/" + module_name + "_fpv.core", "w") as f: |
| 733 | outstr = '''CAPI=2: |
| 734 | # Copyright lowRISC contributors. |
| 735 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 736 | # SPDX-License-Identifier: Apache-2.0 |
| 737 | name: "lowrisc:fpv:{}_fpv:0.1" |
| 738 | description: "SECDED FPV target" |
| 739 | filesets: |
| 740 | files_formal: |
| 741 | depend: |
| 742 | - lowrisc:prim:all |
| 743 | - lowrisc:prim:secded |
| 744 | files: |
| 745 | - vip/{}_assert_fpv.sv |
| 746 | - tb/{}_fpv.sv |
| 747 | - tb/{}_bind_fpv.sv |
| 748 | file_type: systemVerilogSource |
| 749 | |
| 750 | targets: |
| 751 | default: &default_target |
| 752 | # note, this setting is just used |
| 753 | # to generate a file list for jg |
| 754 | default_tool: icarus |
| 755 | filesets: |
| 756 | - files_formal |
| 757 | toplevel: |
| 758 | - {}_fpv |
| 759 | |
| 760 | formal: |
| 761 | <<: *default_target |
| 762 | |
| 763 | lint: |
| 764 | <<: *default_target |
| 765 | |
| 766 | '''.format(module_name, module_name, module_name, module_name, module_name) |
| 767 | f.write(outstr) |
Timothy Chen | eb4b0d3 | 2020-12-28 12:32:53 -0800 | [diff] [blame] | 768 | |
| 769 | |
| 770 | def main(): |
| 771 | parser = argparse.ArgumentParser( |
| 772 | prog="secded_gen", |
| 773 | description='''This tool generates Single Error Correction Double Error |
| 774 | Detection(SECDED) encoder and decoder modules in SystemVerilog. |
| 775 | ''') |
Michael Schaffner | 960e57d | 2021-01-25 15:13:12 -0800 | [diff] [blame] | 776 | parser.add_argument('--no_fpv', |
| 777 | action='store_true', |
| 778 | help='Do not generate FPV testbench.') |
| 779 | parser.add_argument('--outdir', |
| 780 | default='hw/ip/prim/rtl/', |
| 781 | help=''' |
| 782 | Output directory. The output file will be named |
| 783 | `prim_secded_<n>_<k>_enc/dec.sv` (default: %(default)s) |
| 784 | ''') |
| 785 | parser.add_argument('--fpv_outdir', |
| 786 | default='hw/ip/prim/fpv/', |
| 787 | help=''' |
| 788 | FPV output directory. The output files will have |
| 789 | the base name `prim_secded_<n>_<k>_*_fpv` (default: %(default)s) |
| 790 | ''') |
Greg Chadwick | c13cc28 | 2021-05-15 17:09:52 +0100 | [diff] [blame] | 791 | parser.add_argument('--c_outdir', |
| 792 | default='hw/ip/prim/dv/prim_secded', |
| 793 | help=''' |
| 794 | C output directory. The output files are named secded_enc.c and |
| 795 | secded_enc.h |
| 796 | ''') |
Timothy Chen | eb4b0d3 | 2020-12-28 12:32:53 -0800 | [diff] [blame] | 797 | parser.add_argument('--verbose', '-v', action='store_true', help='Verbose') |
| 798 | |
| 799 | args = parser.parse_args() |
| 800 | |
| 801 | if (args.verbose): |
| 802 | log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG) |
| 803 | else: |
| 804 | log.basicConfig(format="%(levelname)s: %(message)s") |
| 805 | |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 806 | with open(SECDED_CFG_FILE, 'r') as infile: |
| 807 | config = hjson.load(infile) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 808 | |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 809 | # Error checking |
| 810 | error = verify(config) |
| 811 | if (error): |
| 812 | exit(1) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 813 | |
Timothy Chen | 6c72d84 | 2021-03-05 16:53:19 -0800 | [diff] [blame] | 814 | # Generate outputs |
Rupert Swarbrick | d6bdc5e | 2021-04-19 11:08:08 +0100 | [diff] [blame] | 815 | generate(config, args) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 816 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 817 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 818 | if __name__ == "__main__": |
| 819 | main() |