Merge at opentitan f243e680 Change-Id: I5c447581944309d63f2d58ec95647815f462bae0
diff --git a/hw/dv/BUILD b/hw/dv/BUILD new file mode 100644 index 0000000..29144e4 --- /dev/null +++ b/hw/dv/BUILD
@@ -0,0 +1,11 @@ +# Copyright 2022 Google contributors. + +package(default_visibility = ["//visibility:public"]) + +# For matcha to get the directory of this folder and run fusesoc. +exports_files(["BUILD"]) + +filegroup( + name ="all_files", + srcs = glob(["**"]), +)
diff --git a/hw/dv/dpi/spidpi/spidpi.c b/hw/dv/dpi/spidpi/spidpi.c index 3827635..a6d9314 100644 --- a/hw/dv/dpi/spidpi/spidpi.c +++ b/hw/dv/dpi/spidpi/spidpi.c
@@ -51,7 +51,12 @@ ctx->driving = P2D_CSB | ((ctx->cpol) ? P2D_SCK : 0); char cwd[PATH_MAX]; char *cwd_rv; - cwd_rv = getcwd(cwd, sizeof(cwd)); + const char *test_out_dir = getenv("TEST_UNDECLARED_OUTPUTS_DIR"); + if (test_out_dir) { + cwd_rv = strncpy(cwd, test_out_dir, sizeof(cwd)); + } else { + cwd_rv = getcwd(cwd, sizeof(cwd)); + } assert(cwd_rv != NULL); int rv;
diff --git a/hw/dv/dpi/uartdpi/uartdpi.c b/hw/dv/dpi/uartdpi/uartdpi.c index fa69858..479162f 100644 --- a/hw/dv/dpi/uartdpi/uartdpi.c +++ b/hw/dv/dpi/uartdpi/uartdpi.c
@@ -57,10 +57,20 @@ } else { FILE *log_file; - log_file = fopen(log_file_path, "w"); + const char *test_out_dir = getenv("TEST_UNDECLARED_OUTPUTS_DIR"); + const int kMaxPathLen = 512; + char test_out_log_file[kMaxPathLen] = {0}; + if (test_out_dir) { + snprintf(test_out_log_file, kMaxPathLen, "%s/%s", test_out_dir, + log_file_path), + log_file = fopen(test_out_log_file, "w"); + } else { + log_file = fopen(log_file_path, "w"); + } if (!log_file) { fprintf(stderr, "UART: Unable to open log file at %s: %s\n", - log_file_path, strerror(errno)); + test_out_dir ? test_out_log_file : log_file_path, + strerror(errno)); } else { // Switch log file output to line buffering to ensure lines written to // the UART device show up in the log file as soon as a newline @@ -70,7 +80,7 @@ ctx->log_file = log_file; printf("UART: Additionally writing all UART output to '%s'.\n", - log_file_path); + test_out_dir ? test_out_log_file : log_file_path); } } }
diff --git a/hw/dv/dpi/usbdpi/usbdpi.c b/hw/dv/dpi/usbdpi/usbdpi.c index 8c3c9c5..3ccfcb3 100644 --- a/hw/dv/dpi/usbdpi/usbdpi.c +++ b/hw/dv/dpi/usbdpi/usbdpi.c
@@ -112,7 +112,12 @@ char cwd[FILENAME_MAX]; char *cwd_rv; - cwd_rv = getcwd(cwd, sizeof(cwd)); + const char *test_out_dir = getenv("TEST_UNDECLARED_OUTPUTS_DIR"); + if (test_out_dir) { + cwd_rv = strncpy(cwd, test_out_dir, sizeof(cwd)); + } else { + cwd_rv = getcwd(cwd, sizeof(cwd)); + } assert(cwd_rv != NULL); // Monitor log file
diff --git a/hw/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.cc b/hw/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.cc index 3c8ddea..53589d9 100644 --- a/hw/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.cc +++ b/hw/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.cc
@@ -319,11 +319,23 @@ } const char *VerilatorSimCtrl::GetTraceFileName() const { + const char *test_output_dir = getenv("TEST_UNDECLARED_OUTPUTS_DIR"); + constexpr int kMaxPathLen = 512; + static char trace_file[kMaxPathLen]; #ifdef VM_TRACE_FMT_FST - return "sim.fst"; + if (test_output_dir) { + snprintf(trace_file, kMaxPathLen, "%s/sim.fst", test_output_dir); + } else { + return "sim.fst"; + } #else - return "sim.vcd"; + if (test_output_dir) { + snprintf(trace_file, kMaxPathLen, "%s/sim.fst", test_output_dir); + } else { + return "sim.vcd"; + } #endif + return trace_file; } void VerilatorSimCtrl::Run() {
diff --git a/hw/ip/otbn/BUILD b/hw/ip/otbn/BUILD index ba54a6a..67d0bb7 100644 --- a/hw/ip/otbn/BUILD +++ b/hw/ip/otbn/BUILD
@@ -10,3 +10,39 @@ "//hw/ip/otbn/data:all_files", ], ) + +genrule( + name = "otbn_simple_smoke_test_obj", + srcs = [ + "dv/smoke/smoke_test.s", + ], + outs = [ + "otbn_simple_smoke_test.o", + ], + cmd = """ + export RV32_TOOL_AS=$(location @lowrisc_rv32imcb_files//:bin/riscv32-unknown-elf-as) + $(location //hw/ip/otbn/util:otbn_as) -o $@ $< + """, + tools = [ + "//hw/ip/otbn/util:otbn_as", + "@lowrisc_rv32imcb_files//:bin/riscv32-unknown-elf-as", + ], +) + +genrule( + name = "otbn_simple_smoke_test_elf", + srcs = [ + ":otbn_simple_smoke_test.o", + ], + outs = [ + "otbn_simple_smoke_test.elf", + ], + cmd = """ + export RV32_TOOL_LD=$(location @lowrisc_rv32imcb_files//:bin/riscv32-unknown-elf-ld) + $(location //hw/ip/otbn/util:otbn_ld) -o $@ $< + """, + tools = [ + "//hw/ip/otbn/util:otbn_ld", + "@lowrisc_rv32imcb_files//:bin/riscv32-unknown-elf-ld", + ], +)
diff --git a/hw/ip/rv_dm/data/rv_dm.hjson b/hw/ip/rv_dm/data/rv_dm.hjson index 5151e59..0c8407f 100644 --- a/hw/ip/rv_dm/data/rv_dm.hjson +++ b/hw/ip/rv_dm/data/rv_dm.hjson
@@ -31,7 +31,8 @@ param_list: [ { name: "NrHarts", type: "int", - default: "1", + // TODO(b/271173103). + default: "2", desc: "Number of hardware threads in the system." local: "true" }, @@ -110,11 +111,13 @@ power down of the core and bus-attached peripherals. ''' }, - { struct: "logic [rv_dm_reg_pkg::NrHarts-1:0]" + { struct: "logic" type: "uni" name: "debug_req" act: "req" desc: "This is the debug request interrupt going to the main processor." + // TODO(b/271173103). + width: "2" }, ] countermeasures: [
diff --git a/hw/ip/rv_dm/rtl/rv_dm_reg_pkg.sv b/hw/ip/rv_dm/rtl/rv_dm_reg_pkg.sv index eb2c96c..f7791ca 100644 --- a/hw/ip/rv_dm/rtl/rv_dm_reg_pkg.sv +++ b/hw/ip/rv_dm/rtl/rv_dm_reg_pkg.sv
@@ -7,7 +7,7 @@ package rv_dm_reg_pkg; // Param list - parameter int NrHarts = 1; + parameter int NrHarts = 2; parameter int NumAlerts = 1; // Address widths within the block
diff --git a/hw/ip/tlul/rtl/tlul_pkg.sv b/hw/ip/tlul/rtl/tlul_pkg.sv index 4e9401f..34f601d 100644 --- a/hw/ip/tlul/rtl/tlul_pkg.sv +++ b/hw/ip/tlul/rtl/tlul_pkg.sv
@@ -13,17 +13,43 @@ // both in terms of area and timing. parameter ArbiterImpl = "PPC"; + // TODO(hoangm): Below enums have been modified to support TL-UH. Eventually + // we'll want to upstream these changes to OT. typedef enum logic [2:0] { PutFullData = 3'h 0, PutPartialData = 3'h 1, - Get = 3'h 4 + ArithmeticData = 3'h 2, + LogicalData = 3'h 3, + Get = 3'h 4, + Intent = 3'h 5 } tl_a_op_e; typedef enum logic [2:0] { AccessAck = 3'h 0, - AccessAckData = 3'h 1 + AccessAckData = 3'h 1, + HintAck = 3'h 2 } tl_d_op_e; + typedef enum logic [2:0] { + Min = 3'h 0, + Max = 3'h 1, + MinU = 3'h 2, + MaxU = 3'h 3, + Add = 3'h 4 + } tl_a_arith_param_e; + + typedef enum logic [2:0] { + Xor = 3'h 0, + Or = 3'h 1, + And = 3'h 2, + Swap = 3'h 3 + } tl_a_logical_param_e; + + typedef enum logic [2:0] { + PrefetchRead = 3'h 0, + PrefetchWrite = 3'h 1 + } tl_a_intent_param_e; + parameter int H2DCmdMaxWidth = 57; parameter int H2DCmdIntgWidth = 7; parameter int H2DCmdFullWidth = H2DCmdMaxWidth + H2DCmdIntgWidth;
diff --git a/hw/lint/BUILD b/hw/lint/BUILD new file mode 100644 index 0000000..2f3e391 --- /dev/null +++ b/hw/lint/BUILD
@@ -0,0 +1,6 @@ +# Copyright 2022 Google contributors. + +package(default_visibility = ["//visibility:public"]) + +# For matcha to get the directory of this folder and run fusesoc. +exports_files(["BUILD"])
diff --git a/hw/vendor/BUILD b/hw/vendor/BUILD new file mode 100644 index 0000000..2f3e391 --- /dev/null +++ b/hw/vendor/BUILD
@@ -0,0 +1,6 @@ +# Copyright 2022 Google contributors. + +package(default_visibility = ["//visibility:public"]) + +# For matcha to get the directory of this folder and run fusesoc. +exports_files(["BUILD"])
diff --git a/python-requirements.txt b/python-requirements.txt index 700f56d..bb655f6 100644 --- a/python-requirements.txt +++ b/python-requirements.txt
@@ -5,7 +5,8 @@ # Keep sorted. hjson==3.1.0 jsonschema==4.17.3; python_version >= "3.7" -libcst==0.4.1 +# Need to pin to 0.4.9 to be compatible with python3.11 +libcst==0.4.9 mako==1.1.6 pluralizer==1.2.0 pycryptodome==3.15.0 @@ -50,12 +51,22 @@ anytree==2.8.0 # Development version with OT-specific changes -git+https://github.com/lowRISC/fusesoc.git@ot-0.3 +# The version is different from upstream OT because ot-0.3 builds HW twice +# somehow and doubles the build time. +git+https://github.com/lowRISC/fusesoc.git@ot-0.1 # Development version with OT-specific changes -git+https://github.com/lowRISC/edalize.git@v0.4.0 +# The version is different from upstream OT because v0.4.0 builds HW twice +# somehow and doubles the build time. +git+https://github.com/lowRISC/edalize.git@ot-0.1 # Development version of minimal ChipWhisperer toolchain with latest features # and bug fixes. We fix the version for improved stability and manually update # if necessary. git+https://github.com/newaetech/chipwhisperer-minimal.git@2643131b71e528791446ee1bab7359120288f4ab#egg=chipwhisperer + +# Dependencies: @matcha//util/image_to_c.py +Pillow == 9.3.0 + +# Dependencies: @matcha//util/run_live_cam.py +pyserial == 3.5
diff --git a/rules/fusesoc.bzl b/rules/fusesoc.bzl index dbb7e02..242bad3 100644 --- a/rules/fusesoc.bzl +++ b/rules/fusesoc.bzl
@@ -67,14 +67,24 @@ args.add_all(ctx.attr.systems) args.add_all(flags) + _inputs = ctx.files.srcs + ctx.files.cores + + # For some reason, the sanboxed fusesoc would call vivado twice. Add an + # option to use the system installed fusesoc. + if ctx.attr.use_system_fusesoc: + _exec = "fusesoc" + else: + _exec = ctx.executable._fusesoc + _inputs += ctx.files._fusesoc + # Note: the `fileset_top` flag used above is specific to the OpenTitan # project to select the correct RTL fileset. ctx.actions.run( mnemonic = "FuseSoC", outputs = outputs, - inputs = ctx.files.srcs + ctx.files.cores + ctx.files._fusesoc, + inputs = _inputs, arguments = [args], - executable = ctx.executable._fusesoc, + executable = _exec, use_default_shell_env = False, execution_requirements = { "no-sandbox": "", @@ -104,6 +114,10 @@ ), "verilator_options": attr.label(), "make_options": attr.label(), + "use_system_fusesoc": attr.bool( + default = False, + doc = "Use non-sanboxed fusesoc (for FPGA vivado build)", + ), "_fusesoc": attr.label( default = entry_point("fusesoc"), executable = True,
diff --git a/rules/opentitan.bzl b/rules/opentitan.bzl index 9a68c64..7d96cfd 100644 --- a/rules/opentitan.bzl +++ b/rules/opentitan.bzl
@@ -326,7 +326,7 @@ "platform": attr.string(default = OPENTITAN_PLATFORM), "_cleanup_script": attr.label( allow_single_file = True, - default = Label("@//rules/scripts:expand_tabs.sh"), + default = Label("@lowrisc_opentitan//rules/scripts:expand_tabs.sh"), ), "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), }, @@ -481,7 +481,7 @@ "otp": attr.label(allow_single_file = True), "vmem": attr.label(allow_single_file = True), "_tool": attr.label( - default = "@//util/design:gen-flash-img", + default = "@lowrisc_opentitan//util/design:gen-flash-img", executable = True, cfg = "exec", ), @@ -529,7 +529,7 @@ "srcs": attr.label_list(allow_files = True), "platform": attr.string(default = OPENTITAN_PLATFORM), "_tool": attr.label( - default = "@//util/device_sw_utils:extract_sw_logs_db", + default = "@lowrisc_opentitan//util/device_sw_utils:extract_sw_logs_db", cfg = "exec", executable = True, ),
diff --git a/rules/opentitan_test.bzl b/rules/opentitan_test.bzl index 03870ed..455e371 100644 --- a/rules/opentitan_test.bzl +++ b/rules/opentitan_test.bzl
@@ -2,7 +2,7 @@ # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 -load("@//rules:opentitan.bzl", "opentitan_flash_binary", "opentitan_rom_binary") +load("@lowrisc_opentitan//rules:opentitan.bzl", "opentitan_flash_binary", "opentitan_rom_binary") load("@bazel_skylib//lib:shell.bzl", "shell") load("@bazel_skylib//lib:collections.bzl", "collections")
diff --git a/rules/ujson.bzl b/rules/ujson.bzl index c7f922a..649510b 100644 --- a/rules/ujson.bzl +++ b/rules/ujson.bzl
@@ -11,6 +11,9 @@ module = ctx.actions.declare_file("{}.rs".format(ctx.label.name)) ujson_lib = ctx.attr.ujson_lib[CcInfo].compilation_context.headers.to_list() + # Get the include search path for ujson. + ujson_lib_root = ctx.attr.ujson_lib[CcInfo].compilation_context.quote_includes.to_list() + srcs = [] includes = [] for src in ctx.attr.srcs: @@ -29,7 +32,8 @@ # 3. Substitute all `rust_attr` for `#`, thus creating rust attributes. # 4. Format it with `rustfmt` so it looks nice and can be inspected. command = """ - {preprocessor} -nostdinc -I. -DRUST_PREPROCESSOR_EMIT=1 -DNOSTDINC=1 {defines} $@ \ + {preprocessor} -nostdinc -I{ujson_lib_root_includes} \ + -DRUST_PREPROCESSOR_EMIT=1 -DNOSTDINC=1 {defines} $@ \ | grep -v '#' \ | sed -e "s/rust_attr/#/g" \ | {rustfmt} > {module}""".format( @@ -37,6 +41,7 @@ defines = " ".join(defines), module = module.path, rustfmt = rustfmt.path, + ujson_lib_root_includes = " -I".join(ujson_lib_root), ) ctx.actions.run_shell(
diff --git a/sw/device/lib/dif/dif_rstmgr.c b/sw/device/lib/dif/dif_rstmgr.c index d3daf35..4379653 100644 --- a/sw/device/lib/dif/dif_rstmgr.c +++ b/sw/device/lib/dif/dif_rstmgr.c
@@ -32,9 +32,10 @@ << RSTMGR_RESET_INFO_HW_REQ_OFFSET), "kDifRstmgrResetInfoHwReq must match the register definition!"); -static_assert( - RSTMGR_PARAM_NUM_SW_RESETS == 8, - "Number of software resets has changed, please update this file!"); +// Turn off the static_assert to enable non-earlgrey top run +// static_assert( +// RSTMGR_PARAM_NUM_SW_RESETS == 8, +// "Number of software resets has changed, please update this file!"); // The Reset Manager implementation will have to be updated if the number // of software resets grows, as it would span across multiple registers, so
diff --git a/sw/device/lib/runtime/ibex.h b/sw/device/lib/runtime/ibex.h index abe608b..634c972 100644 --- a/sw/device/lib/runtime/ibex.h +++ b/sw/device/lib/runtime/ibex.h
@@ -35,6 +35,7 @@ kIbexExcLoadAccessFault = 5, kIbexExcStoreAccessFault = 7, kIbexExcUserECall = 8, + kIbexExcSupervisorECall = 9, kIbexExcMachineECall = 11, kIbexExcMax = 31 } ibex_exc_t;
diff --git a/sw/device/tests/BUILD b/sw/device/tests/BUILD index 6153b52..95eb3ed 100644 --- a/sw/device/tests/BUILD +++ b/sw/device/tests/BUILD
@@ -1117,6 +1117,10 @@ ], test_harness = "//sw/host/tests/chip/gpio", verilator = verilator_params( + tags = [ + # TODO(opentitan/#17812): Verilator test is broken + "broken", + ], timeout = "eternal", test_cmds = [], ), @@ -1883,6 +1887,10 @@ timeout = "eternal", exit_failure = ROM_BOOT_FAILURE_MSG, rom = "//sw/device/silicon_creator/rom:rom_with_fake_keys", + tags = [ + # TODO(opentitan/#17813): The test is broken + "broken", + ], ), deps = [ "//hw/top_earlgrey/sw/autogen:top_earlgrey",
diff --git a/sw/device/tests/autogen/BUILD b/sw/device/tests/autogen/BUILD index d139bea..efb602a 100644 --- a/sw/device/tests/autogen/BUILD +++ b/sw/device/tests/autogen/BUILD
@@ -102,3 +102,52 @@ "//sw/device/lib/testing/test_framework:ottf_main", ], ) + +opentitan_functest( + name = "alert_renode_test", + srcs = ["alert_test.c"], + deps = [ + "//hw/top_earlgrey/sw/autogen:top_earlgrey", + "//sw/device/lib/base:memory", + "//sw/device/lib/base:mmio", + "//sw/device/lib/dif:adc_ctrl", + "//sw/device/lib/dif:aes", + "//sw/device/lib/dif:alert_handler", + "//sw/device/lib/dif:aon_timer", + "//sw/device/lib/dif:clkmgr", + "//sw/device/lib/dif:csrng", + "//sw/device/lib/dif:edn", + "//sw/device/lib/dif:entropy_src", + "//sw/device/lib/dif:flash_ctrl", + "//sw/device/lib/dif:gpio", + "//sw/device/lib/dif:hmac", + "//sw/device/lib/dif:i2c", + "//sw/device/lib/dif:keymgr", + "//sw/device/lib/dif:kmac", + "//sw/device/lib/dif:lc_ctrl", + "//sw/device/lib/dif:otbn", + "//sw/device/lib/dif:otp_ctrl", + "//sw/device/lib/dif:pattgen", + "//sw/device/lib/dif:pinmux", + "//sw/device/lib/dif:pwm", + "//sw/device/lib/dif:pwrmgr", + "//sw/device/lib/dif:rom_ctrl", + "//sw/device/lib/dif:rstmgr", + "//sw/device/lib/dif:rv_core_ibex", + "//sw/device/lib/dif:rv_plic", + "//sw/device/lib/dif:rv_timer", + "//sw/device/lib/dif:sensor_ctrl", + "//sw/device/lib/dif:spi_device", + "//sw/device/lib/dif:spi_host", + "//sw/device/lib/dif:sram_ctrl", + "//sw/device/lib/dif:sysrst_ctrl", + "//sw/device/lib/dif:uart", + "//sw/device/lib/dif:usbdev", + "//sw/device/lib/runtime:log", + "//sw/device/lib/testing:alert_handler_testutils", + "//sw/device/lib/testing/test_framework:ottf_main", + ], + copts = [ + "-DDISABLE_RENODE_TEST", + ], +)
diff --git a/sw/device/tests/autogen/alert_test.c b/sw/device/tests/autogen/alert_test.c index 0a8413c..3aeec02 100644 --- a/sw/device/tests/autogen/alert_test.c +++ b/sw/device/tests/autogen/alert_test.c
@@ -280,6 +280,7 @@ bool is_cause; dif_alert_handler_alert_t exp_alert; +#ifndef DISABLE_RENODE_TEST // Write adc_ctrl's alert_test reg and check alert_cause. for (int i = 0; i < 1; ++i) { CHECK_DIF_OK(dif_adc_ctrl_alert_force(&adc_ctrl_aon, kDifAdcCtrlAlertFatalFault + i)); @@ -294,6 +295,7 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif // Write aes's alert_test reg and check alert_cause. for (int i = 0; i < 2; ++i) { @@ -325,6 +327,7 @@ &alert_handler, exp_alert)); } +#ifndef DISABLE_RENODE_TEST // Write clkmgr's alert_test reg and check alert_cause. for (int i = 0; i < 2; ++i) { CHECK_DIF_OK(dif_clkmgr_alert_force(&clkmgr_aon, kDifClkmgrAlertRecovFault + i)); @@ -339,6 +342,7 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif // Write csrng's alert_test reg and check alert_cause. for (int i = 0; i < 2; ++i) { @@ -355,6 +359,7 @@ &alert_handler, exp_alert)); } +#ifndef DISABLE_RENODE_TEST // Write edn's alert_test reg and check alert_cause. for (int i = 0; i < 2; ++i) { CHECK_DIF_OK(dif_edn_alert_force(&edn0, kDifEdnAlertRecovAlert + i)); @@ -369,7 +374,9 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif +#ifndef DISABLE_RENODE_TEST // Write edn's alert_test reg and check alert_cause. for (int i = 0; i < 2; ++i) { CHECK_DIF_OK(dif_edn_alert_force(&edn1, kDifEdnAlertRecovAlert + i)); @@ -384,7 +391,9 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif +#ifndef DISABLE_RENODE_TEST // Write entropy_src's alert_test reg and check alert_cause. for (int i = 0; i < 2; ++i) { CHECK_DIF_OK(dif_entropy_src_alert_force(&entropy_src, kDifEntropySrcAlertRecovAlert + i)); @@ -399,6 +408,7 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif // Write flash_ctrl's alert_test reg and check alert_cause. for (int i = 0; i < 5; ++i) { @@ -490,6 +500,7 @@ &alert_handler, exp_alert)); } +#ifndef DISABLE_RENODE_TEST // Write keymgr's alert_test reg and check alert_cause. for (int i = 0; i < 2; ++i) { CHECK_DIF_OK(dif_keymgr_alert_force(&keymgr, kDifKeymgrAlertRecovOperationErr + i)); @@ -504,6 +515,7 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif // Write kmac's alert_test reg and check alert_cause. for (int i = 0; i < 2; ++i) { @@ -565,6 +577,7 @@ &alert_handler, exp_alert)); } +#ifndef DISABLE_RENODE_TEST // Write pattgen's alert_test reg and check alert_cause. for (int i = 0; i < 1; ++i) { CHECK_DIF_OK(dif_pattgen_alert_force(&pattgen, kDifPattgenAlertFatalFault + i)); @@ -579,7 +592,9 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif +#ifndef DISABLE_RENODE_TEST // Write pinmux's alert_test reg and check alert_cause. for (int i = 0; i < 1; ++i) { CHECK_DIF_OK(dif_pinmux_alert_force(&pinmux_aon, kDifPinmuxAlertFatalFault + i)); @@ -594,7 +609,9 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif +#ifndef DISABLE_RENODE_TEST // Write pwm's alert_test reg and check alert_cause. for (int i = 0; i < 1; ++i) { CHECK_DIF_OK(dif_pwm_alert_force(&pwm_aon, kDifPwmAlertFatalFault + i)); @@ -609,6 +626,7 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif // Write pwrmgr's alert_test reg and check alert_cause. for (int i = 0; i < 1; ++i) { @@ -670,6 +688,7 @@ &alert_handler, exp_alert)); } +#ifndef DISABLE_RENODE_TEST // Write rv_plic's alert_test reg and check alert_cause. for (int i = 0; i < 1; ++i) { CHECK_DIF_OK(dif_rv_plic_alert_force(&rv_plic, kDifRvPlicAlertFatalFault + i)); @@ -684,6 +703,7 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif // Write rv_timer's alert_test reg and check alert_cause. for (int i = 0; i < 1; ++i) { @@ -700,6 +720,7 @@ &alert_handler, exp_alert)); } +#ifndef DISABLE_RENODE_TEST // Write sensor_ctrl's alert_test reg and check alert_cause. for (int i = 0; i < 2; ++i) { CHECK_DIF_OK(dif_sensor_ctrl_alert_force(&sensor_ctrl, kDifSensorCtrlAlertRecovAlert + i)); @@ -714,7 +735,9 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif +#ifndef DISABLE_RENODE_TEST // Write spi_device's alert_test reg and check alert_cause. for (int i = 0; i < 1; ++i) { CHECK_DIF_OK(dif_spi_device_alert_force(&spi_device, kDifSpiDeviceAlertFatalFault + i)); @@ -729,6 +752,7 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif // Write spi_host's alert_test reg and check alert_cause. for (int i = 0; i < 1; ++i) { @@ -760,6 +784,7 @@ &alert_handler, exp_alert)); } +#ifndef DISABLE_RENODE_TEST // Write sram_ctrl's alert_test reg and check alert_cause. for (int i = 0; i < 1; ++i) { CHECK_DIF_OK(dif_sram_ctrl_alert_force(&sram_ctrl_main, kDifSramCtrlAlertFatalError + i)); @@ -774,7 +799,9 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif +#ifndef DISABLE_RENODE_TEST // Write sram_ctrl's alert_test reg and check alert_cause. for (int i = 0; i < 1; ++i) { CHECK_DIF_OK(dif_sram_ctrl_alert_force(&sram_ctrl_ret_aon, kDifSramCtrlAlertFatalError + i)); @@ -789,7 +816,9 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif +#ifndef DISABLE_RENODE_TEST // Write sysrst_ctrl's alert_test reg and check alert_cause. for (int i = 0; i < 1; ++i) { CHECK_DIF_OK(dif_sysrst_ctrl_alert_force(&sysrst_ctrl_aon, kDifSysrstCtrlAlertFatalFault + i)); @@ -804,6 +833,7 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif // Write uart's alert_test reg and check alert_cause. for (int i = 0; i < 1; ++i) { @@ -865,6 +895,7 @@ &alert_handler, exp_alert)); } +#ifndef DISABLE_RENODE_TEST // Write usbdev's alert_test reg and check alert_cause. for (int i = 0; i < 1; ++i) { CHECK_DIF_OK(dif_usbdev_alert_force(&usbdev, kDifUsbdevAlertFatalFault + i)); @@ -879,6 +910,7 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } +#endif } bool test_main(void) {
diff --git a/sw/device/tests/sim_dv/i2c_host_tx_rx_test.c b/sw/device/tests/sim_dv/i2c_host_tx_rx_test.c index a0a5649..03c8333 100644 --- a/sw/device/tests/sim_dv/i2c_host_tx_rx_test.c +++ b/sw/device/tests/sim_dv/i2c_host_tx_rx_test.c
@@ -9,8 +9,8 @@ #include "sw/device/lib/dif/dif_pinmux.h" #include "sw/device/lib/dif/dif_rv_plic.h" #include "sw/device/lib/runtime/hart.h" -#include "sw/device/lib/runtime/irq.h" #include "sw/device/lib/runtime/log.h" +#include "sw/device/lib/runtime/irq.h" #include "sw/device/lib/testing/i2c_testutils.h" #include "sw/device/lib/testing/rand_testutils.h" #include "sw/device/lib/testing/test_framework/check.h"
diff --git a/sw/host/opentitanlib/BUILD b/sw/host/opentitanlib/BUILD index d7ab74a..6eeb38f 100644 --- a/sw/host/opentitanlib/BUILD +++ b/sw/host/opentitanlib/BUILD
@@ -46,6 +46,7 @@ "src/backend/cw310.rs", "src/backend/hyperdebug.rs", "src/backend/mod.rs", + "src/backend/nexus.rs", "src/backend/proxy.rs", "src/backend/ti50emulator.rs", "src/backend/ultradebug.rs", @@ -118,6 +119,9 @@ "src/transport/hyperdebug/mod.rs", "src/transport/hyperdebug/spi.rs", "src/transport/mod.rs", + "src/transport/nexus/gpio.rs", + "src/transport/nexus/mod.rs", + "src/transport/nexus/spi.rs", "src/transport/proxy/emu.rs", "src/transport/proxy/gpio.rs", "src/transport/proxy/i2c.rs",
diff --git a/sw/host/opentitanlib/src/app/config/mod.rs b/sw/host/opentitanlib/src/app/config/mod.rs index 7913363..36669e0 100644 --- a/sw/host/opentitanlib/src/app/config/mod.rs +++ b/sw/host/opentitanlib/src/app/config/mod.rs
@@ -67,5 +67,6 @@ "/__builtin__/hyperdebug_cw310.json" => include_str!("hyperdebug_cw310.json"), "/__builtin__/opentitan_ultradebug.json" => include_str!("opentitan_ultradebug.json"), "/__builtin__/opentitan_verilator.json" => include_str!("opentitan_verilator.json"), + "/__builtin__/nexus.json" => include_str!("nexus.json"), }; }
diff --git a/sw/host/opentitanlib/src/app/config/nexus.json b/sw/host/opentitanlib/src/app/config/nexus.json new file mode 100644 index 0000000..e34a640 --- /dev/null +++ b/sw/host/opentitanlib/src/app/config/nexus.json
@@ -0,0 +1,64 @@ +{ + "interface": "nexus", + "pins": [ + { + "name": "RESET", + "alias_of": "RESET_B", + "mode": "PushPull", + "level": true, + "pull_mode": "None" + }, + { + "name": "SW_STRAP0", + "mode": "PushPull", + "level": false, + "pull_mode": "None" + }, + { + "name": "SW_STRAP1", + "mode": "PushPull", + "level": false, + "pull_mode": "None" + }, + { + "name": "SW_STRAP2", + "mode": "PushPull", + "level": false, + "pull_mode": "None" + } + ], + "strappings": [ + { + "name": "ROM_BOOTSTRAP", + "pins": [ + { + "name": "SW_STRAP0", + "level": true + }, + { + "name": "SW_STRAP1", + "level": true + }, + { + "name": "SW_STRAP2", + "level": true + } + ] + }, + { + "name": "RESET", + "pins": [ + { + "name": "RESET", + "level": false + } + ] + } + ], + "spi": [ + { + "name": "BOOTSTRAP", + "alias_of": "0" + } + ] +}
diff --git a/sw/host/opentitanlib/src/backend/mod.rs b/sw/host/opentitanlib/src/backend/mod.rs index cca0d56..0570fe3 100644 --- a/sw/host/opentitanlib/src/backend/mod.rs +++ b/sw/host/opentitanlib/src/backend/mod.rs
@@ -16,6 +16,7 @@ mod cw310; mod hyperdebug; +mod nexus; mod proxy; mod ti50emulator; mod ultradebug; @@ -89,6 +90,10 @@ cw310::create(args)?, Some(Path::new("/__builtin__/opentitan_cw310.json")), ), + "nexus" => ( + nexus::create(args)?, + Some(Path::new("/__builtin__/nexus.json")), + ), _ => return Err(Error::UnknownInterface(interface.to_string()).into()), }; let mut env = TransportWrapperBuilder::new(backend);
diff --git a/sw/host/opentitanlib/src/backend/nexus.rs b/sw/host/opentitanlib/src/backend/nexus.rs new file mode 100644 index 0000000..5e7292c --- /dev/null +++ b/sw/host/opentitanlib/src/backend/nexus.rs
@@ -0,0 +1,17 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +use crate::transport::nexus::Nexus; +use crate::transport::Transport; +use anyhow::Result; + +use crate::backend::BackendOpts; + +pub fn create(args: &BackendOpts) -> Result<Box<dyn Transport>> { + Ok(Box::new(Nexus::new( + args.usb_vid, + args.usb_pid, + args.usb_serial.clone(), + ))) +}
diff --git a/sw/host/opentitanlib/src/spiflash/flash.rs b/sw/host/opentitanlib/src/spiflash/flash.rs index 615be32..0012797 100644 --- a/sw/host/opentitanlib/src/spiflash/flash.rs +++ b/sw/host/opentitanlib/src/spiflash/flash.rs
@@ -55,8 +55,11 @@ impl SpiFlash { // Well known SPI Flash opcodes. pub const READ: u8 = 0x03; + pub const READ4: u8 = 0x13; pub const PAGE_PROGRAM: u8 = 0x02; pub const SECTOR_ERASE: u8 = 0x20; + pub const BLOCK_ERASE_3B: u8 = 0xd8; + pub const BLOCK_ERASE_4B: u8 = 0xdc; pub const CHIP_ERASE: u8 = 0xc7; pub const WRITE_ENABLE: u8 = 0x06; pub const WRITE_DISABLE: u8 = 0x04; @@ -230,8 +233,12 @@ ) -> Result<&Self> { // Break the read up according to the maximum chunksize the backend can handle. for chunk in buffer.chunks_mut(spi.max_chunk_size()?) { + let opcode = match self.address_mode { + AddressMode::Mode3b => SpiFlash::READ, + AddressMode::Mode4b => SpiFlash::READ4, + }; spi.run_eeprom_transactions(&mut [Transaction::Read( - MODE_111.cmd_addr(SpiFlash::READ, address, self.address_mode), + MODE_111.cmd_addr(opcode, address, self.address_mode), chunk, )])?; address += chunk.len() as u32; @@ -250,12 +257,49 @@ Ok(self) } + // Erase in 256kB blocks. + pub fn block_erase(&self, spi: &dyn Target, address: u32, length: u32) -> Result<()> { + self.block_erase_with_progress(spi, address, length, |_, _| {}) + } + /// Erase a segment of the SPI flash starting at `address` for `length` bytes. /// The address and length must be sector aligned. pub fn erase(&self, spi: &dyn Target, address: u32, length: u32) -> Result<&Self> { self.erase_with_progress(spi, address, length, |_, _| {}) } + /// Block erase, and a progress bar. + pub fn block_erase_with_progress( + &self, + spi: &dyn Target, + address: u32, + length: u32, + progress: impl Fn(u32, u32), + ) -> Result<()> { + // 256kB sectors + let sector_size = 256 * 1024; + if address % sector_size != 0 { + return Err(Error::BadEraseAddress(address, sector_size).into()); + } + if length % sector_size != 0 { + return Err(Error::BadEraseAddress(length, sector_size).into()); + } + let end = address + length; + for addr in (address..end).step_by(sector_size as usize) { + let opcode = match self.address_mode { + AddressMode::Mode3b => SpiFlash::BLOCK_ERASE_3B, + AddressMode::Mode4b => SpiFlash::BLOCK_ERASE_4B, + }; + spi.run_eeprom_transactions(&mut [ + Transaction::Command(MODE_111.cmd(SpiFlash::WRITE_ENABLE)), + Transaction::Command(MODE_111.cmd_addr(opcode, address, self.address_mode)), + Transaction::WaitForBusyClear, + ])?; + progress(addr, sector_size); + } + Ok(()) + } + /// Erase a segment of the SPI flash starting at `address` for `length` bytes. /// The address and length must be sector aligned. /// The `progress` callback will be invoked after each chunk of the erase operation.
diff --git a/sw/host/opentitanlib/src/spiflash/sfdp.rs b/sw/host/opentitanlib/src/spiflash/sfdp.rs index 0a109c5..4b83a1a 100644 --- a/sw/host/opentitanlib/src/spiflash/sfdp.rs +++ b/sw/host/opentitanlib/src/spiflash/sfdp.rs
@@ -941,7 +941,7 @@ let start = (8 + i * 8) as usize; let end = start + 8; let phdr = SfdpPhdr::try_from(&buf[start..end])?; - len = std::cmp::max(len, phdr.offset + (phdr.dwords * 4) as u32); + len = std::cmp::max(len, phdr.offset + ((phdr.dwords as u32) * 4) as u32); log::debug!("computed sfdp len = {}", len); } Ok(len as usize)
diff --git a/sw/host/opentitanlib/src/transport/mod.rs b/sw/host/opentitanlib/src/transport/mod.rs index cd484d6..cef1824 100644 --- a/sw/host/opentitanlib/src/transport/mod.rs +++ b/sw/host/opentitanlib/src/transport/mod.rs
@@ -19,6 +19,7 @@ pub mod common; pub mod cw310; pub mod hyperdebug; +pub mod nexus; pub mod proxy; pub mod ti50emulator; pub mod ultradebug;
diff --git a/sw/host/opentitanlib/src/transport/nexus/gpio.rs b/sw/host/opentitanlib/src/transport/nexus/gpio.rs new file mode 100644 index 0000000..91350f8 --- /dev/null +++ b/sw/host/opentitanlib/src/transport/nexus/gpio.rs
@@ -0,0 +1,113 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::{ensure, Context, Result}; +use lazy_static::lazy_static; +use safe_ftdi as ftdi; +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; + +use crate::collection; +use crate::io::gpio::{GpioError, GpioPin, PinMode, PullMode}; +use crate::transport::nexus::Nexus; +use crate::transport::ultradebug::mpsse; +use crate::util::parse_int::ParseInt; + +/// Represents the Nexus GPIO pins. +pub struct NexusGpio { + pub device: Rc<RefCell<mpsse::Context>>, +} + +impl NexusGpio { + pub const PIN_SW_STRAP0: u8 = 4; + pub const PIN_SW_STRAP1: u8 = 5; + pub const PIN_SW_STRAP2: u8 = 6; + pub const PIN_RESET_B: u8 = 7; + const LAST_PIN_NUM: u8 = 7; + + pub fn open(nexus: &Nexus) -> Result<Self> { + Ok(NexusGpio { + device: nexus.mpsse(ftdi::Interface::A)?, + }) + } + + pub fn pin(&self, pinname: &str) -> Result<NexusGpioPin> { + Ok(NexusGpioPin { + device: self.device.clone(), + pin_id: self.pin_name_to_number(pinname)?, + }) + } + + /// Given an nexus pin name, return its pin number. + pub fn pin_name_to_number(&self, pinname: &str) -> Result<u8> { + // If the pinname is an integer, use it; otherwise try to see if it + // is a symbolic name of a pin. + if let Ok(pinnum) = u8::from_str(pinname) { + ensure!( + pinnum <= NexusGpio::LAST_PIN_NUM, + GpioError::InvalidPinNumber(pinnum) + ); + return Ok(pinnum); + } + let pinname = pinname.to_uppercase(); + let pn = pinname.as_str(); + PIN_NAMES + .get(pn) + .copied() + .ok_or_else(|| GpioError::InvalidPinName(pinname).into()) + } +} + +pub struct NexusGpioPin { + device: Rc<RefCell<mpsse::Context>>, + pin_id: u8, +} + +impl GpioPin for NexusGpioPin { + /// Reads the value of the the GPIO pin `id`. + fn read(&self) -> Result<bool> { + let bits = self.device.borrow_mut().gpio_get().context("FTDI error")?; + Ok(bits & (1 << self.pin_id) != 0) + } + + /// Sets the value of the GPIO pin `id` to `value`. + fn write(&self, value: bool) -> Result<()> { + self.device + .borrow_mut() + .gpio_set(self.pin_id, value) + .context("FTDI error")?; + Ok(()) + } + + /// Sets the `direction` of GPIO `id` as input or output. + fn set_mode(&self, mode: PinMode) -> Result<()> { + let direction = match mode { + PinMode::Input => false, + PinMode::PushPull => true, + PinMode::OpenDrain => return Err(GpioError::UnsupportedPinMode(mode).into()), + PinMode::AnalogInput | + PinMode::AnalogOutput | + PinMode::Alternate => todo!(), + }; + self.device + .borrow_mut() + .gpio_set_direction(self.pin_id, direction) + .context("FTDI error")?; + Ok(()) + } + + fn set_pull_mode(&self, _mode: PullMode) -> Result<()> { + Ok(()) + } +} + +lazy_static! { + static ref PIN_NAMES: HashMap<&'static str, u8> = collection! { + "SW_STRAP0" => 4, + "SW_STRAP1" => 5, + "SW_STRAP2" => 6, + "RESET_B" => 7, + }; +}
diff --git a/sw/host/opentitanlib/src/transport/nexus/mod.rs b/sw/host/opentitanlib/src/transport/nexus/mod.rs new file mode 100644 index 0000000..40db0a5 --- /dev/null +++ b/sw/host/opentitanlib/src/transport/nexus/mod.rs
@@ -0,0 +1,119 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::{bail, ensure, Context, Result}; +use safe_ftdi as ftdi; +use std::cell::RefCell; +use std::rc::Rc; + +use crate::io::gpio::GpioPin; +use crate::io::spi::Target; +use crate::transport::{ + Capabilities, Capability, Transport, TransportError, TransportInterfaceType, +}; +use crate::transport::ultradebug::mpsse; + +pub mod gpio; +pub mod spi; + +#[derive(Default)] +pub struct Nexus { + pub usb_vid: Option<u16>, + pub usb_pid: Option<u16>, + pub usb_serial: Option<String>, + mpsse_a: RefCell<Option<Rc<RefCell<mpsse::Context>>>>, + inner: RefCell<Inner>, +} + +#[derive(Default)] +struct Inner { + gpio: Option<Rc<gpio::NexusGpio>>, + spi: Option<Rc<dyn Target>>, +} + +impl Nexus { + /// Create a new `Nexus` struct, optionally specifying the USB vid/pid/serial number. + pub fn new(usb_vid: Option<u16>, usb_pid: Option<u16>, usb_serial: Option<String>) -> Self { + Nexus { + usb_vid, + usb_pid, + usb_serial, + ..Default::default() + } + } + + /// Construct an `ftdi::Device` for the specified `interface` on the Nexus device. + pub fn from_interface(&self, interface: ftdi::Interface) -> Result<ftdi::Device> { + Ok(ftdi::Device::from_description_serial( + interface, + self.usb_vid.unwrap_or(0x0403), + self.usb_pid.unwrap_or(0x6011), + None, + self.usb_serial.clone(), + ) + .context("FTDI error")?) + } + + fn mpsse_interface_a(&self) -> Result<Rc<RefCell<mpsse::Context>>> { + let mut mpsse_a = self.mpsse_a.borrow_mut(); + if mpsse_a.is_none() { + let device = self.from_interface(ftdi::Interface::A)?; + device.set_timeouts(5000, 5000); + let mut mpdev = mpsse::Context::new(device).context("FTDI error")?; + mpdev.gpio_direction.insert( + mpsse::GpioDirection::OUT_0 | + mpsse::GpioDirection::OUT_1 | + mpsse::GpioDirection::OUT_3 | + mpsse::GpioDirection::OUT_4 | + mpsse::GpioDirection::OUT_5 | + mpsse::GpioDirection::OUT_6 | + mpsse::GpioDirection::OUT_7); + let _ = mpdev.gpio_get().context("FTDI error")?; + mpdev.gpio_value &= 0xF8; + *mpsse_a = Some(Rc::new(RefCell::new(mpdev))); + } + Ok(Rc::clone(mpsse_a.as_ref().unwrap())) + } + + /// Construct an `mpsse::Context` for the requested interface. + pub fn mpsse(&self, interface: ftdi::Interface) -> Result<Rc<RefCell<mpsse::Context>>> { + match interface { + ftdi::Interface::A => self.mpsse_interface_a(), + _ => { + bail!(TransportError::UsbOpenError(format!( + "I don't know how to create an MPSSE context for interface {:?}", + interface + ))); + } + } + } +} + +impl Transport for Nexus { + fn capabilities(&self) -> Result<Capabilities> { + Ok(Capabilities::new( + Capability::GPIO | Capability::SPI, + )) + } + + fn gpio_pin(&self, instance: &str) -> Result<Rc<dyn GpioPin>> { + let mut inner = self.inner.borrow_mut(); + if inner.gpio.is_none() { + inner.gpio = Some(Rc::new(gpio::NexusGpio::open(self)?)); + } + Ok(Rc::new(inner.gpio.as_ref().unwrap().pin(instance)?)) + } + + fn spi(&self, instance: &str) -> Result<Rc<dyn Target>> { + ensure!( + instance == "0", + TransportError::InvalidInstance(TransportInterfaceType::Spi, instance.to_string()) + ); + let mut inner = self.inner.borrow_mut(); + if inner.spi.is_none() { + inner.spi = Some(Rc::new(spi::NexusSpi::open(self)?)); + } + Ok(Rc::clone(inner.spi.as_ref().unwrap())) + } +}
diff --git a/sw/host/opentitanlib/src/transport/nexus/spi.rs b/sw/host/opentitanlib/src/transport/nexus/spi.rs new file mode 100644 index 0000000..96c8522 --- /dev/null +++ b/sw/host/opentitanlib/src/transport/nexus/spi.rs
@@ -0,0 +1,198 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::{Context, Result}; +use safe_ftdi as ftdi; +use std::cell::RefCell; +use std::rc::Rc; + +use crate::io::spi::{ + AssertChipSelect, ClockPolarity, SpiError, Target, TargetChipDeassert, Transfer, TransferMode, +}; +use crate::transport::nexus::Nexus; +use crate::transport::ultradebug::mpsse; + +struct Inner { + mode: TransferMode, + cs_asserted_count: u32, +} + +/// Represents the Nexus SPI device. +pub struct NexusSpi { + pub device: Rc<RefCell<mpsse::Context>>, + inner: RefCell<Inner>, +} + +impl NexusSpi { + pub const PIN_CLOCK: u8 = 0; + pub const PIN_MOSI: u8 = 1; + pub const PIN_MISO: u8 = 2; + pub const PIN_CHIP_SELECT: u8 = 3; + pub const MASK_CHIP_SELECT: u8 = 1u8 << Self::PIN_CHIP_SELECT; + pub const PIN_SPI_ZB: u8 = 4; + pub fn open(ultradebug: &Nexus) -> Result<Self> { + // let mpsse = ultradebug.mpsse(ftdi::Interface::B)?; + let mpsse = ultradebug.mpsse(ftdi::Interface::A)?; + // Note: platforms ultradebugs tristate their SPI lines + // unless SPI_ZB is driven low. Non-platforms ultradebugs + // don't use SPI_ZB, so this is safe for both types of devices. + // log::debug!("Setting SPI_ZB"); + // mpsse + // .borrow_mut() + // .gpio_set(NexusSpi::PIN_SPI_ZB, false) + // .context("FTDI error f")?; + + Ok(NexusSpi { + device: mpsse, + inner: RefCell::new(Inner { + mode: TransferMode::Mode0, + cs_asserted_count: 0, + }), + }) + } + + fn do_assert_cs(&self, assert: bool) -> Result<()> { + let device = self.device.borrow(); + // Assert or deassert CS# + device + .execute(&mut [mpsse::Command::SetLowGpio( + device.gpio_direction, + if assert { + device.gpio_value & !Self::MASK_CHIP_SELECT + } else { + device.gpio_value | Self::MASK_CHIP_SELECT + }, + )]) + .context("FTDI error g")?; + Ok(()) + } +} + +impl Target for NexusSpi { + fn get_transfer_mode(&self) -> Result<TransferMode> { + Ok(self.inner.borrow().mode) + } + fn set_transfer_mode(&self, mode: TransferMode) -> Result<()> { + self.inner.borrow_mut().mode = mode; + Ok(()) + } + + fn get_bits_per_word(&self) -> Result<u32> { + Ok(8) + } + fn set_bits_per_word(&self, bits_per_word: u32) -> Result<()> { + match bits_per_word { + 8 => Ok(()), + _ => Err(SpiError::InvalidWordSize(bits_per_word).into()), + } + } + + fn get_max_speed(&self) -> Result<u32> { + Ok(self.device.borrow().max_clock_frequency) + } + fn set_max_speed(&self, frequency: u32) -> Result<()> { + let mut device = self.device.borrow_mut(); + device + .set_clock_frequency(frequency) + .context("FTDI errorh ")?; + Ok(()) + } + + fn get_max_transfer_count(&self) -> Result<usize> { + // Arbitrary value: number of `Transfers` that can be in a single transaction. + Ok(42) + } + + fn max_chunk_size(&self) -> Result<usize> { + // Size of the FTDI read buffer. We can't perform a read larger than this; + // the FTDI device simply won't read any more. + Ok(65536) + } + + fn run_transaction(&self, transaction: &mut [Transfer]) -> Result<()> { + let (rdedge, wredge) = match self.inner.borrow().mode.polarity() { + ClockPolarity::IdleLow => (mpsse::ClockEdge::Rising, mpsse::ClockEdge::Falling), + ClockPolarity::IdleHigh => (mpsse::ClockEdge::Falling, mpsse::ClockEdge::Rising), + }; + + let mut command = Vec::new(); + let device = self.device.borrow(); + let cs_not_already_asserted = self.inner.borrow().cs_asserted_count == 0; + if cs_not_already_asserted { + // Assert CS# (drive low). + command.push(mpsse::Command::SetLowGpio( + device.gpio_direction, + device.gpio_value & !Self::MASK_CHIP_SELECT, + )); + } + // Translate SPI Read/Write Transactions into MPSSE Commands. + for transfer in transaction.iter_mut() { + command.push(match transfer { + Transfer::Read(buf) => mpsse::Command::ReadData( + mpsse::DataShiftOptions { + read_clock_edge: rdedge, + read_data: true, + ..Default::default() + }, + buf, + ), + Transfer::Write(buf) => mpsse::Command::WriteData( + mpsse::DataShiftOptions { + write_clock_edge: wredge, + write_data: true, + ..Default::default() + }, + buf, + ), + Transfer::Both(wbuf, rbuf) => mpsse::Command::TransactData( + mpsse::DataShiftOptions { + write_clock_edge: wredge, + write_data: true, + ..Default::default() + }, + wbuf, + mpsse::DataShiftOptions { + read_clock_edge: rdedge, + read_data: true, + ..Default::default() + }, + rbuf, + ), + }); + } + if cs_not_already_asserted { + // Release CS# (allow to float high). + command.push(mpsse::Command::SetLowGpio( + device.gpio_direction, + device.gpio_value | Self::MASK_CHIP_SELECT, + )); + } + device.execute(&mut command).context("FTDI error i")?; + Ok(()) + } + + fn assert_cs(self: Rc<Self>) -> Result<AssertChipSelect> { + { + let mut inner = self.inner.borrow_mut(); + if inner.cs_asserted_count == 0 { + self.do_assert_cs(true)?; + } + inner.cs_asserted_count += 1; + } + Ok(AssertChipSelect::new(self)) + } +} + +impl TargetChipDeassert for NexusSpi { + fn deassert_cs(&self) { + let mut inner = self.inner.borrow_mut(); + inner.cs_asserted_count -= 1; + if inner.cs_asserted_count == 0 { + // We cannot propagate errors through `Drop::drop()`, so panic on any error. (Logging + // would be another option.) + self.do_assert_cs(false) + .expect("Error while deasserting CS"); + } + } +}
diff --git a/sw/host/opentitantool/src/command/spi.rs b/sw/host/opentitantool/src/command/spi.rs index 97d8734..935da52 100644 --- a/sw/host/opentitantool/src/command/spi.rs +++ b/sw/host/opentitantool/src/command/spi.rs
@@ -8,11 +8,13 @@ use std::fs::{self, File}; use std::io::{self, Write}; use std::path::PathBuf; -use std::time::Instant; +use std::time::{Duration, Instant}; use structopt::StructOpt; use opentitanlib::app::command::CommandDispatch; use opentitanlib::app::{self, TransportWrapper}; +// Use PinMode if gpio is enabled +// use opentitanlib::io::gpio::PinMode; use opentitanlib::io::spi::{SpiParams, Transfer}; use opentitanlib::spiflash::SpiFlash; use opentitanlib::tpm; @@ -215,6 +217,25 @@ bytes_per_second: f64, } +#[derive(Debug, StructOpt)] +pub struct SpiBlockErase { + #[structopt(short, long, help = "Start offset.")] + start: u32, + #[structopt(short = "n", long, help = "Number of bytes to erase.")] + length: u32, +} + +#[derive(Debug, serde::Serialize)] +pub struct SpiBlockEraseResponse { + length: u32, + bytes_per_second: f64, +} + +#[derive(Debug, StructOpt)] +pub struct SpiChipErase {} +#[derive(Debug, serde::Serialize)] +pub struct SpiChipEraseResponse {} + impl CommandDispatch for SpiErase { fn run( &self, @@ -242,6 +263,49 @@ } } +impl CommandDispatch for SpiBlockErase { + fn run( + &self, + context: &dyn Any, + transport: &TransportWrapper, + ) -> Result<Option<Box<dyn Annotate>>> { + transport.capabilities()?.request(Capability::SPI).ok()?; + let context = context.downcast_ref::<SpiCommand>().unwrap(); + let spi = context.params.create(transport, "BOOTSTRAP")?; + let mut flash = SpiFlash::from_spi(&*spi)?; + flash.set_address_mode_auto(&*spi)?; + + let progress = app::progress_bar(self.length as u64); + let t0 = Instant::now(); + flash.block_erase_with_progress(&*spi, self.start, self.length, |_, chunk| { + progress.inc(chunk as u64); + })?; + progress.finish(); + let duration = t0.elapsed().as_secs_f64(); + + Ok(Some(Box::new(SpiBlockEraseResponse { + length: self.length, + bytes_per_second: self.length as f64 / duration, + }))) + } +} + +impl CommandDispatch for SpiChipErase { + fn run( + &self, + context: &dyn Any, + transport: &TransportWrapper, + ) -> Result<Option<Box<dyn Annotate>>> { + transport.capabilities()?.request(Capability::SPI).ok()?; + let context = context.downcast_ref::<SpiCommand>().unwrap(); + let spi = context.params.create(transport, "BOOTSTRAP")?; + let mut flash = SpiFlash::from_spi(&*spi)?; + flash.set_address_mode_auto(&*spi)?; + flash.chip_erase(&*spi)?; + Ok(Some(Box::new(SpiChipEraseResponse {}))) + } +} + /// Program data into a SPI EEPROM. #[derive(Debug, StructOpt)] pub struct SpiProgram { @@ -257,6 +321,7 @@ bytes_per_second: f64, } +/// Program data into a SPI EEPROM. impl CommandDispatch for SpiProgram { fn run( &self, @@ -286,6 +351,93 @@ } #[derive(Debug, StructOpt)] +pub struct SpiReadMemory { + #[structopt(short, long, default_value = "0", help = "Start offset.")] + start: u32, + #[structopt(short, long, help = "Length to readback.")] + length: u32, + #[structopt(short, long, help = "Hexdump the data.")] + hexdump: bool, + #[structopt(name = "FILE", default_value = "-")] + filename: PathBuf, +} + +#[derive(Debug, serde::Serialize)] +pub struct SpiReadMemoryResponse { + length: usize, + bytes_per_second: f64, +} + +impl SpiReadMemory { + fn write_file(&self, mut writer: impl Write, buffer: &[u8]) -> Result<()> { + if self.hexdump { + hexdump(writer, buffer)?; + } else { + writer.write_all(buffer)?; + } + Ok(()) + } +} + +impl CommandDispatch for SpiReadMemory { + fn run( + &self, + context: &dyn Any, + transport: &TransportWrapper, + ) -> Result<Option<Box<dyn Annotate>>> { + transport.capabilities()?.request(Capability::SPI).ok()?; + transport.capabilities()?.request(Capability::GPIO).ok()?; + let context = context.downcast_ref::<SpiCommand>().unwrap(); + let spi = context.params.create(transport, "BOOTSTRAP")?; + // Rename to gpio if the gpio loop is used + let _gpio = transport.gpio_pin("SW_STRAP0")?; + spi.set_max_speed(1_000_000)?; + + let mut buffer = vec![0u8; self.length as usize]; + let mut write_buffer = Vec::with_capacity(8); + write_buffer.extend(self.start.to_be_bytes()); + write_buffer.extend(self.length.to_be_bytes()); + spi.run_transaction(&mut [Transfer::Write(&write_buffer)])?; + // TODO(atv): For some reason, the GPIO does not + // read back a changed value in this context (but does, if you try to read it separately) + // If we can resolve this, uncomment out this polling block. For now, however, wait 50ms. + // gpio.set_mode(PinMode::Input)?; + // loop { + // let val = gpio.read()?; + // if val { + // break; + // } + // std::thread::sleep(Duration::from_millis(50)); + // } + // gpio.set_mode(PinMode::PushPull)?; + std::thread::sleep(Duration::from_millis(50)); + let t0 = Instant::now(); + let chunk_size = spi.max_chunk_size()?; + let mut remaining = buffer.len(); + for i in 0..=(buffer.len() / chunk_size) { + let chunk_len = std::cmp::min(chunk_size, remaining); + let start = i * chunk_size; + let end = start + chunk_len; + spi.run_transaction(&mut [Transfer::Read(&mut buffer[start..end])])?; + remaining -= chunk_len; + } + let duration = t0.elapsed().as_secs_f64(); + + if self.filename.to_str() == Some("-") { + self.write_file(io::stdout(), &buffer)?; + Ok(None) + } else { + let file = File::create(&self.filename)?; + self.write_file(file, &buffer)?; + Ok(Some(Box::new(SpiReadMemoryResponse { + length: buffer.len(), + bytes_per_second: buffer.len() as f64 / duration, + }))) + } + } +} + +#[derive(Debug, StructOpt)] pub struct SpiTpm { #[structopt(subcommand)] command: super::tpm::TpmSubCommand, @@ -312,7 +464,10 @@ ReadId(SpiReadId), Read(SpiRead), Erase(SpiErase), + BlockErase(SpiBlockErase), + ChipErase(SpiChipErase), Program(SpiProgram), + ReadMemory(SpiReadMemory), Tpm(SpiTpm), }
diff --git a/third_party/rust/BUILD b/third_party/rust/BUILD index 220a746..96ae281 100644 --- a/third_party/rust/BUILD +++ b/third_party/rust/BUILD
@@ -8,7 +8,7 @@ annotations = { "libudev-sys": [crate.annotation( patch_args = ["-p1"], - patches = ["@//third_party/rust/patches:libudev-sys-0.1.4.patch"], + patches = ["@lowrisc_opentitan//third_party/rust/patches:libudev-sys-0.1.4.patch"], )], }, cargo_lockfile = "//third_party/rust:Cargo.lock",
diff --git a/third_party/rust/crates/defs.bzl b/third_party/rust/crates/defs.bzl index 87101ce..90c0520 100644 --- a/third_party/rust/crates/defs.bzl +++ b/third_party/rust/crates/defs.bzl
@@ -1206,7 +1206,7 @@ "-p1", ], patches = [ - "@//third_party/rust/patches:libudev-sys-0.1.4.patch", + "@lowrisc_opentitan//third_party/rust/patches:libudev-sys-0.1.4.patch", ], sha256 = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324", type = "tar.gz",
diff --git a/third_party/rust/repos.bzl b/third_party/rust/repos.bzl index d3c3f6a..7c848db 100644 --- a/third_party/rust/repos.bzl +++ b/third_party/rust/repos.bzl
@@ -2,7 +2,7 @@ # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 -load("@//rules:repo.bzl", "http_archive_or_local") +load("@lowrisc_opentitan//rules:repo.bzl", "http_archive_or_local") def rust_repos(rules_rust = None, safe_ftdi = None, serde_annotate = None): # We use forked/patched Rust Bazel rules to enable caching repository rules
diff --git a/util/prep-bazel-airgapped-build.sh b/util/prep-bazel-airgapped-build.sh index 6b28ecc..be07ba6 100755 --- a/util/prep-bazel-airgapped-build.sh +++ b/util/prep-bazel-airgapped-build.sh
@@ -164,7 +164,7 @@ readonly LATEST_BISTREAM_HASH_FILE="${SYSTEM_BITSTREAM_CACHE}/latest.txt" # The revision named in latest.txt is not necessarily on disk. Induce the # cache backend to fetch the latest bitstreams. - BITSTREAM=latest ${BAZELISK} fetch @bitstreams//... + BITSTREAM=--offline ${BAZELISK} fetch @bitstreams//... cp "${LATEST_BISTREAM_HASH_FILE}" \ "${BAZEL_AIRGAPPED_DIR}/${BAZEL_BITSTREAMS_CACHE}/" LATEST_BISTREAM_HASH=$(cat "${LATEST_BISTREAM_HASH_FILE}")
diff --git a/util/reggen/bus_interfaces.py b/util/reggen/bus_interfaces.py index fbeb99c..c699ea4 100644 --- a/util/reggen/bus_interfaces.py +++ b/util/reggen/bus_interfaces.py
@@ -50,7 +50,8 @@ protocol = check_str(ed['protocol'], 'protocol field of ' + entry_what) - if protocol != 'tlul': + # TODO(hoangm): Investigate TLUH OT upstream support. + if protocol != 'tlul' and protocol != 'tluh' : raise ValueError('Unknown protocol {!r} at {}' .format(protocol, entry_what))
diff --git a/util/regtool/Cargo.toml b/util/regtool/Cargo.toml new file mode 100644 index 0000000..ed409cc --- /dev/null +++ b/util/regtool/Cargo.toml
@@ -0,0 +1,6 @@ +[package] +name = "regtool" +version = "0.1.0" +edition = "2018" + +[dependencies]
diff --git a/util/regtool/src/lib.rs b/util/regtool/src/lib.rs new file mode 100644 index 0000000..3584abc --- /dev/null +++ b/util/regtool/src/lib.rs
@@ -0,0 +1,124 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#![allow(clippy::needless_doctest_main)] + +//! A library for build scripts to generate `.rs` files using `regtool.py`. +//! +//! This library is intended to be used as a `build-dependencies` entry in +//! `Cargo.toml`: +//! +//! ```toml +//! [build-dependencies] +//! regtool = { path = "path/to/regtool" } +//! ``` +//! +//! By default, the library expects the environment variable `REGTOOL` to point +//! to `regtool.py`. Either set the variable before running cargo, or use +//! `Build::regtool(...)` from `build.rs` to set the path to the script +//! explicitly. +//! +//! # Examples +//! +//! Use the `Build` struct to process `hw/ip/uart/data/uart.hjson`: +//! +//! build.rs: +//! +//! ```no_run +//! fn main() { +//! regtool::Build::new() +//! .in_file_path("hw/ip/uart/data/uart.hjson") +//! .generate("uart.rs"); +//! } +//! ``` +//! +//! src/main.rs: +//! +//! ```no_run +//! include!(concat!(env!("OUT_DIR"), "/uart.rs")); +//! ``` + +use std::env; +use std::path::Path; +use std::path::PathBuf; +use std::process::Command; + +#[derive(Clone, Debug)] +pub struct Build { + in_file_path: Option<PathBuf>, // Must be set; no default + out_dir: Option<PathBuf>, // default: $OUT_DIR + + python: Option<PathBuf>, + regtool: Option<PathBuf>, // Default: $REGTOOL +} + +macro_rules! option_path_setter { + ($name:ident) => { + pub fn $name<P: AsRef<Path>>(&mut self, $name: P) -> &mut Build { + self.$name = Some($name.as_ref().to_owned()); + self + } + }; +} + +impl Build { + pub fn new() -> Self { + Self { + in_file_path: None, + out_dir: None, + + python: None, + regtool: None, + } + } + + // Generate setter functions for Option<PathBuf> fields: + option_path_setter!(in_file_path); + option_path_setter!(out_dir); + option_path_setter!(python); + option_path_setter!(regtool); + + // Run regtool. If out_file is an absolute path, write output to out_file, + // otherwise write output to out_dir/out_file. + pub fn generate(&self, out_file: &str) { + let regtool = if let Some(regtool) = &self.regtool { + regtool.to_owned() + } else { + println!("cargo:rerun-if-env-changed=REGTOOL"); + PathBuf::from(env::var("REGTOOL").expect("missing environment variable 'REGTOOL'")) + }; + println!("cargo:rerun-if-changed={}", regtool.display()); + + let mut out_file_path = if let Some(out_dir) = &self.out_dir { + out_dir.to_owned() + } else { + PathBuf::from(env::var("OUT_DIR").unwrap()) + }; + // NB: If out_file is absolute, it replaces the current path. + out_file_path.push(out_file); + + let in_file_path: &Path = self + .in_file_path + .as_ref() + .expect("'in_file_path' is not set"); + println!("cargo:rerun-if-changed={}", in_file_path.display()); + + let mut cmd; + if let Some(python) = &self.python { + cmd = Command::new(python); + cmd.arg(regtool); + } else { + cmd = Command::new(regtool); + } + cmd.arg("-R").arg("-o").arg(out_file_path).arg(in_file_path); + println!("Running: {:?}", cmd); + assert!(cmd.status().unwrap().success()); + } +} + +impl Default for Build { + fn default() -> Self { + Self::new() + } +}
diff --git a/util/tlgen/xbar.sim_cfg.hjson.tpl b/util/tlgen/xbar.sim_cfg.hjson.tpl index 681945f..627c05b 100644 --- a/util/tlgen/xbar.sim_cfg.hjson.tpl +++ b/util/tlgen/xbar.sim_cfg.hjson.tpl
@@ -16,13 +16,13 @@ testplan: "{proj_root}/hw/ip/tlul/data/tlul_testplan.hjson" // Add xbar_main specific exclusion files. - vcs_cov_excl_files: ["{proj_root}/hw/top_earlgrey/ip/{dut}/dv/autogen/xbar_cov_excl.el"] + vcs_cov_excl_files: ["{proj_root}/${xbar.ip_path}/dv/autogen/xbar_cov_excl.el"] // replace common cover.cfg with a generated one, which includes xbar toggle exclusions overrides: [ { name: default_vcs_cov_cfg_file - value: "-cm_hier {proj_root}/hw/top_earlgrey/ip/{dut}/dv/autogen/xbar_cover.cfg" + value: "-cm_hier {proj_root}/${xbar.ip_path}/dv/autogen/xbar_cover.cfg" } ] // Import additional common sim cfg files.
diff --git a/util/topgen/BUILD b/util/topgen/BUILD index fa87f82..cc73719 100644 --- a/util/topgen/BUILD +++ b/util/topgen/BUILD
@@ -11,7 +11,9 @@ name = "topgen", srcs = [ "__init__.py", + "entropy_buffer_generator.py", "gen_top_docs.py", + "strong_random.py", "validate.py", ], deps = [
diff --git a/util/topgen/templates/BUILD.tpl b/util/topgen/templates/BUILD.tpl index 858d019..e659f12 100644 --- a/util/topgen/templates/BUILD.tpl +++ b/util/topgen/templates/BUILD.tpl
@@ -51,3 +51,22 @@ "//sw/device/lib/testing/test_framework:ottf_main", ], ) + +opentitan_functest( + name = "alert_renode_test", + srcs = ["alert_test.c"], + deps = [ + "//hw/top_earlgrey/sw/autogen:top_earlgrey", + "//sw/device/lib/base:memory", + "//sw/device/lib/base:mmio", +% for n in sorted(alert_peripheral_names + ["alert_handler"]): + "//sw/device/lib/dif:${n}", +% endfor + "//sw/device/lib/runtime:log", + "//sw/device/lib/testing:alert_handler_testutils", + "//sw/device/lib/testing/test_framework:ottf_main", + ], + copts = [ + "-DDISABLE_RENODE_TEST", + ], +)
diff --git a/util/topgen/templates/alert_test.c.tpl b/util/topgen/templates/alert_test.c.tpl index 98a1c16..c098eef 100644 --- a/util/topgen/templates/alert_test.c.tpl +++ b/util/topgen/templates/alert_test.c.tpl
@@ -5,6 +5,12 @@ ${gencmd} <% +## list renode unsupported peripherals +unsupported_peripherals = [ + "adc_ctrl", "clkmgr", "edn", "entropy_src", "keymgr", "pattgen", + "pinmux", "pwm", "rv_plic", "sensor_ctrl", "spi_device", "sram_ctrl", + "sysrst_ctrl", "usbdev" +] alert_peripheral_names = sorted({p.name for p in helper.alert_peripherals}) %>\ #include "sw/device/lib/base/mmio.h" @@ -98,6 +104,9 @@ dif_alert_handler_alert_t exp_alert; % for p in helper.alert_peripherals: + % if p.name in unsupported_peripherals: +#ifndef DISABLE_RENODE_TEST + % endif // Write ${p.name}'s alert_test reg and check alert_cause. for (int i = 0; i < ${p.num_alerts}; ++i) { CHECK_DIF_OK(dif_${p.name}_alert_force(&${p.inst_name}, ${p.dif_alert_name} + i)); @@ -112,6 +121,9 @@ CHECK_DIF_OK(dif_alert_handler_alert_acknowledge( &alert_handler, exp_alert)); } + % if p.name in unsupported_peripherals: +#endif + % endif % endfor }
diff --git a/util/topgen/templates/xbar_env_pkg__params.sv.tpl b/util/topgen/templates/xbar_env_pkg__params.sv.tpl index 996511f..805b06a 100644 --- a/util/topgen/templates/xbar_env_pkg__params.sv.tpl +++ b/util/topgen/templates/xbar_env_pkg__params.sv.tpl
@@ -21,7 +21,7 @@ for host, devices in xbar["connections"].items(): for dev_name in devices: if is_device_a_xbar(dev_name): - edge_devices.extend(get_xbar_edge_nodes()) + edge_devices.extend(get_xbar_edge_nodes(dev_name)) else: edge_devices.append(dev_name)