From dfcda08a600bf01abbdfd4c6c2ede80b5c6a8c15 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Tue, 26 Nov 2024 14:42:06 +0100 Subject: [PATCH] [racl] Implement RACL Control IP Signed-off-by: Robert Schilling --- hw/ip_templates/racl_ctrl/README.md | 1 + .../racl_ctrl/data/racl_ctrl.hjson.tpl | 162 +++++++++++++++ .../racl_ctrl/data/racl_ctrl.tpldesc.hjson | 49 +++++ .../racl_ctrl/doc/interfaces.md.tpl | 4 + .../racl_ctrl/doc/registers.md.tpl | 4 + .../racl_ctrl/lint/racl_ctrl.vlt.tpl | 5 + .../racl_ctrl/lint/racl_ctrl.waiver.tpl | 8 + hw/ip_templates/racl_ctrl/racl_ctrl.core.tpl | 68 +++++++ .../racl_ctrl/rtl/racl_ctrl.sv.tpl | 184 ++++++++++++++++++ util/reggen/ip_block.py | 3 +- util/topgen.py | 29 ++- 11 files changed, 515 insertions(+), 2 deletions(-) create mode 100644 hw/ip_templates/racl_ctrl/README.md create mode 100644 hw/ip_templates/racl_ctrl/data/racl_ctrl.hjson.tpl create mode 100644 hw/ip_templates/racl_ctrl/data/racl_ctrl.tpldesc.hjson create mode 100644 hw/ip_templates/racl_ctrl/doc/interfaces.md.tpl create mode 100644 hw/ip_templates/racl_ctrl/doc/registers.md.tpl create mode 100644 hw/ip_templates/racl_ctrl/lint/racl_ctrl.vlt.tpl create mode 100644 hw/ip_templates/racl_ctrl/lint/racl_ctrl.waiver.tpl create mode 100644 hw/ip_templates/racl_ctrl/racl_ctrl.core.tpl create mode 100644 hw/ip_templates/racl_ctrl/rtl/racl_ctrl.sv.tpl diff --git a/hw/ip_templates/racl_ctrl/README.md b/hw/ip_templates/racl_ctrl/README.md new file mode 100644 index 00000000000000..72afd8ecf96c6f --- /dev/null +++ b/hw/ip_templates/racl_ctrl/README.md @@ -0,0 +1 @@ +# RACL Control Permission IP diff --git a/hw/ip_templates/racl_ctrl/data/racl_ctrl.hjson.tpl b/hw/ip_templates/racl_ctrl/data/racl_ctrl.hjson.tpl new file mode 100644 index 00000000000000..f05cb244f3c0e1 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/data/racl_ctrl.hjson.tpl @@ -0,0 +1,162 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +# RACL Control register template +# +{ + name: "${module_instance_name}", + human_name: "RACL Control", + one_line_desc: "Implements the RACL policy registers to distribute to subscribing IPs.", + one_paragraph_desc: ''' + Implements the RACL policy registers to distribute to subscribing IPs. + ''' + // Unique comportable IP identifier defined under KNOWN_CIP_IDS in the regtool. + cip_id: "43", + design_spec: "../doc", + dv_doc: "../doc/dv", + hw_checklist: "../doc/checklist", + sw_checklist: "/sw/device/lib/dif/dif_racl_ctrl", + revisions: [ + { + version: "1.0.0", + life_stage: "L1", + design_stage: "D0", + verification_stage: "V0", + dif_stage: "S0", + } + ] + clocking: [ + {clock: "clk_i", reset: "rst_ni"}, + ] + bus_interfaces: [ + { protocol: "tlul", direction: "device" } + ], + alert_list: [ + % if enable_shadow_reg: + { name: "recov_ctrl_update_err", + desc: "This recoverable alert is triggered upon detecting an update error in the shadowed Control Register." + } + % endif + { name: "fatal_fault" + desc: "This fatal alert is triggered when a fatal TL-UL bus integrity fault is detected." + } + ], + countermeasures: [ + { name: "BUS.INTEGRITY", + desc: "End-to-end bus integrity scheme." + } + % if enable_shadow_reg: + { name: "RACL_POLICY.CONFIG.SHADOW", + desc: "RACL policy registers are shadowed." + } + % endif + ] + regwidth: "32", + param_list: [ + { name: "NumPolicies", + desc: "Number of policies", + type: "int", + default: "${nr_policies}", + local: "true" + }, + { name: "NumSubscribingIps", + desc: "Number of subscribing RACL IPs", + type: "int", + default: "1", + expose: "true" + local: "true" + }, + ], + inter_signal_list: [ + { struct: "policies", + type: "uni", + name: "policies", + act: "req", + package: "racl_pkg", + desc: ''' + Policy vector distributed to the subscribing RACL IPs. + ''' + }, + ], + + registers: [ + { name: "ERROR_LOG" + desc: "Error logging registers" + swaccess: "ro" + hwaccess: "hwo" + hwqe: "true" + fields: [ + { bits: "0" + name: "valid" + resval: 0x0 + swaccess: "rw1c" + hwaccess: "hrw" + desc: ''' + Indicates a RACL error and the log register contains valid data. + Writing a one clears the error log register. + ''' + } + { bits: "1" + name: "overflow" + resval: 0x0 + desc: ''' + Indicates a RACL error overflow when a RACL error occurred while the log register was set. + ''' + } + { bits: "2" + name: "write_read" + resval: 0x0 + desc: ''' + 0: Write transfer was denied. + 1: Read transfer was denied. + ''' + } + { bits: "${3 + nr_role_bits - 1}:3" + name: "role" + resval: 0x0 + desc: ''' + RACL role causing the error. + ''' + } + { bits: "${3 + nr_role_bits + nr_ctn_uid_bits - 1}:${3 + nr_role_bits}" + name: "ctn_uid" + resval: 0x0 + desc: ''' + CTN UID causing the error. + ''' + } + ] + } + % for policy in policies: + { name: "POLICY_${policy['name'].upper()}${"_SHADOWED" if enable_shadow_reg else ""}" + desc: ''' + Read and write policy for ${policy} + ''' + swaccess: "rw" + hwaccess: "hro" + % if enable_shadow_reg: + shadowed: "true" + update_err_alert: "recov_ctrl_update_err" + storage_err_alert: "fatal_fault" + % endif + fields: [ + { bits: "31:16" + name: "write_perm" + resval: ${policy['wr_default']} + desc: ''' + Write permission for policy ${policy} + ''' + } + { bits: "15:0" + name: "read_perm" + resval: ${policy['rd_default']} + desc: ''' + Read permission for policy ${policy} + ''' + } + ] + } + % endfor + ] +} diff --git a/hw/ip_templates/racl_ctrl/data/racl_ctrl.tpldesc.hjson b/hw/ip_templates/racl_ctrl/data/racl_ctrl.tpldesc.hjson new file mode 100644 index 00000000000000..8aea476f1a1cb9 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/data/racl_ctrl.tpldesc.hjson @@ -0,0 +1,49 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + template_param_list: [ + { + name: "topname" + desc: "Name of top-level design, e.g., 'darjeeling' or 'earlgrey'" + type: "string" + default: "" + } + { + name: "module_instance_name" + desc: "instance name in case there are multiple RACL Ctrl instances" + type: "string" + default: "racl_ctrl" + } + { + name: "enable_shadow_reg" + desc: "Enable shadow reg protection for policy registers" + type: "bool" + default: true + } + { + name: "nr_role_bits" + desc: "Number of RACL bits used for roles" + type: "int" + default: "4" + } + { + name: "nr_ctn_uid_bits" + desc: "Number of CTN UID bits" + type: "int" + default: "8" + } + { + name: "nr_policies" + desc: "Number of policies" + type: "int" + default: "16" + } + { + name: "policies" + desc: "The RACL policies" + type: "object" + default: [] + } + ] +} diff --git a/hw/ip_templates/racl_ctrl/doc/interfaces.md.tpl b/hw/ip_templates/racl_ctrl/doc/interfaces.md.tpl new file mode 100644 index 00000000000000..74cde45d8123d1 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/doc/interfaces.md.tpl @@ -0,0 +1,4 @@ +${"#"} Hardware Interfaces + + + diff --git a/hw/ip_templates/racl_ctrl/doc/registers.md.tpl b/hw/ip_templates/racl_ctrl/doc/registers.md.tpl new file mode 100644 index 00000000000000..04328a53948a30 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/doc/registers.md.tpl @@ -0,0 +1,4 @@ +${"#"} Registers + + + diff --git a/hw/ip_templates/racl_ctrl/lint/racl_ctrl.vlt.tpl b/hw/ip_templates/racl_ctrl/lint/racl_ctrl.vlt.tpl new file mode 100644 index 00000000000000..d6999a9bc50885 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/lint/racl_ctrl.vlt.tpl @@ -0,0 +1,5 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// waiver file for ${module_instance_name} diff --git a/hw/ip_templates/racl_ctrl/lint/racl_ctrl.waiver.tpl b/hw/ip_templates/racl_ctrl/lint/racl_ctrl.waiver.tpl new file mode 100644 index 00000000000000..011f0bb2c509bd --- /dev/null +++ b/hw/ip_templates/racl_ctrl/lint/racl_ctrl.waiver.tpl @@ -0,0 +1,8 @@ +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# waiver file for ${module_instance_name} + +waive -rules {HIER_NET_NOT_READ} -location {${module_instance_name}_reg_top.sv} -regexp {error_log_flds_we\[4:1\]' is not read from in module} ${"\\"} + -comment "Internal register is accepted to not be read. Tracked in #25663." \ No newline at end of file diff --git a/hw/ip_templates/racl_ctrl/racl_ctrl.core.tpl b/hw/ip_templates/racl_ctrl/racl_ctrl.core.tpl new file mode 100644 index 00000000000000..9a1f6efd0ff4a4 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/racl_ctrl.core.tpl @@ -0,0 +1,68 @@ +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: ${instance_vlnv(f"lowrisc:ip:{module_instance_name}:0.1")} +description: "RACL Control permission IP" + +filesets: + files_rtl: + depend: + - lowrisc:ip:tlul + - lowrisc:prim:mubi + - lowrisc:prim:all + - lowrisc:systems:top_racl_pkg + files: + - rtl/${module_instance_name}_reg_pkg.sv + - rtl/${module_instance_name}_reg_top.sv + - rtl/${module_instance_name}.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + files: + - lint/racl_ctrl.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + files: + - lint/racl_ctrl.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + +parameters: + SYNTHESIS: + datatype: bool + paramtype: vlogdefine + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + toplevel: ${module_instance_name} + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" diff --git a/hw/ip_templates/racl_ctrl/rtl/racl_ctrl.sv.tpl b/hw/ip_templates/racl_ctrl/rtl/racl_ctrl.sv.tpl new file mode 100644 index 00000000000000..48473b4a37cb84 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/rtl/racl_ctrl.sv.tpl @@ -0,0 +1,184 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module ${module_instance_name} import ${module_instance_name}_reg_pkg::*; #( + parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}, + parameter int unsigned NumSubscribingIps = 1 +) ( + input logic clk_i, + input logic rst_ni, +% if enable_shadow_reg: + input logic rst_shadowed_ni, +% endif + // Bus Interface (device) + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, + // Alerts + input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i, + output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o, + // Output policy vector for distribution + output top_racl_pkg::racl_policy_vec_t policies_o, + // RACL violation information. + input logic [NumSubscribingIps-1:0] racl_error_i, + input top_racl_pkg::racl_error_log_t [NumSubscribingIps-1:0] racl_error_log_i +); + ${module_instance_name}_reg2hw_t reg2hw; + ${module_instance_name}_hw2reg_t hw2reg; + + ////////////////////////////////////////////////////////////////////////////////////////////////// + // Register Interface + ////////////////////////////////////////////////////////////////////////////////////////////////// + logic reg_intg_error; +% if enable_shadow_reg: + logic shadowed_storage_err, shadowed_update_err; +% endif + + // SEC_CM: BUS.INTEGRITY +% if enable_shadow_reg: + // SEC_CM: RACL_POLICY.CONFIG.SHADOW +% endif + ${module_instance_name}_reg_top u_racl_ctrl_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), +% if enable_shadow_reg: + .rst_shadowed_ni ( rst_shadowed_ni ), +% endif + .tl_i ( tl_i ), + .tl_o ( tl_o ), + .reg2hw ( reg2hw ), + .hw2reg ( hw2reg ), +% if enable_shadow_reg: + .shadowed_storage_err_o ( shadowed_storage_err ), + .shadowed_update_err_o ( shadowed_update_err ), +% endif + .intg_err_o ( reg_intg_error ) + ); + + ////////////////////////////////////////////////////////////////////////////////////////////////// + // Alert Management + ////////////////////////////////////////////////////////////////////////////////////////////////// + logic [NumAlerts-1:0] alert_test, alert; + +% if enable_shadow_reg: + localparam logic [NumAlerts-1:0] IsFatal = {1'b1, 1'b0}; + + assign alert[0] = shadowed_update_err; + assign alert[1] = reg_intg_error | shadowed_storage_err; + + assign alert_test = { + reg2hw.alert_test.fatal_fault.q & + reg2hw.alert_test.fatal_fault.qe, + reg2hw.alert_test.recov_ctrl_update_err.q & + reg2hw.alert_test.recov_ctrl_update_err.qe + }; +% else: + localparam logic [NumAlerts-1:0] IsFatal = {1'b1}; + + assign alert[0] = reg_intg_error; + + assign alert_test = { + reg2hw.alert_test.fatal_fault.q & + reg2hw.alert_test.fatal_fault.qe + }; +% endif + + for (genvar i = 0; i < NumAlerts; i++) begin : gen_alert_tx + prim_alert_sender #( + .AsyncOn ( AlertAsyncOn[i] ), + .IsFatal ( IsFatal[i] ) + ) u_prim_alert_sender ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .alert_test_i ( alert_test[i] ), + .alert_req_i ( alert[i] ), + .alert_ack_o ( ), + .alert_state_o ( ), + .alert_rx_i ( alert_rx_i[i] ), + .alert_tx_o ( alert_tx_o[i] ) + ); + end + + ////////////////////////////////////////////////////////////////////////////////////////////////// + // Policy broadcasting + ////////////////////////////////////////////////////////////////////////////////////////////////// + +% for policy in policies: + top_racl_pkg::racl_policy_t policy_${policy['name'].lower()}; +% endfor + + // Assign register policy values to policy structs +% for policy in policies: + assign policy_${policy['name'].lower()}.read_perm = reg2hw.policy_${policy['name'].lower()}${"_shadowed" if enable_shadow_reg else ""}.read_perm.q; + assign policy_${policy['name'].lower()}.write_perm = reg2hw.policy_${policy['name'].lower()}${"_shadowed" if enable_shadow_reg else ""}.write_perm.q; + +% endfor + // Broadcast all policies via policy vector + assign policies_o = { +% for policy in policies: + policy_${policy['name'].lower()}${',' if not loop.last else ''} +% endfor + }; + + ////////////////////////////////////////////////////////////////////////////////////////////////// + // Error handling + ////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + // A RACL error can only happen for one IP at a time in one RACL domain. Therefore, it is + // safe to OR all RACL error bits together and no arbitration is needed. This is true also + // for the corresponding RACL role or Write/Read information. + logic racl_error; + assign racl_error = |racl_error_i; + + top_racl_pkg::racl_role_t racl_error_role; + top_racl_pkg::ctn_uid_t racl_error_ctn_uid; + logic racl_error_write_read; + + // Reduce all incoming error vectors to a single role and write/read bit. + // Only a single IP can have a RACL error at one time. + always_comb begin + racl_error_role = '0; + racl_error_ctn_uid = '0; + racl_error_write_read = 1'b0; + for (int i = 0; i < NumSubscribingIps; i++) begin + racl_error_role |= racl_error_log_i[i].racl_role; + racl_error_ctn_uid |= racl_error_log_i[i].ctn_uid; + racl_error_write_read |= racl_error_log_i[i].write_read; + end + end + + logic first_error; + assign first_error = ~reg2hw.error_log.valid.q & racl_error; + + // Writing 1 to the error valid bit clears the log again + logic clear_log; + assign clear_log = reg2hw.error_log.valid.q & reg2hw.error_log.valid.qe; + + assign hw2reg.error_log.valid.d = ~clear_log; + assign hw2reg.error_log.valid.de = racl_error | clear_log; + + // Overflow is raised when error is valid and a new error is coming in + assign hw2reg.error_log.overflow.d = ~clear_log; + assign hw2reg.error_log.overflow.de = (reg2hw.error_log.valid.q & racl_error) | clear_log; + + assign hw2reg.error_log.write_read.d = clear_log ? '0 : racl_error_write_read; + assign hw2reg.error_log.write_read.de = first_error | clear_log; + + assign hw2reg.error_log.role.d = clear_log ? '0 : racl_error_role; + assign hw2reg.error_log.role.de = first_error | clear_log; + + assign hw2reg.error_log.ctn_uid.d = clear_log ? '0 : racl_error_ctn_uid; + assign hw2reg.error_log.ctn_uid.de = first_error | clear_log; + + ////////////////////////////////////////////////////////////////////////////////////////////////// + // Assertions + ////////////////////////////////////////////////////////////////////////////////////////////////// + + // All outputs should be known value after reset + `ASSERT_KNOWN(AlertsKnown_A, alert_tx_o) + + // Alert assertions for reg_we onehot check + `ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, u_racl_ctrl_reg, + alert_tx_o[0]) +endmodule diff --git a/util/reggen/ip_block.py b/util/reggen/ip_block.py index 363d23d01c16e7..2a175211a3aab0 100644 --- a/util/reggen/ip_block.py +++ b/util/reggen/ip_block.py @@ -63,7 +63,8 @@ 39: 'keymgr_dpe', 40: 'ascon', 41: 'ac_range_check', - 42: 'soc_dbg_ctrl' + 42: 'soc_dbg_ctrl', + 43: 'racl_ctrl' } REQUIRED_ALIAS_FIELDS = { diff --git a/util/topgen.py b/util/topgen.py index bbe9bec2a23ffb..ae18b91cfb1e25 100755 --- a/util/topgen.py +++ b/util/topgen.py @@ -532,9 +532,36 @@ def generate_racl(topcfg: Dict[str, object], out_path: Path) -> None: # Not all tops use RACL if 'racl_config' not in topcfg: return - + topcfg['racl'] = parse_racl_config(topcfg['racl_config']) + log.info('Generating RACL Control IP with ipgen') + topname = topcfg['name'] + + for racl_group, policies in topcfg['racl']['policies'].items(): + params = { + "nr_role_bits": 4, + "nr_ctn_uid_bits": 8, + "nr_policies": len(policies), + "policies": policies + } + + # If we have more RACL policy groups, uniquify the control IP + # if len(topcfg['racl']['policies']) > 1: + # params['module_instance_name'] = f'racl_ctrl_{racl_group}' + + # # Only render the RACL groups that are really instantiated in that top + # for m in topcfg['module']: + # if m['name'] == params['module_instance_name']: + # ipgen_render("racl_ctrl", topname, params, out_path) + # break + # TODO(#25673): The obove code, would be the correct if ipgen correctly supports rendering + # multiple instances and allow topgen to instantiate right now. This support is not yet + # implemented properly. Therefore, simple render the first RACL group to the RACL control + # IP. + ipgen_render("racl_ctrl", topname, params, out_path) + break + def generate_top_only(top_only_dict: Dict[str, bool], out_path: Path, top_name: str, alt_hjson_path: str) -> None: