Creating a new top
Once a top has been generated by topgen, some steps must be taken for this top to become visible to build system and integrate with the Top selection mechanism. This page documents those steps on an imaginary top named matcha
.
Background references
This page assumes a minimal amount of familiarity with Bazel, but you should at the very least be familiar with the following concepts:
Preliminary steps
Before running topgen
, you must ensure that every IP (including top-specific and generated IPs) contains a BUILD
file and associated defs.bzl
file describing the IP to the build system, in the root directory of the IP. This sections explains those steps in detail. These steps are only necessary if you have custom IPs, as all IPs in the OpenTitan repository are already setup.
Registering non-generated IPs
A BUILD
file must be located in root directory of the IP and contain at the very least the following code:
# In <ip dir>/BUILD:
package(default_visibility = ["//visibility:public"])
# Most likely you will also need to create an `rtl_files` filegroup to declare RTL files if you want to
# build bitstreams or run verilator.
A defs.bzl
file must be located in root directory of the IP and contain at the very least an opentitan_ip
definition, as in the example below for AES.
# In <ip dir>/defs.bzl:
load("//rules/opentitan:hw.bzl", "opentitan_ip")
# The variable name must be the name of the IP in upper case.
AES = opentitan_ip(
name = "aes",
# This must be a **full** label (i.e. you must including the package name)
# pointing to the IP's Hjson. There usually are two options:
#
# Option 1:
# Create a BUILD file in the data/ subdirectory of the IP (here hw/ip/aes/data/BUILD)
# so that it becomes a package. This option can be useful if your IP has more content
# in the data/ directory that you want to make available to Bazel.
hjson = "//hw/ip/aes/data:aes.hjson",
#
# Option 2:
# Do NOT create a BUILD file in the data/ subdirectory of the IP and use a label
# relative to the IP's root package:
hjson = "//hw/ip/aes:data/aes.hjson",
)
For concrete examples, you can look at the AES module or the AST module.
Registering generated IPs
IPs generated by ipgen
also need to contain BUILD
and defs.bzl
files as above but since the content is generated, it is usually recommended to make this file a template so that it is automatically generated by topgen. Furthermore, generated IPs usually should register their ipconfig
file to the build system if want to use more advanced features of dtgen
.
# In <ip template dir>/BUILD.tpl:
package(default_visibility = ["//visibility:public"])
# Most likely you will also need to create an `rtl_files` filegroup to declare RTL files if you want to
# build bitstreams or run verilator.
and
# In <ip template dir>/defs.bzl:
load("//rules/opentitan:hw.bzl", "opentitan_ip")
${module_instance_name.upper()} = opentitan_ip(
name = "${module_instance_name}",
# Example using Option 2, see the explanation above.
hjson = "//hw/top_${topname}/ip_autogen/${module_instance_name}/data:${module_instance_name}.hjson",
# NEW: optional but necessary if you want to use dtgen with IP template parameters.
ipconfig = "//hw/top_${topname}/ip_autogen/${module_instance_name}:data/top_${topname}_${module_instance_name}.ipconfig.hjson",
)
For concrete examples, you can look at the clkmgr module.
Automatically generated files
After running topgen, the following files should have been created in hw/top_matcha
:
- A
sw/autogen
directory containing along others: including aBUILD
file and some top library/ld code, - A
data/autogen
directory containing aBUILD
file, adefs.bzl
file and the generated complete top descriptiontop_matcha.gen.hjson
You MUST not edit those files.
Manual steps
The following steps require manual edits or creation of files and only need to be done once (though the content of some files may require future edits).
Adding your top to the build system
The first step is to make the build system aware of your top so you can use the top-selection mechanism.
To do this, edit hw/top/defs.bzl
and add the following lines marked as NEW
:
# In hw/top/defs.bzl`:
load("//rules/opentitan:hw.bzl", "opentitan_top")
load("//hw/top_earlgrey/data/autogen:defs.bzl", "EARLGREY")
load("//hw/top_darjeeling/data/autogen:defs.bzl", "DARJEELING")
load("//hw/top_englishbreakfast/data/autogen:defs.bzl", "ENGLISHBREAKFAST")
# NEW: load your top's description
load("//hw/top_matcha/data/autogen:defs.bzl", "MATCHA")
ALL_TOPS = [
EARLGREY,
DARJEELING,
ENGLISHBREAKFAST,
# NEW: add your top to the build system
MATCHA,
]
Once done, your top can be selected using --//hw/top=matcha
when building targets.
Declaring execution environment
In order to build and run tests, the build system requires you to declare execution environments which describe the necessary steps required to build images. At minimum, you will want to declare a DV environment. To do so, create (or edit) a BUILD
file in your top’s root directory and add the following:
# In hw/top_matcha/BUILD:
load(
"//rules/opentitan:defs.bzl",
"DEFAULT_TEST_FAILURE_MSG",
"DEFAULT_TEST_SUCCESS_MSG",
"sim_dv",
)
package(default_visibility = ["//visibility:public"])
###########################################################################
# Sim DV Environments
#
# The sim_dv_base target is only meant to be used for building ROMs and
# other items without `testonly=True`.
###########################################################################
sim_dv(
name = "sim_dv_base",
design = "matcha",
exec_env = "sim_dv",
extract_sw_logs = "//util/device_sw_utils:extract_sw_logs_db",
flash_scramble_tool = "//util/design:gen-flash-img",
libs = [
"//sw/device/lib/arch:boot_stage_rom_ext",
"//sw/device/lib/arch:sim_dv",
"//hw/top_darjeeling/sw/dt:sim_dv",
],
linker_script = "//sw/device/lib/testing/test_framework:ottf_ld_silicon_creator_slot_a",
rom_scramble_config = "//hw/top_darjeeling/data/autogen:top_matcha.gen.hjson",
)
sim_dv(
name = "sim_dv",
testonly = True,
base = ":sim_dv_base",
exec_env = "sim_dv",
rom = "//sw/device/lib/testing/test_rom:test_rom",
)
For concrete examples see Darjeeling (simple) or Earlgrey (very complex).
Important note: creating an execution environment does not automatically enroll tests, those must manually be enabled for your execution environment.
Build the test ROM for your top
At minimum, you will want to build the test ROM for your DV execution environment. To do so, edit sw/device/lib/testing/test_rom/BUILD
and add the following:
# In sw/device/lib/testing/test_rom/BUILD
opentitan_binary(
name = "test_rom",
exec_env = [
# ...
"//hw/top_darjeeling:sim_dv_base",
# NEW: note that this is using the sim_dv_base environment
"//hw/top_matcha:sim_dv_base",
],
# ...
)
Once done, you should be able to compile a test ROM for your top by running:
./bazelisk.sh build --//hw/top=matcha //sw/device/lib/testing/test_rom
NOTE: you might run into compilation errors due to top-specific constructs in the code of the test ROM. See below for more details.
Adding your execution environment to tests
This is usually done in two ways:
- manually add a
//hw/top_match:sim_dv
to theexec_env
attribute of a test, seeexample_test_from_rom
for example:
opentitan_test(
name = "example_test_from_rom",
srcs = ["example_test_from_rom.c"],
exec_env = {
"//hw/top_earlgrey:sim_dv": None,
"//hw/top_earlgrey:sim_verilator": None,
"//hw/top_darjeeling:sim_dv": None,
# NEW:
"//hw/top_matcha:sim_dv": None,
},
# ...
)
- create a new list of execution environment for your top in
rules/opentitan/defs.bzl
# In rules/opentitan/defs.bzl
# NEW
# The default set of test environments for Matcha.
MATCHA_TEST_ENVS = {
"//hw/top_matcha:sim_dv": None,
}
and add it to tests that you want to enroll, e.g. in sw/device/tests/BUILD
# In sw/device/tests/BUILD:
load(
"//rules/opentitan:defs.bzl",
# NEW
"MATCHA_TEST_ENVS"
# ...
)
# ...
opentitan_test(
name = "uart_smoketest",
srcs = ["uart_smoketest.c"],
exec_env = dicts.add(
EARLGREY_TEST_ENVS,
EARLGREY_SILICON_OWNER_ROM_EXT_ENVS,
EARLGREY_CW340_TEST_ENVS,
DARJEELING_TEST_ENVS,
# NEW
MATCHA_TEST_ENVS,
{
"//hw/top_earlgrey:silicon_creator": None,
},
),
)
Once done, you should be able to compile a DV UART smoke test for your top by running:
./bazelisk.sh build --//hw/top=matcha //sw/device/tests:uart_smoketest_sim_dv
NOTE: you might run into compilation errors due to top-specific constructs in the code of the test framework. See below for more details.
Porting the software to your top
TODO