Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport 25533 to earlgrey 1.0.0 #25669

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions ci/scripts/build-bitstream-vivado.sh
Original file line number Diff line number Diff line change
@@ -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"
4 changes: 2 additions & 2 deletions doc/contributing/fpga/debugging_with_ila.md
Original file line number Diff line number Diff line change
@@ -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:
3 changes: 1 addition & 2 deletions doc/contributing/fpga/ref_manual_fpga.md
Original file line number Diff line number Diff line change
@@ -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

38 changes: 13 additions & 25 deletions hw/bitstream/BUILD
Original file line number Diff line number Diff line change
@@ -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",
29 changes: 10 additions & 19 deletions hw/bitstream/cw340/BUILD
Original file line number Diff line number Diff line change
@@ -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: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,
)
29 changes: 10 additions & 19 deletions hw/bitstream/hyperdebug/BUILD
Original file line number Diff line number Diff line change
@@ -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,
)
4 changes: 2 additions & 2 deletions hw/bitstream/universal/BUILD
Original file line number Diff line number Diff line change
@@ -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",
96 changes: 44 additions & 52 deletions hw/bitstream/vivado/BUILD
Original file line number Diff line number Diff line change
@@ -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,17 @@ bitstream_manifest_fragment(
srcs = [":fpga_cw310"],
bitstream = "bitstream",
design = "chip_earlgrey_cw310",
memory_maps = {
"rom_mmi": "rom",
"otp_mmi": "otp",
},
memories = [
"rom",
"otp",
"flash0_info0",
"flash0_info1",
"flash0_info2",
"flash1_info0",
"flash1_info1",
"flash1_info2",
],
memory_map_file = ":cw310_mmi",
tags = ["manual"],
)

@@ -212,10 +192,17 @@ 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",
"flash0_info0",
"flash0_info1",
"flash0_info2",
"flash1_info0",
"flash1_info1",
"flash1_info2",
],
memory_map_file = ":cw310_hyperdebug_mmi",
tags = ["manual"],
)

@@ -235,10 +222,17 @@ bitstream_manifest_fragment(
srcs = [":fpga_cw340"],
bitstream = "bitstream",
design = "chip_earlgrey_cw340",
memory_maps = {
"rom_mmi": "rom",
"otp_mmi": "otp",
},
memories = [
"rom",
"otp",
"flash0_info0",
"flash0_info1",
"flash0_info2",
"flash1_info0",
"flash1_info1",
"flash1_info2",
],
memory_map_file = ":cw340_mmi",
tags = ["manual"],
)

@@ -257,9 +251,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 +262,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"],
19 changes: 19 additions & 0 deletions hw/ip/prim_xilinx/prim_xilinx_default_pkg.core
Original file line number Diff line number Diff line change
@@ -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
44 changes: 44 additions & 0 deletions hw/ip/prim_xilinx/prim_xilinx_ram_1p.core
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions hw/ip/prim_xilinx/rtl/prim_xilinx_pkg.sv
Original file line number Diff line number Diff line change
@@ -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
77 changes: 77 additions & 0 deletions hw/ip/prim_xilinx/rtl/prim_xilinx_ram_1p.sv
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions hw/ip/prim_xilinx_ultrascale/prim_xilinx_ultrascale_ram_1p.core
Original file line number Diff line number Diff line change
@@ -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
35 changes: 35 additions & 0 deletions hw/ip/prim_xilinx_ultrascale/rtl/prim_xilinx_ultrascale_ram_1p.sv
Original file line number Diff line number Diff line change
@@ -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
12 changes: 4 additions & 8 deletions hw/top_earlgrey/BUILD
Original file line number Diff line number Diff line change
@@ -91,16 +91,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}"
@@ -186,15 +185,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}"
@@ -324,10 +322,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}"
@@ -345,10 +342,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}"
1 change: 1 addition & 0 deletions hw/top_earlgrey/chip_earlgrey_cw310.core
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions hw/top_earlgrey/chip_earlgrey_cw310_hyperdebug.core
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions hw/top_earlgrey/chip_earlgrey_cw340.core
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions hw/top_earlgrey/earlgrey_xilinx_prim_pkg.core
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions hw/top_earlgrey/rtl/prim_xilinx_pkg.sv
Original file line number Diff line number Diff line change
@@ -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
205 changes: 132 additions & 73 deletions hw/top_earlgrey/util/vivado_hook_write_bitstream_pre.tcl
Original file line number Diff line number Diff line change
@@ -19,89 +19,127 @@ 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 <AddressSpace> and <DataWidth> tags.
# addr_end_multiplier: A coefficient applied to the address space. Influences
# the values of the MMI's <AddressSpace> and
# <AddressRange> 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 <AddressSpace> and <DataWidth> tags.
# addr_end_multiplier: A coefficient applied to the address space. Influences
# the values of the MMI's <AddressSpace> and
# <AddressRange> tags.
# schema: Either "Processor" or "MemoryArray"
# 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 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
puts $fileout "<MemInfo Version=\"1\" Minor=\"1\">"
puts $fileout " <Processor Endianness=\"Little\" InstPath=\"dummy\">"
puts $fileout " <AddressSpace Name=\"dummy_addrspace\" Begin=\"0\" End=\"$space\">"
puts $fileout " <BusBlock>"

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
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]]
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 width [expr {$width + $slice_width}]
set last_slice_width $slice_width
}
set space [expr {($space * $addr_end_multiplier / 8) - 1}]

