From be49437c546fd30bd0fbf051d058599a913d45fd Mon Sep 17 00:00:00 2001 From: Alexander Williams Date: Sat, 30 Nov 2024 08:28:54 -0800 Subject: [PATCH] [bitstream] Collect MMI data into one file Reduce BUILD file boilerplate by placing all memory map info in a single file. Use a separate Processor element for each map, so the InstData attribute can be used as a key to identify the memory to be updated. This construction also permits multiple memories to be spliced in a single call to updatemem, though that is not implemented in this commit. Adjust MMI dump to use a regex for mem type and a dictionary for each memory's parameters. For the mem type, some memories in the FPGA can consist of both RAMB36 and RAMB18 types, depending on the width of the array. The regex allows more flexible memory cell detection. Rev up the bitstream manifest schema to v3 so the memories contained in the MMI file are described. Add a function to collect MMI data from v2 cache entries and rewrite in the expected v3 format, with the MMI data in a single file. Finally, drop support for bitstream cache entries that do not contain a manifest. The tools to create a manifest have been in the repo for awhile, and as of this commit's creation, no such cache entries remain in the public cloud storage bucket. Signed-off-by: Alexander Williams (cherry picked from commit 9e1ef80bca3d6648b1091efd1e5e7b3776a220f2) --- ci/scripts/build-bitstream-vivado.sh | 5 +- doc/contributing/fpga/debugging_with_ila.md | 4 +- doc/contributing/fpga/ref_manual_fpga.md | 3 +- hw/bitstream/BUILD | 38 +-- hw/bitstream/cw340/BUILD | 29 +-- hw/bitstream/hyperdebug/BUILD | 29 +-- hw/bitstream/universal/BUILD | 4 +- hw/bitstream/vivado/BUILD | 78 ++---- hw/top_earlgrey/BUILD | 12 +- .../util/vivado_hook_write_bitstream_pre.tcl | 162 ++++++------ rules/bitstreams.bzl | 42 ++-- rules/opentitan/exec_env.bzl | 14 +- rules/opentitan/splice.bzl | 13 +- .../scripts/bitstreams_manifest.example.json | 30 +-- rules/scripts/bitstreams_manifest.schema.json | 20 +- .../bitstreams_manifest_v2.example.json | 39 +++ .../bitstreams_manifest_v2.schema.json | 80 ++++++ rules/scripts/bitstreams_workspace.py | 233 +++++++++--------- rules/scripts/bitstreams_workspace_test.py | 102 ++++---- rules/splice.bzl | 3 +- sw/device/silicon_creator/rom/e2e/BUILD | 3 +- .../rom/e2e/boot_policy_valid/BUILD | 3 +- .../rom/e2e/sigverify_spx/BUILD | 3 +- sw/device/tests/pmod/BUILD | 3 +- util/fpga/splice_rom.sh | 8 +- util/py/scripts/bitstream_cache_create.py | 9 +- .../bitstreams_fragment_from_manifest.py | 13 +- 27 files changed, 532 insertions(+), 450 deletions(-) create mode 100644 rules/scripts/bitstreams_manifest_v2.example.json create mode 100644 rules/scripts/bitstreams_manifest_v2.schema.json diff --git a/ci/scripts/build-bitstream-vivado.sh b/ci/scripts/build-bitstream-vivado.sh index badf8223e844da..29e039b712eb38 100755 --- a/ci/scripts/build-bitstream-vivado.sh +++ b/ci/scripts/build-bitstream-vivado.sh @@ -106,7 +106,4 @@ BITSTREAM_FNAME="lowrisc_systems_chip_${FLAVOUR}_${TARGET}_0.1.bit" BITSTREAM_PATH="$OBJ_DIR/hw/synth-vivado/$BITSTREAM_FNAME" cp "$BITSTREAM_PATH" "$TOPLEVEL_BIN_DIR" -cp "$OBJ_DIR/hw/synth-vivado/rom.mmi" "$TOPLEVEL_BIN_DIR" -if [ $HAS_OTP == 1 ]; then - cp "$OBJ_DIR/hw/synth-vivado/otp.mmi" "$TOPLEVEL_BIN_DIR" -fi +cp "$OBJ_DIR/hw/synth-vivado/memories.mmi" "$TOPLEVEL_BIN_DIR" diff --git a/doc/contributing/fpga/debugging_with_ila.md b/doc/contributing/fpga/debugging_with_ila.md index d0a306437e6320..58a9bc1acec8c2 100644 --- a/doc/contributing/fpga/debugging_with_ila.md +++ b/doc/contributing/fpga/debugging_with_ila.md @@ -237,7 +237,7 @@ The path to the synthesis log file is usually `bazel-out/k8-fastbuild/bin/hw/bit Once Vivado has successfully generated a bitstream, locate its directory with `dirname $(./bazelisk.sh outquery-all //hw/bitstream:rom --define bitstream=vivado)` (it will usually be `bazel-out/k8-fastbuild/bin/hw/bitstream/vivado`). Append `/build.fpga_cw310/synth-vivado` to that path. -In the resulting directory, you should find `otp.mmi`, `rom.mmi`, and `lowrisc_systems_chip_earlgrey_cw310_0.1.bit`. +In the resulting directory, you should find `memories.mmi`, and `lowrisc_systems_chip_earlgrey_cw310_0.1.bit`. We will next copy those files into a local bitstream cache that Bazel can use. If you don't have a local bitstream cache yet, create one as follows: @@ -254,7 +254,7 @@ mkdir -p $BAZEL_BITSTREAMS_CACHE/cache Create a directory with the name of the Git hash for which you have built the bitstream under `$BAZEL_BITSTREAMS_CACHE/cache/` (e.g,. `$BAZEL_BITSTREAMS_CACHE/cache/2e5a31b7d80b6eb97e114b2ca8f9e132ec7c83a6`). (You can find the relevant Git hash with `git log`, for example. If you have not committed the changes to implement the ILA yet, we recommend doing so at least locally.) -Copy `otp.mmi` and `rom.mmi` to that directory. +Copy `memories.mmi` to that directory. Copy `lowrisc_systems_chip_earlgrey_cw310_0.1.bit` also to that directory, then rename the copy to `lowrisc_systems_chip_earlgrey_cw310_0.1.bit.orig`. Now instruct Bazel to use a bitstream from the local cache by setting an `--offline` argument in the `BITSTREAM` environment variable; for example: diff --git a/doc/contributing/fpga/ref_manual_fpga.md b/doc/contributing/fpga/ref_manual_fpga.md index ef539f6d4004c8..61aa684bb30f4a 100644 --- a/doc/contributing/fpga/ref_manual_fpga.md +++ b/doc/contributing/fpga/ref_manual_fpga.md @@ -143,8 +143,7 @@ The following files are produced as a result: * `fpga_cw310_rom.bit` (ROM, RMA OTP image) * `fpga_cw310_rom_otp_dev.bit` (ROM, DEV OTP image) * `lowrisc_systems_chip_earlgrey_cw310_0.1.bit` (test ROM, RMA OTP image) -* `otp.mmi` -* `rom.mmi` +* `memories.mmi` If CI is working on the `master` branch, it puts selected build artifacts into a tarball, which it then uploads to the GCS bucket. The latest tarball is available here: https://storage.googleapis.com/opentitan-bitstreams/master/bitstream-latest.tar.gz diff --git a/hw/bitstream/BUILD b/hw/bitstream/BUILD index 383526c2d92979..c0d313228e583b 100644 --- a/hw/bitstream/BUILD +++ b/hw/bitstream/BUILD @@ -70,25 +70,13 @@ filegroup( ) filegroup( - name = "rom_mmi", + name = "cw310_mmi", testonly = True, srcs = select({ "bitstream_skip": ["//hw/bitstream/universal:none"], - "bitstream_vivado": ["//hw/bitstream/vivado:rom_mmi"], - "bitstream_gcp_splice": ["@bitstreams//:chip_earlgrey_cw310_rom_mmi"], - "//conditions:default": ["@bitstreams//:chip_earlgrey_cw310_rom_mmi"], - }), - tags = ["manual"], -) - -filegroup( - name = "otp_mmi", - testonly = True, - srcs = select({ - "bitstream_skip": ["//hw/bitstream/universal:none"], - "bitstream_vivado": ["//hw/bitstream/vivado:otp_mmi"], - "bitstream_gcp_splice": ["@bitstreams//:chip_earlgrey_cw310_otp_mmi"], - "//conditions:default": ["@bitstreams//:chip_earlgrey_cw310_otp_mmi"], + "bitstream_vivado": ["//hw/bitstream/vivado:cw310_mmi"], + "bitstream_gcp_splice": ["@bitstreams//:chip_earlgrey_cw310_mmi"], + "//conditions:default": ["@bitstreams//:chip_earlgrey_cw310_mmi"], }), tags = ["manual"], ) @@ -99,7 +87,8 @@ bitstream_splice( testonly = True, src = ":bitstream", data = "//sw/device/lib/testing/test_rom:test_rom_fpga_cw310_scr_vmem", - meminfo = ":rom_mmi", + instance = "rom", + meminfo = ":cw310_mmi", tags = ["manual"], update_usr_access = True, ) @@ -109,7 +98,8 @@ bitstream_splice( testonly = True, src = ":bitstream", data = "//sw/device/silicon_creator/rom:mask_rom_fpga_cw310_scr_vmem", - meminfo = ":rom_mmi", + instance = "rom", + meminfo = ":cw310_mmi", tags = ["manual"], update_usr_access = True, ) @@ -121,7 +111,8 @@ bitstream_splice( testonly = True, src = ":mask_rom", data = img_target, - meminfo = ":otp_mmi", + instance = "otp", + meminfo = ":cw310_mmi", tags = ["manual"], update_usr_access = True, ) @@ -133,8 +124,7 @@ bitstream_fragment_from_manifest( name = "chip_earlgrey_cw310_cached_fragment", srcs = [ "@bitstreams//:chip_earlgrey_cw310_bitstream", - "@bitstreams//:chip_earlgrey_cw310_otp_mmi", - "@bitstreams//:chip_earlgrey_cw310_rom_mmi", + "@bitstreams//:chip_earlgrey_cw310_mmi", ], design = "chip_earlgrey_cw310", manifest = "@bitstreams//:manifest", @@ -155,8 +145,7 @@ bitstream_fragment_from_manifest( name = "chip_earlgrey_cw310_hyperdebug_cached_fragment", srcs = [ "@bitstreams//:chip_earlgrey_cw310_hyperdebug_bitstream", - "@bitstreams//:chip_earlgrey_cw310_hyperdebug_otp_mmi", - "@bitstreams//:chip_earlgrey_cw310_hyperdebug_rom_mmi", + "@bitstreams//:chip_earlgrey_cw310_hyperdebug_mmi", ], design = "chip_earlgrey_cw310_hyperdebug", manifest = "@bitstreams//:manifest", @@ -177,8 +166,7 @@ bitstream_fragment_from_manifest( name = "chip_earlgrey_cw340_cached_fragment", srcs = [ "@bitstreams//:chip_earlgrey_cw340_bitstream", - "@bitstreams//:chip_earlgrey_cw340_otp_mmi", - "@bitstreams//:chip_earlgrey_cw340_rom_mmi", + "@bitstreams//:chip_earlgrey_cw340_mmi", ], design = "chip_earlgrey_cw340", manifest = "@bitstreams//:manifest", diff --git a/hw/bitstream/cw340/BUILD b/hw/bitstream/cw340/BUILD index 9443eaf57edbc1..5a06b2abb31cd3 100644 --- a/hw/bitstream/cw340/BUILD +++ b/hw/bitstream/cw340/BUILD @@ -25,25 +25,13 @@ filegroup( ) filegroup( - name = "rom_mmi", + name = "mmi", testonly = True, srcs = select({ "//hw/bitstream:bitstream_skip": ["//hw/bitstream/universal:none"], - "//hw/bitstream:bitstream_vivado": ["//hw/bitstream/vivado:fpga_cw340_rom_mmi"], - "//hw/bitstream:bitstream_gcp_splice": ["@bitstreams//:chip_earlgrey_cw340_rom_mmi"], - "//conditions:default": ["@bitstreams//:chip_earlgrey_cw340_rom_mmi"], - }), - tags = ["manual"], -) - -filegroup( - name = "otp_mmi", - testonly = True, - srcs = select({ - "//hw/bitstream:bitstream_skip": ["//hw/bitstream/universal:none"], - "//hw/bitstream:bitstream_vivado": ["//hw/bitstream/vivado:fpga_cw340_otp_mmi"], - "//hw/bitstream:bitstream_gcp_splice": ["@bitstreams//:chip_earlgrey_cw340_otp_mmi"], - "//conditions:default": ["@bitstreams//:chip_earlgrey_cw340_otp_mmi"], + "//hw/bitstream:bitstream_vivado": ["//hw/bitstream/vivado:fpga_cw340_mmi"], + "//hw/bitstream:bitstream_gcp_splice": ["@bitstreams//:chip_earlgrey_cw340_mmi"], + "//conditions:default": ["@bitstreams//:chip_earlgrey_cw340_mmi"], }), tags = ["manual"], ) @@ -54,7 +42,8 @@ bitstream_splice( testonly = True, src = ":bitstream", data = "//sw/device/lib/testing/test_rom:test_rom_fpga_cw340_scr_vmem", - meminfo = ":rom_mmi", + instance = "rom", + meminfo = ":mmi", tags = ["manual"], update_usr_access = True, ) @@ -65,7 +54,8 @@ bitstream_splice( testonly = True, src = ":bitstream", data = "//sw/device/silicon_creator/rom:mask_rom_fpga_cw340_scr_vmem", - meminfo = ":rom_mmi", + instance = "rom", + meminfo = ":mmi", tags = ["manual"], update_usr_access = True, ) @@ -77,7 +67,8 @@ bitstream_splice( testonly = True, src = ":mask_rom", data = img_target, - meminfo = ":otp_mmi", + instance = "otp", + meminfo = ":mmi", tags = ["manual"], update_usr_access = True, ) diff --git a/hw/bitstream/hyperdebug/BUILD b/hw/bitstream/hyperdebug/BUILD index fcbddeccf46bb3..077205ef53baa6 100644 --- a/hw/bitstream/hyperdebug/BUILD +++ b/hw/bitstream/hyperdebug/BUILD @@ -21,25 +21,13 @@ filegroup( ) filegroup( - name = "rom_mmi", + name = "mmi", testonly = True, srcs = select({ "//hw/bitstream:bitstream_skip": ["//hw/bitstream/universal:none"], - "//hw/bitstream:bitstream_vivado": ["//hw/bitstream/vivado:rom_mmi_hyp"], - "//hw/bitstream:bitstream_gcp_splice": ["@bitstreams//:chip_earlgrey_cw310_hyperdebug_rom_mmi"], - "//conditions:default": ["@bitstreams//:chip_earlgrey_cw310_hyperdebug_rom_mmi"], - }), - tags = ["manual"], -) - -filegroup( - name = "otp_mmi", - testonly = True, - srcs = select({ - "//hw/bitstream:bitstream_skip": ["//hw/bitstream/universal:none"], - "//hw/bitstream:bitstream_vivado": ["//hw/bitstream/vivado:otp_mmi_hyp"], - "//hw/bitstream:bitstream_gcp_splice": ["@bitstreams//:chip_earlgrey_cw310_hyperdebug_otp_mmi"], - "//conditions:default": ["@bitstreams//:chip_earlgrey_cw310_hyperdebug_otp_mmi"], + "//hw/bitstream:bitstream_vivado": ["//hw/bitstream/vivado:cw310_hyperdebug_mmi"], + "//hw/bitstream:bitstream_gcp_splice": ["@bitstreams//:chip_earlgrey_cw310_hyperdebug_mmi"], + "//conditions:default": ["@bitstreams//:chip_earlgrey_cw310_hyperdebug_mmi"], }), tags = ["manual"], ) @@ -50,7 +38,8 @@ bitstream_splice( testonly = True, src = ":bitstream", data = "//sw/device/lib/testing/test_rom:test_rom_fpga_cw310_scr_vmem", - meminfo = ":rom_mmi", + instance = "rom", + meminfo = ":mmi", tags = ["manual"], update_usr_access = True, ) @@ -61,7 +50,8 @@ bitstream_splice( testonly = True, src = ":bitstream", data = "//sw/device/silicon_creator/rom:mask_rom_fpga_cw310_scr_vmem", - meminfo = ":rom_mmi", + instance = "rom", + meminfo = ":mmi", tags = ["manual"], update_usr_access = True, ) @@ -73,7 +63,8 @@ bitstream_splice( testonly = True, src = ":mask_rom", data = img_target, - meminfo = ":otp_mmi", + instance = "otp", + meminfo = ":mmi", tags = ["manual"], update_usr_access = True, ) diff --git a/hw/bitstream/universal/BUILD b/hw/bitstream/universal/BUILD index 8e73aade0069f6..8670738b5d1593 100644 --- a/hw/bitstream/universal/BUILD +++ b/hw/bitstream/universal/BUILD @@ -29,8 +29,8 @@ label_flag( universal_splice( name = "splice", testonly = True, - # When the src bitstream, rom_mmi and otp_mmi fields are empty, - # the rule will use values from the exec_env. + # When the src bitstream and mmi fields are empty, the rule will use values + # from the exec_env. exec_env = ":env", otp = ":otp", rom = ":rom", diff --git a/hw/bitstream/vivado/BUILD b/hw/bitstream/vivado/BUILD index 398fcd5e542da3..06597d9f1517a7 100644 --- a/hw/bitstream/vivado/BUILD +++ b/hw/bitstream/vivado/BUILD @@ -52,8 +52,7 @@ fusesoc_build( ], output_groups = { "bitstream": ["synth-vivado/lowrisc_systems_chip_earlgrey_cw310_0.1.bit"], - "rom_mmi": ["synth-vivado/rom.mmi"], - "otp_mmi": ["synth-vivado/otp.mmi"], + "mmi": ["synth-vivado/memories.mmi"], }, systems = ["lowrisc:systems:chip_earlgrey_cw310"], tags = ["manual"], @@ -69,18 +68,10 @@ filegroup( ) filegroup( - name = "rom_mmi", + name = "cw310_mmi", testonly = True, srcs = [":fpga_cw310"], - output_group = "rom_mmi", - tags = ["manual"], -) - -filegroup( - name = "otp_mmi", - testonly = True, - srcs = [":fpga_cw310"], - output_group = "otp_mmi", + output_group = "mmi", tags = ["manual"], ) @@ -101,8 +92,7 @@ fusesoc_build( ], output_groups = { "bitstream": ["synth-vivado/lowrisc_systems_chip_earlgrey_cw310_hyperdebug_0.1.bit"], - "rom_mmi": ["synth-vivado/rom.mmi"], - "otp_mmi": ["synth-vivado/otp.mmi"], + "mmi": ["synth-vivado/memories.mmi"], }, systems = ["lowrisc:systems:chip_earlgrey_cw310_hyperdebug"], tags = ["manual"], @@ -118,18 +108,10 @@ filegroup( ) filegroup( - name = "rom_mmi_hyp", - testonly = True, - srcs = [":fpga_cw310_hyperdebug"], - output_group = "rom_mmi", - tags = ["manual"], -) - -filegroup( - name = "otp_mmi_hyp", + name = "cw310_hyperdebug_mmi", testonly = True, srcs = [":fpga_cw310_hyperdebug"], - output_group = "otp_mmi", + output_group = "mmi", tags = ["manual"], ) @@ -150,8 +132,7 @@ fusesoc_build( ], output_groups = { "bitstream": ["synth-vivado/lowrisc_systems_chip_earlgrey_cw340_0.1.bit"], - "rom_mmi": ["synth-vivado/rom.mmi"], - "otp_mmi": ["synth-vivado/otp.mmi"], + "mmi": ["synth-vivado/memories.mmi"], }, systems = ["lowrisc:systems:chip_earlgrey_cw340"], tags = ["manual"], @@ -167,18 +148,10 @@ filegroup( ) filegroup( - name = "fpga_cw340_rom_mmi", + name = "cw340_mmi", testonly = True, srcs = [":fpga_cw340"], - output_group = "rom_mmi", - tags = ["manual"], -) - -filegroup( - name = "fpga_cw340_otp_mmi", - testonly = True, - srcs = [":fpga_cw340"], - output_group = "otp_mmi", + output_group = "mmi", tags = ["manual"], ) @@ -189,10 +162,11 @@ bitstream_manifest_fragment( srcs = [":fpga_cw310"], bitstream = "bitstream", design = "chip_earlgrey_cw310", - memory_maps = { - "rom_mmi": "rom", - "otp_mmi": "otp", - }, + memories = [ + "rom", + "otp", + ], + memory_map_file = ":cw310_mmi", tags = ["manual"], ) @@ -212,10 +186,11 @@ bitstream_manifest_fragment( srcs = [":fpga_cw310_hyperdebug"], bitstream = "bitstream", design = "chip_earlgrey_cw310_hyperdebug", - memory_maps = { - "rom_mmi": "rom", - "otp_mmi": "otp", - }, + memories = [ + "rom", + "otp", + ], + memory_map_file = ":cw310_hyperdebug_mmi", tags = ["manual"], ) @@ -235,10 +210,11 @@ bitstream_manifest_fragment( srcs = [":fpga_cw340"], bitstream = "bitstream", design = "chip_earlgrey_cw340", - memory_maps = { - "rom_mmi": "rom", - "otp_mmi": "otp", - }, + memories = [ + "rom", + "otp", + ], + memory_map_file = ":cw340_mmi", tags = ["manual"], ) @@ -257,9 +233,8 @@ pkg_files( name = "standard", testonly = True, srcs = [ + ":cw310_mmi", ":fpga_cw310_test_rom", - ":otp_mmi", - ":rom_mmi", ], prefix = "earlgrey/fpga_cw310/standard", tags = ["manual"], @@ -269,9 +244,8 @@ pkg_files( name = "hyperdebug", testonly = True, srcs = [ + ":cw310_hyperdebug_mmi", ":fpga_cw310_test_rom_hyp", - ":otp_mmi_hyp", - ":rom_mmi_hyp", ], prefix = "earlgrey/fpga_cw310/hyperdebug", tags = ["manual"], diff --git a/hw/top_earlgrey/BUILD b/hw/top_earlgrey/BUILD index 71358e08f8d8b1..aa511576d0f55a 100644 --- a/hw/top_earlgrey/BUILD +++ b/hw/top_earlgrey/BUILD @@ -89,16 +89,15 @@ fpga_cw310( base = ":fpga_cw310", base_bitstream = "//hw/bitstream:bitstream", exec_env = "fpga_cw310_test_rom", + mmi = "//hw/bitstream:cw310_mmi", openocd_adapter_config = "//third_party/openocd:jtag_olimex_cfg", otp = "//hw/ip/otp_ctrl/data:img_rma", - otp_mmi = "//hw/bitstream:otp_mmi", param = { "interface": "cw310", "exit_success": DEFAULT_TEST_SUCCESS_MSG, "exit_failure": DEFAULT_TEST_FAILURE_MSG, }, rom = "//sw/device/lib/testing/test_rom:test_rom", - rom_mmi = "//hw/bitstream:rom_mmi", test_cmd = """ --exec="transport init" --exec="fpga load-bitstream {bitstream}" @@ -161,15 +160,14 @@ fpga_cw310( ecdsa_key = {"//sw/device/silicon_creator/rom/keys/fake/ecdsa:test_key_0_ecdsa_p256": "test_key_0"}, exec_env = "fpga_hyper310_rom_with_fake_keys", manifest = "//sw/device/silicon_creator/rom_ext:manifest", + mmi = "//hw/bitstream/hyperdebug:mmi", otp = "//hw/ip/otp_ctrl/data:img_rma", - otp_mmi = "//hw/bitstream/hyperdebug:otp_mmi", param = { "interface": "hyper310", "exit_success": DEFAULT_TEST_SUCCESS_MSG, "exit_failure": DEFAULT_TEST_FAILURE_MSG, }, rom = "//sw/device/silicon_creator/rom:mask_rom", - rom_mmi = "//hw/bitstream/hyperdebug:rom_mmi", test_cmd = """ --exec="transport init" --exec="fpga load-bitstream {bitstream}" @@ -299,10 +297,9 @@ fpga_cw340( base = ":fpga_cw340", base_bitstream = "//hw/bitstream/cw340:bitstream", exec_env = "fpga_cw340_test_rom", + mmi = "//hw/bitstream/cw340:mmi", otp = "//hw/ip/otp_ctrl/data:img_rma", - otp_mmi = "//hw/bitstream/cw340:otp_mmi", rom = "//sw/device/lib/testing/test_rom:test_rom", - rom_mmi = "//hw/bitstream/cw340:rom_mmi", test_cmd = """ --exec="transport init" --exec="fpga load-bitstream {bitstream}" @@ -320,10 +317,9 @@ fpga_cw340( ecdsa_key = {"//sw/device/silicon_creator/rom/keys/fake/ecdsa:test_key_0_ecdsa_p256": "test_key_0"}, exec_env = "fpga_cw340_rom_with_fake_keys", manifest = "//sw/device/silicon_creator/rom_ext:manifest", + mmi = "//hw/bitstream/cw340:mmi", otp = "//hw/ip/otp_ctrl/data:img_rma", - otp_mmi = "//hw/bitstream/cw340:otp_mmi", rom = "//sw/device/silicon_creator/rom:mask_rom", - rom_mmi = "//hw/bitstream/cw340:rom_mmi", test_cmd = """ --exec="transport init" --exec="fpga load-bitstream {bitstream}" diff --git a/hw/top_earlgrey/util/vivado_hook_write_bitstream_pre.tcl b/hw/top_earlgrey/util/vivado_hook_write_bitstream_pre.tcl index a399145c43827f..58e96ff78e9b07 100644 --- a/hw/top_earlgrey/util/vivado_hook_write_bitstream_pre.tcl +++ b/hw/top_earlgrey/util/vivado_hook_write_bitstream_pre.tcl @@ -19,89 +19,96 @@ set_property BITSTREAM.CONFIG.USR_ACCESS TIMESTAMP [current_design] # # Args: # filename: Path to the output file. -# brams: A list of BRAM cells. -# mem_type: The BRAM type, e.g. "RAMB36". -# fake_word_width: If non-zero, pretend that $brams covers -# `fake_word_width` bits. Influences the values of the -# MMI's and tags. -# addr_end_multiplier: A coefficient applied to the address space. Influences -# the values of the MMI's and -# tags. +# mem_info: Dictionary of dictionaries with following properties for each (inner) value: +# brams: A list of BRAM cells. +# mem_type_regex: The BRAM type regex, dividing the mem type and site, e.g. {(RAMB\d+)_(\w+)}. +# fake_word_width: If non-zero, pretend that $brams covers +# `fake_word_width` bits. Influences the values of the +# MMI's and tags. +# addr_end_multiplier: A coefficient applied to the address space. Influences +# the values of the MMI's and +# tags. # designtask_count: A number used for logging with `send_msg`. -proc generate_mmi {filename brams mem_type fake_word_width addr_end_multiplier designtask_count} { +proc generate_mmi {filename mem_infos designtask_count} { send_msg "${designtask_count}-1" INFO "Dumping MMI to ${filename}" - if {[llen $brams] == 0} { - send_msg "${designtask_count}-1" INFO "Cannot make MMI for zero BRAMs" - return - } - set workroot [file dirname [info script]] set filepath "${workroot}/${filename}" set fileout [open $filepath "w"] - - set fake_slice_width [expr $fake_word_width / [llen $brams]] - - # Calculate the overall address space. - set space 0 - foreach inst [lsort -dictionary $brams] { - set slice_begin [get_property ram_slice_begin [get_cells $inst]] - set slice_end [get_property ram_slice_end [get_cells $inst]] - if {$slice_begin eq {} || $slice_end eq {}} { - send_msg "${designtask_count}-2" ERROR "Extraction of ${filename} information failed." - } - set slice_width [expr {$slice_end - $slice_begin + 1}] - if {$slice_width < $fake_slice_width} { - set slice_end [expr {$slice_begin + $fake_slice_width - 1}] - set slice_width $fake_slice_width - } - set addr_begin [get_property ram_addr_begin [get_cells $inst]] - set addr_end [get_property ram_addr_end [get_cells $inst]] - if {$addr_begin eq {} || $addr_end eq {}} { - send_msg "${designtask_count}-3" ERROR "Extraction of ${filename} MMI information failed." - } - - # Calculate total number of bits. - set space [expr {$space + ($addr_end - $addr_begin + 1) * $slice_width}] - set last_slice_width $slice_width - } - set space [expr {($space * $addr_end_multiplier / 8) - 1}] - - # Generate the MMI. + set part [get_property PART [current_design]] puts $fileout "" puts $fileout "" - puts $fileout " " - puts $fileout " " - puts $fileout " " - set loc_prefix "${mem_type}_" + dict for {id mem_info} $mem_infos { + dict with mem_info { + if {[llen $brams] == 0} { + send_msg "${designtask_count}-1" INFO "Cannot make MMI for zero BRAMs" + return + } - set part [get_property PART [current_design]] - foreach inst [lsort -dictionary $brams] { - set loc [get_property LOC [get_cells $inst]] - set loc [string trimleft $loc $loc_prefix] - set slice_begin [get_property ram_slice_begin [get_cells $inst]] - set slice_end [get_property ram_slice_end [get_cells $inst]] - set slice_width [expr {$slice_end - $slice_begin + 1}] - if {$slice_width < $fake_slice_width} { - set slice_end [expr {$slice_begin + $fake_slice_width - 1}] - set slice_width $fake_slice_width + set fake_slice_width [expr $fake_word_width / [llen $brams]] + + # Calculate the overall address space. + set space 0 + foreach inst [lsort -dictionary $brams] { + set slice_begin [get_property ram_slice_begin [get_cells $inst]] + set slice_end [get_property ram_slice_end [get_cells $inst]] + if {$slice_begin eq {} || $slice_end eq {}} { + send_msg "${designtask_count}-2" ERROR "Extraction of ${filename} information failed." + } + set slice_width [expr {$slice_end - $slice_begin + 1}] + if {$slice_width < $fake_slice_width} { + set slice_end [expr {$slice_begin + $fake_slice_width - 1}] + set slice_width $fake_slice_width + } + set addr_begin [get_property ram_addr_begin [get_cells $inst]] + set addr_end [get_property ram_addr_end [get_cells $inst]] + if {$addr_begin eq {} || $addr_end eq {}} { + send_msg "${designtask_count}-3" ERROR "Extraction of ${filename} MMI information failed." + } + + # Calculate total number of bits. + set space [expr {$space + ($addr_end - $addr_begin + 1) * $slice_width}] + set last_slice_width $slice_width + } + set space [expr {($space * $addr_end_multiplier / 8) - 1}] + + # Generate the MMI. + puts $fileout " " + puts $fileout " " + puts $fileout " " + + foreach inst [lsort -dictionary $brams] { + set loc [get_property LOC [get_cells $inst]] + set loc_matches [regexp $mem_type_regex $loc loc_match loc_prefix loc_suffix] + if {$loc_matches == 0} { + send_msg "${designtask_count}-4" ERROR "Extraction of ${filename} mem location failed." + } + set slice_begin [get_property ram_slice_begin [get_cells $inst]] + set slice_end [get_property ram_slice_end [get_cells $inst]] + set slice_width [expr {$slice_end - $slice_begin + 1}] + if {$slice_width < $fake_slice_width} { + set slice_end [expr {$slice_begin + $fake_slice_width - 1}] + set slice_width $fake_slice_width + } + set addr_begin [get_property ram_addr_begin [get_cells $inst]] + set addr_end [get_property ram_addr_end [get_cells $inst]] + set addr_end [expr {($addr_end + 1) * $addr_end_multiplier - 1}] + puts $fileout " " + puts $fileout " " + puts $fileout " " + puts $fileout " " + puts $fileout " " + } + puts $fileout " " + puts $fileout " " + puts $fileout " " } - set addr_begin [get_property ram_addr_begin [get_cells $inst]] - set addr_end [get_property ram_addr_end [get_cells $inst]] - set addr_end [expr {($addr_end + 1) * $addr_end_multiplier - 1}] - puts $fileout " " - puts $fileout " " - puts $fileout " " - puts $fileout " " - puts $fileout " " } - puts $fileout " " - puts $fileout " " - puts $fileout " " - puts $fileout "" - puts $fileout " " + + puts $fileout " " + puts $fileout " " puts $fileout "" close $fileout send_msg "${designtask_count}-4" INFO "MMI dumped to ${filepath}" @@ -161,6 +168,7 @@ switch ${fpga_family} { set bram_regex "BMEM.*.*" } } +set mem_type_regex {(RAMB\d+)_(\w+)} # The scrambled Boot ROM is actually 39 bits wide, but we need to pretend that # it's 40 bits, or else we will be unable to encode our ROM data in a MEM file @@ -177,7 +185,10 @@ switch ${fpga_family} { # A hack that works is to pretend the data width is actually 40 bits. Updatemem # seems to write that extra zero bit into the ether without complaint. set rom_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_rom_ctrl*"] " "] -generate_mmi "rom.mmi" $rom_brams "RAMB36" 40 1 1 +dict set memInfo rom brams $rom_brams +dict set memInfo rom mem_type_regex $mem_type_regex +dict set memInfo rom fake_word_width 40 +dict set memInfo rom addr_end_multiplier 1 # OTP does not require faking the word width, but it has its own quirk. It seems # each 22-bit OTP word is followed by 15 zero words. The MMI's @@ -185,7 +196,12 @@ generate_mmi "rom.mmi" $rom_brams "RAMB36" 40 1 1 # that its data input overruns the address space. The workaround is to pretend # the address space is 16 times larger than we would normally compute. set otp_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_otp_ctrl*"] " "] -generate_mmi "otp.mmi" $otp_brams "RAMB18" 0 16 2 +dict set memInfo otp brams $otp_brams +dict set memInfo otp mem_type_regex $mem_type_regex +dict set memInfo otp fake_word_width 0 +dict set memInfo otp addr_end_multiplier 16 + +generate_mmi "memories.mmi" $memInfo 1 # For debugging purposes, dump the INIT_XX strings for ROM and OTP. dump_init_strings "rom_init_strings.txt" $rom_brams 3 diff --git a/rules/bitstreams.bzl b/rules/bitstreams.bzl index a356c940fe7c82..a1dbb7b2e59cd4 100644 --- a/rules/bitstreams.bzl +++ b/rules/bitstreams.bzl @@ -153,9 +153,14 @@ BitstreamManifestFragmentInfo = provider( }, ) -def _bitstream_generate_manifest_fragment(design, srcs, bitstream, memory_maps): +def _bitstream_generate_manifest_fragment( + design, + srcs, + bitstream, + memory_map_file, + memories): fragment = { - "schema_version": 2, + "schema_version": 3, "designs": {}, } metadata = {} @@ -176,19 +181,14 @@ def _bitstream_generate_manifest_fragment(design, srcs, bitstream, memory_maps): } deps.append(bitstream_outputs[0]) - memory_map_info = {} - for output_group, mem_id in memory_maps.items(): - if output_group in output_groups: - memory_files = output_groups[output_group].to_list() - if len(memory_files) != 1: - fail("Too many memory map outputs for output group") - file_path = "/".join([design, memory_files[0].basename]) - memory_map_info[mem_id] = { - "file": file_path, - "build_target": str(src.label), - } - deps.append(memory_files[0]) - metadata["memory_map_info"] = memory_map_info + file_path = "/".join([design, memory_map_file.basename]) + memory_map_info = { + "file": file_path, + "build_target": str(src.label), + "memories": memories, + } + deps.append(memory_map_file) + metadata["memory_map_info"] = memory_map_info fragment["designs"][design] = metadata return (fragment, deps) @@ -199,7 +199,8 @@ def _bitstream_manifest_fragment_impl(ctx): ctx.attr.design, ctx.attr.srcs, ctx.attr.bitstream, - ctx.attr.memory_maps, + ctx.file.memory_map_file, + ctx.attr.memories, ) for dep in deps: file_path = "/".join([ctx.attr.name, ctx.attr.design, dep.basename]) @@ -243,9 +244,12 @@ bitstream_manifest_fragment = rule( doc = "The output group for the programmable bitstream file.", mandatory = True, ), - "memory_maps": attr.string_dict( - doc = """A map from memory map files' output groups to their memory -IDs (e.g. rom, otp, etc.)""", + "memories": attr.string_list( + doc = """List of memory names supported by the memory map file.""", + ), + "memory_map_file": attr.label( + doc = """A file containing the memory map info of the design.""", + allow_single_file = True, ), }, ) diff --git a/rules/opentitan/exec_env.bzl b/rules/opentitan/exec_env.bzl index 12fd57dbaf1a1b..df285013f39463 100644 --- a/rules/opentitan/exec_env.bzl +++ b/rules/opentitan/exec_env.bzl @@ -18,10 +18,9 @@ _FIELDS = { "spx_key": ("attr.spx_key", False), "manifest": ("file.manifest", False), "rom": ("attr.rom", False), - "rom_mmi": ("file.rom_mmi", False), "rom_ext": ("attr.rom_ext", False), "otp": ("file.otp", False), - "otp_mmi": ("file.otp_mmi", False), + "mmi": ("file.mmi", False), "base_bitstream": ("file.base_bitstream", False), "args": ("attr.args", False), "test_cmd": ("attr.test_cmd", False), @@ -147,11 +146,6 @@ def exec_env_common_attrs(**kwargs): allow_files = True, doc = "ROM image to use in this environment", ), - "rom_mmi": attr.label( - default = kwargs.get("rom_mmi"), - allow_single_file = True, - doc = "Memory layout description for ROM splicing", - ), "rom_ext": attr.label( default = kwargs.get("rom_ext"), allow_files = True, @@ -162,10 +156,10 @@ def exec_env_common_attrs(**kwargs): allow_single_file = True, doc = "OTP image to use in this environment", ), - "otp_mmi": attr.label( - default = kwargs.get("otp_mmi"), + "mmi": attr.label( + default = kwargs.get("mmi"), allow_single_file = True, - doc = "Memory layout description for OTP splicing", + doc = "Memory layout description for splicing", ), "base_bitstream": attr.label( default = kwargs.get("base_bitstream"), diff --git a/rules/opentitan/splice.bzl b/rules/opentitan/splice.bzl index 5478c02b8deb47..bb1891e5b3c66a 100644 --- a/rules/opentitan/splice.bzl +++ b/rules/opentitan/splice.bzl @@ -35,7 +35,7 @@ def gen_vivado_mem_file(ctx, name, src, tool, swap_nibbles = True): ) return update -def vivado_updatemem(ctx, name, src, mmi, update, debug = False): +def vivado_updatemem(ctx, name, src, instance, mmi, update, debug = False): spliced = ctx.actions.declare_file("{}.bit".format(name)) # Vivado's `updatemem` only accepts bitstream filenames that end with `.bit`. @@ -52,7 +52,7 @@ def vivado_updatemem(ctx, name, src, mmi, update, debug = False): args.add("--meminfo", mmi) args.add("--data", update) args.add("--bit", src) - args.add("--proc", "dummy") + args.add("--proc", instance) args.add("--out", spliced) ctx.actions.run( @@ -129,7 +129,8 @@ def _bitstream_splice_impl(ctx): ctx = ctx, name = "{}-rom".format(ctx.label.name), src = src, - mmi = get_fallback(ctx, "file.rom_mmi", exec_env), + instance = "rom", + mmi = get_fallback(ctx, "file.mmi", exec_env), update = mem, debug = ctx.attr.debug, ) @@ -151,7 +152,8 @@ def _bitstream_splice_impl(ctx): ctx = ctx, name = "{}-otp".format(ctx.label.name), src = src, - mmi = get_fallback(ctx, "file.otp_mmi", exec_env), + instance = "otp", + mmi = get_fallback(ctx, "file.mmi", exec_env), update = mem, debug = ctx.attr.debug, ) @@ -169,9 +171,8 @@ bitstream_splice_ = rule( attrs = { "src": attr.label(allow_single_file = True, doc = "The bitstream to splice"), "otp": attr.label(allow_single_file = True, doc = "The OTP image to splice into the bitstream"), - "otp_mmi": attr.label(allow_single_file = True, doc = "The OTP meminfo file"), + "mmi": attr.label(allow_single_file = True, doc = "The meminfo file"), "rom": attr.label(doc = "The ROM image to splice into the bitstream"), - "rom_mmi": attr.label(allow_single_file = True, doc = "The ROM meminfo file"), "exec_env": attr.label(providers = [[ExecEnvInfo], [DefaultInfo]], mandatory = True, doc = "The exec_env to splice for"), "swap_nibbles": attr.bool(default = True, doc = "Swap nybbles while preparing the memory image"), "debug": attr.bool(default = False, doc = "Emit debug info while updating"), diff --git a/rules/scripts/bitstreams_manifest.example.json b/rules/scripts/bitstreams_manifest.example.json index 5daa94742fbd90..2c2209e9d053d6 100644 --- a/rules/scripts/bitstreams_manifest.example.json +++ b/rules/scripts/bitstreams_manifest.example.json @@ -1,5 +1,5 @@ { - "schema_version": 2, + "schema_version": 3, "designs": { "chip_earlgrey_cw310": { "build_id": "aabbcc", @@ -8,14 +8,12 @@ "build_target": "//hw/bitstream/vivado:fpga_cw310" }, "memory_map_info": { - "otp": { - "file": "otp.mmi", - "build_target": "//hw/bitstream/vivado:fpga_cw310" - }, - "rom": { - "file": "rom.mmi", - "build_target": "//hw/bitstream/vivado:fpga_cw310" - } + "file": "memories.mmi", + "build_target": "//hw/bitstream/vivado:fpga_cw310", + "memories": [ + "otp", + "rom" + ] } }, "chip_earlgrey_cw310_hyperdebug": { @@ -25,14 +23,12 @@ "build_target": "//hw/bitstream/vivado:fpga_cw310_hyperdebug" }, "memory_map_info": { - "otp": { - "file": "chip_earlgrey_cw310_hyperdebug/otp.mmi", - "build_target": "//hw/bitstream/vivado:fpga_cw310_hyperdebug" - }, - "rom": { - "file": "chip_earlgrey_cw310_hyperdebug/rom.mmi", - "build_target": "//hw/bitstream/vivado:fpga_cw310_hyperdebug" - } + "file": "chip_earlgrey_cw310_hyperdebug/memories.mmi", + "build_target": "//hw/bitstream/vivado:fpga_cw310_hyperdebug", + "memories": [ + "otp", + "rom" + ] } } } diff --git a/rules/scripts/bitstreams_manifest.schema.json b/rules/scripts/bitstreams_manifest.schema.json index e88ed5b11f5d5d..8f0dbfc52d8139 100644 --- a/rules/scripts/bitstreams_manifest.schema.json +++ b/rules/scripts/bitstreams_manifest.schema.json @@ -1,7 +1,7 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://raw.githubusercontent.com/lowRISC/opentitan/master/rules/scripts/bitstreams_manifest.schema.json", - "title": "Bitstreams Cache Manifest v2", + "title": "Bitstreams Cache Manifest v3", "description": "A manifest of bitstreams in a cache entry, informing what is there and how to reproduce", "type": "object", @@ -9,8 +9,8 @@ "schema_version": { "description": "Version number of this manifest's schema", "type": "number", - "minimum": 2, - "maximum": 2 + "minimum": 3, + "maximum": 3 }, "designs": { "description": "Map of designs to their files and metadata objects", @@ -51,6 +51,14 @@ "build_target": { "description": "Build target that generated the output", "type": "string" + }, + "memories": { + "description": "Memory names or keys supported by the file", + "type": "array", + "items": { + "description": "Identifier for a memory in the MMI file", + "type": "string" + } } }, "required": [ @@ -66,11 +74,7 @@ "type": "string" }, "bitstream": { "$ref": "#/$defs/bitstream_info" }, - "memory_map_info": { - "description": "Map of name or key identifying the associated memory to file describing contents", - "type": "object", - "additionalProperties": { "$ref": "#/$defs/memory_map_info" } - } + "memory_map_info": { "$ref": "#/$defs/memory_map_info" } }, "required": [ "bitstream" diff --git a/rules/scripts/bitstreams_manifest_v2.example.json b/rules/scripts/bitstreams_manifest_v2.example.json new file mode 100644 index 00000000000000..5daa94742fbd90 --- /dev/null +++ b/rules/scripts/bitstreams_manifest_v2.example.json @@ -0,0 +1,39 @@ +{ + "schema_version": 2, + "designs": { + "chip_earlgrey_cw310": { + "build_id": "aabbcc", + "bitstream": { + "file": "lowrisc_systems_chip_earlgrey_cw310_0.1.bit", + "build_target": "//hw/bitstream/vivado:fpga_cw310" + }, + "memory_map_info": { + "otp": { + "file": "otp.mmi", + "build_target": "//hw/bitstream/vivado:fpga_cw310" + }, + "rom": { + "file": "rom.mmi", + "build_target": "//hw/bitstream/vivado:fpga_cw310" + } + } + }, + "chip_earlgrey_cw310_hyperdebug": { + "build_id": "ddeeff", + "bitstream": { + "file": "chip_earlgrey_cw310_hyperdebug/lowrisc_systems_chip_earlgrey_cw310_hyperdebug_0.1.bit", + "build_target": "//hw/bitstream/vivado:fpga_cw310_hyperdebug" + }, + "memory_map_info": { + "otp": { + "file": "chip_earlgrey_cw310_hyperdebug/otp.mmi", + "build_target": "//hw/bitstream/vivado:fpga_cw310_hyperdebug" + }, + "rom": { + "file": "chip_earlgrey_cw310_hyperdebug/rom.mmi", + "build_target": "//hw/bitstream/vivado:fpga_cw310_hyperdebug" + } + } + } + } +} diff --git a/rules/scripts/bitstreams_manifest_v2.schema.json b/rules/scripts/bitstreams_manifest_v2.schema.json new file mode 100644 index 00000000000000..e9cb187c75e0be --- /dev/null +++ b/rules/scripts/bitstreams_manifest_v2.schema.json @@ -0,0 +1,80 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/lowRISC/opentitan/master/rules/scripts/bitstreams_manifest_v2.schema.json", + "title": "Bitstreams Cache Manifest v2", + "description": + "A manifest of bitstreams in a cache entry, informing what is there and how to reproduce", + "type": "object", + "properties": { + "schema_version": { + "description": "Version number of this manifest's schema", + "type": "number", + "minimum": 2, + "maximum": 2 + }, + "designs": { + "description": "Map of designs to their files and metadata objects", + "type": "object", + "additionalProperties": { "$ref": "#/$defs/design" } + } + }, + "required": [ + "schema_version", + "designs" + ], + "$defs": { + "bitstream_info": { + "description": "Bitstream build output", + "type": "object", + "properties": { + "file": { + "description": "Path to bitstream file relative to root", + "type": "string" + }, + "build_target": { + "description": "Build target that generated the output", + "type": "string" + } + }, + "required": [ + "file" + ] + }, + "memory_map_info": { + "description": "Build output that maps memories to cells in a hardware implementation", + "type": "object", + "properties": { + "file": { + "description": "Path to file with memory contents, relative to root", + "type": "string" + }, + "build_target": { + "description": "Build target that generated the output", + "type": "string" + } + }, + "required": [ + "file" + ] + }, + "design": { + "description": "Files and metadata related to a design", + "type": "object", + "properties": { + "build_id": { + "description": "Build ID associated with this design's output (typically a git hash)", + "type": "string" + }, + "bitstream": { "$ref": "#/$defs/bitstream_info" }, + "memory_map_info": { + "description": "Map of name or key identifying the associated memory to file describing contents", + "type": "object", + "additionalProperties": { "$ref": "#/$defs/memory_map_info" } + } + }, + "required": [ + "bitstream" + ] + } + } +} diff --git a/rules/scripts/bitstreams_workspace.py b/rules/scripts/bitstreams_workspace.py index 2b9febe1de0842..4912ea4a3a3cf2 100755 --- a/rules/scripts/bitstreams_workspace.py +++ b/rules/scripts/bitstreams_workspace.py @@ -23,7 +23,8 @@ # The schema version used for legacy cache entries, JSON files missing a version # entry, and entries that use a higher version of the schema than supported here # (attempted in case there is forwards compatibility). -MANIFEST_SCHEMA_VERSION = 2 +MANIFEST_SCHEMA_VERSION_MIN = 2 +MANIFEST_SCHEMA_VERSION_MAX = 3 # Default location of the bitstreams cache. CACHE_DIR = '~/.cache/opentitan-bitstreams' @@ -37,18 +38,15 @@ KNOWN_DESIGNS = { "chip_earlgrey_cw310": { "bitstream": "@//hw/bitstream/vivado:fpga_cw310_test_rom", - "rom_mmi": "@//hw/bitstream/vivado:rom_mmi", - "otp_mmi": "@//hw/bitstream/vivado:otp_mmi", + "mmi": "@//hw/bitstream/vivado:cw310_mmi", }, "chip_earlgrey_cw310_hyperdebug": { "bitstream": "@//hw/bitstream/vivado:fpga_cw310_test_rom_hyp", - "rom_mmi": "@//hw/bitstream/vivado:rom_mmi_hyp", - "otp_mmi": "@//hw/bitstream/vivado:otp_mmi_hyp", + "mmi": "@//hw/bitstream/vivado:cw310_hyperdebug_mmi", }, "chip_earlgrey_cw340": { "bitstream": "@//hw/bitstream/vivado:fpga_cw340_test_rom", - "rom_mmi": "@//hw/bitstream/vivado:fpga_cw340_rom_mmi", - "otp_mmi": "@//hw/bitstream/vivado:fpga_cw340_otp_mmi", + "mmi": "@//hw/bitstream/vivado:cw340_mmi", }, } @@ -276,7 +274,7 @@ def _GetFromRemote(self, key): if not os.path.exists(self.latest_update): self.Touch(key) - def _GenerateLegacyManifest(self, key: str, files: [str]) -> Dict: + def _GenerateMemoryMapFromV2(self, key: str, mmi_v2: Dict) -> Dict: """Generate a manifest for old cache entries without them. Args: @@ -285,59 +283,75 @@ def _GenerateLegacyManifest(self, key: str, files: [str]) -> Dict: Returns: A dictionary mapping file paths to manifest entries """ - legacy_files = { - "chip_earlgrey_cw310": { - "build_id": key, - "bitstream": { - "file": "lowrisc_systems_chip_earlgrey_cw310_0.1.bit.orig", - "build_target": "//hw/bitstream/vivado:fpga_cw310", - }, - "memory_map_info": { - "otp": { - "file": "otp.mmi", - "build_target": "//hw/bitstream/vivado:fpga_cw310", - }, - "rom": { - "file": "rom.mmi", - "build_target": "//hw/bitstream/vivado:fpga_cw310", - }, - }, - }, - "chip_earlgrey_cw310_hyperdebug": { - "build_id": key, - "bitstream": { - "file": "chip_earlgrey_cw310_hyperdebug/" - "lowrisc_systems_chip_earlgrey_cw310_hyperdebug_0.1.bit", - "build_target": "//hw/bitstream/vivado:fpga_cw310_hyperdebug", - }, - "memory_map_info": { - "otp": { - "file": "chip_earlgrey_cw310_hyperdebug/otp.mmi", - "build_target": "//hw/bitstream/vivado:fpga_cw310_hyperdebug", - }, - "rom": { - "file": "chip_earlgrey_cw310_hyperdebug/rom.mmi", - "build_target": "//hw/bitstream/vivado:fpga_cw310_hyperdebug", - }, - }, - }, + filepath = None + memories = [] + cache_path = os.path.join("cache", key) + merged_tree = None + merged_root = None + for memory, info in mmi_v2.items(): + mmi_path = os.path.join(cache_path, info["file"]) + mmi_tree = xml.etree.ElementTree.parse(mmi_path) + mmi_root = mmi_tree.getroot() + assert mmi_root.tag == "MemInfo" + processor_elem = mmi_root.findall("Processor") + assert len(processor_elem) == 1 + processor_elem = processor_elem[0] + # Adjust the Processor element's InstPath to use the memory ID + processor_elem.attrib["InstPath"] = memory + memories.append(memory) + + if filepath is None: + filepath = os.path.dirname(mmi_path) + filepath = os.path.join(filepath, "memories.mmi") + if merged_root is None: + merged_tree = mmi_tree + merged_root = mmi_root + continue + else: + merged_root.append(processor_elem) + + # The Config element must be at the end + config_elem = merged_root.findall("Config") + assert len(config_elem) == 1 + config_elem = config_elem[0] + merged_root.remove(config_elem) + merged_root.append(config_elem) + + # Write the merged XML file + merged_tree.write(filepath) + + # Rewrite memory map info in v3 format + memory_map_info = { + "file": os.path.relpath(filepath, cache_path), + "memories": memories, } - designs = collections.defaultdict(dict) + return memory_map_info + + def _GenerateV3ManifestFromV2(self, key: str, manifest: Dict) -> Dict: + """Generate a manifest for cache entries in old v2 format. - for design, metadata in legacy_files.items(): - design_is_present = True - required_files = [metadata["bitstream"]["file"]] - for mmi in metadata["memory_map_info"].values(): - required_files.append(mmi["file"]) - for required_file in required_files: - if os.path.join("cache", key, required_file) not in files: - design_is_present = False - break - if design_is_present: - designs[design] = metadata - - manifest = {"schema_version": MANIFEST_SCHEMA_VERSION, - "designs": designs} + Aggregates MMI files into one merged file, per v3 requirements. + + Args: + manifest: The v2 manifest in the cache entry + Returns: + An updated manifest Dict with v3 memory map info + """ + try: + import jsonschema + except ImportError: + logging.warning("jsonschema not found, skipping schema validation") + else: + schema_path = os.path.join(MANIFESTS_DIR, "bitstreams_manifest_v2.schema.json") + with open(schema_path) as schema_file: + schema = json.load(schema_file) + jsonschema.validate(manifest, schema) + + for design, metadata in manifest["designs"].items(): + memory_map_info = metadata["memory_map_info"] + memory_map_info = self._GenerateMemoryMapFromV2(key, memory_map_info) + metadata["memory_map_info"] = memory_map_info + manifest["schema_version"] = 3 return manifest def GetFromCache(self, key: str) -> (Dict, Path): @@ -354,20 +368,16 @@ def GetFromCache(self, key: str) -> (Dict, Path): files = self._GetFromLocal(key) manifest_path = os.path.join("cache", key, "manifest.json") - if manifest_path not in files: - logging.warning("No manifest found." - " Attempting to generate manifest from legacy file" - " paths.") - return (self._GenerateLegacyManifest(key, files), None) - with open(manifest_path, "r") as manifest_file: manifest = json.load(manifest_file) - if "schema_version" not in manifest: - logging.error("schema is missing a version number." - " Generating legacy manifest instead...") - return (self._GenerateLegacyManifest(key, files), None) + assert "schema_version" in manifest + schema_version = int(manifest["schema_version"]) + assert schema_version >= MANIFEST_SCHEMA_VERSION_MIN + assert schema_version <= MANIFEST_SCHEMA_VERSION_MAX + if schema_version == 2: + return (self._GenerateV3ManifestFromV2(key, manifest), None) return (manifest, manifest_path) @staticmethod @@ -379,43 +389,27 @@ def _WriteSubstituteManifest(contents: Dict, path: Path): with open(path, "w") as manifest_file: json.dump(contents, manifest_file, indent=True) - def _ConstructBazelString(self, build_file: Path, key: str) -> str: - # If `key` passed in is "latest", this updates the `key` to be the hash - # that "latest" points to. - if key == 'latest': - key = self.available['latest'] - - (manifest, manifest_path) = self.GetFromCache(key) - - # Schema version 1 was never used and is not valid - if manifest["schema_version"] <= 1: - msg_template = "Invalid schema_version {} found in manifest" - raise Exception(msg_template.format(manifest["schema_version"])) - + def _ConstructBazelString(self, build_file: Path, key: str, manifest: Dict, + manifest_path: Path) -> str: designs = collections.defaultdict(dict) - if manifest["schema_version"] > MANIFEST_SCHEMA_VERSION: - logging.warning("Warning: Manifest is newer than available schemas") - logging.warning("Will try parsing an available schema with highest version") - manifest["schema_version"] = MANIFEST_SCHEMA_VERSION - - if manifest["schema_version"] == 2: - # Attempt to check the schema if `jsonschema` is available. - try: - import jsonschema - except ImportError: - logging.warning("jsonschema not found, skipping schema validation") - else: - schema_path = os.path.join(MANIFESTS_DIR, "bitstreams_manifest.schema.json") - with open(schema_path) as schema_file: - schema = json.load(schema_file) - jsonschema.validate(manifest, schema) - - for design_name, metadata in manifest["designs"].items(): - design = collections.defaultdict(dict) - design["bitstream"] = metadata["bitstream"]["file"] - for mmi_id, mmi_entry in metadata["memory_map_info"].items(): - design[mmi_id + "_mmi"] = mmi_entry["file"] - designs[design_name] = design + + # Attempt to check the schema if `jsonschema` is available. + try: + import jsonschema + except ImportError: + logging.warning("jsonschema not found, skipping schema validation") + else: + schema_path = os.path.join(MANIFESTS_DIR, "bitstreams_manifest.schema.json") + with open(schema_path) as schema_file: + schema = json.load(schema_file) + jsonschema.validate(manifest, schema) + + for design_name, metadata in manifest["designs"].items(): + design = collections.defaultdict(dict) + design["bitstream"] = metadata["bitstream"]["file"] + design["mmi"] = metadata["memory_map_info"]["file"] + # What to do about the memory list? + designs[design_name] = design bazel_lines = [ '# This file was autogenerated. Do not edit!', @@ -474,14 +468,6 @@ def alias_lines(name, target): bazel_lines += filegroup_lines(target_name, target_file) - if manifest_path is None: - # Write substitute manifest if none came with the cache entry. - manifest_path = os.path.join(cache_base_dir, - "substitute_manifest.json") - abs_manifest_path = os.path.join(self.cachedir, - "substitute_manifest.json") - self._WriteSubstituteManifest(manifest, abs_manifest_path) - bazel_lines += filegroup_lines("manifest", manifest_path) for design_name in sorted(KNOWN_DESIGNS.keys()): @@ -501,12 +487,25 @@ def WriteBazelFiles(self, build_file: Path, key: str) -> str: Returns: Either `key` or the corresponding commit hash if `key` is 'latest'. """ + # If `key` passed in is "latest", this updates the `key` to be the hash + # that "latest" points to. + if key == 'latest': + key = self.available['latest'] + + (manifest, manifest_path) = self.GetFromCache(key) + + if manifest_path is None: + # Write substitute manifest if none came with the cache entry. + manifest_path = os.path.join("cache", key, + "substitute_manifest.json") + abs_manifest_path = os.path.join(self.cachedir, key, + "substitute_manifest.json") + self._WriteSubstituteManifest(manifest, abs_manifest_path) + with open(build_file, 'wt') as f: - f.write(self._ConstructBazelString(build_file, key)) + f.write(self._ConstructBazelString(build_file, key, manifest, manifest_path)) - if key != 'latest': - return key - return self.available[key] + return key def main(argv): diff --git a/rules/scripts/bitstreams_workspace_test.py b/rules/scripts/bitstreams_workspace_test.py index a43b95a675a7c3..9340f5f71f85d6 100644 --- a/rules/scripts/bitstreams_workspace_test.py +++ b/rules/scripts/bitstreams_workspace_test.py @@ -8,6 +8,24 @@ from bitstreams_workspace import BitstreamCache +MOCK_MANIFEST = """{ + "schema_version": 3, + "designs": { + "chip_earlgrey_cw310": { + "build_id": "abcd", + "bitstream": { + "file": "lowrisc_systems_chip_earlgrey_cw310_0.1.bit", + "build_target": "//hw/bitstream/vivado:fpga_cw310" + }, + "memory_map_info": { + "file": "memories.mmi", + "build_target": "//hw/bitstream/vivado:fpga_cw310", + "memories": ["otp", "rom"] + } + } + } +}""" + class TestBitstreamCache(unittest.TestCase): @@ -17,13 +35,12 @@ def test_make_with_default(self): BitstreamCache.MakeWithDefaults() def test_get_from_cache(self): - BITSTREAM_ORIG = 'lowrisc_systems_chip_earlgrey_cw310_0.1.bit.orig' - BITSTREAM_SPLICE = 'lowrisc_systems_chip_earlgrey_cw310_0.1.bit.splice' + MOCK_BITSTREAM = 'lowrisc_systems_chip_earlgrey_cw310_0.1.bit' MOCKED_OS_WALK_RETURN = [ # os.walk() yields tuples of the form (root, dir, files). ('cache/abcd', [], - [BITSTREAM_ORIG, BITSTREAM_SPLICE, 'rom.mmi', 'otp.mmi']), + [MOCK_BITSTREAM, 'manifest.json', 'memories.mmi']), ] os.walk = unittest.mock.MagicMock(name='os.walk', return_value=MOCKED_OS_WALK_RETURN) @@ -34,31 +51,30 @@ def test_get_from_cache(self): offline=True) cache.InitRepository = unittest.mock.MagicMock(name='method') - (cached_files, manifest_path) = cache.GetFromCache('abcd') + m = unittest.mock.mock_open(read_data=MOCK_MANIFEST) + with unittest.mock.patch('bitstreams_workspace.open', m): + (manifest, manifest_path) = cache.GetFromCache('abcd') + m.assert_called_once_with('cache/abcd/manifest.json', 'r') # This is more of an implementation detail, but it verifies that we hit # the mocked `os.walk` function as expected. os.walk.assert_called_once_with('cache/abcd') + self.maxDiff = None self.assertEqual( - dict(cached_files), { - "schema_version": 2, + manifest, { + "schema_version": 3, "designs": { "chip_earlgrey_cw310": { "build_id": "abcd", "bitstream": { - "file": BITSTREAM_ORIG, + "file": MOCK_BITSTREAM, "build_target": "//hw/bitstream/vivado:fpga_cw310", }, "memory_map_info": { - "otp": { - "file": "otp.mmi", - "build_target": "//hw/bitstream/vivado:fpga_cw310", - }, - "rom": { - "file": "rom.mmi", - "build_target": "//hw/bitstream/vivado:fpga_cw310", - }, + "file": "memories.mmi", + "build_target": "//hw/bitstream/vivado:fpga_cw310", + "memories": ["otp", "rom"], }, }, }, @@ -73,7 +89,7 @@ def test_write_build_file(self): MOCKED_OS_WALK_RETURN = [ # os.walk() yields tuples of the form (root, dir, files). ('cache/abcd', [], - [BITSTREAM_ORIG, BITSTREAM_SPLICE, 'rom.mmi', 'otp.mmi']), + [BITSTREAM_ORIG, BITSTREAM_SPLICE, 'manifest.json', 'memories.mmi']), ] os.walk = unittest.mock.MagicMock(name='os.walk', return_value=MOCKED_OS_WALK_RETURN) @@ -86,10 +102,25 @@ def test_write_build_file(self): '/tmp/cache/opentitan-bitstreams', 'latest.txt', offline=True) - cache.InitRepository = unittest.mock.MagicMock(name='method') - cache._WriteSubstituteManifest = unittest.mock.MagicMock(name='method') - - bazel_string = cache._ConstructBazelString('BUILD.mock', 'abcd') + manifest = { + "schema_version": 3, + "designs": { + "chip_earlgrey_cw310": { + "build_id": "abcd", + "bitstream": { + "file": "lowrisc_systems_chip_earlgrey_cw310_0.1.bit.orig", + "build_target": "//hw/bitstream/vivado:fpga_cw310", + }, + "memory_map_info": { + "file": "memories.mmi", + "build_target": "//hw/bitstream/vivado:fpga_cw310", + "memories": ["otp", "rom"], + }, + }, + }, + } + manifest_path = "cache/abcd/substitute_manifest.json" + bazel_string = cache._ConstructBazelString('BUILD.mock', 'abcd', manifest, manifest_path) self.maxDiff = None self.assertEqual( bazel_string, '''# This file was autogenerated. Do not edit! @@ -106,13 +137,8 @@ def test_write_build_file(self): ) filegroup( - name = "chip_earlgrey_cw310_otp_mmi", - srcs = ["cache/abcd/otp.mmi"], -) - -filegroup( - name = "chip_earlgrey_cw310_rom_mmi", - srcs = ["cache/abcd/rom.mmi"], + name = "chip_earlgrey_cw310_mmi", + srcs = ["cache/abcd/memories.mmi"], ) filegroup( @@ -126,13 +152,8 @@ def test_write_build_file(self): ) alias( - name = "chip_earlgrey_cw310_hyperdebug_rom_mmi", - actual = "@//hw/bitstream/vivado:rom_mmi_hyp", -) - -alias( - name = "chip_earlgrey_cw310_hyperdebug_otp_mmi", - actual = "@//hw/bitstream/vivado:otp_mmi_hyp", + name = "chip_earlgrey_cw310_hyperdebug_mmi", + actual = "@//hw/bitstream/vivado:cw310_hyperdebug_mmi", ) alias( @@ -141,20 +162,11 @@ def test_write_build_file(self): ) alias( - name = "chip_earlgrey_cw340_rom_mmi", - actual = "@//hw/bitstream/vivado:fpga_cw340_rom_mmi", -) - -alias( - name = "chip_earlgrey_cw340_otp_mmi", - actual = "@//hw/bitstream/vivado:fpga_cw340_otp_mmi", + name = "chip_earlgrey_cw340_mmi", + actual = "@//hw/bitstream/vivado:cw340_mmi", ) ''') - # This is more of an implementation detail, but it verifies that we hit - # the mocked `os.walk` function as expected. - os.walk.assert_called_once_with('cache/abcd') - class TestFetchAvailableBitstreams(unittest.TestCase): """ diff --git a/rules/splice.bzl b/rules/splice.bzl index 0bc4fbdbf8ad32..43ed796e0c9a63 100644 --- a/rules/splice.bzl +++ b/rules/splice.bzl @@ -45,7 +45,7 @@ def _bitstream_splice_impl(ctx): updatemem_args.add("--meminfo", ctx.file.meminfo) updatemem_args.add("--data", update) updatemem_args.add("--bit", tmpsrc) - updatemem_args.add("--proc", "dummy") + updatemem_args.add("--proc", ctx.attr.instance) updatemem_args.add("--out", spliced) if ctx.attr.debug: updatemem_args.add("--debug") @@ -105,6 +105,7 @@ bitstream_splice_ = rule( "meminfo": attr.label(allow_single_file = True, doc = "Memory layout info file (an .mmi file)"), "src": attr.label(allow_single_file = True, doc = "The bitstream to splice"), "data": attr.label(allow_single_file = True, doc = "The memory image to splice into the bitstream"), + "instance": attr.string(mandatory = True, doc = "The instance ID for the memory to splice into the bitstream"), "swap_nybbles": attr.bool(default = True, doc = "Swap nybbles while preparing the memory image"), "debug": attr.bool(default = False, doc = "Emit debug info while updating"), "update_usr_access": attr.bool(default = False, doc = "Update the USR_ACCESS value of the bitstream, breaks hermeticity"), diff --git a/sw/device/silicon_creator/rom/e2e/BUILD b/sw/device/silicon_creator/rom/e2e/BUILD index 202cc2982c660a..df2d9d8e28d813 100644 --- a/sw/device/silicon_creator/rom/e2e/BUILD +++ b/sw/device/silicon_creator/rom/e2e/BUILD @@ -87,7 +87,8 @@ bitstream_splice( name = "bitstream_default_otp", src = "//hw/bitstream:mask_rom", data = ":otp_img_default", - meminfo = "//hw/bitstream:otp_mmi", + instance = "otp", + meminfo = "//hw/bitstream:cw310_mmi", tags = maybe_skip_in_ci(CONST.LCV.RMA), update_usr_access = True, visibility = ["//visibility:private"], diff --git a/sw/device/silicon_creator/rom/e2e/boot_policy_valid/BUILD b/sw/device/silicon_creator/rom/e2e/boot_policy_valid/BUILD index b690786d78ba2f..a75fd5b2ec8550 100644 --- a/sw/device/silicon_creator/rom/e2e/boot_policy_valid/BUILD +++ b/sw/device/silicon_creator/rom/e2e/boot_policy_valid/BUILD @@ -63,7 +63,8 @@ BOOT_POLICY_VALID_CASES = [ name = "bitstream_boot_policy_valid_{}".format(lc_state), src = "//hw/bitstream:mask_rom", data = ":otp_img_boot_policy_valid_{}".format(lc_state), - meminfo = "//hw/bitstream:otp_mmi", + instance = "otp", + meminfo = "//hw/bitstream:cw310_mmi", update_usr_access = True, ) for lc_state, _ in get_lc_items() diff --git a/sw/device/silicon_creator/rom/e2e/sigverify_spx/BUILD b/sw/device/silicon_creator/rom/e2e/sigverify_spx/BUILD index 61c85d6d1d5328..840689f9670f96 100644 --- a/sw/device/silicon_creator/rom/e2e/sigverify_spx/BUILD +++ b/sw/device/silicon_creator/rom/e2e/sigverify_spx/BUILD @@ -173,7 +173,8 @@ opentitan_binary( lc_state, t["name"], ), - meminfo = "//hw/bitstream:otp_mmi", + instance = "otp", + meminfo = "//hw/bitstream:cw310_mmi", update_usr_access = True, visibility = ["//visibility:private"], ) diff --git a/sw/device/tests/pmod/BUILD b/sw/device/tests/pmod/BUILD index 9ca0d0adc3a422..4852b0e1269a0d 100644 --- a/sw/device/tests/pmod/BUILD +++ b/sw/device/tests/pmod/BUILD @@ -33,14 +33,13 @@ fpga_cw310( base = "//hw/top_earlgrey:fpga_cw310_sival_rom_ext", # Override the hyperdebug bitstream, interface, and OTP & ROM MMIs. base_bitstream = "//hw/bitstream:bitstream", - otp_mmi = "//hw/bitstream:otp_mmi", + mmi = "//hw/bitstream:cw310_mmi", param = { "interface": "cw310", "exit_success": DEFAULT_TEST_SUCCESS_MSG, "exit_failure": DEFAULT_TEST_FAILURE_MSG, "assemble": "{rom_ext}@0 {firmware}@0x10000", }, - rom_mmi = "//hw/bitstream:rom_mmi", visibility = ["//visibility:private"], ) diff --git a/util/fpga/splice_rom.sh b/util/fpga/splice_rom.sh index dcf4b14a11b32c..166a4bf96719b4 100755 --- a/util/fpga/splice_rom.sh +++ b/util/fpga/splice_rom.sh @@ -106,8 +106,8 @@ if [[ ! -f "${TARGET_PATH}" ]]; then exit 1 fi -if [[ ! -f "${FPGA_BIN_DIR}/rom.mmi" ]]; then - echo "Unable to find ${FPGA_BIN_DIR}/rom.mmi." >&2 +if [[ ! -f "${FPGA_BIN_DIR}/memories.mmi" ]]; then + echo "Unable to find ${FPGA_BIN_DIR}/memories.mmi." >&2 exit 1 fi @@ -128,9 +128,9 @@ hw/ip/rom_ctrl/util/gen_vivado_mem_image.py \ # the implemented design in Vivado and then inspecting the cell properties of # the corresponding BRAM cells. This information is very useful when debugging # the splicing flow. -updatemem -force --meminfo "${FPGA_BIN_DIR}/rom.mmi" \ +updatemem -force --meminfo "${FPGA_BIN_DIR}/meomries.mmi" \ --data "${TARGET}.updatemem.mem" \ - --bit "${FPGA_BIN_DIR}/${FPGA_BIT_NAME}.bit" --proc dummy \ + --bit "${FPGA_BIN_DIR}/${FPGA_BIT_NAME}.bit" --proc rom \ --out "${FPGA_BIN_DIR}/${FPGA_BIT_NAME}.splice.bit" \ --debug diff --git a/util/py/scripts/bitstream_cache_create.py b/util/py/scripts/bitstream_cache_create.py index c4a81831594d13..b845b39902c248 100755 --- a/util/py/scripts/bitstream_cache_create.py +++ b/util/py/scripts/bitstream_cache_create.py @@ -45,10 +45,9 @@ def collect_file_map(fragment: Dict, fragment_dir: Path): bitstream = os.path.join(fragment_dir, metadata['bitstream']['file']) bitstream_renamed = os.path.join(design, os.path.basename(bitstream)) file_map[bitstream] = bitstream_renamed - for mmi in metadata['memory_map_info'].values(): - mmi_file = os.path.join(fragment_dir, mmi['file']) - mmi_renamed = os.path.join(design, os.path.basename(mmi_file)) - file_map[mmi_file] = mmi_renamed + mmi_file = os.path.join(fragment_dir, metadata['memory_map_info']['file']) + mmi_renamed = os.path.join(design, os.path.basename(mmi_file)) + file_map[mmi_file] = mmi_renamed return file_map @@ -94,7 +93,7 @@ def main(argv: list[str]): args = parser.parse_args(args=argv[1:]) stamp = get_scm_revision(args.stamp_file) manifest = { - 'schema_version': 2, + 'schema_version': 3, 'designs': {}, } file_map = {} diff --git a/util/py/scripts/bitstreams_fragment_from_manifest.py b/util/py/scripts/bitstreams_fragment_from_manifest.py index 2fc28eacb6c741..b9e2e8a28c5087 100755 --- a/util/py/scripts/bitstreams_fragment_from_manifest.py +++ b/util/py/scripts/bitstreams_fragment_from_manifest.py @@ -51,13 +51,12 @@ def main(argv): if (args.create_symlinks): os.symlink(bitstream, os.path.join(args.out, bitstream_renamed)) - # Replace the MMI paths. - for mmi in metadata['memory_map_info'].values(): - mmi_file = os.path.join(manifest_dir, mmi['file']) - mmi_renamed = os.path.join(args.design, os.path.basename(mmi_file)) - mmi['file'] = mmi_renamed - if (args.create_symlinks): - os.symlink(mmi_file, os.path.join(args.out, mmi_renamed)) + # Replace the MMI path. + mmi_file = os.path.join(manifest_dir, metadata['memory_map_info']['file']) + mmi_renamed = os.path.join(args.design, os.path.basename(mmi_file)) + metadata['memory_map_info']['file'] = mmi_renamed + if (args.create_symlinks): + os.symlink(mmi_file, os.path.join(args.out, mmi_renamed)) # Dump the manifest to the specified location. manifest_path = os.path.join(args.out, 'manifest.json')