diff --git a/util/reggen/bus_interfaces.py b/util/reggen/bus_interfaces.py index 54821109a32b3..b3dc380b61bd1 100644 --- a/util/reggen/bus_interfaces.py +++ b/util/reggen/bus_interfaces.py @@ -7,7 +7,7 @@ from typing import Dict, List, Optional, Tuple from reggen.inter_signal import InterSignal -from reggen.lib import check_list, check_keys, check_str, check_optional_str +from reggen.lib import check_list, check_keys, check_str, check_optional_bool, check_optional_str class BusInterfaces: @@ -18,7 +18,8 @@ def __init__(self, has_unnamed_device: bool, named_devices: List[str], device_async: Dict[Optional[str], str], - device_hier_paths: Dict[Optional[str], str]): + device_hier_paths: Dict[Optional[str], str], + racl_support: Dict[Optional[str], bool]): assert has_unnamed_device or named_devices assert len(named_hosts) == len(set(named_hosts)) assert len(named_devices) == len(set(named_devices)) @@ -30,6 +31,7 @@ def __init__(self, self.named_devices = named_devices self.device_async = device_async self.device_hier_paths = device_hier_paths + self.racl_support = racl_support @staticmethod def from_raw(raw: object, where: str) -> 'BusInterfaces': @@ -41,12 +43,13 @@ def from_raw(raw: object, where: str) -> 'BusInterfaces': named_devices = [] device_async = {} device_hier_paths = {} + racl_support_map = {} for idx, raw_entry in enumerate(check_list(raw, where)): entry_what = 'entry {} of {}'.format(idx + 1, where) ed = check_keys(raw_entry, entry_what, ['protocol', 'direction'], - ['name', 'async', 'hier_path']) + ['name', 'async', 'hier_path', 'racl_support']) protocol = check_str(ed['protocol'], 'protocol field of ' + entry_what) @@ -69,6 +72,9 @@ def from_raw(raw: object, where: str) -> 'BusInterfaces': hier_path = check_optional_str(ed.get('hier_path'), 'hier_path field of ' + entry_what) + racl_support = check_optional_bool(ed.get('racl_support'), + 'racl_support field of ' + entry_what) + if direction == 'host': if name is None: if has_unnamed_host: @@ -111,12 +117,14 @@ def from_raw(raw: object, where: str) -> 'BusInterfaces': else: device_hier_paths[name] = 'u_reg' + racl_support_map[name] = bool(racl_support) + if not (has_unnamed_device or named_devices): raise ValueError('No device interface at ' + where) return BusInterfaces(has_unnamed_host, named_hosts, host_async, has_unnamed_device, named_devices, - device_async, device_hier_paths) + device_async, device_hier_paths, racl_support_map) def has_host(self) -> bool: return bool(self.has_unnamed_host or self.named_hosts) diff --git a/util/reggen/lib.py b/util/reggen/lib.py index 8839a1569b258..60a09164edc7e 100644 --- a/util/reggen/lib.py +++ b/util/reggen/lib.py @@ -234,6 +234,11 @@ def check_xint(obj: object, what: str) -> Optional[int]: .format(what, type(obj).__name__)) +def check_optional_bool(obj: object, what: str) -> Optional[bool]: + '''Check that obj is a bool or None''' + return None if obj is None else check_bool(obj, what) + + def check_optional_str(obj: object, what: str) -> Optional[str]: '''Check that obj is a string or None''' return None if obj is None else check_str(obj, what) diff --git a/util/reggen/reg_top.sv.tpl b/util/reggen/reg_top.sv.tpl index 06b281eb63acb..e0c028c9a5ba8 100644 --- a/util/reggen/reg_top.sv.tpl +++ b/util/reggen/reg_top.sv.tpl @@ -33,6 +33,8 @@ reg2hw_t = gen_rtl.get_iface_tx_type(block, if_name, False) hw2reg_t = gen_rtl.get_iface_tx_type(block, if_name, True) + racl_support = block.bus_interfaces.racl_support[if_name] + win_array_decl = f' [{num_wins}]' if num_wins > 1 else '' # Calculate whether we're going to need an AW parameter. We use it if there @@ -115,7 +117,13 @@ %> `include "prim_assert.sv" -module ${mod_name} ( +module ${mod_name}${' (' if not racl_support else ''} +% if racl_support: + # ( + parameter bit EnableRacl = 1'b0, + parameter bit RaclErrorRsp = 1'b1 + ) ( +% endif input clk_i, input rst_ni, % if rb.has_internal_shadowed_reg(): @@ -147,6 +155,13 @@ module ${mod_name} ( output logic shadowed_update_err_o, %endif +% if racl_support: + // RACL interface + input top_racl_pkg::racl_policy_vec_t racl_policies_i, + input top_racl_pkg::racl_policy_sel_vec_t racl_policy_sel_vec_i, + output logic racl_error_o, + output top_racl_pkg::racl_error_log_t racl_error_log_o, +% endif // Integrity check errors output logic intg_err_o ); @@ -387,7 +402,12 @@ module ${mod_name} ( .be_o (reg_be), .busy_i (reg_busy), .rdata_i (reg_rdata), + % if racl_support: + // Translate RACL error to TLUL error if enabled + .error_i (reg_error | (RaclErrorRsp & racl_error_o)) + % else: .error_i (reg_error) + % endif ); // cdc oversampling signals @@ -640,15 +660,67 @@ ${finst_gen(sr, field, finst_name, fsig_name, fidx)} % endfor % endfor +% if racl_support: + racl_rolt_t racl_role; + + if (EnableRacl) begin : gen_racl_role_logic + // Retrieve RACL role from user bits and one-hot encode that for the comparison bitmap + assign racl_role = top_racl_pkg::tlul_extract_racl_bits(tl_i.a_user.rsvd); + + logic [(2**top_racl_pkg::RaclRoleWidth)-1:0] racl_role_vec; + prim_onehot_enc #( + .OneHotWidth(2**top_racl_pkg::RaclRoleWidth) + ) u_racl_role_encode ( + .in_i ( racl_role ), + .en_i ( 1'b1 ), + .out_o( racl_role_vec ) + ); + end else begin + assign racl_role = '0; + end +% endif logic [${len(regs_flat)-1}:0] addr_hit; +% if racl_support: + logic [${len(regs_flat)-1}:0] racl_read_addr_hit; + logic [${len(regs_flat)-1}:0] racl_write_addr_hit; +% endif always_comb begin addr_hit = '0; + % if racl_support: + racl_read_addr_hit = '0; + racl_write_addr_hit = '0; + % endif % for i,r in enumerate(regs_flat): - addr_hit[${"{}".format(i).rjust(max_regs_char)}] = (reg_addr == ${ublock}_${r.name.upper()}_OFFSET); +<% slice = '{}'.format(i).rjust(max_regs_char) %>\ + addr_hit[${slice}] = (reg_addr == ${ublock}_${r.name.upper()}_OFFSET); + % if racl_support: + if (EnableRacl) begin : gen_racl_hit + racl_read_addr_hit[${slice}] = addr_hit[${slice}] & (|(racl_policies_i[racl_policy_sel_vec_i[${slice}]].read_perm & racl_role_vec)); + racl_write_addr_hit[${slice}] = addr_hit[${slice}] & (|(racl_policies_i[racl_policy_sel_vec_i[${slice}]].write_perm & racl_role_vec)); + end + % endif % endfor + % if racl_support: + if (!EnableRacl) begin : gen_no_racl + racl_read_addr_hit = addr_hit; + racl_write_addr_hit = addr_hit; + end + % endif end assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; +% if racl_support: + // Address hit but failed the RACL check + assign racl_error_o = (|addr_hit) & ~(|(addr_hit & (racl_read_addr_hit | racl_write_addr_hit))); + + if (EnableRacl) begin : gen_racl_log + assign racl_error_log_o.racl_role = racl_role; + assign racl_error_log_o.write_read = tl_i.a_opcode == tlul_pkg::Get; + end else begin : gen_no_racl_log + assign racl_error_log_o.racl_role = '0; + assign racl_error_log_o.write_read = 1'b0; + end +% endif % if regs_flat: <% @@ -656,9 +728,11 @@ ${finst_gen(sr, field, finst_name, fsig_name, fidx)} # any bytes that aren't supported by a register. That's true if a # addr_hit[i] and a bit is set in reg_be but not in *_PERMIT[i]. - wr_err_terms = ['(addr_hit[{idx}] & (|({mod}_PERMIT[{idx}] & ~reg_be)))' + wr_addr_hit = 'racl_write_addr_hit' if racl_support else 'addr_hit' + wr_err_terms = ['({wr_addr_hit}[{idx}] & (|({mod}_PERMIT[{idx}] & ~reg_be)))' .format(idx=str(i).rjust(max_regs_char), - mod=u_mod_base) + mod=u_mod_base, + wr_addr_hit=wr_addr_hit) for i in range(len(regs_flat))] wr_err_expr = (' |\n' + (' ' * 15)).join(wr_err_terms) %>\ @@ -715,18 +789,19 @@ ${field_wd_gen(f, r.name.lower() + "_" + f.name.lower(), r.hwext, r.shadowed, r. always_comb begin reg_rdata_next = '0; unique case (1'b1) +<% read_addr_hit = 'racl_read_addr_hit' if racl_support else 'addr_hit' %>\ % for i, r in enumerate(regs_flat): % if r.async_clk: - addr_hit[${i}]: begin + ${read_addr_hit}[${i}]: begin reg_rdata_next = DW'(${r.name.lower()}_qs); end % elif len(r.fields) == 1: - addr_hit[${i}]: begin + ${read_addr_hit}[${i}]: begin ${rdata_gen(r.fields[0], r.name.lower())}\ end % else: - addr_hit[${i}]: begin + ${read_addr_hit}[${i}]: begin % for f in r.fields: ${rdata_gen(f, r.name.lower() + "_" + f.name.lower())}\ % endfor @@ -942,7 +1017,7 @@ ${bits.msb}\ mubi_expr = "1'b1" else: mubi_expr = "1'b0" - + # when async, the outputs are aggregated first by the cdc module async_suffix = '_int' if reg.async_clk else '' qs_expr = f'{clk_base_name}{finst_name}_qs{async_suffix}' if field.swaccess.allows_read() else '' @@ -1068,11 +1143,12 @@ ${bits.msb}\ % endif \ <%def name="reg_enable_gen(reg, idx)">\ +<% wr_addr_hit = 'racl_write_addr_hit' if racl_support else 'addr_hit'%>\ % if reg.needs_re(): - assign ${reg.name.lower()}_re = addr_hit[${idx}] & reg_re & !reg_error; + assign ${reg.name.lower()}_re = ${wr_addr_hit}[${idx}] & reg_re & !reg_error; % endif % if reg.needs_we(): - assign ${reg.name.lower()}_we = addr_hit[${idx}] & reg_we & !reg_error; + assign ${reg.name.lower()}_we = ${wr_addr_hit}[${idx}] & reg_we & !reg_error; % endif \ <%def name="field_wd_gen(field, sig_name, hwext, shadowed, async_clk, reg_name, idx)">\