# Generate the MMI.
if { $schema eq "Processor" } {
puts $fileout " <Processor Endianness=\"Little\" InstPath=\"$id\">"
puts $fileout " <AddressSpace Name=\"dummy_addrspace\" Begin=\"0\" End=\"$space\">"
puts $fileout " <BusBlock>"
} else {
puts $fileout " <MemoryArray InstPath=\"$id\" MemoryPrimitive=\"auto\" MemoryConfiguration=\"enabled_configuration\">"
# Memory type could be retrieved from the RTL_RAM_TYPE property, if desired,
# but we hard-code it here for now.
puts $fileout " <MemoryLayout Name=\"$id\" CoreMemory_Width=\"$width\" MemoryType=\"RAM_SP\">"
}

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}]
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 " <BitLane MemType=\"$loc_prefix\" Placement=\"$loc_suffix\">"
puts $fileout " <DataWidth MSB=\"$slice_end\" LSB=\"$slice_begin\"/>"
puts $fileout " <AddressRange Begin=\"$addr_begin\" End=\"$addr_end\"/>"
puts $fileout " <Parity ON=\"false\" NumBits=\"0\"/>"
puts $fileout " </BitLane>"
} else {
puts $fileout " <BRAM MemType=\"$loc_prefix\" Placement=\"$loc_suffix\" Read_Width_A=\"$read_width_a\" Read_Width_B=\"$read_width_b\" SLR_INDEX=\"$slr_index\">"
puts $fileout " <DataWidth_PortA MSB=\"$slice_end\" LSB=\"$slice_begin\"/>"
puts $fileout " <AddressRange_PortA Begin=\"$addr_begin\" End=\"$addr_end\"/>"
puts $fileout " <BitLayout_PortA pattern=\"$bit_layout\"/>"
puts $fileout " <DataWidth_PortB MSB=\"0\" LSB=\"0\"/>"
puts $fileout " <AddressRange_PortB Begin=\"0\" End=\"0\"/>"
puts $fileout " <BitLayout_PortB pattern=\"\"/>"
puts $fileout " <Parity ON=\"false\" NumBits=\"0\"/>"
puts $fileout " </BRAM>"
}
}
if {$schema eq "Processor"} {
puts $fileout " </BusBlock>"
puts $fileout " </AddressSpace>"
puts $fileout " </Processor>"
} else {
puts $fileout " </MemoryLayout>"
puts $fileout " </MemoryArray>"
}
}
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 " <BitLane MemType=\"$mem_type\" Placement=\"$loc\">"
puts $fileout " <DataWidth MSB=\"$slice_end\" LSB=\"$slice_begin\"/>"
puts $fileout " <AddressRange Begin=\"$addr_begin\" End=\"$addr_end\"/>"
puts $fileout " <Parity ON=\"false\" NumBits=\"0\"/>"
puts $fileout " </BitLane>"
}
puts $fileout " </BusBlock>"
puts $fileout " </AddressSpace>"
puts $fileout " </Processor>"
puts $fileout "<Config>"
puts $fileout " <Option Name=\"Part\" Val=\"$part\"/>"
puts $fileout "</Config>"

