blob: 8e411cca4024f3d872fef477de5d9a04b9765d83 [file] [log] [blame]
Miguel Young de la Sotaff15b052020-09-09 11:26:17 -04001#!/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
Timothy Trippelce9232e2021-09-13 22:48:18 +00005"""make_new_dif.py is a script for instantiating templates needed to begin
6development on a new DIF.
Miguel Young de la Sotaff15b052020-09-09 11:26:17 -04007
Timothy Trippeled24b152021-11-03 03:05:04 +00008To instantiate the files for a new IP named ip_ctrl, run the command:
9
10$ util/make_new_dif.py --ip-name-snake "ip_ctrl" --ip-name-long "IP Controller"
11
12where "IP Controller" is a documentation-friendly name for the IP.
13For example, compare "pwrmgr" and "Power Manager".
Timothy Trippelce9232e2021-09-13 22:48:18 +000014
15It will instantiate:
Timothy Trippeled24b152021-11-03 03:05:04 +000016- `sw/device/lib/dif/dif_template.h.tpl` as the DIF Header boilerplate
17 (into `dif_<ip>.h`), which should be manually edited/enhanced.
Timothy Trippelce9232e2021-09-13 22:48:18 +000018- `sw/device/lib/dif/templates/dif_autogen.h.tpl` as the autogenerated DIF
Timothy Trippeled24b152021-11-03 03:05:04 +000019 Header (into `dif_<ip>_autogen.h`).
20- `sw/device/lib/dif/templates/dif_autogen.c.tpl` as the autogenerated DIF
21 implementation (into `dif_<ip>_autogen.c`).
22- `sw/device/lib/dif/templates/dif_autogen_unittest.cc.tpl` as the
23 autogenerated DIF unit tests (into `dif_<ip>_autogen_unittest.cc`).
24- `doc/project/sw_checklist.md.tpl` as the DIF Checklist (into dif_<ip>.md),
25 which should be manually edited.
Timothy Trippelce9232e2021-09-13 22:48:18 +000026
27See both templates for more information.
28
29You can also use the `--only=header`, `--only=autogen`, `--only=checklist` to
30instantiate a subset of the templates. This can be passed multiple times, and
31including `--only=all` will instantiate every template.
32
33Note: the non-autogenerated files will still need some cleaning up before they
34can be used.
35"""
Miguel Young de la Sotaff15b052020-09-09 11:26:17 -040036
37import argparse
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +000038import glob
Timothy Trippelce9232e2021-09-13 22:48:18 +000039import logging
40import shutil
41import subprocess
42import sys
Miguel Young de la Sotaff15b052020-09-09 11:26:17 -040043from pathlib import Path
44
Timothy Trippel48a9fb72022-02-10 22:36:40 -080045import hjson
Miguel Young de la Sotaff15b052020-09-09 11:26:17 -040046from mako.template import Template
47
Timothy Trippel48a9fb72022-02-10 22:36:40 -080048import topgen.lib as lib
Timothy Trippel2f945982021-11-03 22:31:07 +000049from autogen_banner import get_autogen_banner
Timothy Trippel553475e2022-02-22 18:05:28 -080050from autogen_testutils.gen import gen_testutils
Timothy Trippel1c4f7c22021-10-27 21:46:38 +000051from make_new_dif.ip import Ip
52
Miguel Young de la Sotaff15b052020-09-09 11:26:17 -040053# This file is $REPO_TOP/util/make_new_dif.py, so it takes two parent()
54# calls to get back to the top.
55REPO_TOP = Path(__file__).resolve().parent.parent
56
Timothy Trippelce9232e2021-09-13 22:48:18 +000057ALL_PARTS = ["header", "checklist", "autogen"]
58
Miguel Young de la Sotaff15b052020-09-09 11:26:17 -040059
60def main():
Timothy Trippelce9232e2021-09-13 22:48:18 +000061 dif_dir = REPO_TOP / "sw/device/lib/dif"
62 autogen_dif_dir = dif_dir / "autogen"
Miguel Young de la Sotaff15b052020-09-09 11:26:17 -040063
64 parser = argparse.ArgumentParser()
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +000065 parser.add_argument(
66 "--mode",
67 "-m",
Timothy Trippeled24b152021-11-03 03:05:04 +000068 choices=["new", "regen"],
69 default="new",
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +000070 required=True,
Timothy Trippeled24b152021-11-03 03:05:04 +000071 help="mode to generate DIF code. Use 'new' if no DIF code exists."
Timothy Trippel553475e2022-02-22 18:05:28 -080072 "Use 'regen' to regenerate all auto-generated DIFs for all IPs.")
Timothy Trippel48a9fb72022-02-10 22:36:40 -080073 parser.add_argument("--topcfg", "-t", help="path of the top hjson file.")
Timothy Trippeled24b152021-11-03 03:05:04 +000074 parser.add_argument("--ip-name-snake",
Timothy Trippelce9232e2021-09-13 22:48:18 +000075 "-i",
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +000076 help="the short name of the IP, in snake_case.")
Timothy Trippeled24b152021-11-03 03:05:04 +000077 parser.add_argument("--ip-name-long",
78 "-l",
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +000079 help="the documentation-friendly name of the IP.")
Timothy Trippelce9232e2021-09-13 22:48:18 +000080 parser.add_argument("--only",
81 choices=ALL_PARTS,
Sam Elliottf18219f2020-09-23 17:46:10 +010082 default=[],
Timothy Trippelce9232e2021-09-13 22:48:18 +000083 action="append",
84 help="only create some files; defaults to all.")
Miguel Young de la Sotaff15b052020-09-09 11:26:17 -040085 args = parser.parse_args()
86
Timothy Trippelce9232e2021-09-13 22:48:18 +000087 # Parse CMD line args.
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +000088 ips = []
89
Timothy Trippel553475e2022-02-22 18:05:28 -080090 # Parse toplevel Hjson to get IPs that are templated / generated with IPgen.
Timothy Chen65ff5102022-01-20 18:42:09 -080091 topcfg_path = REPO_TOP / "hw/top_earlgrey/data/top_earlgrey.hjson"
92 if args.topcfg:
93 topcfg_path = args.topcfg
Timothy Chen65ff5102022-01-20 18:42:09 -080094 try:
95 with open(topcfg_path, 'r') as ftop:
Timothy Trippel48a9fb72022-02-10 22:36:40 -080096 topcfg = hjson.load(ftop, use_decimal=True)
Timothy Chen65ff5102022-01-20 18:42:09 -080097 except FileNotFoundError:
98 print(f"hjson {topcfg_path} could not be found")
99 sys.exit(1)
Timothy Chen65ff5102022-01-20 18:42:09 -0800100 templated_modules = lib.get_templated_modules(topcfg)
101 ipgen_modules = lib.get_ipgen_modules(topcfg)
Timothy Trippela2298252022-04-12 10:59:39 -0700102 reggen_top_modules = lib.get_top_reggen_modules(topcfg)
Timothy Chen65ff5102022-01-20 18:42:09 -0800103
Timothy Trippeled24b152021-11-03 03:05:04 +0000104 # Check for regeneration mode (used in CI check:
105 # ci/scripts/check-generated.sh)
106 if args.mode == "regen":
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000107 if len(args.only) != 1 or args.only[0] != "autogen":
108 raise RuntimeError(
Timothy Trippeled24b152021-11-03 03:05:04 +0000109 "can only regenerate DIF code that is auto-generated.")
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000110 # Create list of IPs to re-generate DIF code for.
111 for autogen_src_filename in glob.iglob(
112 str(REPO_TOP / "sw/device/lib/dif/autogen/*.c")):
113 # NOTE: the line below takes as input a file path
114 # (/path/to/dif_uart_autogen.c) and returns the IP name in lower
115 # case snake mode (i.e., uart).
116 ip_name_snake = Path(autogen_src_filename).stem[4:-8]
117 # NOTE: ip.name_long_* not needed for auto-generated files which
Timothy Trippeled24b152021-11-03 03:05:04 +0000118 # are the only files (re-)generated in regen mode.
Timothy Trippel48a9fb72022-02-10 22:36:40 -0800119 ips.append(
Timothy Trippela2298252022-04-12 10:59:39 -0700120 Ip(ip_name_snake, "AUTOGEN", templated_modules, ipgen_modules,
121 reggen_top_modules))
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000122 else:
Timothy Trippeled24b152021-11-03 03:05:04 +0000123 assert args.ip_name_snake and args.ip_name_long, \
124 "ERROR: pass --ip-name-snake and --ip-name-long when --mode=new."
Timothy Trippel48a9fb72022-02-10 22:36:40 -0800125 ips.append(
126 Ip(args.ip_name_snake, args.ip_name_long, templated_modules,
Timothy Trippela2298252022-04-12 10:59:39 -0700127 ipgen_modules, reggen_top_modules))
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000128
Timothy Trippelce9232e2021-09-13 22:48:18 +0000129 # Default to generating all parts.
Sam Elliottf18219f2020-09-23 17:46:10 +0100130 if len(args.only) == 0:
Sam Elliottf18219f2020-09-23 17:46:10 +0100131 args.only += ALL_PARTS
132
Timothy Trippelce9232e2021-09-13 22:48:18 +0000133 # Create output directories if needed.
Sam Elliottf18219f2020-09-23 17:46:10 +0100134 if len(args.only) > 0:
Timothy Trippelce9232e2021-09-13 22:48:18 +0000135 dif_dir.mkdir(exist_ok=True)
136 autogen_dif_dir.mkdir(exist_ok=True)
Miguel Young de la Sotaff15b052020-09-09 11:26:17 -0400137
Timothy Trippel553475e2022-02-22 18:05:28 -0800138 # Render DIF templates.
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000139 for ip in ips:
140 if "header" in args.only:
Timothy Trippel1c4f7c22021-10-27 21:46:38 +0000141 header_template_file = (
142 REPO_TOP / "util/make_new_dif/templates/dif_template.h.tpl")
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000143 header_out_file = dif_dir / "dif_{}.h".format(ip.name_snake)
Timothy Trippeled0abd22021-10-26 03:24:23 +0000144 if header_out_file.is_file():
145 raise FileExistsError(
Timothy Trippel1c4f7c22021-10-27 21:46:38 +0000146 "DIF header already exists for the IP. To overwrite, "
147 "delete existing header and try again.")
Timothy Trippeled0abd22021-10-26 03:24:23 +0000148 header_template = Template(header_template_file.read_text())
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000149 header_out_file.write_text(header_template.render(ip=ip))
150 print("DIF header successfully written to {}.".format(
151 str(header_out_file)))
Miguel Young de la Sotaff15b052020-09-09 11:26:17 -0400152
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000153 if "autogen" in args.only:
154 # Render all templates
Timothy Trippel440f9602021-09-22 00:34:29 +0000155 for filetype in [".h", ".c", "_unittest.cc"]:
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000156 # Build input/output file names.
Timothy Trippel7b6113d2021-09-15 21:51:11 +0000157 template_file = (
Timothy Trippel1c4f7c22021-10-27 21:46:38 +0000158 REPO_TOP /
159 f"util/make_new_dif/templates/dif_autogen{filetype}.tpl")
Timothy Trippel7b6113d2021-09-15 21:51:11 +0000160 out_file = (autogen_dif_dir /
161 f"dif_{ip.name_snake}_autogen{filetype}")
Sam Elliottf18219f2020-09-23 17:46:10 +0100162
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000163 # Read in template.
Timothy Trippel67137492021-09-24 22:54:13 +0000164 template = Template(template_file.read_text(),
165 strict_undefined=True)
Sam Elliottf18219f2020-09-23 17:46:10 +0100166
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000167 # Generate output file.
Timothy Trippel2f945982021-11-03 22:31:07 +0000168 out_file.write_text(
169 template.render(
170 ip=ip,
171 autogen_banner=get_autogen_banner(
172 "util/make_new_dif.py --mode=regen --only=autogen",
173 "//")))
Sam Elliottf18219f2020-09-23 17:46:10 +0100174
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000175 # Format autogenerated file with clang-format.
176 assert (shutil.which("clang-format") and
177 "ERROR: clang-format must be installed to format "
178 " autogenerated code to pass OpenTitan CI checks.")
179 try:
180 subprocess.check_call(["clang-format", "-i", out_file])
181 except subprocess.CalledProcessError:
182 logging.error(
183 f"failed to format {out_file} with clang-format.")
184 sys.exit(1)
Sam Elliottf18219f2020-09-23 17:46:10 +0100185
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000186 print("Autogenerated DIF successfully written to {}.".format(
187 out_file))
Sam Elliottf18219f2020-09-23 17:46:10 +0100188
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000189 if "checklist" in args.only:
190 checklist_template_file = REPO_TOP / "doc/project/sw_checklist.md.tpl"
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000191 checklist_out_file = dif_dir / "dif_{}.md".format(ip.name_snake)
Timothy Trippeled0abd22021-10-26 03:24:23 +0000192 if checklist_out_file.is_file():
193 raise FileExistsError(
Timothy Trippel1c4f7c22021-10-27 21:46:38 +0000194 "DIF checklist already exists for the IP. To "
195 "overwrite, delete existing checklist and try again.")
Timothy Trippeled0abd22021-10-26 03:24:23 +0000196 markdown_template = Template(checklist_template_file.read_text())
Timothy Trippeld7cb3ae2021-09-20 21:28:38 +0000197 checklist_out_file.write_text(markdown_template.render(ip=ip))
198 print("DIF Checklist successfully written to {}.".format(
199 str(checklist_out_file)))
Miguel Young de la Sotaff15b052020-09-09 11:26:17 -0400200
Timothy Trippel553475e2022-02-22 18:05:28 -0800201 # Render testutils templates.
202 if args.mode == "regen" or "autogen" in args.only:
203 gen_testutils(ips)
204
Miguel Young de la Sotaff15b052020-09-09 11:26:17 -0400205
Timothy Trippelce9232e2021-09-13 22:48:18 +0000206if __name__ == "__main__":
Miguel Young de la Sotaff15b052020-09-09 11:26:17 -0400207 main()