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"""Top Module Generator |
| 6 | """ |
| 7 | import argparse |
| 8 | import logging as log |
| 9 | import sys |
Weicai Yang | a60ae7d | 2020-02-21 14:32:50 -0800 | [diff] [blame] | 10 | from collections import OrderedDict |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 11 | from io import StringIO |
| 12 | from pathlib import Path |
| 13 | |
| 14 | import hjson |
Eunchan Kim | cb28a17 | 2019-10-08 16:35:48 -0700 | [diff] [blame] | 15 | from mako import exceptions |
Weicai Yang | a60ae7d | 2020-02-21 14:32:50 -0800 | [diff] [blame] | 16 | from mako.template import Template |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 17 | |
| 18 | import tlgen |
Weicai Yang | a60ae7d | 2020-02-21 14:32:50 -0800 | [diff] [blame] | 19 | from reggen import gen_dv, gen_rtl, validate |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 20 | from topgen import get_hjsonobj_xbars, merge_top, search_ips, validate_top |
| 21 | |
| 22 | # Filter from IP list but adding generated hjson |
Cindy Chen | ecc70ea | 2020-04-08 14:17:48 -0700 | [diff] [blame] | 23 | filter_list = ['rv_plic', 'pinmux', 'alert_handler'] |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 24 | |
| 25 | # Common header for generated files |
| 26 | genhdr = '''// Copyright lowRISC contributors. |
| 27 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 28 | // SPDX-License-Identifier: Apache-2.0 |
| 29 | // |
| 30 | // ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// |
| 31 | // PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: |
| 32 | ''' |
| 33 | |
Eunchan Kim | c6a6a3b | 2019-09-12 14:27:43 -0700 | [diff] [blame] | 34 | |
Eunchan Kim | 436d224 | 2019-10-29 17:25:51 -0700 | [diff] [blame] | 35 | def generate_top(top, tpl_filename): |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 36 | top_rtl_tpl = Template(filename=tpl_filename) |
| 37 | |
Eunchan Kim | cb28a17 | 2019-10-08 16:35:48 -0700 | [diff] [blame] | 38 | try: |
| 39 | out_rtl = top_rtl_tpl.render(top=top) |
Eunchan Kim | 6599ba9 | 2020-04-13 15:27:16 -0700 | [diff] [blame^] | 40 | except: # noqa: E722 |
Eunchan Kim | cb28a17 | 2019-10-08 16:35:48 -0700 | [diff] [blame] | 41 | log.error(exceptions.text_error_template().render()) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 42 | return out_rtl |
| 43 | |
| 44 | |
| 45 | def generate_xbars(top, out_path): |
Michael Schaffner | 7f13496 | 2019-11-03 12:44:50 -0800 | [diff] [blame] | 46 | gencmd = ("// util/topgen.py -t hw/top_earlgrey/data/top_earlgrey.hjson " |
Eunchan Kim | 6df9a1f | 2019-10-09 14:46:05 -0700 | [diff] [blame] | 47 | "-o hw/top_earlgrey/\n\n") |
| 48 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 49 | for obj in top["xbar"]: |
Eunchan Kim | c745294 | 2019-12-19 17:04:37 -0800 | [diff] [blame] | 50 | xbar_path = out_path / 'ip/xbar_{}/data/autogen'.format(obj["name"]) |
| 51 | xbar_path.mkdir(parents=True, exist_ok=True) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 52 | xbar = tlgen.validate(obj) |
Weicai Yang | a60ae7d | 2020-02-21 14:32:50 -0800 | [diff] [blame] | 53 | xbar.ip_path = 'hw/top_' + top["name"] + '/ip/{dut}' |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 54 | |
Eunchan Kim | 6df9a1f | 2019-10-09 14:46:05 -0700 | [diff] [blame] | 55 | # Generate output of crossbar with complete fields |
| 56 | xbar_hjson_path = xbar_path / "xbar_{}.gen.hjson".format(xbar.name) |
| 57 | xbar_hjson_path.write_text(genhdr + gencmd + |
Eunchan Kim | 2af98ed | 2019-10-09 15:33:27 -0700 | [diff] [blame] | 58 | hjson.dumps(obj, for_json=True)) |
Eunchan Kim | 6df9a1f | 2019-10-09 14:46:05 -0700 | [diff] [blame] | 59 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 60 | if not tlgen.elaborate(xbar): |
| 61 | log.error("Elaboration failed." + repr(xbar)) |
| 62 | |
Eunchan Kim | cb28a17 | 2019-10-08 16:35:48 -0700 | [diff] [blame] | 63 | try: |
Eunchan Kim | c745294 | 2019-12-19 17:04:37 -0800 | [diff] [blame] | 64 | out_rtl, out_pkg, out_core = tlgen.generate(xbar) |
Eunchan Kim | 6599ba9 | 2020-04-13 15:27:16 -0700 | [diff] [blame^] | 65 | except: # noqa: E722 |
Eunchan Kim | cb28a17 | 2019-10-08 16:35:48 -0700 | [diff] [blame] | 66 | log.error(exceptions.text_error_template().render()) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 67 | |
Eunchan Kim | c745294 | 2019-12-19 17:04:37 -0800 | [diff] [blame] | 68 | rtl_path = out_path / 'ip/xbar_{}/rtl/autogen'.format(obj["name"]) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 69 | rtl_path.mkdir(parents=True, exist_ok=True) |
Eunchan Kim | c745294 | 2019-12-19 17:04:37 -0800 | [diff] [blame] | 70 | dv_path = out_path / 'ip/xbar_{}/dv/autogen'.format(obj["name"]) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 71 | dv_path.mkdir(parents=True, exist_ok=True) |
| 72 | |
| 73 | rtl_filename = "xbar_%s.sv" % (xbar.name) |
| 74 | rtl_filepath = rtl_path / rtl_filename |
| 75 | with rtl_filepath.open(mode='w', encoding='UTF-8') as fout: |
| 76 | fout.write(out_rtl) |
| 77 | |
| 78 | pkg_filename = "tl_%s_pkg.sv" % (xbar.name) |
| 79 | pkg_filepath = rtl_path / pkg_filename |
| 80 | with pkg_filepath.open(mode='w', encoding='UTF-8') as fout: |
| 81 | fout.write(out_pkg) |
| 82 | |
Eunchan Kim | c745294 | 2019-12-19 17:04:37 -0800 | [diff] [blame] | 83 | core_filename = "xbar_%s.core" % (xbar.name) |
| 84 | core_filepath = rtl_path / core_filename |
| 85 | with core_filepath.open(mode='w', encoding='UTF-8') as fout: |
| 86 | fout.write(out_core) |
| 87 | |
Weicai Yang | e4315d2 | 2020-01-09 10:37:42 -0800 | [diff] [blame] | 88 | # generate testbench for xbar |
| 89 | tlgen.generate_tb(xbar, dv_path) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 90 | |
Eunchan Kim | 6a4b49e | 2020-02-18 10:33:39 -0800 | [diff] [blame] | 91 | |
Michael Schaffner | 666dde1 | 2019-10-25 11:57:54 -0700 | [diff] [blame] | 92 | def generate_alert_handler(top, out_path): |
| 93 | # default values |
Eunchan Kim | c745294 | 2019-12-19 17:04:37 -0800 | [diff] [blame] | 94 | esc_cnt_dw = 32 |
| 95 | accu_cnt_dw = 16 |
| 96 | lfsr_seed = 2**31 - 1 |
| 97 | async_on = "'0" |
Michael Schaffner | 666dde1 | 2019-10-25 11:57:54 -0700 | [diff] [blame] | 98 | # leave this constant |
Eunchan Kim | c745294 | 2019-12-19 17:04:37 -0800 | [diff] [blame] | 99 | n_classes = 4 |
Michael Schaffner | 666dde1 | 2019-10-25 11:57:54 -0700 | [diff] [blame] | 100 | |
| 101 | # check if there are any params to be passed through reggen and placed into |
| 102 | # the generated package |
| 103 | ip_list_in_top = [x["name"].lower() for x in top["module"]] |
| 104 | ah_idx = ip_list_in_top.index("alert_handler") |
| 105 | if 'localparam' in top['module'][ah_idx]: |
| 106 | if 'EscCntDw' in top['module'][ah_idx]['localparam']: |
| 107 | esc_cnt_dw = int(top['module'][ah_idx]['localparam']['EscCntDw']) |
| 108 | if 'AccuCntDw' in top['module'][ah_idx]['localparam']: |
| 109 | accu_cnt_dw = int(top['module'][ah_idx]['localparam']['AccuCntDw']) |
| 110 | if 'LfsrSeed' in top['module'][ah_idx]['localparam']: |
| 111 | lfsr_seed = int(top['module'][ah_idx]['localparam']['LfsrSeed'], 0) |
| 112 | |
| 113 | if esc_cnt_dw < 1: |
| 114 | log.error("EscCntDw must be larger than 0") |
| 115 | if accu_cnt_dw < 1: |
| 116 | log.error("AccuCntDw must be larger than 0") |
| 117 | if (lfsr_seed & 0xFFFFFFFF) == 0 or lfsr_seed > 2**32: |
| 118 | log.error("LFSR seed out of range or zero") |
| 119 | |
| 120 | # Count number of interrupts |
| 121 | n_alerts = sum([x["width"] if "width" in x else 1 for x in top["alert"]]) |
| 122 | |
| 123 | if n_alerts < 1: |
| 124 | # set number of alerts to 1 such that the config is still valid |
| 125 | # that input will be tied off |
| 126 | n_alerts = 1 |
Eunchan Kim | c745294 | 2019-12-19 17:04:37 -0800 | [diff] [blame] | 127 | log.warning("no alerts are defined in the system") |
Michael Schaffner | 666dde1 | 2019-10-25 11:57:54 -0700 | [diff] [blame] | 128 | else: |
| 129 | async_on = "" |
| 130 | for alert in top['alert']: |
| 131 | async_on = str(alert['async']) + async_on |
| 132 | async_on = ("%d'b" % n_alerts) + async_on |
| 133 | |
| 134 | log.info("alert handler parameterization:") |
| 135 | log.info("NAlerts = %d" % n_alerts) |
| 136 | log.info("EscCntDw = %d" % esc_cnt_dw) |
| 137 | log.info("AccuCntDw = %d" % accu_cnt_dw) |
| 138 | log.info("LfsrSeed = %d" % lfsr_seed) |
| 139 | log.info("AsyncOn = %s" % async_on) |
| 140 | |
Michael Schaffner | 666dde1 | 2019-10-25 11:57:54 -0700 | [diff] [blame] | 141 | # Define target path |
| 142 | rtl_path = out_path / 'ip/alert_handler/rtl/autogen' |
| 143 | rtl_path.mkdir(parents=True, exist_ok=True) |
| 144 | doc_path = out_path / 'ip/alert_handler/data/autogen' |
| 145 | doc_path.mkdir(parents=True, exist_ok=True) |
| 146 | |
| 147 | # Generating IP top module script is not generalized yet. |
| 148 | # So, topgen reads template files from alert_handler directory directly. |
| 149 | tpl_path = out_path / '../ip/alert_handler/data' |
| 150 | hjson_tpl_path = tpl_path / 'alert_handler.hjson.tpl' |
| 151 | |
| 152 | # Generate Register Package and RTLs |
| 153 | out = StringIO() |
| 154 | with hjson_tpl_path.open(mode='r', encoding='UTF-8') as fin: |
| 155 | hjson_tpl = Template(fin.read()) |
| 156 | try: |
| 157 | out = hjson_tpl.render(n_alerts=n_alerts, |
| 158 | esc_cnt_dw=esc_cnt_dw, |
| 159 | accu_cnt_dw=accu_cnt_dw, |
| 160 | lfsr_seed=lfsr_seed, |
| 161 | async_on=async_on, |
| 162 | n_classes=n_classes) |
Eunchan Kim | 6599ba9 | 2020-04-13 15:27:16 -0700 | [diff] [blame^] | 163 | except: # noqa: E722 |
Michael Schaffner | 666dde1 | 2019-10-25 11:57:54 -0700 | [diff] [blame] | 164 | log.error(exceptions.text_error_template().render()) |
| 165 | log.info("alert_handler hjson: %s" % out) |
| 166 | |
| 167 | if out == "": |
| 168 | log.error("Cannot generate alert_handler config file") |
| 169 | return |
| 170 | |
| 171 | hjson_gen_path = doc_path / "alert_handler.hjson" |
| 172 | gencmd = ( |
| 173 | "// util/topgen.py -t hw/top_earlgrey/doc/top_earlgrey.hjson --alert-handler-only " |
| 174 | "-o hw/top_earlgrey/\n\n") |
| 175 | with hjson_gen_path.open(mode='w', encoding='UTF-8') as fout: |
| 176 | fout.write(genhdr + gencmd + out) |
| 177 | |
| 178 | # Generate register RTLs (currently using shell execute) |
| 179 | # TODO: More secure way to gneerate RTL |
| 180 | hjson_obj = hjson.loads(out, |
| 181 | use_decimal=True, |
| 182 | object_pairs_hook=validate.checking_dict) |
| 183 | validate.validate(hjson_obj) |
| 184 | gen_rtl.gen_rtl(hjson_obj, str(rtl_path)) |
| 185 | |
| 186 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 187 | def generate_plic(top, out_path): |
| 188 | # Count number of interrupts |
Eunchan Kim | 88a8615 | 2020-04-13 16:12:08 -0700 | [diff] [blame] | 189 | # Interrupt source 0 is tied to 0 to conform RISC-V PLIC spec. |
| 190 | # So, total number of interrupts are the number of entries in the list + 1 |
| 191 | src = sum([x["width"] if "width" in x else 1 |
| 192 | for x in top["interrupt"]]) + 1 |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 193 | |
| 194 | # Target and priority: Currently fixed |
| 195 | target = int(top["num_cores"], 0) if "num_cores" in top else 1 |
| 196 | prio = 3 |
| 197 | |
| 198 | # Define target path |
| 199 | # rtl: rv_plic.sv & rv_plic_reg_pkg.sv & rv_plic_reg_top.sv |
Eunchan Kim | 436d224 | 2019-10-29 17:25:51 -0700 | [diff] [blame] | 200 | # data: rv_plic.hjson |
Eunchan Kim | a7fac5b | 2019-10-04 11:56:25 -0700 | [diff] [blame] | 201 | rtl_path = out_path / 'ip/rv_plic/rtl/autogen' |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 202 | rtl_path.mkdir(parents=True, exist_ok=True) |
Michael Schaffner | 666dde1 | 2019-10-25 11:57:54 -0700 | [diff] [blame] | 203 | doc_path = out_path / 'ip/rv_plic/data/autogen' |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 204 | doc_path.mkdir(parents=True, exist_ok=True) |
Michael Schaffner | 7f13496 | 2019-11-03 12:44:50 -0800 | [diff] [blame] | 205 | hjson_path = out_path / 'ip/rv_plic/data/autogen' |
| 206 | hjson_path.mkdir(parents=True, exist_ok=True) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 207 | |
| 208 | # Generating IP top module script is not generalized yet. |
| 209 | # So, topgen reads template files from rv_plic directory directly. |
| 210 | # Next, if the ip top gen tool is placed in util/ we can import the library. |
Tobias Wölfel | 4c5fbec | 2019-10-23 12:43:17 +0200 | [diff] [blame] | 211 | tpl_path = out_path / '../ip/rv_plic/data' |
Michael Schaffner | c703936 | 2019-10-22 16:16:06 -0700 | [diff] [blame] | 212 | hjson_tpl_path = tpl_path / 'rv_plic.hjson.tpl' |
| 213 | rtl_tpl_path = tpl_path / 'rv_plic.sv.tpl' |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 214 | |
| 215 | # Generate Register Package and RTLs |
| 216 | out = StringIO() |
| 217 | with hjson_tpl_path.open(mode='r', encoding='UTF-8') as fin: |
| 218 | hjson_tpl = Template(fin.read()) |
Eunchan Kim | cb28a17 | 2019-10-08 16:35:48 -0700 | [diff] [blame] | 219 | try: |
| 220 | out = hjson_tpl.render(src=src, target=target, prio=prio) |
Eunchan Kim | 6599ba9 | 2020-04-13 15:27:16 -0700 | [diff] [blame^] | 221 | except: # noqa: E722 |
Eunchan Kim | cb28a17 | 2019-10-08 16:35:48 -0700 | [diff] [blame] | 222 | log.error(exceptions.text_error_template().render()) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 223 | log.info("RV_PLIC hjson: %s" % out) |
| 224 | |
| 225 | if out == "": |
| 226 | log.error("Cannot generate interrupt controller config file") |
| 227 | return |
| 228 | |
Michael Schaffner | 7f13496 | 2019-11-03 12:44:50 -0800 | [diff] [blame] | 229 | hjson_gen_path = hjson_path / "rv_plic.hjson" |
Eunchan Kim | c6a6a3b | 2019-09-12 14:27:43 -0700 | [diff] [blame] | 230 | gencmd = ( |
Michael Schaffner | 7f13496 | 2019-11-03 12:44:50 -0800 | [diff] [blame] | 231 | "// util/topgen.py -t hw/top_earlgrey/data/top_earlgrey.hjson --plic-only " |
Eunchan Kim | c6a6a3b | 2019-09-12 14:27:43 -0700 | [diff] [blame] | 232 | "-o hw/top_earlgrey/\n\n") |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 233 | with hjson_gen_path.open(mode='w', encoding='UTF-8') as fout: |
| 234 | fout.write(genhdr + gencmd + out) |
| 235 | |
| 236 | # Generate register RTLs (currently using shell execute) |
| 237 | # TODO: More secure way to gneerate RTL |
| 238 | hjson_obj = hjson.loads(out, |
| 239 | use_decimal=True, |
Eunchan Kim | 6a4b49e | 2020-02-18 10:33:39 -0800 | [diff] [blame] | 240 | object_pairs_hook=OrderedDict) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 241 | validate.validate(hjson_obj) |
| 242 | gen_rtl.gen_rtl(hjson_obj, str(rtl_path)) |
| 243 | |
| 244 | # Generate RV_PLIC Top Module |
| 245 | with rtl_tpl_path.open(mode='r', encoding='UTF-8') as fin: |
| 246 | rtl_tpl = Template(fin.read()) |
Eunchan Kim | cb28a17 | 2019-10-08 16:35:48 -0700 | [diff] [blame] | 247 | try: |
| 248 | out = rtl_tpl.render(src=src, target=target, prio=prio) |
Eunchan Kim | 6599ba9 | 2020-04-13 15:27:16 -0700 | [diff] [blame^] | 249 | except: # noqa: E722 |
Eunchan Kim | cb28a17 | 2019-10-08 16:35:48 -0700 | [diff] [blame] | 250 | log.error(exceptions.text_error_template().render()) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 251 | log.info("RV_PLIC RTL: %s" % out) |
| 252 | |
| 253 | if out == "": |
| 254 | log.error("Cannot generate interrupt controller RTL") |
| 255 | return |
| 256 | |
| 257 | rtl_gen_path = rtl_path / "rv_plic.sv" |
| 258 | with rtl_gen_path.open(mode='w', encoding='UTF-8') as fout: |
| 259 | fout.write(genhdr + gencmd + out) |
| 260 | |
| 261 | |
Eunchan Kim | 436d224 | 2019-10-29 17:25:51 -0700 | [diff] [blame] | 262 | def generate_pinmux(top, out_path): |
| 263 | # MIO Pads |
| 264 | num_mio = top["pinmux"]["num_mio"] |
| 265 | if num_mio <= 0: |
| 266 | log.warning( |
| 267 | "No PINMUX is generated. The top %s has no multiplexed IO ports." % |
| 268 | top["name"]) |
| 269 | return |
| 270 | |
| 271 | # Total inputs/outputs |
| 272 | num_inputs = sum( |
| 273 | [x["width"] if "width" in x else 1 for x in top["pinmux"]["inputs"]]) |
| 274 | num_outputs = sum( |
| 275 | [x["width"] if "width" in x else 1 for x in top["pinmux"]["outputs"]]) |
| 276 | num_inouts = sum( |
| 277 | [x["width"] if "width" in x else 1 for x in top["pinmux"]["inouts"]]) |
| 278 | |
| 279 | n_periph_in = num_inouts + num_inputs |
| 280 | n_periph_out = num_inouts + num_outputs |
| 281 | |
| 282 | # Target path |
| 283 | # rtl: pinmux_reg_pkg.sv & pinmux_reg_top.sv |
| 284 | # data: pinmux.hjson |
| 285 | rtl_path = out_path / 'ip/pinmux/rtl/autogen' |
| 286 | rtl_path.mkdir(parents=True, exist_ok=True) |
| 287 | data_path = out_path / 'ip/pinmux/data/autogen' |
| 288 | data_path.mkdir(parents=True, exist_ok=True) |
| 289 | |
| 290 | # Template path |
| 291 | tpl_path = out_path / '../ip/pinmux/data/pinmux.hjson.tpl' |
| 292 | |
| 293 | # Generate register package and RTLs |
Michael Schaffner | 7f13496 | 2019-11-03 12:44:50 -0800 | [diff] [blame] | 294 | gencmd = ("// util/topgen.py -t hw/top_earlgrey/data/top_earlgrey.hjson " |
Eunchan Kim | 436d224 | 2019-10-29 17:25:51 -0700 | [diff] [blame] | 295 | "-o hw/top_earlgrey/\n\n") |
| 296 | |
| 297 | hjson_gen_path = data_path / "pinmux.hjson" |
| 298 | |
| 299 | out = StringIO() |
| 300 | with tpl_path.open(mode='r', encoding='UTF-8') as fin: |
| 301 | hjson_tpl = Template(fin.read()) |
| 302 | try: |
| 303 | out = hjson_tpl.render(n_periph_in=n_periph_in, |
| 304 | n_periph_out=n_periph_out, |
| 305 | n_mio_pads=num_mio) |
Eunchan Kim | 6599ba9 | 2020-04-13 15:27:16 -0700 | [diff] [blame^] | 306 | except: # noqa: E722 |
Eunchan Kim | 436d224 | 2019-10-29 17:25:51 -0700 | [diff] [blame] | 307 | log.error(exceptions.text_error_template().render()) |
| 308 | log.info("PINMUX HJSON: %s" % out) |
| 309 | |
| 310 | if out == "": |
| 311 | log.error("Cannot generate pinmux HJSON") |
| 312 | return |
| 313 | |
| 314 | with hjson_gen_path.open(mode='w', encoding='UTF-8') as fout: |
| 315 | fout.write(genhdr + gencmd + out) |
| 316 | |
| 317 | hjson_obj = hjson.loads(out, |
| 318 | use_decimal=True, |
| 319 | object_pairs_hook=validate.checking_dict) |
| 320 | validate.validate(hjson_obj) |
| 321 | gen_rtl.gen_rtl(hjson_obj, str(rtl_path)) |
| 322 | |
| 323 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 324 | def generate_top_ral(top, ip_objs, out_path): |
| 325 | # construct top ral block |
| 326 | top_block = gen_rtl.Block() |
| 327 | top_block.name = "chip" |
| 328 | top_block.base_addr = 0 |
| 329 | top_block.width = int(top["datawidth"]) |
| 330 | |
| 331 | # add blocks |
| 332 | for ip_obj in ip_objs: |
| 333 | top_block.blocks.append(gen_rtl.json_to_reg(ip_obj)) |
| 334 | |
| 335 | # add memories |
| 336 | if "memory" in top.keys(): |
| 337 | for item in list(top["memory"]): |
| 338 | mem = gen_rtl.Window() |
| 339 | mem.name = item["name"] |
| 340 | mem.base_addr = int(item["base_addr"], 0) |
| 341 | mem.limit_addr = int(item["base_addr"], 0) + int(item["size"], 0) |
| 342 | # TODO: need to add mem access info for memories in topcfg |
| 343 | mem.dvrights = "RW" |
| 344 | mem.n_bits = top_block.width |
| 345 | top_block.wins.append(mem) |
| 346 | |
| 347 | # get sub-block base addresses from top cfg |
| 348 | for block in top_block.blocks: |
| 349 | for module in top["module"]: |
| 350 | if block.name == module["name"]: |
| 351 | block.base_addr = module["base_addr"] |
| 352 | break |
Srikrishna Iyer | 1c0171a | 2019-10-29 11:59:46 -0700 | [diff] [blame] | 353 | |
| 354 | top_block.blocks.sort(key=lambda block: block.base_addr) |
| 355 | top_block.wins.sort(key=lambda win: win.base_addr) |
| 356 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 357 | # generate the top ral model with template |
| 358 | gen_dv.gen_ral(top_block, str(out_path)) |
| 359 | |
| 360 | |
| 361 | def main(): |
| 362 | parser = argparse.ArgumentParser(prog="topgen") |
Eunchan Kim | c6a6a3b | 2019-09-12 14:27:43 -0700 | [diff] [blame] | 363 | parser.add_argument('--topcfg', |
| 364 | '-t', |
| 365 | required=True, |
| 366 | help="`top_{name}.hjson` file.") |
Eunchan Kim | 632c6f7 | 2019-09-30 11:11:51 -0700 | [diff] [blame] | 367 | parser.add_argument( |
| 368 | '--tpl', |
| 369 | '-c', |
| 370 | help= |
Michael Schaffner | c703936 | 2019-10-22 16:16:06 -0700 | [diff] [blame] | 371 | "The directory having top_{name}_core.sv.tpl and top_{name}.tpl.sv.") |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 372 | parser.add_argument( |
| 373 | '--outdir', |
| 374 | '-o', |
| 375 | help='''Target TOP directory. |
| 376 | Module is created under rtl/. (default: dir(topcfg)/..) |
Eunchan Kim | 6599ba9 | 2020-04-13 15:27:16 -0700 | [diff] [blame^] | 377 | ''') # yapf: disable |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 378 | parser.add_argument('--verbose', '-v', action='store_true', help="Verbose") |
| 379 | |
| 380 | # Generator options: 'no' series. cannot combined with 'only' series |
| 381 | parser.add_argument( |
| 382 | '--no-top', |
| 383 | action='store_true', |
| 384 | help="If defined, topgen doesn't generate top_{name} RTLs.") |
| 385 | parser.add_argument( |
| 386 | '--no-xbar', |
| 387 | action='store_true', |
| 388 | help="If defined, topgen doesn't generate crossbar RTLs.") |
| 389 | parser.add_argument( |
| 390 | '--no-plic', |
| 391 | action='store_true', |
| 392 | help="If defined, topgen doesn't generate the interrup controller RTLs." |
| 393 | ) |
| 394 | parser.add_argument( |
| 395 | '--no-gen-hjson', |
| 396 | action='store_true', |
| 397 | help='''If defined, the tool assumes topcfg as a generated hjson. |
| 398 | So it bypasses the validation step and doesn't read ip and |
| 399 | xbar configurations |
| 400 | ''') |
| 401 | |
| 402 | # Generator options: 'only' series. cannot combined with 'no' series |
| 403 | parser.add_argument( |
| 404 | '--top-only', |
| 405 | action='store_true', |
Eunchan Kim | 6599ba9 | 2020-04-13 15:27:16 -0700 | [diff] [blame^] | 406 | help="If defined, the tool generates top RTL only") # yapf:disable |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 407 | parser.add_argument( |
| 408 | '--xbar-only', |
| 409 | action='store_true', |
| 410 | help="If defined, the tool generates crossbar RTLs only") |
| 411 | parser.add_argument( |
| 412 | '--plic-only', |
| 413 | action='store_true', |
Philipp Wagner | 14a3fee | 2019-11-21 10:07:02 +0000 | [diff] [blame] | 414 | help="If defined, the tool generates RV_PLIC RTL and Hjson only") |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 415 | parser.add_argument( |
Michael Schaffner | 666dde1 | 2019-10-25 11:57:54 -0700 | [diff] [blame] | 416 | '--alert-handler-only', |
| 417 | action='store_true', |
| 418 | help="If defined, the tool generates alert handler hjson only") |
| 419 | parser.add_argument( |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 420 | '--hjson-only', |
| 421 | action='store_true', |
Philipp Wagner | 14a3fee | 2019-11-21 10:07:02 +0000 | [diff] [blame] | 422 | help="If defined, the tool generates complete Hjson only") |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 423 | # Generator options: generate dv ral model |
| 424 | parser.add_argument( |
| 425 | '--top_ral', |
| 426 | '-r', |
| 427 | default=False, |
| 428 | action='store_true', |
| 429 | help="If set, the tool generates top level RAL model for DV") |
| 430 | |
| 431 | args = parser.parse_args() |
| 432 | |
| 433 | # check combinations |
| 434 | if args.top_ral: |
| 435 | args.hjson_only = True |
| 436 | args.no_top = True |
| 437 | |
| 438 | if args.hjson_only: |
| 439 | args.no_gen_hjson = False |
| 440 | |
| 441 | if (args.no_top or args.no_xbar or |
| 442 | args.no_plic) and (args.top_only or args.xbar_only or |
Michael Schaffner | 666dde1 | 2019-10-25 11:57:54 -0700 | [diff] [blame] | 443 | args.plic_only or args.alert_handler_only): |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 444 | log.error( |
| 445 | "'no' series options cannot be used with 'only' series options") |
| 446 | raise SystemExit(sys.exc_info()[1]) |
| 447 | |
Eunchan Kim | c745294 | 2019-12-19 17:04:37 -0800 | [diff] [blame] | 448 | if not (args.hjson_only or args.plic_only or args.alert_handler_only or |
| 449 | args.tpl): |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 450 | log.error( |
| 451 | "Template file can be omitted only if '--hjson-only' is true") |
| 452 | raise SystemExit(sys.exc_info()[1]) |
| 453 | |
| 454 | if args.verbose: |
| 455 | log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG) |
| 456 | else: |
| 457 | log.basicConfig(format="%(levelname)s: %(message)s") |
| 458 | |
| 459 | if not args.outdir: |
| 460 | outdir = Path(args.topcfg).parent / ".." |
| 461 | log.info("TOP directory not given. Use %s", (outdir)) |
| 462 | elif not Path(args.outdir).is_dir(): |
| 463 | log.error("'--outdir' should point to writable directory") |
| 464 | raise SystemExit(sys.exc_info()[1]) |
| 465 | else: |
| 466 | outdir = Path(args.outdir) |
| 467 | |
| 468 | out_path = Path(outdir) |
| 469 | |
| 470 | if not args.no_gen_hjson or args.hjson_only: |
| 471 | # load top configuration |
| 472 | try: |
| 473 | with open(args.topcfg, 'r') as ftop: |
Eunchan Kim | 6a4b49e | 2020-02-18 10:33:39 -0800 | [diff] [blame] | 474 | topcfg = hjson.load(ftop, |
| 475 | use_decimal=True, |
| 476 | object_pairs_hook=OrderedDict) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 477 | except ValueError: |
| 478 | raise SystemExit(sys.exc_info()[1]) |
| 479 | |
| 480 | # Sweep the IP directory and gather the config files |
| 481 | ip_dir = Path(__file__).parents[1] / 'hw/ip' |
| 482 | ips = search_ips(ip_dir) |
| 483 | |
| 484 | # exclude rv_plic (to use top_earlgrey one) and |
| 485 | ips = [x for x in ips if not x.parents[1].name in filter_list] |
| 486 | |
| 487 | # It may require two passes to check if the module is needed. |
| 488 | # TODO: first run of topgen will fail due to the absent of rv_plic. |
| 489 | # It needs to run up to amend_interrupt in merge_top function |
| 490 | # then creates rv_plic.hjson then run xbar generation. |
| 491 | hjson_dir = Path(args.topcfg).parent |
Michael Schaffner | 7f13496 | 2019-11-03 12:44:50 -0800 | [diff] [blame] | 492 | rv_plic_hjson = hjson_dir.parent / 'ip/rv_plic/data/autogen/rv_plic.hjson' |
Eunchan Kim | a7fac5b | 2019-10-04 11:56:25 -0700 | [diff] [blame] | 493 | ips.append(rv_plic_hjson) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 494 | |
Eunchan Kim | 436d224 | 2019-10-29 17:25:51 -0700 | [diff] [blame] | 495 | pinmux_hjson = hjson_dir.parent / 'ip/pinmux/data/autogen/pinmux.hjson' |
| 496 | ips.append(pinmux_hjson) |
Eunchan Kim | 632c6f7 | 2019-09-30 11:11:51 -0700 | [diff] [blame] | 497 | |
Cindy Chen | ecc70ea | 2020-04-08 14:17:48 -0700 | [diff] [blame] | 498 | alert_handler_hjson = hjson_dir.parent / 'ip/alert_handler/data/autogen/alert_handler.hjson' |
| 499 | ips.append(alert_handler_hjson) |
| 500 | |
Philipp Wagner | 14a3fee | 2019-11-21 10:07:02 +0000 | [diff] [blame] | 501 | # load Hjson and pass validate from reggen |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 502 | try: |
| 503 | ip_objs = [] |
| 504 | for x in ips: |
| 505 | # Skip if it is not in the module list |
Eunchan Kim | 6599ba9 | 2020-04-13 15:27:16 -0700 | [diff] [blame^] | 506 | if x.stem not in [ip["type"] for ip in topcfg["module"]]: |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 507 | log.info( |
| 508 | "Skip module %s as it isn't in the top module list" % |
| 509 | x.stem) |
| 510 | continue |
| 511 | |
Eunchan Kim | c6a6a3b | 2019-09-12 14:27:43 -0700 | [diff] [blame] | 512 | obj = hjson.load(x.open('r'), |
| 513 | use_decimal=True, |
Eunchan Kim | 6a4b49e | 2020-02-18 10:33:39 -0800 | [diff] [blame] | 514 | object_pairs_hook=OrderedDict) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 515 | if validate.validate(obj) != 0: |
| 516 | log.info("Parsing IP %s configuration failed. Skip" % x) |
| 517 | continue |
| 518 | ip_objs.append(obj) |
| 519 | |
| 520 | except ValueError: |
| 521 | raise SystemExit(sys.exc_info()[1]) |
| 522 | |
| 523 | # Read the crossbars under the top directory |
| 524 | xbar_objs = get_hjsonobj_xbars(hjson_dir) |
| 525 | |
| 526 | log.info("Detected crossbars: %s" % |
| 527 | (", ".join([x["name"] for x in xbar_objs]))) |
| 528 | |
Timothy Chen | 3193b00 | 2019-10-04 16:56:05 -0700 | [diff] [blame] | 529 | topcfg, error = validate_top(topcfg, ip_objs, xbar_objs) |
Eunchan Kim | 632c6f7 | 2019-09-30 11:11:51 -0700 | [diff] [blame] | 530 | if error != 0: |
| 531 | raise SystemExit("Error occured while validating top.hjson") |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 532 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 533 | completecfg = merge_top(topcfg, ip_objs, xbar_objs) |
| 534 | |
Eunchan Kim | 1a95dd9 | 2019-10-11 11:18:13 -0700 | [diff] [blame] | 535 | genhjson_path = hjson_dir / ("autogen/top_%s.gen.hjson" % |
| 536 | completecfg["name"]) |
Eunchan Kim | c6a6a3b | 2019-09-12 14:27:43 -0700 | [diff] [blame] | 537 | gencmd = ( |
Michael Schaffner | 7f13496 | 2019-11-03 12:44:50 -0800 | [diff] [blame] | 538 | "// util/topgen.py -t hw/top_earlgrey/data/top_earlgrey.hjson --hjson-only " |
Eunchan Kim | c6a6a3b | 2019-09-12 14:27:43 -0700 | [diff] [blame] | 539 | "-o hw/top_earlgrey/\n") |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 540 | |
| 541 | if args.top_ral: |
| 542 | generate_top_ral(completecfg, ip_objs, out_path) |
| 543 | else: |
| 544 | genhjson_path.write_text(genhdr + gencmd + |
| 545 | hjson.dumps(completecfg, for_json=True)) |
| 546 | |
| 547 | if args.hjson_only: |
| 548 | log.info("hjson is generated. Exiting...") |
| 549 | sys.exit() |
| 550 | |
| 551 | if args.no_gen_hjson: |
| 552 | # load top.complete configuration |
| 553 | try: |
| 554 | with open(args.topcfg, 'r') as ftop: |
Eunchan Kim | 6a4b49e | 2020-02-18 10:33:39 -0800 | [diff] [blame] | 555 | completecfg = hjson.load(ftop, |
| 556 | use_decimal=True, |
| 557 | object_pairs_hook=OrderedDict) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 558 | except ValueError: |
| 559 | raise SystemExit(sys.exc_info()[1]) |
| 560 | |
| 561 | # Generate PLIC |
Eunchan Kim | 6599ba9 | 2020-04-13 15:27:16 -0700 | [diff] [blame^] | 562 | if not args.no_plic and \ |
Michael Schaffner | 666dde1 | 2019-10-25 11:57:54 -0700 | [diff] [blame] | 563 | not args.alert_handler_only and \ |
| 564 | not args.xbar_only: |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 565 | generate_plic(completecfg, out_path) |
Michael Schaffner | 666dde1 | 2019-10-25 11:57:54 -0700 | [diff] [blame] | 566 | if args.plic_only: |
| 567 | sys.exit() |
| 568 | |
| 569 | # Generate Alert Handler |
| 570 | if not args.xbar_only: |
| 571 | generate_alert_handler(completecfg, out_path) |
| 572 | if args.alert_handler_only: |
| 573 | sys.exit() |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 574 | |
Eunchan Kim | 436d224 | 2019-10-29 17:25:51 -0700 | [diff] [blame] | 575 | generate_pinmux(completecfg, out_path) |
| 576 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 577 | # Generate xbars |
| 578 | if not args.no_xbar or args.xbar_only: |
| 579 | generate_xbars(completecfg, out_path) |
| 580 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 581 | top_name = completecfg["name"] |
| 582 | |
| 583 | if not args.no_top or args.top_only: |
Eunchan Kim | 632c6f7 | 2019-09-30 11:11:51 -0700 | [diff] [blame] | 584 | tpl_path = Path(args.tpl) |
Michael Schaffner | c703936 | 2019-10-22 16:16:06 -0700 | [diff] [blame] | 585 | top_tplpath = tpl_path / ("top_%s.sv.tpl" % (top_name)) |
Eunchan Kim | 436d224 | 2019-10-29 17:25:51 -0700 | [diff] [blame] | 586 | out_top = generate_top(completecfg, str(top_tplpath)) |
Eunchan Kim | 632c6f7 | 2019-09-30 11:11:51 -0700 | [diff] [blame] | 587 | |
Eunchan Kim | 1a95dd9 | 2019-10-11 11:18:13 -0700 | [diff] [blame] | 588 | rtl_path = out_path / 'rtl/autogen' |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 589 | rtl_path.mkdir(parents=True, exist_ok=True) |
Eunchan Kim | 632c6f7 | 2019-09-30 11:11:51 -0700 | [diff] [blame] | 590 | top_path = rtl_path / ("top_%s.sv" % top_name) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 591 | |
Eunchan Kim | 632c6f7 | 2019-09-30 11:11:51 -0700 | [diff] [blame] | 592 | with top_path.open(mode='w', encoding='UTF-8') as fout: |
| 593 | fout.write(out_top) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 594 | |
Eunchan Kim | 436d224 | 2019-10-29 17:25:51 -0700 | [diff] [blame] | 595 | # C header |
| 596 | top_tplpath = tpl_path / ("top_%s.h.tpl" % top_name) |
| 597 | out_cheader = generate_top(completecfg, str(top_tplpath)) |
| 598 | |
| 599 | sw_path = out_path / "sw/autogen" |
| 600 | sw_path.mkdir(parents=True, exist_ok=True) |
| 601 | cheader_path = sw_path / ("top_%s.h" % top_name) |
| 602 | |
| 603 | with cheader_path.open(mode='w', encoding='UTF-8') as fout: |
| 604 | fout.write(out_cheader) |
| 605 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 606 | |
| 607 | if __name__ == "__main__": |
| 608 | main() |