blob: 6e0f0ab2848c5d10a38f2648d3c7c7de2cd22efb [file] [log] [blame]
lowRISC Contributors802543a2019-08-31 12:12:56 +01001#!/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
5r"""Top Module Generator
6"""
7import argparse
8import logging as log
Weicai Yanga6670362020-11-24 17:19:52 -08009import random
Srikrishna Iyer689981e2021-09-16 12:12:37 -070010import shutil
Michael Schaffner60157962020-05-01 19:11:28 -070011import sys
Philipp Wagnerf9ee7972021-10-28 16:21:02 +010012import tempfile
Weicai Yanga60ae7d2020-02-21 14:32:50 -080013from collections import OrderedDict
Weicai Yanga6670362020-11-24 17:19:52 -080014from copy import deepcopy
lowRISC Contributors802543a2019-08-31 12:12:56 +010015from io import StringIO
16from pathlib import Path
Cindy Chenc8389ed2021-11-04 10:53:07 -070017from typing import Dict, List, Optional, Tuple
Michael Schaffner3452c712022-01-21 20:15:12 -080018from itertools import chain
lowRISC Contributors802543a2019-08-31 12:12:56 +010019
20import hjson
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -080021import tlgen
Philipp Wagner270e28b2021-03-16 20:23:04 +000022from ipgen import (IpBlockRenderer, IpConfig, IpDescriptionOnlyRenderer,
23 IpTemplate, TemplateRenderError)
Eunchan Kimcb28a172019-10-08 16:35:48 -070024from mako import exceptions
Weicai Yanga60ae7d2020-02-21 14:32:50 -080025from mako.template import Template
Srikrishna Iyera463e172022-02-02 12:09:39 -080026from reggen import access, gen_rtl, gen_sec_cm_testplan, window
Rupert Swarbrickb6500262021-02-23 15:16:16 +000027from reggen.inter_signal import InterSignal
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000028from reggen.ip_block import IpBlock
Michael Schaffner3452c712022-01-21 20:15:12 -080029from reggen.countermeasure import CounterMeasure
Rupert Swarbrickb6500262021-02-23 15:16:16 +000030from reggen.lib import check_list
Rupert Swarbrick5aa249f2021-07-19 18:35:11 +010031from topgen import get_hjsonobj_xbars
Eunchan Kim2c813b42020-08-03 16:22:56 -070032from topgen import intermodule as im
Timothy Chen94432212021-03-01 22:29:18 -080033from topgen import lib as lib
Rupert Swarbrickbc2bc582021-02-09 13:30:37 +000034from topgen import merge_top, search_ips, validate_top
Srikrishna Iyer689981e2021-09-16 12:12:37 -070035from topgen.c_test import TopGenCTest
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -080036from topgen.clocks import Clocks
Rupert Swarbrick10184102021-03-31 09:12:42 +010037from topgen.gen_dv import gen_dv
Michael Schaffner426ed202021-08-02 17:44:42 -070038from topgen.gen_top_docs import gen_top_docs
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -080039from topgen.merge import connect_clocks, create_alert_lpgs, extract_clocks
Timothy Chen63c66062021-07-24 00:25:22 -070040from topgen.resets import Resets
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -080041from topgen.top import Top
lowRISC Contributors802543a2019-08-31 12:12:56 +010042
43# Common header for generated files
Cindy Liuc6db72f2022-03-24 14:12:53 -070044warnhdr = """//
lowRISC Contributors802543a2019-08-31 12:12:56 +010045// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------//
46// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND:
Cindy Liuc6db72f2022-03-24 14:12:53 -070047"""
48genhdr = """// Copyright lowRISC contributors.
Michael Schaffner7b0807d2020-10-27 19:54:52 -070049// Licensed under the Apache License, Version 2.0, see LICENSE for details.
50// SPDX-License-Identifier: Apache-2.0
Cindy Liuc6db72f2022-03-24 14:12:53 -070051""" + warnhdr
lowRISC Contributors802543a2019-08-31 12:12:56 +010052
Srikrishna Iyer689981e2021-09-16 12:12:37 -070053GENCMD = ("// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson\n"
54 "// -o hw/top_{topname}")
55
Philipp Wagnerea3cd4c2020-05-07 17:28:18 +010056SRCTREE_TOP = Path(__file__).parent.parent.resolve()
Eunchan Kimc6a6a3b2019-09-12 14:27:43 -070057
Cindy Liuc6db72f2022-03-24 14:12:53 -070058TOPGEN_TEMPLATE_PATH = Path(__file__).parent / "topgen/templates"
Philipp Wagnerfb443ab2021-03-05 11:10:28 +000059
Philipp Wagner270e28b2021-03-16 20:23:04 +000060
61def ipgen_render(template_name: str, topname: str, params: Dict,
62 out_path: Path):
63 """ Render an IP template for a specific toplevel using ipgen.
64
Cindy Liuc6db72f2022-03-24 14:12:53 -070065 The generated IP block is placed in the "ip_autogen" directory of the
Philipp Wagner270e28b2021-03-16 20:23:04 +000066 toplevel.
67
68 Aborts the program execution in case of an error.
69 """
Cindy Liu88109a02022-03-30 19:19:24 -070070 module_name = params.get("module_instance_name", template_name)
71 instance_name = f"top_{topname}_{module_name}"
Philipp Wagner270e28b2021-03-16 20:23:04 +000072 ip_template = IpTemplate.from_template_path(
Cindy Liuc6db72f2022-03-24 14:12:53 -070073 SRCTREE_TOP / "hw/ip_templates" / template_name)
Philipp Wagner270e28b2021-03-16 20:23:04 +000074
75 try:
76 ip_config = IpConfig(ip_template.params, instance_name, params)
77 except ValueError as e:
78 log.error(f"Unable to render IP template {template_name!r}: {str(e)}")
79 sys.exit(1)
80
81 try:
82 renderer = IpBlockRenderer(ip_template, ip_config)
Cindy Liu88109a02022-03-30 19:19:24 -070083 renderer.render(out_path / "ip_autogen" / module_name,
Philipp Wagner270e28b2021-03-16 20:23:04 +000084 overwrite_output_dir=True)
85 except TemplateRenderError as e:
86 log.error(e.verbose_str())
87 sys.exit(1)
88
Eunchan Kimfd561be2020-04-24 15:42:11 -070089
Rupert Swarbrickeb619e62021-03-05 15:01:54 +000090def generate_top(top, name_to_block, tpl_filename, **kwargs):
Sam Elliott7e36bd72020-04-22 14:05:49 +010091 top_tpl = Template(filename=tpl_filename)
lowRISC Contributors802543a2019-08-31 12:12:56 +010092
Eunchan Kimcb28a172019-10-08 16:35:48 -070093 try:
Rupert Swarbrickeb619e62021-03-05 15:01:54 +000094 return top_tpl.render(top=top, name_to_block=name_to_block, **kwargs)
Eunchan Kim6599ba92020-04-13 15:27:16 -070095 except: # noqa: E722
Eunchan Kimcb28a172019-10-08 16:35:48 -070096 log.error(exceptions.text_error_template().render())
Sam Elliott7e36bd72020-04-22 14:05:49 +010097 return ""
lowRISC Contributors802543a2019-08-31 12:12:56 +010098
99
100def generate_xbars(top, out_path):
Eunchan Kimba970df2020-04-17 10:21:01 -0700101 topname = top["name"]
102 gencmd = ("// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson "
103 "-o hw/top_{topname}/\n\n".format(topname=topname))
Eunchan Kim6df9a1f2019-10-09 14:46:05 -0700104
lowRISC Contributors802543a2019-08-31 12:12:56 +0100105 for obj in top["xbar"]:
Cindy Liuc6db72f2022-03-24 14:12:53 -0700106 xbar_path = out_path / "ip/xbar_{}/data/autogen".format(obj["name"])
Eunchan Kimc7452942019-12-19 17:04:37 -0800107 xbar_path.mkdir(parents=True, exist_ok=True)
lowRISC Contributors802543a2019-08-31 12:12:56 +0100108 xbar = tlgen.validate(obj)
Cindy Liuc6db72f2022-03-24 14:12:53 -0700109 xbar.ip_path = "hw/top_" + top["name"] + "/ip/{dut}"
lowRISC Contributors802543a2019-08-31 12:12:56 +0100110
Eunchan Kim6df9a1f2019-10-09 14:46:05 -0700111 # Generate output of crossbar with complete fields
112 xbar_hjson_path = xbar_path / "xbar_{}.gen.hjson".format(xbar.name)
113 xbar_hjson_path.write_text(genhdr + gencmd +
Eunchan Kim2af98ed2019-10-09 15:33:27 -0700114 hjson.dumps(obj, for_json=True))
Eunchan Kim6df9a1f2019-10-09 14:46:05 -0700115
lowRISC Contributors802543a2019-08-31 12:12:56 +0100116 if not tlgen.elaborate(xbar):
117 log.error("Elaboration failed." + repr(xbar))
118
Eunchan Kimcb28a172019-10-08 16:35:48 -0700119 try:
Eunchan Kim9191f262020-07-30 16:37:40 -0700120 results = tlgen.generate(xbar, "top_" + top["name"])
Eunchan Kim6599ba92020-04-13 15:27:16 -0700121 except: # noqa: E722
Eunchan Kimcb28a172019-10-08 16:35:48 -0700122 log.error(exceptions.text_error_template().render())
lowRISC Contributors802543a2019-08-31 12:12:56 +0100123
Cindy Liuc6db72f2022-03-24 14:12:53 -0700124 ip_path = out_path / "ip/xbar_{}".format(obj["name"])
Eunchan Kim9191f262020-07-30 16:37:40 -0700125
126 for filename, filecontent in results:
127 filepath = ip_path / filename
128 filepath.parent.mkdir(parents=True, exist_ok=True)
Cindy Liuc6db72f2022-03-24 14:12:53 -0700129 with filepath.open(mode="w", encoding="UTF-8") as fout:
Eunchan Kim9191f262020-07-30 16:37:40 -0700130 fout.write(filecontent)
131
Cindy Liuc6db72f2022-03-24 14:12:53 -0700132 dv_path = out_path / "ip/xbar_{}/dv/autogen".format(obj["name"])
lowRISC Contributors802543a2019-08-31 12:12:56 +0100133 dv_path.mkdir(parents=True, exist_ok=True)
134
Weicai Yange4315d22020-01-09 10:37:42 -0800135 # generate testbench for xbar
Eunchan Kim8f2cb382020-05-13 11:53:09 -0700136 tlgen.generate_tb(xbar, dv_path, "top_" + top["name"])
lowRISC Contributors802543a2019-08-31 12:12:56 +0100137
Eunchan Kime0d37fe2020-08-03 12:05:21 -0700138 # Read back the comportable IP and amend to Xbar
139 xbar_ipfile = ip_path / ("data/autogen/xbar_%s.hjson" % obj["name"])
140 with xbar_ipfile.open() as fxbar:
141 xbar_ipobj = hjson.load(fxbar,
142 use_decimal=True,
143 object_pairs_hook=OrderedDict)
144
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800145 r_inter_signal_list = check_list(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700146 xbar_ipobj.get("inter_signal_list", []),
147 "inter_signal_list field")
148 obj["inter_signal_list"] = [
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800149 InterSignal.from_raw(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700150 "entry {} of the inter_signal_list field".format(idx + 1),
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800151 entry) for idx, entry in enumerate(r_inter_signal_list)
Rupert Swarbrickb6500262021-02-23 15:16:16 +0000152 ]
Eunchan Kime0d37fe2020-08-03 12:05:21 -0700153
Eunchan Kim6a4b49e2020-02-18 10:33:39 -0800154
Michael Schaffner666dde12019-10-25 11:57:54 -0700155def generate_alert_handler(top, out_path):
Philipp Wagner0d7bd2c2021-03-19 10:33:52 +0000156 topname = top["name"]
157
Michael Schaffner666dde12019-10-25 11:57:54 -0700158 # default values
Eunchan Kimc7452942019-12-19 17:04:37 -0800159 esc_cnt_dw = 32
160 accu_cnt_dw = 16
Michael Schaffner3b511b82021-10-01 11:11:09 -0700161 async_on = []
Michael Schaffner666dde12019-10-25 11:57:54 -0700162 # leave this constant
Eunchan Kimc7452942019-12-19 17:04:37 -0800163 n_classes = 4
Michael Schaffner4fe78462021-09-28 17:06:07 -0700164 # low power groups
Michael Schaffnerf668fff2021-09-20 17:36:16 -0700165 n_lpg = 1
Michael Schaffner3b511b82021-10-01 11:11:09 -0700166 lpg_map = []
Michael Schaffner666dde12019-10-25 11:57:54 -0700167
Michael Schaffner4fe78462021-09-28 17:06:07 -0700168 # Count number of alerts and LPGs
Michael Schaffner666dde12019-10-25 11:57:54 -0700169 n_alerts = sum([x["width"] if "width" in x else 1 for x in top["alert"]])
Cindy Liuc6db72f2022-03-24 14:12:53 -0700170 n_lpg = len(top["alert_lpgs"])
Michael Schaffner4fe78462021-09-28 17:06:07 -0700171 n_lpg_width = n_lpg.bit_length()
Michael Schaffner3b511b82021-10-01 11:11:09 -0700172 # format used to print out indices in binary format
173 async_on_format = "1'b{:01b}"
174 lpg_idx_format = str(n_lpg_width) + "'d{:d}"
Michael Schaffner4fe78462021-09-28 17:06:07 -0700175
176 # Double check that all these values are greated than 0
177 if esc_cnt_dw < 1:
178 raise ValueError("esc_cnt_dw must be larger than 0")
179 if accu_cnt_dw < 1:
180 raise ValueError("accu_cnt_dw must be larger than 0")
181 if n_lpg < 1:
182 raise ValueError("n_lpg must be larger than 0")
Michael Schaffner666dde12019-10-25 11:57:54 -0700183
184 if n_alerts < 1:
185 # set number of alerts to 1 such that the config is still valid
186 # that input will be tied off
187 n_alerts = 1
Eunchan Kimc7452942019-12-19 17:04:37 -0800188 log.warning("no alerts are defined in the system")
Michael Schaffner666dde12019-10-25 11:57:54 -0700189 else:
Michael Schaffner3b511b82021-10-01 11:11:09 -0700190 async_on = []
191 lpg_map = []
Cindy Liuc6db72f2022-03-24 14:12:53 -0700192 for alert in top["alert"]:
193 for k in range(alert["width"]):
194 async_on.append(async_on_format.format(int(alert["async"])))
195 lpg_map.append(lpg_idx_format.format(int(alert["lpg_idx"])))
Michael Schaffner666dde12019-10-25 11:57:54 -0700196
Philipp Wagner0d7bd2c2021-03-19 10:33:52 +0000197 params = {
Cindy Liuc6db72f2022-03-24 14:12:53 -0700198 "n_alerts": n_alerts,
199 "esc_cnt_dw": esc_cnt_dw,
200 "accu_cnt_dw": accu_cnt_dw,
201 "async_on": async_on,
202 "n_classes": n_classes,
203 "n_lpg": n_lpg,
204 "lpg_map": lpg_map,
Philipp Wagner0d7bd2c2021-03-19 10:33:52 +0000205 }
Michael Schaffner666dde12019-10-25 11:57:54 -0700206
Cindy Liuc6db72f2022-03-24 14:12:53 -0700207 ipgen_render("alert_handler", topname, params, out_path)
Michael Schaffner666dde12019-10-25 11:57:54 -0700208
209
lowRISC Contributors802543a2019-08-31 12:12:56 +0100210def generate_plic(top, out_path):
Eunchan Kimba970df2020-04-17 10:21:01 -0700211 topname = top["name"]
Philipp Wagnerc720ac82021-03-03 15:34:53 +0000212 params = {}
213
lowRISC Contributors802543a2019-08-31 12:12:56 +0100214 # Count number of interrupts
Eunchan Kim88a86152020-04-13 16:12:08 -0700215 # Interrupt source 0 is tied to 0 to conform RISC-V PLIC spec.
216 # So, total number of interrupts are the number of entries in the list + 1
Cindy Liuc6db72f2022-03-24 14:12:53 -0700217 params["src"] = sum(
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800218 [x["width"] if "width" in x else 1 for x in top["interrupt"]]) + 1
lowRISC Contributors802543a2019-08-31 12:12:56 +0100219
220 # Target and priority: Currently fixed
Cindy Liuc6db72f2022-03-24 14:12:53 -0700221 params["target"] = int(top["num_cores"], 0) if "num_cores" in top else 1
222 params["prio"] = 3
lowRISC Contributors802543a2019-08-31 12:12:56 +0100223
Cindy Liuc6db72f2022-03-24 14:12:53 -0700224 ipgen_render("rv_plic", topname, params, out_path)
lowRISC Contributors802543a2019-08-31 12:12:56 +0100225
Eunchan Kimba970df2020-04-17 10:21:01 -0700226
Michael Schaffner3452c712022-01-21 20:15:12 -0800227# TODO: For generated IPs that are generated legacy style (i.e., without IPgen)
228# we have to search both the source and destination RTL directories, since not
229# all files are copied over. This is a workaround which can be removed once
230# all generated IPs have transitioned to IPgen.
231def generate_regfile_from_path(hjson_path: Path,
232 generated_rtl_path: Path,
233 original_rtl_path: Path = None):
Cindy Liuc6db72f2022-03-24 14:12:53 -0700234 """Generate RTL register file from path and check countermeasure labels"""
Michael Schaffner3452c712022-01-21 20:15:12 -0800235 obj = IpBlock.from_path(str(hjson_path), [])
236
237 # If this block has countermeasures, we grep for RTL annotations in
238 # all .sv implementation files and check whether they match up
239 # with what is defined inside the Hjson.
Cindy Liuc6db72f2022-03-24 14:12:53 -0700240 sv_files = generated_rtl_path.glob("*.sv")
Michael Schaffner3452c712022-01-21 20:15:12 -0800241 if original_rtl_path is not None:
Cindy Liuc6db72f2022-03-24 14:12:53 -0700242 sv_files = chain(sv_files, original_rtl_path.glob("*.sv"))
Michael Schaffner3452c712022-01-21 20:15:12 -0800243 rtl_names = CounterMeasure.search_rtl_files(sv_files)
244 obj.check_cm_annotations(rtl_names, str(hjson_path))
245 gen_rtl.gen_rtl(obj, str(generated_rtl_path))
Srikrishna Iyera463e172022-02-02 12:09:39 -0800246 gen_sec_cm_testplan.gen_sec_cm_testplan(obj, hjson_path.parent)
Michael Schaffner3452c712022-01-21 20:15:12 -0800247
248
Michael Schaffner43ce8d52021-02-10 17:04:57 -0800249def generate_pinmux(top, out_path):
Michael Schaffner60157962020-05-01 19:11:28 -0700250
Cindy Liuc6db72f2022-03-24 14:12:53 -0700251 topname = top["name"]
252 pinmux = top["pinmux"]
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700253
254 # Generation without pinmux and pinout configuration is not supported.
Cindy Liuc6db72f2022-03-24 14:12:53 -0700255 assert "pinmux" in top
256 assert "pinout" in top
Michael Schaffner57c490d2020-04-29 15:08:55 -0700257
258 # Get number of wakeup detectors
Cindy Liuc6db72f2022-03-24 14:12:53 -0700259 if "num_wkup_detect" in pinmux:
260 num_wkup_detect = pinmux["num_wkup_detect"]
Michael Schaffner57c490d2020-04-29 15:08:55 -0700261 else:
262 num_wkup_detect = 1
263
264 if num_wkup_detect <= 0:
265 # TODO: add support for no wakeup counter case
Cindy Liuc6db72f2022-03-24 14:12:53 -0700266 log.error("Topgen does currently not support generation of a top " +
267 "without DIOs.")
Michael Schaffner57c490d2020-04-29 15:08:55 -0700268 return
269
Cindy Liuc6db72f2022-03-24 14:12:53 -0700270 if "wkup_cnt_width" in pinmux:
271 wkup_cnt_width = pinmux["wkup_cnt_width"]
Michael Schaffner57c490d2020-04-29 15:08:55 -0700272 else:
273 wkup_cnt_width = 8
274
275 if wkup_cnt_width <= 1:
Cindy Liuc6db72f2022-03-24 14:12:53 -0700276 log.error("Wakeup counter width must be greater equal 2.")
Eunchan Kim436d2242019-10-29 17:25:51 -0700277 return
278
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700279 # MIO Pads
Cindy Liuc6db72f2022-03-24 14:12:53 -0700280 n_mio_pads = pinmux["io_counts"]["muxed"]["pads"]
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700281
Eunchan Kim436d2242019-10-29 17:25:51 -0700282 # Total inputs/outputs
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700283 # Reuse the counts from the merge phase
Cindy Liuc6db72f2022-03-24 14:12:53 -0700284 n_mio_periph_in = (pinmux["io_counts"]["muxed"]["inouts"] +
285 pinmux["io_counts"]["muxed"]["inputs"])
286 n_mio_periph_out = (pinmux["io_counts"]["muxed"]["inouts"] +
287 pinmux["io_counts"]["muxed"]["outputs"])
288 n_dio_periph_in = (pinmux["io_counts"]["dedicated"]["inouts"] +
289 pinmux["io_counts"]["dedicated"]["inputs"])
290 n_dio_periph_out = (pinmux["io_counts"]["dedicated"]["inouts"] +
291 pinmux["io_counts"]["dedicated"]["outputs"])
292 n_dio_pads = (pinmux["io_counts"]["dedicated"]["inouts"] +
293 pinmux["io_counts"]["dedicated"]["inputs"] +
294 pinmux["io_counts"]["dedicated"]["outputs"])
Michael Schaffner57c490d2020-04-29 15:08:55 -0700295
Michael Schaffner43ce8d52021-02-10 17:04:57 -0800296 # TODO: derive this value
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700297 attr_dw = 13
Michael Schaffner43ce8d52021-02-10 17:04:57 -0800298
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700299 # Generation with zero MIO/DIO pads is currently not supported.
300 assert (n_mio_pads > 0)
301 assert (n_dio_pads > 0)
Michael Schaffner57c490d2020-04-29 15:08:55 -0700302
Cindy Liuc6db72f2022-03-24 14:12:53 -0700303 log.info("Generating pinmux with following info from hjson:")
304 log.info("attr_dw: %d" % attr_dw)
305 log.info("num_wkup_detect: %d" % num_wkup_detect)
306 log.info("wkup_cnt_width: %d" % wkup_cnt_width)
307 log.info("n_mio_periph_in: %d" % n_mio_periph_in)
308 log.info("n_mio_periph_out: %d" % n_mio_periph_out)
309 log.info("n_dio_periph_in: %d" % n_dio_periph_in)
310 log.info("n_dio_periph_out: %d" % n_dio_periph_out)
311 log.info("n_dio_pads: %d" % n_dio_pads)
Eunchan Kim436d2242019-10-29 17:25:51 -0700312
313 # Target path
314 # rtl: pinmux_reg_pkg.sv & pinmux_reg_top.sv
315 # data: pinmux.hjson
Cindy Liuc6db72f2022-03-24 14:12:53 -0700316 rtl_path = out_path / "ip/pinmux/rtl/autogen"
Eunchan Kim436d2242019-10-29 17:25:51 -0700317 rtl_path.mkdir(parents=True, exist_ok=True)
Cindy Liuc6db72f2022-03-24 14:12:53 -0700318 data_path = out_path / "ip/pinmux/data/autogen"
Eunchan Kim436d2242019-10-29 17:25:51 -0700319 data_path.mkdir(parents=True, exist_ok=True)
320
321 # Template path
Weicai Yang5c02d782020-12-08 12:17:51 -0800322 tpl_path = Path(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700323 __file__).resolve().parent / "../hw/ip/pinmux/data/pinmux.hjson.tpl"
Michael Schaffner3452c712022-01-21 20:15:12 -0800324 original_rtl_path = Path(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700325 __file__).resolve().parent / "../hw/ip/pinmux/rtl"
Eunchan Kim436d2242019-10-29 17:25:51 -0700326
327 # Generate register package and RTLs
Eunchan Kimba970df2020-04-17 10:21:01 -0700328 gencmd = ("// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson "
329 "-o hw/top_{topname}/\n\n".format(topname=topname))
Eunchan Kim436d2242019-10-29 17:25:51 -0700330
331 hjson_gen_path = data_path / "pinmux.hjson"
332
333 out = StringIO()
Cindy Liuc6db72f2022-03-24 14:12:53 -0700334 with tpl_path.open(mode="r", encoding="UTF-8") as fin:
Eunchan Kim436d2242019-10-29 17:25:51 -0700335 hjson_tpl = Template(fin.read())
336 try:
Michael Schaffner60157962020-05-01 19:11:28 -0700337 out = hjson_tpl.render(
338 n_mio_periph_in=n_mio_periph_in,
339 n_mio_periph_out=n_mio_periph_out,
340 n_mio_pads=n_mio_pads,
341 # each DIO has in, out and oe wires
342 # some of these have to be tied off in the
343 # top, depending on the type.
344 n_dio_periph_in=n_dio_pads,
345 n_dio_periph_out=n_dio_pads,
346 n_dio_pads=n_dio_pads,
Michael Schaffner43ce8d52021-02-10 17:04:57 -0800347 attr_dw=attr_dw,
Michael Schaffner60157962020-05-01 19:11:28 -0700348 n_wkup_detect=num_wkup_detect,
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800349 wkup_cnt_width=wkup_cnt_width)
Eunchan Kim6599ba92020-04-13 15:27:16 -0700350 except: # noqa: E722
Eunchan Kim436d2242019-10-29 17:25:51 -0700351 log.error(exceptions.text_error_template().render())
352 log.info("PINMUX HJSON: %s" % out)
353
354 if out == "":
355 log.error("Cannot generate pinmux HJSON")
356 return
357
Cindy Liuc6db72f2022-03-24 14:12:53 -0700358 with hjson_gen_path.open(mode="w", encoding="UTF-8") as fout:
Eunchan Kim436d2242019-10-29 17:25:51 -0700359 fout.write(genhdr + gencmd + out)
360
Michael Schaffner3452c712022-01-21 20:15:12 -0800361 # Generate reg file
362 generate_regfile_from_path(hjson_gen_path, rtl_path, original_rtl_path)
Eunchan Kim436d2242019-10-29 17:25:51 -0700363
Michael Schaffner60157962020-05-01 19:11:28 -0700364
Timothy Chenf56c1b52020-04-28 17:00:43 -0700365def generate_clkmgr(top, cfg_path, out_path):
366
367 # Target paths
Cindy Liuc6db72f2022-03-24 14:12:53 -0700368 rtl_path = out_path / "ip/clkmgr/rtl/autogen"
Timothy Chenf56c1b52020-04-28 17:00:43 -0700369 rtl_path.mkdir(parents=True, exist_ok=True)
Cindy Liuc6db72f2022-03-24 14:12:53 -0700370 data_path = out_path / "ip/clkmgr/data/autogen"
Timothy Chenf56c1b52020-04-28 17:00:43 -0700371 data_path.mkdir(parents=True, exist_ok=True)
372
373 # Template paths
Cindy Liuc6db72f2022-03-24 14:12:53 -0700374 hjson_tpl = cfg_path / "../ip/clkmgr/data/clkmgr.hjson.tpl"
375 rtl_tpl = cfg_path / "../ip/clkmgr/data/clkmgr.sv.tpl"
376 pkg_tpl = cfg_path / "../ip/clkmgr/data/clkmgr_pkg.sv.tpl"
377 original_rtl_path = cfg_path / "../ip/clkmgr/rtl"
Timothy Chenf56c1b52020-04-28 17:00:43 -0700378
Cindy Liuc6db72f2022-03-24 14:12:53 -0700379 hjson_out = data_path / "clkmgr.hjson"
380 rtl_out = rtl_path / "clkmgr.sv"
381 pkg_out = rtl_path / "clkmgr_pkg.sv"
Timothy Chenf56c1b52020-04-28 17:00:43 -0700382
383 tpls = [hjson_tpl, rtl_tpl, pkg_tpl]
384 outputs = [hjson_out, rtl_out, pkg_out]
Cindy Liuc6db72f2022-03-24 14:12:53 -0700385 names = ["clkmgr.hjson", "clkmgr.sv", "clkmgr_pkg.sv"]
Timothy Chenf56c1b52020-04-28 17:00:43 -0700386
Cindy Liuc6db72f2022-03-24 14:12:53 -0700387 clocks = top["clocks"]
Rupert Swarbrick127b1092021-07-16 17:10:39 +0100388 assert isinstance(clocks, Clocks)
Timothy Chenf56c1b52020-04-28 17:00:43 -0700389
Rupert Swarbrick710e3c92021-07-13 15:28:59 +0100390 typed_clocks = clocks.typed_clocks()
391 hint_names = typed_clocks.hint_names()
Rupert Swarbrick56544b02021-06-17 14:20:40 +0100392
Timothy Chenf56c1b52020-04-28 17:00:43 -0700393 for idx, tpl in enumerate(tpls):
Sam Elliott6dd4e4a2020-07-30 18:52:22 +0100394 out = ""
Cindy Liuc6db72f2022-03-24 14:12:53 -0700395 with tpl.open(mode="r", encoding="UTF-8") as fin:
Timothy Chenf56c1b52020-04-28 17:00:43 -0700396 tpl = Template(fin.read())
397 try:
398 out = tpl.render(cfg=top,
Rupert Swarbrickab5c1492021-07-19 13:00:37 +0100399 clocks=clocks,
400 typed_clocks=typed_clocks,
Rupert Swarbrick710e3c92021-07-13 15:28:59 +0100401 hint_names=hint_names)
Timothy Chenf56c1b52020-04-28 17:00:43 -0700402 except: # noqa: E722
403 log.error(exceptions.text_error_template().render())
404
405 if out == "":
406 log.error("Cannot generate {}".format(names[idx]))
407 return
408
Cindy Liuc6db72f2022-03-24 14:12:53 -0700409 with outputs[idx].open(mode="w", encoding="UTF-8") as fout:
Timothy Chenf56c1b52020-04-28 17:00:43 -0700410 fout.write(genhdr + out)
411
412 # Generate reg files
Michael Schaffner3452c712022-01-21 20:15:12 -0800413 generate_regfile_from_path(hjson_out, rtl_path, original_rtl_path)
Eunchan Kim436d2242019-10-29 17:25:51 -0700414
Eunchan Kimc75c6c12020-05-12 12:48:34 -0700415
Timothy Chen4ba25312020-06-17 13:08:57 -0700416# generate pwrmgr
417def generate_pwrmgr(top, out_path):
418 log.info("Generating pwrmgr")
419
Timothy Chen6faf4f12020-09-18 18:52:47 -0700420 # Count number of wakeups
Timothy Chen4ba25312020-06-17 13:08:57 -0700421 n_wkups = len(top["wakeups"])
422 log.info("Found {} wakeup signals".format(n_wkups))
423
Timothy Chen6faf4f12020-09-18 18:52:47 -0700424 # Count number of reset requests
425 n_rstreqs = len(top["reset_requests"])
426 log.info("Found {} reset request signals".format(n_rstreqs))
427
Timothy Chen4ba25312020-06-17 13:08:57 -0700428 if n_wkups < 1:
429 n_wkups = 1
Eunchan Kim9191f262020-07-30 16:37:40 -0700430 log.warning(
Michael Schaffner52bce9f2021-07-13 15:08:08 -0700431 "The design has no wakeup sources. Low power not supported.")
432
433 if n_rstreqs < 1:
434 n_rstreqs = 1
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800435 log.warning("The design has no reset request sources. "
436 "Reset requests are not supported.")
Timothy Chen4ba25312020-06-17 13:08:57 -0700437
438 # Define target path
Cindy Liuc6db72f2022-03-24 14:12:53 -0700439 rtl_path = out_path / "ip/pwrmgr/rtl/autogen"
Timothy Chen4ba25312020-06-17 13:08:57 -0700440 rtl_path.mkdir(parents=True, exist_ok=True)
Cindy Liuc6db72f2022-03-24 14:12:53 -0700441 doc_path = out_path / "ip/pwrmgr/data/autogen"
Timothy Chen4ba25312020-06-17 13:08:57 -0700442 doc_path.mkdir(parents=True, exist_ok=True)
443
444 # So, read template files from ip directory.
Cindy Liuc6db72f2022-03-24 14:12:53 -0700445 tpl_path = Path(__file__).resolve().parent / "../hw/ip/pwrmgr/data"
446 hjson_tpl_path = tpl_path / "pwrmgr.hjson.tpl"
447 original_rtl_path = Path(__file__).resolve().parent / "../hw/ip/pwrmgr/rtl"
Timothy Chen4ba25312020-06-17 13:08:57 -0700448
449 # Render and write out hjson
450 out = StringIO()
Cindy Liuc6db72f2022-03-24 14:12:53 -0700451 with hjson_tpl_path.open(mode="r", encoding="UTF-8") as fin:
Timothy Chen4ba25312020-06-17 13:08:57 -0700452 hjson_tpl = Template(fin.read())
453 try:
Timothy Chenbea7b6a2021-01-21 13:59:20 -0800454 out = hjson_tpl.render(NumWkups=n_wkups,
455 Wkups=top["wakeups"],
456 NumRstReqs=n_rstreqs)
Timothy Chen4ba25312020-06-17 13:08:57 -0700457
458 except: # noqa: E722
459 log.error(exceptions.text_error_template().render())
460 log.info("pwrmgr hjson: %s" % out)
461
462 if out == "":
463 log.error("Cannot generate pwrmgr config file")
464 return
465
466 hjson_path = doc_path / "pwrmgr.hjson"
Cindy Liuc6db72f2022-03-24 14:12:53 -0700467 with hjson_path.open(mode="w", encoding="UTF-8") as fout:
Timothy Chen4ba25312020-06-17 13:08:57 -0700468 fout.write(genhdr + out)
469
470 # Generate reg files
Michael Schaffner3452c712022-01-21 20:15:12 -0800471 generate_regfile_from_path(hjson_path, rtl_path, original_rtl_path)
Timothy Chen4ba25312020-06-17 13:08:57 -0700472
473
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700474# generate rstmgr
475def generate_rstmgr(topcfg, out_path):
476 log.info("Generating rstmgr")
477
478 # Define target path
Cindy Liuc6db72f2022-03-24 14:12:53 -0700479 rtl_path = out_path / "ip/rstmgr/rtl/autogen"
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700480 rtl_path.mkdir(parents=True, exist_ok=True)
Cindy Liuc6db72f2022-03-24 14:12:53 -0700481 doc_path = out_path / "ip/rstmgr/data/autogen"
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700482 doc_path.mkdir(parents=True, exist_ok=True)
Cindy Liuc6db72f2022-03-24 14:12:53 -0700483 tpl_path = Path(__file__).resolve().parent / "../hw/ip/rstmgr/data"
484 original_rtl_path = Path(__file__).resolve().parent / "../hw/ip/rstmgr/rtl"
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700485
486 # Read template files from ip directory.
487 tpls = []
488 outputs = []
Cindy Liuc6db72f2022-03-24 14:12:53 -0700489 names = ["rstmgr.hjson", "rstmgr.sv", "rstmgr_pkg.sv"]
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700490
491 for x in names:
492 tpls.append(tpl_path / Path(x + ".tpl"))
493 if "hjson" in x:
494 outputs.append(doc_path / Path(x))
495 else:
496 outputs.append(rtl_path / Path(x))
497
498 # Parameters needed for generation
Cindy Liuc6db72f2022-03-24 14:12:53 -0700499 reset_obj = topcfg["resets"]
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700500
Timothy Chen63c66062021-07-24 00:25:22 -0700501 # The original resets dict is transformed to the reset class
502 assert isinstance(reset_obj, Resets)
503
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700504 # unique clocks
Timothy Chen7c3de6e2021-07-19 16:46:45 -0700505 clks = reset_obj.get_clocks()
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700506
507 # resets sent to reset struct
Timothy Chen7c3de6e2021-07-19 16:46:45 -0700508 output_rsts = reset_obj.get_top_resets()
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700509
510 # sw controlled resets
Timothy Chen7c3de6e2021-07-19 16:46:45 -0700511 sw_rsts = reset_obj.get_sw_resets()
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700512
513 # leaf resets
Timothy Chen7c3de6e2021-07-19 16:46:45 -0700514 leaf_rsts = reset_obj.get_generated_resets()
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700515
Timothy Chen6faf4f12020-09-18 18:52:47 -0700516 # Number of reset requests
517 n_rstreqs = len(topcfg["reset_requests"])
518
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700519 # Generate templated files
520 for idx, t in enumerate(tpls):
521 out = StringIO()
Cindy Liuc6db72f2022-03-24 14:12:53 -0700522 with t.open(mode="r", encoding="UTF-8") as fin:
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700523 tpl = Template(fin.read())
524 try:
525 out = tpl.render(clks=clks,
Cindy Liuc6db72f2022-03-24 14:12:53 -0700526 power_domains=topcfg["power"]["domains"],
Timothy Chen6faf4f12020-09-18 18:52:47 -0700527 num_rstreqs=n_rstreqs,
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700528 sw_rsts=sw_rsts,
529 output_rsts=output_rsts,
Timothy Chen4c8905e2020-08-26 10:34:33 -0700530 leaf_rsts=leaf_rsts,
Cindy Liuc6db72f2022-03-24 14:12:53 -0700531 export_rsts=topcfg["exported_rsts"],
532 reset_obj=topcfg["resets"])
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700533
534 except: # noqa: E722
535 log.error(exceptions.text_error_template().render())
536
537 if out == "":
538 log.error("Cannot generate {}".format(names[idx]))
539 return
540
Cindy Liuc6db72f2022-03-24 14:12:53 -0700541 with outputs[idx].open(mode="w", encoding="UTF-8") as fout:
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700542 fout.write(genhdr + out)
543
544 # Generate reg files
545 hjson_path = outputs[0]
Michael Schaffner3452c712022-01-21 20:15:12 -0800546 generate_regfile_from_path(hjson_path, rtl_path, original_rtl_path)
Timothy Chend5b1c0f2020-08-11 14:10:40 -0700547
548
Timothy Chen1daf5822020-10-26 17:28:15 -0700549# generate flash
550def generate_flash(topcfg, out_path):
551 log.info("Generating flash")
552
553 # Define target path
Cindy Liuc6db72f2022-03-24 14:12:53 -0700554 rtl_path = out_path / "ip/flash_ctrl/rtl/autogen"
Timothy Chen1daf5822020-10-26 17:28:15 -0700555 rtl_path.mkdir(parents=True, exist_ok=True)
Cindy Liuc6db72f2022-03-24 14:12:53 -0700556 doc_path = out_path / "ip/flash_ctrl/data/autogen"
Timothy Chen1daf5822020-10-26 17:28:15 -0700557 doc_path.mkdir(parents=True, exist_ok=True)
Cindy Liuc6db72f2022-03-24 14:12:53 -0700558 tpl_path = Path(__file__).resolve().parent / "../hw/ip/flash_ctrl/data"
Michael Schaffner3452c712022-01-21 20:15:12 -0800559 original_rtl_path = Path(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700560 __file__).resolve().parent / "../hw/ip/flash_ctrl/rtl"
Timothy Chen1daf5822020-10-26 17:28:15 -0700561
562 # Read template files from ip directory.
563 tpls = []
564 outputs = []
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800565 names = [
Cindy Liuc6db72f2022-03-24 14:12:53 -0700566 "flash_ctrl.hjson", "flash_ctrl.sv", "flash_ctrl_pkg.sv",
567 "flash_ctrl_region_cfg.sv"
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800568 ]
Timothy Chen1daf5822020-10-26 17:28:15 -0700569
570 for x in names:
571 tpls.append(tpl_path / Path(x + ".tpl"))
572 if "hjson" in x:
573 outputs.append(doc_path / Path(x))
574 else:
575 outputs.append(rtl_path / Path(x))
576
577 # Parameters needed for generation
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800578 flash_mems = [
Cindy Liuc6db72f2022-03-24 14:12:53 -0700579 module for module in topcfg["module"] if module["type"] == "flash_ctrl"
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800580 ]
Timothy Chen1daf5822020-10-26 17:28:15 -0700581 if len(flash_mems) > 1:
582 log.error("This design does not currently support multiple flashes")
583 return
584
Cindy Liuc6db72f2022-03-24 14:12:53 -0700585 cfg = flash_mems[0]["memory"]["mem"]["config"]
Timothy Chen1daf5822020-10-26 17:28:15 -0700586
587 # Generate templated files
588 for idx, t in enumerate(tpls):
589 out = StringIO()
Cindy Liuc6db72f2022-03-24 14:12:53 -0700590 with t.open(mode="r", encoding="UTF-8") as fin:
Timothy Chen1daf5822020-10-26 17:28:15 -0700591 tpl = Template(fin.read())
592 try:
593 out = tpl.render(cfg=cfg)
594
595 except: # noqa: E722
596 log.error(exceptions.text_error_template().render())
597
598 if out == "":
599 log.error("Cannot generate {}".format(names[idx]))
600 return
601
Cindy Liuc6db72f2022-03-24 14:12:53 -0700602 with outputs[idx].open(mode="w", encoding="UTF-8") as fout:
Timothy Chen1daf5822020-10-26 17:28:15 -0700603 fout.write(genhdr + out)
604
605 # Generate reg files
606 hjson_path = outputs[0]
Michael Schaffner3452c712022-01-21 20:15:12 -0800607 generate_regfile_from_path(hjson_path, rtl_path, original_rtl_path)
Timothy Chen1daf5822020-10-26 17:28:15 -0700608
609
Timothy Chen4c0e61f2021-11-11 14:32:53 -0800610def generate_top_only(top_only_dict, out_path, topname, alt_hjson_path):
Timothy Chen322f2542020-08-05 16:28:18 -0700611 log.info("Generating top only modules")
612
Timothy Chen4c0e61f2021-11-11 14:32:53 -0800613 for ip, reggen_only in top_only_dict.items():
614
615 if reggen_only and alt_hjson_path is not None:
616 hjson_dir = Path(alt_hjson_path)
617 else:
618 hjson_dir = Path(__file__).resolve(
619 ).parent / f"../hw/top_{topname}/ip/{ip}/data/"
620
621 hjson_path = hjson_dir / f"{ip}.hjson"
622
Pirmin Vogelfb8c4472020-11-25 18:08:34 +0100623 genrtl_dir = out_path / "ip/{}/rtl".format(ip)
624 genrtl_dir.mkdir(parents=True, exist_ok=True)
Timothy Chen322f2542020-08-05 16:28:18 -0700625 log.info("Generating top modules {}, hjson: {}, output: {}".format(
Pirmin Vogelfb8c4472020-11-25 18:08:34 +0100626 ip, hjson_path, genrtl_dir))
Timothy Chen322f2542020-08-05 16:28:18 -0700627
628 # Generate reg files
Michael Schaffner3452c712022-01-21 20:15:12 -0800629 generate_regfile_from_path(hjson_path, genrtl_dir)
Timothy Chen322f2542020-08-05 16:28:18 -0700630
631
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800632def generate_top_ral(top: Dict[str, object], name_to_block: Dict[str, IpBlock],
633 dv_base_names: List[str], out_path: str):
lowRISC Contributors802543a2019-08-31 12:12:56 +0100634 # construct top ral block
lowRISC Contributors802543a2019-08-31 12:12:56 +0100635
Cindy Liuc6db72f2022-03-24 14:12:53 -0700636 regwidth = int(top["datawidth"])
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000637 assert regwidth % 8 == 0
638 addrsep = regwidth // 8
lowRISC Contributors802543a2019-08-31 12:12:56 +0100639
Rupert Swarbrick200d8b42021-03-08 12:32:11 +0000640 # Generate a map from instance name to the block that it instantiates,
641 # together with a map of interface addresses.
642 inst_to_block = {} # type: Dict[str, str]
Cindy Liuc6db72f2022-03-24 14:12:53 -0700643 if_addrs = {} # type: Dict[Tuple[str, Optional[str]], int]
Timothy Chen62dabf72021-03-24 12:09:27 -0700644 attrs = {} # type: Dict[str, str]
Rupert Swarbrick200d8b42021-03-08 12:32:11 +0000645
Cindy Liuc6db72f2022-03-24 14:12:53 -0700646 for module in top["module"]:
647 inst_name = module["name"]
648 block_name = module["type"]
Rupert Swarbrick200d8b42021-03-08 12:32:11 +0000649 block = name_to_block[block_name]
Weicai Yangcf02fd22021-03-22 20:49:45 -0700650 if "attr" in module:
Cindy Liuc6db72f2022-03-24 14:12:53 -0700651 if module["attr"] not in ["templated", "ipgen", "reggen_top",
652 "reggen_only"]:
653 raise ValueError("Unsupported value for attr field of {}: {!r}"
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700654 .format(inst_name, module["attr"]))
Weicai Yangcf02fd22021-03-22 20:49:45 -0700655 attrs[inst_name] = module["attr"]
Rupert Swarbrick200d8b42021-03-08 12:32:11 +0000656
657 inst_to_block[inst_name] = block_name
658 for if_name in block.reg_blocks.keys():
659 if_addr = int(module["base_addrs"][if_name], 0)
660 if_addrs[(inst_name, if_name)] = if_addr
Rupert Swarbrickbc2bc582021-02-09 13:30:37 +0000661
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000662 # Collect up the memories to add
Rupert Swarbrick1db6fcd2021-02-11 14:56:20 +0000663 mems = []
Rupert Swarbrickbc2bc582021-02-09 13:30:37 +0000664 for item in list(top.get("memory", [])):
Weicai Yang7d2e9942021-07-27 23:16:24 -0700665 mems.append(create_mem(item, addrsep, regwidth))
Rupert Swarbrickbc2bc582021-02-09 13:30:37 +0000666
Weicai Yang7d2e9942021-07-27 23:16:24 -0700667 # Top-level may override the mem setting. Store the new type to name_to_block
668 # If no other instance uses the orignal type, delete it
669 original_types = set()
Cindy Liuc6db72f2022-03-24 14:12:53 -0700670 for module in top["module"]:
671 if "memory" in module.keys() and len(module["memory"]) > 0:
672 newtype = "{}_{}".format(module["type"], module["name"])
Weicai Yang7d2e9942021-07-27 23:16:24 -0700673 assert newtype not in name_to_block
674
Cindy Liuc6db72f2022-03-24 14:12:53 -0700675 block = deepcopy(name_to_block[module["type"]])
Weicai Yang7d2e9942021-07-27 23:16:24 -0700676 name_to_block[newtype] = block
Cindy Liuc6db72f2022-03-24 14:12:53 -0700677 inst_to_block[module["name"]] = newtype
Weicai Yang7d2e9942021-07-27 23:16:24 -0700678
Cindy Liuc6db72f2022-03-24 14:12:53 -0700679 original_types.add(module["type"])
Weicai Yang7d2e9942021-07-27 23:16:24 -0700680
Cindy Liuc6db72f2022-03-24 14:12:53 -0700681 for mem_name, item in module["memory"].items():
Weicai Yang7d2e9942021-07-27 23:16:24 -0700682 assert block.reg_blocks[mem_name]
683 assert len(block.reg_blocks[mem_name].windows) <= 1
Cindy Liuc6db72f2022-03-24 14:12:53 -0700684 item["name"] = mem_name
Weicai Yang7d2e9942021-07-27 23:16:24 -0700685
686 win = create_mem(item, addrsep, regwidth)
687 if len(block.reg_blocks[mem_name].windows) > 0:
688 blk_win = block.reg_blocks[mem_name].windows[0]
689
690 # Top can only add new info for mem, shouldn't overwrite
691 # existing configuration
692 assert win.items == blk_win.items
693 assert win.byte_write == blk_win.byte_write
694 assert win.data_intg_passthru == blk_win.data_intg_passthru
695
696 block.reg_blocks[mem_name].windows[0] = win
697 else:
698 block.reg_blocks[mem_name].windows.append(win)
699
700 for t in original_types:
701 if t not in inst_to_block.values():
702 del name_to_block[t]
lowRISC Contributors802543a2019-08-31 12:12:56 +0100703
Weicai Yangcf02fd22021-03-22 20:49:45 -0700704 chip = Top(regwidth, name_to_block, inst_to_block, if_addrs, mems, attrs)
Srikrishna Iyer1c0171a2019-10-29 11:59:46 -0700705
lowRISC Contributors802543a2019-08-31 12:12:56 +0100706 # generate the top ral model with template
Cindy Chene16009b2021-11-01 19:35:51 -0700707 return gen_dv(chip, dv_base_names, str(out_path))
lowRISC Contributors802543a2019-08-31 12:12:56 +0100708
709
Weicai Yang7d2e9942021-07-27 23:16:24 -0700710def create_mem(item, addrsep, regwidth):
Cindy Liuc6db72f2022-03-24 14:12:53 -0700711 byte_write = ("byte_write" in item and
Weicai Yang7d2e9942021-07-27 23:16:24 -0700712 item["byte_write"].lower() == "true")
Cindy Liuc6db72f2022-03-24 14:12:53 -0700713 data_intg_passthru = ("data_intg_passthru" in item and
Weicai Yang7d2e9942021-07-27 23:16:24 -0700714 item["data_intg_passthru"].lower() == "true")
Cindy Liuc6db72f2022-03-24 14:12:53 -0700715 size_in_bytes = int(item["size"], 0)
Weicai Yang7d2e9942021-07-27 23:16:24 -0700716 num_regs = size_in_bytes // addrsep
Cindy Liuc6db72f2022-03-24 14:12:53 -0700717 swaccess = access.SWAccess("top-level memory", item.get("swaccess", "rw"))
Weicai Yang7d2e9942021-07-27 23:16:24 -0700718
Cindy Liuc6db72f2022-03-24 14:12:53 -0700719 return window.Window(name=item["name"],
720 desc="(generated from top-level)",
Weicai Yang7d2e9942021-07-27 23:16:24 -0700721 unusual=False,
722 byte_write=byte_write,
723 data_intg_passthru=data_intg_passthru,
724 validbits=regwidth,
725 items=num_regs,
726 size_in_bytes=size_in_bytes,
Cindy Liuc6db72f2022-03-24 14:12:53 -0700727 offset=int(item.get("base_addr", "0"), 0),
Weicai Yang7d2e9942021-07-27 23:16:24 -0700728 swaccess=swaccess)
729
730
Timothy Chen315b1212021-01-22 11:08:03 -0800731def _process_top(topcfg, args, cfg_path, out_path, pass_idx):
732 # Create generated list
733 # These modules are generated through topgen
Timothy Chen65ff5102022-01-20 18:42:09 -0800734 templated_list = lib.get_templated_modules(topcfg)
735 log.info("Templated list is {}".format(templated_list))
736
737 ipgen_list = lib.get_ipgen_modules(topcfg)
738 log.info("Ip gen list is {}".format(ipgen_list))
739
740 generated_list = templated_list + ipgen_list
Timothy Chen315b1212021-01-22 11:08:03 -0800741
742 # These modules are NOT generated but belong to a specific top
743 # and therefore not part of "hw/ip"
Timothy Chen4c0e61f2021-11-11 14:32:53 -0800744 top_only_dict = {
Cindy Liuc6db72f2022-03-24 14:12:53 -0700745 module["type"]: lib.is_reggen_only(module)
746 for module in topcfg["module"] if lib.is_top_reggen(module)
Timothy Chen4c0e61f2021-11-11 14:32:53 -0800747 }
748 log.info("Filtered dict is {}".format(top_only_dict))
Timothy Chen315b1212021-01-22 11:08:03 -0800749
750 topname = topcfg["name"]
751
752 # Sweep the IP directory and gather the config files
Cindy Liuc6db72f2022-03-24 14:12:53 -0700753 ip_dir = Path(__file__).parents[1] / "hw/ip"
Timothy Chen315b1212021-01-22 11:08:03 -0800754 ips = search_ips(ip_dir)
755
756 # exclude filtered IPs (to use top_${topname} one) and
Timothy Chen4c0e61f2021-11-11 14:32:53 -0800757 exclude_list = generated_list + list(top_only_dict.keys())
Timothy Chen315b1212021-01-22 11:08:03 -0800758 ips = [x for x in ips if not x.parents[1].name in exclude_list]
759
760 # Hack alert
761 # Generate clkmgr.hjson here so that it can be included below
762 # Unlike other generated hjsons, clkmgr thankfully does not require
763 # ip.hjson information. All the information is embedded within
764 # the top hjson file
Cindy Liuc6db72f2022-03-24 14:12:53 -0700765 topcfg["clocks"] = Clocks(topcfg["clocks"])
Rupert Swarbrick5aa249f2021-07-19 18:35:11 +0100766 extract_clocks(topcfg)
Timothy Chen315b1212021-01-22 11:08:03 -0800767 generate_clkmgr(topcfg, cfg_path, out_path)
768
769 # It may require two passes to check if the module is needed.
770 # TODO: first run of topgen will fail due to the absent of rv_plic.
771 # It needs to run up to amend_interrupt in merge_top function
772 # then creates rv_plic.hjson then run xbar generation.
773 hjson_dir = Path(args.topcfg).parent
774
775 for ip in generated_list:
Timothy Chen744c3c02021-01-20 20:23:22 -0800776 # For modules that are generated prior to gathering, we need to take it from
Timothy Chen5302fff2021-01-22 14:36:54 -0800777 # the output path. For modules not generated before, it may exist in a
Timothy Chen744c3c02021-01-20 20:23:22 -0800778 # pre-defined area already.
Timothy Chen315b1212021-01-22 11:08:03 -0800779 log.info("Appending {}".format(ip))
Timothy Chen65ff5102022-01-20 18:42:09 -0800780 if ip in ipgen_list:
Cindy Liuc6db72f2022-03-24 14:12:53 -0700781 ip_relpath = "ip_autogen"
782 desc_file_relpath = "data"
Timothy Chen315b1212021-01-22 11:08:03 -0800783 else:
Cindy Liuc6db72f2022-03-24 14:12:53 -0700784 ip_relpath = "ip"
785 desc_file_relpath = "data/autogen"
Philipp Wagner270e28b2021-03-16 20:23:04 +0000786
Cindy Liuc6db72f2022-03-24 14:12:53 -0700787 if ip == "clkmgr" or (pass_idx > 0):
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800788 ip_hjson = (Path(out_path) / ip_relpath / ip / desc_file_relpath /
789 f"{ip}.hjson")
Philipp Wagner270e28b2021-03-16 20:23:04 +0000790 else:
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800791 ip_hjson = (hjson_dir.parent / ip_relpath / ip /
792 desc_file_relpath / f"{ip}.hjson")
Timothy Chen315b1212021-01-22 11:08:03 -0800793 ips.append(ip_hjson)
794
Timothy Chen4c0e61f2021-11-11 14:32:53 -0800795 for ip, reggen_only in top_only_dict.items():
Timothy Chen315b1212021-01-22 11:08:03 -0800796 log.info("Appending {}".format(ip))
Timothy Chen4c0e61f2021-11-11 14:32:53 -0800797
798 if reggen_only and args.hjson_path:
799 ip_hjson = Path(args.hjson_path) / f"{ip}.hjson"
800 else:
801 ip_hjson = hjson_dir.parent / f"ip/{ip}/data/{ip}.hjson"
802
Timothy Chen315b1212021-01-22 11:08:03 -0800803 ips.append(ip_hjson)
804
805 # load Hjson and pass validate from reggen
806 try:
807 ip_objs = []
Philipp Wagner270e28b2021-03-16 20:23:04 +0000808 for ip_desc_file in ips:
809 ip_name = ip_desc_file.stem
Timothy Chen315b1212021-01-22 11:08:03 -0800810 # Skip if it is not in the module list
Philipp Wagner270e28b2021-03-16 20:23:04 +0000811 if ip_name not in [ip["type"] for ip in topcfg["module"]]:
Timothy Chen315b1212021-01-22 11:08:03 -0800812 log.info("Skip module %s as it isn't in the top module list" %
Philipp Wagner270e28b2021-03-16 20:23:04 +0000813 ip_name)
Timothy Chen315b1212021-01-22 11:08:03 -0800814 continue
815
816 # The auto-generated hjson might not yet exist. It will be created
817 # later, see generate_{ip_name}() calls below. For the initial
Philipp Wagner270e28b2021-03-16 20:23:04 +0000818 # validation, use the Hjson file with default values.
819 # TODO: All of this is a rather ugly hack that we need to get rid
820 # of as soon as we don't arbitrarily template IP description Hjson
821 # files any more.
822 if ip_name in generated_list and not ip_desc_file.is_file():
Timothy Chen65ff5102022-01-20 18:42:09 -0800823 if ip_name in ipgen_list:
Philipp Wagner270e28b2021-03-16 20:23:04 +0000824 log.info(
825 "To-be-auto-generated Hjson %s does not yet exist. "
826 "Falling back to the default configuration of template "
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800827 "%s for initial validation." % (ip_desc_file, ip_name))
Timothy Chen315b1212021-01-22 11:08:03 -0800828
Cindy Liuc6db72f2022-03-24 14:12:53 -0700829 tpl_path = SRCTREE_TOP / "hw/ip_templates" / ip_name
Philipp Wagner270e28b2021-03-16 20:23:04 +0000830 ip_template = IpTemplate.from_template_path(tpl_path)
831 ip_config = IpConfig(ip_template.params,
Cindy Liuc6db72f2022-03-24 14:12:53 -0700832 f"top_{topname}_{ip_name}")
Philipp Wagner270e28b2021-03-16 20:23:04 +0000833
Philipp Wagner092b7552021-10-27 21:34:09 +0100834 try:
835 ip_desc = IpDescriptionOnlyRenderer(
836 ip_template, ip_config).render()
837 except TemplateRenderError as e:
838 log.error(e.verbose_str())
839 sys.exit(1)
Cindy Liuc6db72f2022-03-24 14:12:53 -0700840 s = "default description of IP template {}".format(ip_name)
Philipp Wagner270e28b2021-03-16 20:23:04 +0000841 ip_objs.append(IpBlock.from_text(ip_desc, [], s))
842 else:
843 # TODO: Remove this block as soon as all IP templates use
844 # ipgen.
845 template_hjson_file = ip_dir / "{}/data/{}.hjson".format(
846 ip_name, ip_name)
847 log.info(
848 "To-be-auto-generated Hjson %s does not yet exist. "
849 "Falling back to Hjson description file %s shipped "
850 "with the IP template for initial validation." %
851 (ip_desc_file, template_hjson_file))
852
853 ip_objs.append(
854 IpBlock.from_path(str(template_hjson_file), []))
855 else:
856 ip_objs.append(IpBlock.from_path(str(ip_desc_file), []))
Timothy Chen315b1212021-01-22 11:08:03 -0800857
858 except ValueError:
859 raise SystemExit(sys.exc_info()[1])
860
Rupert Swarbrick5aa249f2021-07-19 18:35:11 +0100861 name_to_block = {} # type: Dict[str, IpBlock]
862 for block in ip_objs:
863 lblock = block.name.lower()
864 assert lblock not in name_to_block
865 name_to_block[lblock] = block
866
867 connect_clocks(topcfg, name_to_block)
868
Timothy Chen315b1212021-01-22 11:08:03 -0800869 # Read the crossbars under the top directory
870 xbar_objs = get_hjsonobj_xbars(hjson_dir)
871
872 log.info("Detected crossbars: %s" %
873 (", ".join([x["name"] for x in xbar_objs])))
874
875 # If specified, override the seed for random netlist constant computation.
876 if args.rnd_cnst_seed:
Cindy Liuc6db72f2022-03-24 14:12:53 -0700877 log.warning("Commandline override of rnd_cnst_seed with {}.".format(
Timothy Chen315b1212021-01-22 11:08:03 -0800878 args.rnd_cnst_seed))
Cindy Liuc6db72f2022-03-24 14:12:53 -0700879 topcfg["rnd_cnst_seed"] = args.rnd_cnst_seed
Timothy Chen315b1212021-01-22 11:08:03 -0800880 # Otherwise, we either take it from the top_{topname}.hjson if present, or
881 # randomly generate a new seed if not.
882 else:
883 random.seed()
884 new_seed = random.getrandbits(64)
Cindy Liuc6db72f2022-03-24 14:12:53 -0700885 if topcfg.setdefault("rnd_cnst_seed", new_seed) == new_seed:
Timothy Chen315b1212021-01-22 11:08:03 -0800886 log.warning(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700887 "No rnd_cnst_seed specified, setting to {}.".format(new_seed))
Timothy Chen315b1212021-01-22 11:08:03 -0800888
889 topcfg, error = validate_top(topcfg, ip_objs, xbar_objs)
890 if error != 0:
891 raise SystemExit("Error occured while validating top.hjson")
892
Rupert Swarbrickeb619e62021-03-05 15:01:54 +0000893 completecfg = merge_top(topcfg, name_to_block, xbar_objs)
Timothy Chen315b1212021-01-22 11:08:03 -0800894
Timothy Chen744c3c02021-01-20 20:23:22 -0800895 # Generate flash controller and flash memory
896 generate_flash(topcfg, out_path)
897
Timothy Chen315b1212021-01-22 11:08:03 -0800898 # Generate PLIC
899 if not args.no_plic and \
900 not args.alert_handler_only and \
901 not args.xbar_only:
902 generate_plic(completecfg, out_path)
903 if args.plic_only:
904 sys.exit()
905
Michael Schaffner4fe78462021-09-28 17:06:07 -0700906 # Create Alert Handler LPGs before
907 # generating the Alert Handler
908 create_alert_lpgs(topcfg, name_to_block)
909
Timothy Chen315b1212021-01-22 11:08:03 -0800910 # Generate Alert Handler
911 if not args.xbar_only:
912 generate_alert_handler(completecfg, out_path)
913 if args.alert_handler_only:
914 sys.exit()
915
916 # Generate Pinmux
Michael Schaffner43ce8d52021-02-10 17:04:57 -0800917 generate_pinmux(completecfg, out_path)
Timothy Chen315b1212021-01-22 11:08:03 -0800918
919 # Generate Pwrmgr
920 generate_pwrmgr(completecfg, out_path)
921
922 # Generate rstmgr
923 generate_rstmgr(completecfg, out_path)
924
Timothy Chen315b1212021-01-22 11:08:03 -0800925 # Generate top only modules
926 # These modules are not templated, but are not in hw/ip
Timothy Chen4c0e61f2021-11-11 14:32:53 -0800927 generate_top_only(top_only_dict, out_path, topname, args.hjson_path)
Timothy Chen315b1212021-01-22 11:08:03 -0800928
Rupert Swarbrickeb619e62021-03-05 15:01:54 +0000929 return completecfg, name_to_block
Timothy Chen315b1212021-01-22 11:08:03 -0800930
931
lowRISC Contributors802543a2019-08-31 12:12:56 +0100932def main():
933 parser = argparse.ArgumentParser(prog="topgen")
Cindy Liuc6db72f2022-03-24 14:12:53 -0700934 parser.add_argument("--topcfg",
935 "-t",
Eunchan Kimc6a6a3b2019-09-12 14:27:43 -0700936 required=True,
937 help="`top_{name}.hjson` file.")
Eunchan Kim632c6f72019-09-30 11:11:51 -0700938 parser.add_argument(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700939 "--outdir",
940 "-o",
941 help="""Target TOP directory.
lowRISC Contributors802543a2019-08-31 12:12:56 +0100942 Module is created under rtl/. (default: dir(topcfg)/..)
Cindy Liuc6db72f2022-03-24 14:12:53 -0700943 """) # yapf: disable
Timothy Chen4c0e61f2021-11-11 14:32:53 -0800944 parser.add_argument(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700945 "--hjson-path",
946 help="""
Timothy Chen4c0e61f2021-11-11 14:32:53 -0800947 If defined, topgen uses supplied path to search for ip hjson.
948 This applies only to ip's with the `reggen_only` attribute.
949 If an hjson is located both in the conventional path and the alternate
950 path, the alternate path has priority.
Cindy Liuc6db72f2022-03-24 14:12:53 -0700951 """)
952 parser.add_argument("--verbose", "-v", action="store_true", help="Verbose")
lowRISC Contributors802543a2019-08-31 12:12:56 +0100953
954 # Generator options: 'no' series. cannot combined with 'only' series
955 parser.add_argument(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700956 "--no-top",
957 action="store_true",
lowRISC Contributors802543a2019-08-31 12:12:56 +0100958 help="If defined, topgen doesn't generate top_{name} RTLs.")
959 parser.add_argument(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700960 "--no-xbar",
961 action="store_true",
lowRISC Contributors802543a2019-08-31 12:12:56 +0100962 help="If defined, topgen doesn't generate crossbar RTLs.")
963 parser.add_argument(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700964 "--no-plic",
965 action="store_true",
lowRISC Contributors802543a2019-08-31 12:12:56 +0100966 help="If defined, topgen doesn't generate the interrup controller RTLs."
967 )
lowRISC Contributors802543a2019-08-31 12:12:56 +0100968
969 # Generator options: 'only' series. cannot combined with 'no' series
970 parser.add_argument(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700971 "--top-only",
972 action="store_true",
Eunchan Kim6599ba92020-04-13 15:27:16 -0700973 help="If defined, the tool generates top RTL only") # yapf:disable
lowRISC Contributors802543a2019-08-31 12:12:56 +0100974 parser.add_argument(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700975 "--xbar-only",
976 action="store_true",
lowRISC Contributors802543a2019-08-31 12:12:56 +0100977 help="If defined, the tool generates crossbar RTLs only")
978 parser.add_argument(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700979 "--plic-only",
980 action="store_true",
Philipp Wagner14a3fee2019-11-21 10:07:02 +0000981 help="If defined, the tool generates RV_PLIC RTL and Hjson only")
lowRISC Contributors802543a2019-08-31 12:12:56 +0100982 parser.add_argument(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700983 "--alert-handler-only",
984 action="store_true",
Michael Schaffner666dde12019-10-25 11:57:54 -0700985 help="If defined, the tool generates alert handler hjson only")
lowRISC Contributors802543a2019-08-31 12:12:56 +0100986 # Generator options: generate dv ral model
987 parser.add_argument(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700988 "--top_ral",
989 "-r",
lowRISC Contributors802543a2019-08-31 12:12:56 +0100990 default=False,
Cindy Liuc6db72f2022-03-24 14:12:53 -0700991 action="store_true",
lowRISC Contributors802543a2019-08-31 12:12:56 +0100992 help="If set, the tool generates top level RAL model for DV")
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800993 parser.add_argument(
Cindy Liuc6db72f2022-03-24 14:12:53 -0700994 "--dv-base-names",
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -0800995 nargs="+",
Cindy Liuc6db72f2022-03-24 14:12:53 -0700996 help="Names or prefix for the DV register classes from which "
997 "the register models are derived.")
Michael Schaffner7b0807d2020-10-27 19:54:52 -0700998 # Generator options for compile time random netlist constants
Weicai Yanga6670362020-11-24 17:19:52 -0800999 parser.add_argument(
Cindy Liuc6db72f2022-03-24 14:12:53 -07001000 "--rnd_cnst_seed",
Weicai Yanga6670362020-11-24 17:19:52 -08001001 type=int,
Cindy Liuc6db72f2022-03-24 14:12:53 -07001002 metavar="<seed>",
1003 help="Custom seed for RNG to compute netlist constants.")
Srikrishna Iyere42c8f62021-08-20 01:06:40 -07001004 # Miscellaneous: only return the list of blocks and exit.
Cindy Liuc6db72f2022-03-24 14:12:53 -07001005 parser.add_argument("--get_blocks",
Srikrishna Iyere42c8f62021-08-20 01:06:40 -07001006 default=False,
Cindy Liuc6db72f2022-03-24 14:12:53 -07001007 action="store_true",
Srikrishna Iyere42c8f62021-08-20 01:06:40 -07001008 help="Only return the list of blocks and exit.")
lowRISC Contributors802543a2019-08-31 12:12:56 +01001009
1010 args = parser.parse_args()
1011
1012 # check combinations
1013 if args.top_ral:
lowRISC Contributors802543a2019-08-31 12:12:56 +01001014 args.no_top = True
1015
lowRISC Contributors802543a2019-08-31 12:12:56 +01001016 if (args.no_top or args.no_xbar or
1017 args.no_plic) and (args.top_only or args.xbar_only or
Michael Schaffner666dde12019-10-25 11:57:54 -07001018 args.plic_only or args.alert_handler_only):
lowRISC Contributors802543a2019-08-31 12:12:56 +01001019 log.error(
1020 "'no' series options cannot be used with 'only' series options")
1021 raise SystemExit(sys.exc_info()[1])
1022
Srikrishna Iyere42c8f62021-08-20 01:06:40 -07001023 # Don't print warnings when querying the list of blocks.
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -08001024 log_level = (log.ERROR
1025 if args.get_blocks else log.DEBUG if args.verbose else None)
Rupert Swarbrick9bfdaab2021-08-27 16:16:04 +01001026
Srikrishna Iyere42c8f62021-08-20 01:06:40 -07001027 log.basicConfig(format="%(levelname)s: %(message)s", level=log_level)
lowRISC Contributors802543a2019-08-31 12:12:56 +01001028
1029 if not args.outdir:
1030 outdir = Path(args.topcfg).parent / ".."
1031 log.info("TOP directory not given. Use %s", (outdir))
1032 elif not Path(args.outdir).is_dir():
1033 log.error("'--outdir' should point to writable directory")
1034 raise SystemExit(sys.exc_info()[1])
1035 else:
1036 outdir = Path(args.outdir)
1037
Timothy Chen4c0e61f2021-11-11 14:32:53 -08001038 if args.hjson_path is not None:
Srikrishna Iyer934ef172021-11-20 21:07:17 -08001039 log.info(f"Alternate hjson path is {args.hjson_path}")
Timothy Chen4c0e61f2021-11-11 14:32:53 -08001040
lowRISC Contributors802543a2019-08-31 12:12:56 +01001041 out_path = Path(outdir)
Timothy Chenf56c1b52020-04-28 17:00:43 -07001042 cfg_path = Path(args.topcfg).parents[1]
lowRISC Contributors802543a2019-08-31 12:12:56 +01001043
Eunchan Kim66f7ae22020-08-03 23:24:53 -07001044 try:
Cindy Liuc6db72f2022-03-24 14:12:53 -07001045 with open(args.topcfg, "r") as ftop:
Eunchan Kim66f7ae22020-08-03 23:24:53 -07001046 topcfg = hjson.load(ftop,
1047 use_decimal=True,
1048 object_pairs_hook=OrderedDict)
1049 except ValueError:
1050 raise SystemExit(sys.exc_info()[1])
lowRISC Contributors802543a2019-08-31 12:12:56 +01001051
Timothy Chen315b1212021-01-22 11:08:03 -08001052 # TODO, long term, the levels of dependency should be automatically determined instead
1053 # of hardcoded. The following are a few examples:
1054 # Example 1: pinmux depends on amending all modules before calculating the correct number of
1055 # pins.
1056 # This would be 1 level of dependency and require 2 passes.
1057 # Example 2: pinmux depends on amending all modules, and pwrmgr depends on pinmux generation to
1058 # know correct number of wakeups. This would be 2 levels of dependency and require 3
1059 # passes.
1060 #
1061 # How does mulit-pass work?
1062 # In example 1, the first pass gathers all modules and merges them. However, the merge process
1063 # uses a stale pinmux. The correct pinmux is then generated using the merged configuration. The
1064 # second pass now merges all the correct modules (including the generated pinmux) and creates
1065 # the final merged config.
1066 #
1067 # In example 2, the first pass gathers all modules and merges them. However, the merge process
1068 # uses a stale pinmux and pwrmgr. The correct pinmux is then generated using the merged
1069 # configuration. However, since pwrmgr is dependent on this new pinmux, it is still generated
1070 # incorrectly. The second pass merge now has an updated pinmux but stale pwrmgr. The correct
1071 # pwrmgr can now be generated. The final pass then merges all the correct modules and creates
1072 # the final configuration.
1073 #
1074 # This fix is related to #2083
1075 process_dependencies = 1
Philipp Wagnerf9ee7972021-10-28 16:21:02 +01001076
1077 # topgen generates IP blocks and associated Hjson configuration in multiple
1078 # steps. After each step, the IP Hjson configuration is read back and then
1079 # combined into the toplevel configuration. To generate the chip-level RAL,
1080 # we need to run the full generation step, but ultimately only care about
1081 # the toplevel configuration (a single Hjson file). Since we don't have a
1082 # better way at the moment dump all output into a temporary directory, and
1083 # delete it after the fact, retaining only the toplevel configuration.
1084 if args.top_ral:
1085 out_path_gen = Path(tempfile.mkdtemp())
1086 else:
1087 out_path_gen = out_path
1088
Timothy Chen315b1212021-01-22 11:08:03 -08001089 for pass_idx in range(process_dependencies + 1):
1090 log.debug("Generation pass {}".format(pass_idx))
1091 if pass_idx < process_dependencies:
1092 cfg_copy = deepcopy(topcfg)
Philipp Wagnerf9ee7972021-10-28 16:21:02 +01001093 _process_top(cfg_copy, args, cfg_path, out_path_gen, pass_idx)
Timothy Chen315b1212021-01-22 11:08:03 -08001094 else:
Philipp Wagnerf9ee7972021-10-28 16:21:02 +01001095 completecfg, name_to_block = _process_top(topcfg, args, cfg_path,
1096 out_path_gen, pass_idx)
Timothy Chenf56c1b52020-04-28 17:00:43 -07001097
Eunchan Kim66f7ae22020-08-03 23:24:53 -07001098 topname = topcfg["name"]
Eunchan Kimba970df2020-04-17 10:21:01 -07001099
Philipp Wagnerf9ee7972021-10-28 16:21:02 +01001100 # Create the chip-level RAL only
1101 if args.top_ral:
1102 # See above: we only need `completeconfig` and `name_to_block`, not all
1103 # the other files (e.g. RTL files) generated through topgen.
1104 shutil.rmtree(out_path_gen, ignore_errors=True)
1105
1106 exit_code = generate_top_ral(completecfg, name_to_block,
Cindy Chene16009b2021-11-01 19:35:51 -07001107 args.dv_base_names, out_path)
Philipp Wagnerf9ee7972021-10-28 16:21:02 +01001108 sys.exit(exit_code)
1109
Srikrishna Iyere42c8f62021-08-20 01:06:40 -07001110 if args.get_blocks:
1111 print("\n".join(name_to_block.keys()))
1112 sys.exit(0)
1113
lowRISC Contributors802543a2019-08-31 12:12:56 +01001114 # Generate xbars
1115 if not args.no_xbar or args.xbar_only:
1116 generate_xbars(completecfg, out_path)
1117
Eunchan Kime0d37fe2020-08-03 12:05:21 -07001118 # All IPs are generated. Connect phase now
Eunchan Kim2c813b42020-08-03 16:22:56 -07001119 # Find {memory, module} <-> {xbar} connections first.
Rupert Swarbricka5e687f2021-03-01 11:51:41 +00001120 im.autoconnect(completecfg, name_to_block)
Eunchan Kim2c813b42020-08-03 16:22:56 -07001121
1122 # Generic Inter-module connection
1123 im.elab_intermodule(completecfg)
Eunchan Kime0d37fe2020-08-03 12:05:21 -07001124
Eunchan Kime0d37fe2020-08-03 12:05:21 -07001125 # Generate top.gen.hjson right before rendering
Philipp Wagnerfb443ab2021-03-05 11:10:28 +00001126 genhjson_dir = out_path / "data/autogen"
Pirmin Vogel71bfd9b2020-11-24 17:28:20 +01001127 genhjson_dir.mkdir(parents=True, exist_ok=True)
1128 genhjson_path = genhjson_dir / ("top_%s.gen.hjson" % completecfg["name"])
Michael Schaffner7b0807d2020-10-27 19:54:52 -07001129
1130 # Header for HJSON
Cindy Liuc6db72f2022-03-24 14:12:53 -07001131 gencmd = """//
Michael Schaffner7b0807d2020-10-27 19:54:52 -07001132// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson \\
1133// -o hw/top_{topname}/ \\
1134// --hjson-only \\
1135// --rnd_cnst_seed {seed}
Cindy Liuc6db72f2022-03-24 14:12:53 -07001136""".format(topname=topname, seed=completecfg["rnd_cnst_seed"])
Eunchan Kime0d37fe2020-08-03 12:05:21 -07001137
Eunchan Kim66f7ae22020-08-03 23:24:53 -07001138 genhjson_path.write_text(genhdr + gencmd +
Pirmin Vogel432474a2022-04-22 17:06:40 +02001139 hjson.dumps(completecfg, for_json=True) + '\n')
Eunchan Kime0d37fe2020-08-03 12:05:21 -07001140
lowRISC Contributors802543a2019-08-31 12:12:56 +01001141 if not args.no_top or args.top_only:
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -08001142
1143 def render_template(template_path: str, rendered_path: Path,
1144 **other_info):
Rupert Swarbrickeb619e62021-03-05 15:01:54 +00001145 template_contents = generate_top(completecfg, name_to_block,
Philipp Wagnerfb443ab2021-03-05 11:10:28 +00001146 str(template_path), **other_info)
lowRISC Contributors802543a2019-08-31 12:12:56 +01001147
Philipp Wagnerfb443ab2021-03-05 11:10:28 +00001148 rendered_path.parent.mkdir(exist_ok=True, parents=True)
Cindy Liuc6db72f2022-03-24 14:12:53 -07001149 with rendered_path.open(mode="w", encoding="UTF-8") as fout:
Sam Elliott7e36bd72020-04-22 14:05:49 +01001150 fout.write(template_contents)
Eunchan Kim436d2242019-10-29 17:25:51 -07001151
Michael Schaffner7b0807d2020-10-27 19:54:52 -07001152 # Header for SV files
Cindy Liuc6db72f2022-03-24 14:12:53 -07001153 gencmd = warnhdr + """//
Michael Schaffner7b0807d2020-10-27 19:54:52 -07001154// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson \\
Michael Schaffner7b0807d2020-10-27 19:54:52 -07001155// -o hw/top_{topname}/ \\
1156// --rnd_cnst_seed {seed}
Cindy Liuc6db72f2022-03-24 14:12:53 -07001157""".format(topname=topname, seed=topcfg["rnd_cnst_seed"])
Michael Schaffner7b0807d2020-10-27 19:54:52 -07001158
Sam Elliott7e36bd72020-04-22 14:05:49 +01001159 # SystemVerilog Top:
Cindy Liuc6db72f2022-03-24 14:12:53 -07001160 # "toplevel.sv.tpl" -> "rtl/autogen/top_{topname}.sv"
Philipp Wagnerfb443ab2021-03-05 11:10:28 +00001161 render_template(TOPGEN_TEMPLATE_PATH / "toplevel.sv.tpl",
1162 out_path / f"rtl/autogen/top_{topname}.sv",
1163 gencmd=gencmd)
Eunchan Kim436d2242019-10-29 17:25:51 -07001164
Michael Schaffner74c4ff22021-03-30 15:43:46 -07001165 # Multiple chip-levels (ASIC, FPGA, Verilator, etc)
Cindy Liuc6db72f2022-03-24 14:12:53 -07001166 for target in topcfg["targets"]:
1167 target_name = target["name"]
Michael Schaffner74c4ff22021-03-30 15:43:46 -07001168 render_template(TOPGEN_TEMPLATE_PATH / "chiplevel.sv.tpl",
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -08001169 out_path /
Cindy Liuc6db72f2022-03-24 14:12:53 -07001170 f"rtl/autogen/chip_{topname}_{target_name}.sv",
Michael Schaffner74c4ff22021-03-30 15:43:46 -07001171 gencmd=gencmd,
1172 target=target)
1173
Srikrishna Iyerd7bed872020-10-14 11:51:10 -07001174 # The C / SV file needs some complex information, so we initialize this
Sam Elliottd1790472020-07-24 23:25:10 +01001175 # object to store it.
Srikrishna Iyer689981e2021-09-16 12:12:37 -07001176 c_helper = TopGenCTest(completecfg, name_to_block)
Sam Elliott7e36bd72020-04-22 14:05:49 +01001177
Cindy Liuc6db72f2022-03-24 14:12:53 -07001178 # "toplevel_pkg.sv.tpl" -> "rtl/autogen/top_{topname}_pkg.sv"
Philipp Wagnerfb443ab2021-03-05 11:10:28 +00001179 render_template(TOPGEN_TEMPLATE_PATH / "toplevel_pkg.sv.tpl",
1180 out_path / f"rtl/autogen/top_{topname}_pkg.sv",
Michael Schaffner7b0807d2020-10-27 19:54:52 -07001181 helper=c_helper,
Weicai Yanga6670362020-11-24 17:19:52 -08001182 gencmd=gencmd)
Michael Schaffner7b0807d2020-10-27 19:54:52 -07001183
1184 # compile-time random netlist constants
Philipp Wagnerfb443ab2021-03-05 11:10:28 +00001185 render_template(TOPGEN_TEMPLATE_PATH / "toplevel_rnd_cnst_pkg.sv.tpl",
Srikrishna Iyeraea37dd2021-11-20 21:11:40 -08001186 out_path /
1187 f"rtl/autogen/top_{topname}_rnd_cnst_pkg.sv",
Philipp Wagnerfb443ab2021-03-05 11:10:28 +00001188 gencmd=gencmd)
Srikrishna Iyerd7bed872020-10-14 11:51:10 -07001189
1190 # C Header + C File + Clang-format file
1191
Pirmin Vogel5c7c19a2020-12-01 13:53:13 +01001192 # Since SW does not use FuseSoC and instead expects those files always
1193 # to be in hw/top_{topname}/sw/autogen, we currently create these files
1194 # twice:
1195 # - Once under out_path/sw/autogen
1196 # - Once under hw/top_{topname}/sw/autogen
Cindy Liu2b8c7f52022-03-22 22:55:14 -07001197 root_paths = [out_path.resolve(), SRCTREE_TOP]
1198 out_paths = [
1199 out_path.resolve(),
1200 (SRCTREE_TOP / "hw/top_{}/".format(topname)).resolve()
1201 ]
1202 for idx, path in enumerate(out_paths):
Cindy Liuc6db72f2022-03-24 14:12:53 -07001203 # "clang-format" -> "sw/autogen/.clang-format"
1204 cformat_tplpath = TOPGEN_TEMPLATE_PATH / "clang-format"
1205 cformat_dir = path / "sw/autogen"
Pirmin Vogel5c7c19a2020-12-01 13:53:13 +01001206 cformat_dir.mkdir(parents=True, exist_ok=True)
Cindy Liuc6db72f2022-03-24 14:12:53 -07001207 cformat_path = cformat_dir / ".clang-format"
Pirmin Vogel5c7c19a2020-12-01 13:53:13 +01001208 cformat_path.write_text(cformat_tplpath.read_text())
Sam Elliott7e36bd72020-04-22 14:05:49 +01001209
Cindy Liu2b8c7f52022-03-22 22:55:14 -07001210 # Save the header macro prefix into `c_helper`
1211 rel_header_dir = cformat_dir.relative_to(root_paths[idx])
1212 c_helper.header_macro_prefix = (
1213 "OPENTITAN_" + str(rel_header_dir).replace("/", "_").upper())
1214
Cindy Liuc6db72f2022-03-24 14:12:53 -07001215 # "top_{topname}.h.tpl" -> "sw/autogen/top_{topname}.h"
Philipp Wagnerfb443ab2021-03-05 11:10:28 +00001216 cheader_path = cformat_dir / f"top_{topname}.h"
1217 render_template(TOPGEN_TEMPLATE_PATH / "toplevel.h.tpl",
1218 cheader_path,
1219 helper=c_helper)
Sam Elliott7e36bd72020-04-22 14:05:49 +01001220
Cindy Liu2b8c7f52022-03-22 22:55:14 -07001221 # Save the relative header path into `c_helper`
1222 rel_header_path = cheader_path.relative_to(root_paths[idx])
Pirmin Vogel5c7c19a2020-12-01 13:53:13 +01001223 c_helper.header_path = str(rel_header_path)
lowRISC Contributors802543a2019-08-31 12:12:56 +01001224
Cindy Liuc6db72f2022-03-24 14:12:53 -07001225 # "toplevel.c.tpl" -> "sw/autogen/top_{topname}.c"
Philipp Wagnerfb443ab2021-03-05 11:10:28 +00001226 render_template(TOPGEN_TEMPLATE_PATH / "toplevel.c.tpl",
1227 cformat_dir / f"top_{topname}.c",
1228 helper=c_helper)
Sam Elliott519f7462020-05-11 16:53:24 +01001229
Cindy Liuc6db72f2022-03-24 14:12:53 -07001230 # "toplevel_memory.ld.tpl" -> "sw/autogen/top_{topname}_memory.ld"
Philipp Wagnerfb443ab2021-03-05 11:10:28 +00001231 render_template(TOPGEN_TEMPLATE_PATH / "toplevel_memory.ld.tpl",
1232 cformat_dir / f"top_{topname}_memory.ld")
Sam Elliottf7cb5f42020-07-27 21:29:26 +01001233
Cindy Liuc6db72f2022-03-24 14:12:53 -07001234 # "toplevel_memory.h.tpl" -> "sw/autogen/top_{topname}_memory.h"
Philipp Wagnerfb443ab2021-03-05 11:10:28 +00001235 memory_cheader_path = cformat_dir / f"top_{topname}_memory.h"
1236 render_template(TOPGEN_TEMPLATE_PATH / "toplevel_memory.h.tpl",
Timothy Chen4ca4a2f2021-03-19 13:51:39 -07001237 memory_cheader_path,
1238 helper=c_helper)
Pirmin Vogel5c7c19a2020-12-01 13:53:13 +01001239
Cindy Chene1184aa2020-09-01 11:45:07 -07001240 # generate chip level xbar and alert_handler TB
Weicai Yanga6670362020-11-24 17:19:52 -08001241 tb_files = [
1242 "xbar_env_pkg__params.sv", "tb__xbar_connect.sv",
Weicai Yang3a2d0bc2021-08-25 15:41:41 -07001243 "tb__alert_handler_connect.sv", "xbar_tgl_excl.cfg"
Weicai Yanga6670362020-11-24 17:19:52 -08001244 ]
Weicai Yang1777ebb2020-05-19 17:32:24 -07001245 for fname in tb_files:
1246 tpl_fname = "%s.tpl" % (fname)
Philipp Wagnerfb443ab2021-03-05 11:10:28 +00001247 xbar_chip_data_path = TOPGEN_TEMPLATE_PATH / tpl_fname
Rupert Swarbrickeb619e62021-03-05 15:01:54 +00001248 template_contents = generate_top(completecfg, name_to_block,
Weicai Yang1777ebb2020-05-19 17:32:24 -07001249 str(xbar_chip_data_path))
1250
Cindy Liuc6db72f2022-03-24 14:12:53 -07001251 rendered_dir = out_path / "dv/autogen"
Weicai Yang1777ebb2020-05-19 17:32:24 -07001252 rendered_dir.mkdir(parents=True, exist_ok=True)
1253 rendered_path = rendered_dir / fname
1254
Cindy Liuc6db72f2022-03-24 14:12:53 -07001255 with rendered_path.open(mode="w", encoding="UTF-8") as fout:
Weicai Yang1777ebb2020-05-19 17:32:24 -07001256 fout.write(template_contents)
1257
Philipp Wagner44071092021-03-05 19:29:12 +00001258 # generate parameters for chip-level environment package
Cindy Liuc6db72f2022-03-24 14:12:53 -07001259 tpl_fname = "chip_env_pkg__params.sv.tpl"
Philipp Wagnerfb443ab2021-03-05 11:10:28 +00001260 alert_handler_chip_data_path = TOPGEN_TEMPLATE_PATH / tpl_fname
Rupert Swarbrickeb619e62021-03-05 15:01:54 +00001261 template_contents = generate_top(completecfg, name_to_block,
Cindy Chene1184aa2020-09-01 11:45:07 -07001262 str(alert_handler_chip_data_path))
1263
Cindy Liuc6db72f2022-03-24 14:12:53 -07001264 rendered_dir = out_path / "dv/env/autogen"
Cindy Chene1184aa2020-09-01 11:45:07 -07001265 rendered_dir.mkdir(parents=True, exist_ok=True)
Cindy Liuc6db72f2022-03-24 14:12:53 -07001266 rendered_path = rendered_dir / "chip_env_pkg__params.sv"
Cindy Chene1184aa2020-09-01 11:45:07 -07001267
Cindy Liuc6db72f2022-03-24 14:12:53 -07001268 with rendered_path.open(mode="w", encoding="UTF-8") as fout:
Cindy Chene1184aa2020-09-01 11:45:07 -07001269 fout.write(template_contents)
1270
Michael Schaffner426ed202021-08-02 17:44:42 -07001271 # generate documentation for toplevel
1272 gen_top_docs(completecfg, c_helper, out_path)
1273
Cindy Liuc6db72f2022-03-24 14:12:53 -07001274 # Auto-generate tests in "sw/device/tests/autogen" area.
Srikrishna Iyer689981e2021-09-16 12:12:37 -07001275 gencmd = warnhdr + GENCMD.format(topname=topname)
Cindy Chen21447a22022-04-18 17:55:25 -07001276 for fname in ["plic_all_irqs_test.c", "alert_test.c", "meson.build", "BUILD"]:
Cindy Liuc6db72f2022-03-24 14:12:53 -07001277 outfile = SRCTREE_TOP / "sw/device/tests/autogen" / fname
Srikrishna Iyer689981e2021-09-16 12:12:37 -07001278 render_template(TOPGEN_TEMPLATE_PATH / f"{fname}.tpl",
1279 outfile,
1280 helper=c_helper,
1281 gencmd=gencmd)
Srikrishna Iyer689981e2021-09-16 12:12:37 -07001282
Sam Elliott37d4fbe2020-04-22 14:05:49 +01001283
lowRISC Contributors802543a2019-08-31 12:12:56 +01001284if __name__ == "__main__":
1285 main()