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