From 9e1ef80bca3d6648b1091efd1e5e7b3776a220f2 Mon Sep 17 00:00:00 2001 From: Alexander Williams Date: Sat, 30 Nov 2024 08:28:54 -0800 Subject: [PATCH 1/3] [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 --- 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 badf8223e844d..29e039b712eb3 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 d0a306437e632..58a9bc1acec8c 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 ef539f6d4004c..61aa684bb30f4 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 6686634c5d4f1..5d59b72ed7aea 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 9443eaf57edbc..5a06b2abb31cd 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 fcbddeccf46bb..077205ef53baa 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 8e73aade0069f..8670738b5d159 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 05cbb01217928..ae932c2cc218a 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"], "logs": ["synth-vivado/lowrisc_systems_chip_earlgrey_cw310_0.1.runs/"], }, systems = ["lowrisc:systems:chip_earlgrey_cw310"], @@ -70,18 +69,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"], ) @@ -102,8 +93,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"], "logs": ["synth-vivado/lowrisc_systems_chip_earlgrey_cw310_hyperdebug_0.1.runs/"], }, systems = ["lowrisc:systems:chip_earlgrey_cw310_hyperdebug"], @@ -120,18 +110,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"], ) @@ -152,8 +134,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"], "logs": ["synth-vivado/lowrisc_systems_chip_earlgrey_cw340_0.1.runs/"], }, systems = ["lowrisc:systems:chip_earlgrey_cw340"], @@ -170,18 +151,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"], ) @@ -192,10 +165,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"], ) @@ -215,10 +189,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"], ) @@ -238,10 +213,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"], ) @@ -260,9 +236,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"], @@ -272,9 +247,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 872e7410a6649..d192a6bad6115 100644 --- a/hw/top_earlgrey/BUILD +++ b/hw/top_earlgrey/BUILD @@ -90,16 +90,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}" @@ -156,15 +155,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}" @@ -296,10 +294,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}" @@ -317,10 +314,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 a399145c43827..58e96ff78e9b0 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 a356c940fe7c8..a1dbb7b2e59cd 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 bb30ad67352e4..e456028d50a72 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 5478c02b8deb4..bb1891e5b3c66 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 5daa94742fbd9..2c2209e9d053d 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 e88ed5b11f5d5..8f0dbfc52d813 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 0000000000000..5daa94742fbd9 --- /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 0000000000000..e9cb187c75e0b --- /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 2b9febe1de084..4912ea4a3a3cf 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 a43b95a675a7c..9340f5f71f85d 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 0bc4fbdbf8ad3..43ed796e0c9a6 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 c266c65ca7b92..cffafdcfbbec5 100644 --- a/sw/device/silicon_creator/rom/e2e/BUILD +++ b/sw/device/silicon_creator/rom/e2e/BUILD @@ -70,7 +70,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 b690786d78ba2..a75fd5b2ec855 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 a03832f468316..5a56146ddcbca 100644 --- a/sw/device/silicon_creator/rom/e2e/sigverify_spx/BUILD +++ b/sw/device/silicon_creator/rom/e2e/sigverify_spx/BUILD @@ -157,7 +157,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 9ca0d0adc3a42..4852b0e1269a0 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 dcf4b14a11b32..166a4bf96719b 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 c4a81831594d1..b845b39902c24 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 2fc28eacb6c74..b9e2e8a28c508 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') From bf595b6f694043433ea71064735cc8088cf6b3b5 Mon Sep 17 00:00:00 2001 From: Alexander Williams Date: Sat, 30 Nov 2024 08:39:30 -0800 Subject: [PATCH 2/3] [fpga] Dump flash info maps to the MMI file For earlgrey, export the flash info memory maps to the MMI file. Add knowledge of these maps existing to the bitstream cache entries. The flash info arrays are identified by a key in this format: flash_info Add KEEP_HIERARCHY to the prim_ram_1p instances representing the flash info arrays. This helps keep intelligible hierarchical paths to the memories, so we can readily select the correct cells. This lays groundwork for a future PR where we can splice these memories. Signed-off-by: Alexander Williams --- hw/bitstream/vivado/BUILD | 18 +++++++++ hw/top_earlgrey/chip_earlgrey_cw310.core | 1 + .../chip_earlgrey_cw310_hyperdebug.core | 1 + hw/top_earlgrey/chip_earlgrey_cw340.core | 1 + hw/top_earlgrey/data/synth.xdc | 14 +++++++ .../util/vivado_hook_write_bitstream_pre.tcl | 37 +++++++++++++++++++ 6 files changed, 72 insertions(+) create mode 100644 hw/top_earlgrey/data/synth.xdc diff --git a/hw/bitstream/vivado/BUILD b/hw/bitstream/vivado/BUILD index ae932c2cc218a..bfe1e6e804af6 100644 --- a/hw/bitstream/vivado/BUILD +++ b/hw/bitstream/vivado/BUILD @@ -168,6 +168,12 @@ bitstream_manifest_fragment( memories = [ "rom", "otp", + "flash0_info0", + "flash0_info1", + "flash0_info2", + "flash1_info0", + "flash1_info1", + "flash1_info2", ], memory_map_file = ":cw310_mmi", tags = ["manual"], @@ -192,6 +198,12 @@ bitstream_manifest_fragment( memories = [ "rom", "otp", + "flash0_info0", + "flash0_info1", + "flash0_info2", + "flash1_info0", + "flash1_info1", + "flash1_info2", ], memory_map_file = ":cw310_hyperdebug_mmi", tags = ["manual"], @@ -216,6 +228,12 @@ bitstream_manifest_fragment( memories = [ "rom", "otp", + "flash0_info0", + "flash0_info1", + "flash0_info2", + "flash1_info0", + "flash1_info1", + "flash1_info2", ], memory_map_file = ":cw340_mmi", tags = ["manual"], diff --git a/hw/top_earlgrey/chip_earlgrey_cw310.core b/hw/top_earlgrey/chip_earlgrey_cw310.core index ea79d3f5419dc..5ed38b7e7d54c 100644 --- a/hw/top_earlgrey/chip_earlgrey_cw310.core +++ b/hw/top_earlgrey/chip_earlgrey_cw310.core @@ -23,6 +23,7 @@ filesets: - data/clocks.xdc - data/pins_cw310.xdc - data/placement.xdc + - data/synth.xdc file_type: xdc files_tcl: diff --git a/hw/top_earlgrey/chip_earlgrey_cw310_hyperdebug.core b/hw/top_earlgrey/chip_earlgrey_cw310_hyperdebug.core index 5c04b28f56a40..74f8ae3c9e96f 100644 --- a/hw/top_earlgrey/chip_earlgrey_cw310_hyperdebug.core +++ b/hw/top_earlgrey/chip_earlgrey_cw310_hyperdebug.core @@ -25,6 +25,7 @@ filesets: # same as lowrisc:systems:chip_earlgrey_cw310. - data/pins_cw310_hyperdebug.xdc - data/placement.xdc + - data/synth.xdc file_type: xdc files_tcl: diff --git a/hw/top_earlgrey/chip_earlgrey_cw340.core b/hw/top_earlgrey/chip_earlgrey_cw340.core index e8adf5346d96d..462f42f771417 100644 --- a/hw/top_earlgrey/chip_earlgrey_cw340.core +++ b/hw/top_earlgrey/chip_earlgrey_cw340.core @@ -22,6 +22,7 @@ filesets: files: - data/clocks_cw341.xdc - data/pins_cw341.xdc + - data/synth.xdc file_type: xdc files_tcl: diff --git a/hw/top_earlgrey/data/synth.xdc b/hw/top_earlgrey/data/synth.xdc new file mode 100644 index 0000000000000..2615f456d1e63 --- /dev/null +++ b/hw/top_earlgrey/data/synth.xdc @@ -0,0 +1,14 @@ +## Copyright lowRISC contributors (OpenTitan project). +## Licensed under the Apache License, Version 2.0, see LICENSE for details. +## SPDX-License-Identifier: Apache-2.0 + +## Synthesis constraints + +# Prevent Vivado from combining these BRAMs, so the memories can be readily +# spliced with updatemem. +set_property KEEP_HIERARCHY TRUE [get_cells "top_earlgrey/u_flash_ctrl/u_eflash/u_flash/gen_generic.u_impl_generic/gen_prim_flash_banks[0].u_prim_flash_bank/gen_info_types[0].u_info_mem"] +set_property KEEP_HIERARCHY TRUE [get_cells "top_earlgrey/u_flash_ctrl/u_eflash/u_flash/gen_generic.u_impl_generic/gen_prim_flash_banks[0].u_prim_flash_bank/gen_info_types[1].u_info_mem"] +set_property KEEP_HIERARCHY TRUE [get_cells "top_earlgrey/u_flash_ctrl/u_eflash/u_flash/gen_generic.u_impl_generic/gen_prim_flash_banks[0].u_prim_flash_bank/gen_info_types[2].u_info_mem"] +set_property KEEP_HIERARCHY TRUE [get_cells "top_earlgrey/u_flash_ctrl/u_eflash/u_flash/gen_generic.u_impl_generic/gen_prim_flash_banks[1].u_prim_flash_bank/gen_info_types[0].u_info_mem"] +set_property KEEP_HIERARCHY TRUE [get_cells "top_earlgrey/u_flash_ctrl/u_eflash/u_flash/gen_generic.u_impl_generic/gen_prim_flash_banks[1].u_prim_flash_bank/gen_info_types[1].u_info_mem"] +set_property KEEP_HIERARCHY TRUE [get_cells "top_earlgrey/u_flash_ctrl/u_eflash/u_flash/gen_generic.u_impl_generic/gen_prim_flash_banks[1].u_prim_flash_bank/gen_info_types[2].u_info_mem"] 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 58e96ff78e9b0..26a832965ca8a 100644 --- a/hw/top_earlgrey/util/vivado_hook_write_bitstream_pre.tcl +++ b/hw/top_earlgrey/util/vivado_hook_write_bitstream_pre.tcl @@ -201,6 +201,43 @@ 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 +# The flash banks have 76-bit wide words. 64 bits are data, and 12 bits are metadata. +set flash_info_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_flash_ctrl*gen_prim_flash_banks[0]*gen_info_types[0].u_info_mem*"] " "] +dict set memInfo flash0_info0 brams $flash_info_brams +dict set memInfo flash0_info0 mem_type_regex $mem_type_regex +dict set memInfo flash0_info0 fake_word_width 0 +dict set memInfo flash0_info0 addr_end_multiplier 1 + +set flash_info_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_flash_ctrl*gen_prim_flash_banks[0]*gen_info_types[1].u_info_mem*"] " "] +dict set memInfo flash0_info1 brams $flash_info_brams +dict set memInfo flash0_info1 mem_type_regex $mem_type_regex +dict set memInfo flash0_info1 fake_word_width 0 +dict set memInfo flash0_info1 addr_end_multiplier 1 + +set flash_info_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_flash_ctrl*gen_prim_flash_banks[0]*gen_info_types[2].u_info_mem*"] " "] +dict set memInfo flash0_info2 brams $flash_info_brams +dict set memInfo flash0_info2 mem_type_regex $mem_type_regex +dict set memInfo flash0_info2 fake_word_width 0 +dict set memInfo flash0_info2 addr_end_multiplier 1 + +set flash_info_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_flash_ctrl*gen_prim_flash_banks[1]*gen_info_types[0].u_info_mem*"] " "] +dict set memInfo flash1_info0 brams $flash_info_brams +dict set memInfo flash1_info0 mem_type_regex $mem_type_regex +dict set memInfo flash1_info0 fake_word_width 0 +dict set memInfo flash1_info0 addr_end_multiplier 1 + +set flash_info_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_flash_ctrl*gen_prim_flash_banks[1]*gen_info_types[1].u_info_mem*"] " "] +dict set memInfo flash1_info1 brams $flash_info_brams +dict set memInfo flash1_info1 mem_type_regex $mem_type_regex +dict set memInfo flash1_info1 fake_word_width 0 +dict set memInfo flash1_info1 addr_end_multiplier 1 + +set flash_info_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_flash_ctrl*gen_prim_flash_banks[1]*gen_info_types[2].u_info_mem*"] " "] +dict set memInfo flash1_info2 brams $flash_info_brams +dict set memInfo flash1_info2 mem_type_regex $mem_type_regex +dict set memInfo flash1_info2 fake_word_width 0 +dict set memInfo flash1_info2 addr_end_multiplier 1 + generate_mmi "memories.mmi" $memInfo 1 # For debugging purposes, dump the INIT_XX strings for ROM and OTP. From 9776162eb91b785d2dabc01021dda00e015edd96 Mon Sep 17 00:00:00 2001 From: Alexander Williams Date: Tue, 3 Dec 2024 19:32:30 -0800 Subject: [PATCH 3/3] [prim,earlgrey,fpga] Add specialized ram_1p prim Add a specialized ram_1p prim for the prim_xilinx library. This prim adds some prim_xilinx-specific parameters to enable selecting particular layouts for embedded memories. The intention is to have the top-level override the parameters with hierarchical assignment. This feature is intended for use with memories that are too wide to fit in updatemem's capabilities (where splicing is required). The flash info pages are one such type, since updatemem can only handle up to 64-bit wide items, and the info page array is 76 bits wide. Synthesis chooses the most efficient layout of the memories, but the layout is awkward for handling splices. Force earlgrey's flash info data and metadata into separate arrays for the FPGA. Signed-off-by: Alexander Williams --- doc/contributing/fpga/debugging_with_ila.md | 2 +- .../prim_xilinx/prim_xilinx_default_pkg.core | 19 +++ hw/ip/prim_xilinx/prim_xilinx_ram_1p.core | 44 +++++++ hw/ip/prim_xilinx/rtl/prim_xilinx_pkg.sv | 16 +++ hw/ip/prim_xilinx/rtl/prim_xilinx_ram_1p.sv | 77 ++++++++++++ .../prim_xilinx_ultrascale_ram_1p.core | 19 +++ .../rtl/prim_xilinx_ultrascale_ram_1p.sv | 35 ++++++ hw/top_earlgrey/chip_earlgrey_cw310.core | 2 +- .../chip_earlgrey_cw310_hyperdebug.core | 2 +- hw/top_earlgrey/chip_earlgrey_cw340.core | 2 +- hw/top_earlgrey/data/synth.xdc | 14 --- hw/top_earlgrey/earlgrey_xilinx_prim_pkg.core | 19 +++ hw/top_earlgrey/rtl/prim_xilinx_pkg.sv | 20 +++ .../util/vivado_hook_write_bitstream_pre.tcl | 118 +++++++++--------- .../chip_englishbreakfast_cw305.core | 1 + 15 files changed, 316 insertions(+), 74 deletions(-) create mode 100644 hw/ip/prim_xilinx/prim_xilinx_default_pkg.core create mode 100644 hw/ip/prim_xilinx/prim_xilinx_ram_1p.core create mode 100644 hw/ip/prim_xilinx/rtl/prim_xilinx_pkg.sv create mode 100644 hw/ip/prim_xilinx/rtl/prim_xilinx_ram_1p.sv create mode 100644 hw/ip/prim_xilinx_ultrascale/prim_xilinx_ultrascale_ram_1p.core create mode 100644 hw/ip/prim_xilinx_ultrascale/rtl/prim_xilinx_ultrascale_ram_1p.sv delete mode 100644 hw/top_earlgrey/data/synth.xdc create mode 100644 hw/top_earlgrey/earlgrey_xilinx_prim_pkg.core create mode 100644 hw/top_earlgrey/rtl/prim_xilinx_pkg.sv diff --git a/doc/contributing/fpga/debugging_with_ila.md b/doc/contributing/fpga/debugging_with_ila.md index 58a9bc1acec8c..e02032798645d 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 `memories.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: diff --git a/hw/ip/prim_xilinx/prim_xilinx_default_pkg.core b/hw/ip/prim_xilinx/prim_xilinx_default_pkg.core new file mode 100644 index 0000000000000..efcff71f05a70 --- /dev/null +++ b/hw/ip/prim_xilinx/prim_xilinx_default_pkg.core @@ -0,0 +1,19 @@ +CAPI=2: +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim_xilinx:prim_xilinx_default_pkg" +description: "Single port RAM" +virtual: + - lowrisc:prim_xilinx:prim_xilinx_pkg +filesets: + files_rtl: + files: + - rtl/prim_xilinx_pkg.sv + file_type: systemVerilogSource + +targets: + default: &default_target + filesets: + - files_rtl diff --git a/hw/ip/prim_xilinx/prim_xilinx_ram_1p.core b/hw/ip/prim_xilinx/prim_xilinx_ram_1p.core new file mode 100644 index 0000000000000..3ee8918a2bfa7 --- /dev/null +++ b/hw/ip/prim_xilinx/prim_xilinx_ram_1p.core @@ -0,0 +1,44 @@ +CAPI=2: +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim_xilinx:ram_1p" +description: "Single port RAM" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:ram_1p_pkg + # Note that prim_xilinx_pkg is a virtual core that the top should provide. + # It maps parameters to instructions for how to split memories into + # logical groups of bits. See prim_xilinx_default_pkg for an example. + - lowrisc:prim_xilinx:prim_xilinx_pkg + - lowrisc:prim_generic:ram_1p + - lowrisc:prim:util_memload + files: + - rtl/prim_xilinx_ram_1p.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/hw/ip/prim_xilinx/rtl/prim_xilinx_pkg.sv b/hw/ip/prim_xilinx/rtl/prim_xilinx_pkg.sv new file mode 100644 index 0000000000000..cf46bc19512c4 --- /dev/null +++ b/hw/ip/prim_xilinx/rtl/prim_xilinx_pkg.sv @@ -0,0 +1,16 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package prim_xilinx_pkg; + + // Returns the maximum width of an individual xpm memory. Note that this API + // may evolve over time, but currently it uses the width and depth + // parameters to identify memories that may need to be broken up into + // separate groups. This can help work around updatemem's maximum width for + // words, which is 64 bits at the time of writing. + function automatic int get_ram_max_width(int width, int depth); + return 0; + endfunction + +endpackage : prim_xilinx_pkg diff --git a/hw/ip/prim_xilinx/rtl/prim_xilinx_ram_1p.sv b/hw/ip/prim_xilinx/rtl/prim_xilinx_ram_1p.sv new file mode 100644 index 0000000000000..582fa2a1f239b --- /dev/null +++ b/hw/ip/prim_xilinx/rtl/prim_xilinx_ram_1p.sv @@ -0,0 +1,77 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Synchronous single-port SRAM model + +`include "prim_assert.sv" + +module prim_xilinx_ram_1p import prim_ram_1p_pkg::*; #( + parameter int Width = 32, // bit + parameter int Depth = 128, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter MemInitFile = "", // VMEM file to initialize the memory with + + localparam int Aw = $clog2(Depth) // derived parameter +) ( + input logic clk_i, + + input logic req_i, + input logic write_i, + input logic [Aw-1:0] addr_i, + input logic [Width-1:0] wdata_i, + input logic [Width-1:0] wmask_i, + output logic [Width-1:0] rdata_o, // Read data. Data is returned one cycle after req_i is high. + input ram_1p_cfg_t cfg_i +); + + localparam int PrimMaxWidth = prim_xilinx_pkg::get_ram_max_width(Width, Depth); + + if (PrimMaxWidth <= 0) begin : gen_generic + prim_generic_ram_1p #( + .Width(Width), + .Depth(Depth), + .DataBitsPerMask(DataBitsPerMask), + .MemInitFile(MemInitFile) + ) u_ram_1p ( + .* + ); + end else begin : gen_xpm + logic wr_en; + assign wr_en = write_i & wmask_i[0]; + + logic unused_cfg_i; + assign unused_cfg_i = cfg_i; + + for (genvar k = 0; k < Width; k = k + PrimMaxWidth) begin : gen_split + localparam int PrimWidth = ((Width - k) > PrimMaxWidth) ? PrimMaxWidth : Width - k; + localparam string PrimMemoryInitFile = (MemInitFile != "") ? MemInitFile : "none"; + + xpm_memory_spram #( + .ADDR_WIDTH_A(Aw), + .BYTE_WRITE_WIDTH_A(PrimWidth), // Masks are not supported + .MEMORY_INIT_FILE(PrimMemoryInitFile), + .MEMORY_SIZE(Depth * PrimWidth), + .READ_DATA_WIDTH_A(PrimWidth), + .READ_LATENCY_A(1), + .USE_MEM_INIT_MMI(1), + .WRITE_DATA_WIDTH_A(PrimWidth) + ) u_ram_1p ( + .clka(clk_i), + .addra(addr_i), + .dbiterra(), + .dina(wdata_i[k +: PrimWidth]), + .douta(rdata_o[k +: PrimWidth]), + .ena(req_i), + .injectdbiterra(1'b0), + .injectsbiterra(1'b0), + .regcea(1'b1), + .rsta(1'b0), + .sbiterra(), + .sleep(1'b0), + .wea(wr_en) + ); + end + end + +endmodule diff --git a/hw/ip/prim_xilinx_ultrascale/prim_xilinx_ultrascale_ram_1p.core b/hw/ip/prim_xilinx_ultrascale/prim_xilinx_ultrascale_ram_1p.core new file mode 100644 index 0000000000000..720fe34c26e52 --- /dev/null +++ b/hw/ip/prim_xilinx_ultrascale/prim_xilinx_ultrascale_ram_1p.core @@ -0,0 +1,19 @@ +CAPI=2: +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim_xilinx_ultrascale:ram_1p" +description: "Single port RAM" +filesets: + files_rtl: + depend: + - lowrisc:prim_xilinx:ram_1p + files: + - rtl/prim_xilinx_ultrascale_ram_1p.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/hw/ip/prim_xilinx_ultrascale/rtl/prim_xilinx_ultrascale_ram_1p.sv b/hw/ip/prim_xilinx_ultrascale/rtl/prim_xilinx_ultrascale_ram_1p.sv new file mode 100644 index 0000000000000..741a51ebc1fb5 --- /dev/null +++ b/hw/ip/prim_xilinx_ultrascale/rtl/prim_xilinx_ultrascale_ram_1p.sv @@ -0,0 +1,35 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Synchronous single-port SRAM model + +`include "prim_assert.sv" + +module prim_xilinx_ultrascale_ram_1p import prim_ram_1p_pkg::*; #( + parameter int Width = 32, // bit + parameter int Depth = 128, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter MemInitFile = "", // VMEM file to initialize the memory with + + localparam int Aw = $clog2(Depth) // derived parameter +) ( + input logic clk_i, + + input logic req_i, + input logic write_i, + input logic [Aw-1:0] addr_i, + input logic [Width-1:0] wdata_i, + input logic [Width-1:0] wmask_i, + output logic [Width-1:0] rdata_o, // Read data. Data is returned one cycle after req_i is high. + input ram_1p_cfg_t cfg_i +); + + prim_xilinx_ram_1p #( + .Width(Width), + .Depth(Depth), + .DataBitsPerMask(DataBitsPerMask), + .MemInitFile(MemInitFile) + ) u_inst (.*); + +endmodule diff --git a/hw/top_earlgrey/chip_earlgrey_cw310.core b/hw/top_earlgrey/chip_earlgrey_cw310.core index 5ed38b7e7d54c..63a60d53d2940 100644 --- a/hw/top_earlgrey/chip_earlgrey_cw310.core +++ b/hw/top_earlgrey/chip_earlgrey_cw310.core @@ -7,6 +7,7 @@ description: "Earl Grey toplevel for the ChipWhisperer CW310 board" filesets: files_rtl_cw310: depend: + - lowrisc:prim_xilinx:earlgrey_xilinx_prim_pkg - lowrisc:systems:top_earlgrey:0.1 - lowrisc:systems:top_earlgrey_pkg - lowrisc:systems:ast @@ -23,7 +24,6 @@ filesets: - data/clocks.xdc - data/pins_cw310.xdc - data/placement.xdc - - data/synth.xdc file_type: xdc files_tcl: diff --git a/hw/top_earlgrey/chip_earlgrey_cw310_hyperdebug.core b/hw/top_earlgrey/chip_earlgrey_cw310_hyperdebug.core index 74f8ae3c9e96f..2b9d8a98b1983 100644 --- a/hw/top_earlgrey/chip_earlgrey_cw310_hyperdebug.core +++ b/hw/top_earlgrey/chip_earlgrey_cw310_hyperdebug.core @@ -7,6 +7,7 @@ description: "Earl Grey toplevel for the ChipWhisperer CW310 board's hyperdebug filesets: files_rtl_cw310: depend: + - lowrisc:prim_xilinx:earlgrey_xilinx_prim_pkg - lowrisc:systems:top_earlgrey:0.1 - lowrisc:systems:top_earlgrey_pkg - lowrisc:systems:ast @@ -25,7 +26,6 @@ filesets: # same as lowrisc:systems:chip_earlgrey_cw310. - data/pins_cw310_hyperdebug.xdc - data/placement.xdc - - data/synth.xdc file_type: xdc files_tcl: diff --git a/hw/top_earlgrey/chip_earlgrey_cw340.core b/hw/top_earlgrey/chip_earlgrey_cw340.core index 462f42f771417..9aa63ad4ea0ab 100644 --- a/hw/top_earlgrey/chip_earlgrey_cw340.core +++ b/hw/top_earlgrey/chip_earlgrey_cw340.core @@ -7,6 +7,7 @@ description: "Earl Grey toplevel for the ChipWhisperer CW340 board" filesets: files_rtl_cw340: depend: + - lowrisc:prim_xilinx:earlgrey_xilinx_prim_pkg - lowrisc:systems:top_earlgrey:0.1 - lowrisc:systems:top_earlgrey_pkg - lowrisc:systems:ast @@ -22,7 +23,6 @@ filesets: files: - data/clocks_cw341.xdc - data/pins_cw341.xdc - - data/synth.xdc file_type: xdc files_tcl: diff --git a/hw/top_earlgrey/data/synth.xdc b/hw/top_earlgrey/data/synth.xdc deleted file mode 100644 index 2615f456d1e63..0000000000000 --- a/hw/top_earlgrey/data/synth.xdc +++ /dev/null @@ -1,14 +0,0 @@ -## Copyright lowRISC contributors (OpenTitan project). -## Licensed under the Apache License, Version 2.0, see LICENSE for details. -## SPDX-License-Identifier: Apache-2.0 - -## Synthesis constraints - -# Prevent Vivado from combining these BRAMs, so the memories can be readily -# spliced with updatemem. -set_property KEEP_HIERARCHY TRUE [get_cells "top_earlgrey/u_flash_ctrl/u_eflash/u_flash/gen_generic.u_impl_generic/gen_prim_flash_banks[0].u_prim_flash_bank/gen_info_types[0].u_info_mem"] -set_property KEEP_HIERARCHY TRUE [get_cells "top_earlgrey/u_flash_ctrl/u_eflash/u_flash/gen_generic.u_impl_generic/gen_prim_flash_banks[0].u_prim_flash_bank/gen_info_types[1].u_info_mem"] -set_property KEEP_HIERARCHY TRUE [get_cells "top_earlgrey/u_flash_ctrl/u_eflash/u_flash/gen_generic.u_impl_generic/gen_prim_flash_banks[0].u_prim_flash_bank/gen_info_types[2].u_info_mem"] -set_property KEEP_HIERARCHY TRUE [get_cells "top_earlgrey/u_flash_ctrl/u_eflash/u_flash/gen_generic.u_impl_generic/gen_prim_flash_banks[1].u_prim_flash_bank/gen_info_types[0].u_info_mem"] -set_property KEEP_HIERARCHY TRUE [get_cells "top_earlgrey/u_flash_ctrl/u_eflash/u_flash/gen_generic.u_impl_generic/gen_prim_flash_banks[1].u_prim_flash_bank/gen_info_types[1].u_info_mem"] -set_property KEEP_HIERARCHY TRUE [get_cells "top_earlgrey/u_flash_ctrl/u_eflash/u_flash/gen_generic.u_impl_generic/gen_prim_flash_banks[1].u_prim_flash_bank/gen_info_types[2].u_info_mem"] diff --git a/hw/top_earlgrey/earlgrey_xilinx_prim_pkg.core b/hw/top_earlgrey/earlgrey_xilinx_prim_pkg.core new file mode 100644 index 0000000000000..2284fd3ba0b9a --- /dev/null +++ b/hw/top_earlgrey/earlgrey_xilinx_prim_pkg.core @@ -0,0 +1,19 @@ +CAPI=2: +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim_xilinx:earlgrey_xilinx_prim_pkg" +description: "Single port RAM" +virtual: + - lowrisc:prim_xilinx:prim_xilinx_pkg +filesets: + files_rtl: + files: + - rtl/prim_xilinx_pkg.sv + file_type: systemVerilogSource + +targets: + default: &default_target + filesets: + - files_rtl diff --git a/hw/top_earlgrey/rtl/prim_xilinx_pkg.sv b/hw/top_earlgrey/rtl/prim_xilinx_pkg.sv new file mode 100644 index 0000000000000..aa1a2b0b8f6c4 --- /dev/null +++ b/hw/top_earlgrey/rtl/prim_xilinx_pkg.sv @@ -0,0 +1,20 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package prim_xilinx_pkg; + + // Accommodates updatemem by breaking up flash info arrays into data and + // metadata subarrays. The 76-bit width and 1 BRAM depth identifies these + // memories in earlgrey, and we limit the subarray size to 64 bits, which is + // the size of the data portion. The leftover 12 bits get placed into their + // own memory with a unique hierarchical path. See prim_xilinx_ram_1p.sv to + // see how this works. + function automatic int get_ram_max_width(int width, int depth); + if (width == 76 && depth < 4096) begin + return 64; + end + return 0; + endfunction + +endpackage : prim_xilinx_pkg 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 26a832965ca8a..e0138e072edf0 100644 --- a/hw/top_earlgrey/util/vivado_hook_write_bitstream_pre.tcl +++ b/hw/top_earlgrey/util/vivado_hook_write_bitstream_pre.tcl @@ -28,6 +28,7 @@ set_property BITSTREAM.CONFIG.USR_ACCESS TIMESTAMP [current_design] # addr_end_multiplier: A coefficient applied to the address space. Influences # the values of the MMI's and # tags. +# schema: Either "Processor" or "MemoryArray" # designtask_count: A number used for logging with `send_msg`. proc generate_mmi {filename mem_infos designtask_count} { send_msg "${designtask_count}-1" INFO "Dumping MMI to ${filename}" @@ -50,6 +51,7 @@ proc generate_mmi {filename mem_infos designtask_count} { # Calculate the overall address space. set space 0 + set width 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]] @@ -69,14 +71,22 @@ proc generate_mmi {filename mem_infos designtask_count} { # Calculate total number of bits. set space [expr {$space + ($addr_end - $addr_begin + 1) * $slice_width}] + set width [expr {$width + $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 " " + if { $schema eq "Processor" } { + puts $fileout " " + puts $fileout " " + puts $fileout " " + } else { + puts $fileout " " + # Memory type could be retrieved from the RTL_RAM_TYPE property, if desired, + # but we hard-code it here for now. + puts $fileout " " + } foreach inst [lsort -dictionary $brams] { set loc [get_property LOC [get_cells $inst]] @@ -94,15 +104,36 @@ proc generate_mmi {filename mem_infos designtask_count} { 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 " " + set bit_layout [get_property "MEM.PORTA.DATA_BIT_LAYOUT" [get_cells $inst]] + set read_width_a [get_property "READ_WIDTH_A" [get_cells $inst]] + set read_width_b [get_property "READ_WIDTH_B" [get_cells $inst]] + set slr_index [get_property "SLR_INDEX" [get_cells $inst]] + if {$schema eq "Processor"} { + puts $fileout " " + puts $fileout " " + puts $fileout " " + puts $fileout " " + puts $fileout " " + } else { + puts $fileout " " + puts $fileout " " + puts $fileout " " + puts $fileout " " + puts $fileout " " + puts $fileout " " + puts $fileout " " + puts $fileout " " + puts $fileout " " + } + } + if {$schema eq "Processor"} { + puts $fileout " " + puts $fileout " " + puts $fileout " " + } else { + puts $fileout " " + puts $fileout " " } - puts $fileout " " - puts $fileout " " - puts $fileout " " } } @@ -170,6 +201,14 @@ switch ${fpga_family} { } set mem_type_regex {(RAMB\d+)_(\w+)} +set gen_mem_info {{brams mem_type_regex fake_word_width addr_end_multiplier schema} { + dict set mem_info brams $brams + dict set mem_info mem_type_regex $mem_type_regex + dict set mem_info fake_word_width $fake_word_width + dict set mem_info addr_end_multiplier $addr_end_multiplier + return [dict set mem_info schema $schema] +}} + # 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 # that updatemem will understand. @@ -185,10 +224,7 @@ set mem_type_regex {(RAMB\d+)_(\w+)} # 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*"] " "] -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 +dict set memInfo rom [apply $gen_mem_info $rom_brams $mem_type_regex 40 1 "Processor"] # 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 @@ -196,47 +232,17 @@ dict set memInfo rom addr_end_multiplier 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*"] " "] -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 - -# The flash banks have 76-bit wide words. 64 bits are data, and 12 bits are metadata. -set flash_info_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_flash_ctrl*gen_prim_flash_banks[0]*gen_info_types[0].u_info_mem*"] " "] -dict set memInfo flash0_info0 brams $flash_info_brams -dict set memInfo flash0_info0 mem_type_regex $mem_type_regex -dict set memInfo flash0_info0 fake_word_width 0 -dict set memInfo flash0_info0 addr_end_multiplier 1 - -set flash_info_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_flash_ctrl*gen_prim_flash_banks[0]*gen_info_types[1].u_info_mem*"] " "] -dict set memInfo flash0_info1 brams $flash_info_brams -dict set memInfo flash0_info1 mem_type_regex $mem_type_regex -dict set memInfo flash0_info1 fake_word_width 0 -dict set memInfo flash0_info1 addr_end_multiplier 1 - -set flash_info_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_flash_ctrl*gen_prim_flash_banks[0]*gen_info_types[2].u_info_mem*"] " "] -dict set memInfo flash0_info2 brams $flash_info_brams -dict set memInfo flash0_info2 mem_type_regex $mem_type_regex -dict set memInfo flash0_info2 fake_word_width 0 -dict set memInfo flash0_info2 addr_end_multiplier 1 - -set flash_info_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_flash_ctrl*gen_prim_flash_banks[1]*gen_info_types[0].u_info_mem*"] " "] -dict set memInfo flash1_info0 brams $flash_info_brams -dict set memInfo flash1_info0 mem_type_regex $mem_type_regex -dict set memInfo flash1_info0 fake_word_width 0 -dict set memInfo flash1_info0 addr_end_multiplier 1 - -set flash_info_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_flash_ctrl*gen_prim_flash_banks[1]*gen_info_types[1].u_info_mem*"] " "] -dict set memInfo flash1_info1 brams $flash_info_brams -dict set memInfo flash1_info1 mem_type_regex $mem_type_regex -dict set memInfo flash1_info1 fake_word_width 0 -dict set memInfo flash1_info1 addr_end_multiplier 1 - -set flash_info_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_flash_ctrl*gen_prim_flash_banks[1]*gen_info_types[2].u_info_mem*"] " "] -dict set memInfo flash1_info2 brams $flash_info_brams -dict set memInfo flash1_info2 mem_type_regex $mem_type_regex -dict set memInfo flash1_info2 fake_word_width 0 -dict set memInfo flash1_info2 addr_end_multiplier 1 +dict set memInfo otp [apply $gen_mem_info $otp_brams $mem_type_regex 0 16 "Processor"] + +# The flash banks have 76-bit wide words. 64 bits are data, and 12 bits are metadata / integrity. +for {set bank 0} {$bank < 2} {incr bank} { + for {set partition 0} {$partition < 3} {incr partition} { + set flash_info_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_flash_ctrl*gen_prim_flash_banks[${bank}]*gen_info_types[${partition}].u_info_mem*gen_xpm.gen_split[0].*"] " "] + dict set memInfo "flash${bank}_info${partition}_data" [apply $gen_mem_info $flash_info_brams $mem_type_regex 0 1 "MemoryArray"] + set flash_info_brams [split [get_cells -hierarchical -filter " PRIMITIVE_TYPE =~ ${bram_regex} && NAME =~ *u_flash_ctrl*gen_prim_flash_banks[${bank}]*gen_info_types[${partition}].u_info_mem*gen_xpm.gen_split[64].*"] " "] + dict set memInfo "flash${bank}_info${partition}_intg" [apply $gen_mem_info $flash_info_brams $mem_type_regex 0 1 "MemoryArray"] + } +} generate_mmi "memories.mmi" $memInfo 1 diff --git a/hw/top_englishbreakfast/chip_englishbreakfast_cw305.core b/hw/top_englishbreakfast/chip_englishbreakfast_cw305.core index edacf154d3857..22b0e1d6d588a 100644 --- a/hw/top_englishbreakfast/chip_englishbreakfast_cw305.core +++ b/hw/top_englishbreakfast/chip_englishbreakfast_cw305.core @@ -8,6 +8,7 @@ description: "English Breakfast toplevel for the ChipWhisperer CW305 board" filesets: files_rtl_cw305: depend: + - lowrisc:prim_xilinx:prim_xilinx_default_pkg - lowrisc:systems:top_englishbreakfast:0.1 - lowrisc:systems:ast - lowrisc:systems:topgen