puts $fileout " <Config>"
puts $fileout " <Option Name=\"Part\" Val=\"$part\"/>"
puts $fileout " </Config>"
puts $fileout "</MemInfo>"
close $fileout
send_msg "${designtask_count}-4" INFO "MMI dumped to ${filepath}"
@@ -161,6 +199,15 @@ switch ${fpga_family} {
set bram_regex "BMEM.*.*"
}
}
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
@@ -177,15 +224,27 @@ 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 [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 <AddressSpace>
# and <AddressRange> tags need to account for this or else updatemem will think
# 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 [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

# For debugging purposes, dump the INIT_XX strings for ROM and OTP.
dump_init_strings "rom_init_strings.txt" $rom_brams 3
1 change: 1 addition & 0 deletions hw/top_englishbreakfast/chip_englishbreakfast_cw305.core
Original file line number Diff line number Diff line change
@@ -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
42 changes: 23 additions & 19 deletions rules/bitstreams.bzl
Original file line number Diff line number Diff line change
@@ -154,9 +154,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 = {}
@@ -177,19 +182,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)

@@ -200,7 +200,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])
@@ -244,9 +245,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,
),
},
)
14 changes: 4 additions & 10 deletions rules/opentitan/exec_env.bzl
Original file line number Diff line number Diff line change
@@ -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"),
13 changes: 7 additions & 6 deletions rules/opentitan/splice.bzl
Original file line number Diff line number Diff line change
@@ -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"),
30 changes: 13 additions & 17 deletions rules/scripts/bitstreams_manifest.example.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
}
}
20 changes: 12 additions & 8 deletions rules/scripts/bitstreams_manifest.schema.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"$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",
"properties": {
"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"
39 changes: 39 additions & 0 deletions rules/scripts/bitstreams_manifest_v2.example.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
}
}
80 changes: 80 additions & 0 deletions rules/scripts/bitstreams_manifest_v2.schema.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
}
}
233 changes: 116 additions & 117 deletions rules/scripts/bitstreams_workspace.py
Original file line number Diff line number Diff line change
@@ -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",
},
}

@@ -279,7 +277,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:
@@ -288,59 +286,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):
@@ -357,20 +371,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
@@ -382,43 +392,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!',
@@ -477,14 +471,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()):
@@ -504,12 +490,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):
102 changes: 57 additions & 45 deletions rules/scripts/bitstreams_workspace_test.py
Original file line number Diff line number Diff line change
@@ -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):
"""
3 changes: 2 additions & 1 deletion rules/splice.bzl
Original file line number Diff line number Diff line change
@@ -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"),
3 changes: 2 additions & 1 deletion sw/device/silicon_creator/rom/e2e/BUILD
Original file line number Diff line number Diff line change
@@ -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"],
3 changes: 2 additions & 1 deletion sw/device/silicon_creator/rom/e2e/boot_policy_valid/BUILD
Original file line number Diff line number Diff line change
@@ -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()
3 changes: 2 additions & 1 deletion sw/device/silicon_creator/rom/e2e/sigverify_spx/BUILD
Original file line number Diff line number Diff line change
@@ -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"],
)
3 changes: 1 addition & 2 deletions sw/device/tests/pmod/BUILD
Original file line number Diff line number Diff line change
@@ -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"],
)

8 changes: 4 additions & 4 deletions util/fpga/splice_rom.sh
Original file line number Diff line number Diff line change
@@ -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

9 changes: 4 additions & 5 deletions util/py/scripts/bitstream_cache_create.py
Original file line number Diff line number Diff line change
@@ -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 = {}
13 changes: 6 additions & 7 deletions util/py/scripts/bitstreams_fragment_from_manifest.py
Original file line number Diff line number Diff line change
@@ -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')