Skip to content

Commit

Permalink
[racl] Implement RACL checks in reggen
Browse files Browse the repository at this point in the history
Signed-off-by: Robert Schilling <[email protected]>
  • Loading branch information
Razer6 committed Dec 17, 2024
1 parent dfcda08 commit ade3d52
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 14 deletions.
16 changes: 12 additions & 4 deletions util/reggen/bus_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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))
Expand All @@ -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':
Expand All @@ -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)
Expand All @@ -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:
Expand Down Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions util/reggen/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
96 changes: 86 additions & 10 deletions util/reggen/reg_top.sv.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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
);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -640,25 +660,79 @@ ${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:
<%
# We want to signal wr_err if reg_be (the byte enable signal) is true for
# 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)
%>\
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ''
Expand Down Expand Up @@ -1068,11 +1143,12 @@ ${bits.msb}\
% endif
</%def>\
<%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>\
<%def name="field_wd_gen(field, sig_name, hwext, shadowed, async_clk, reg_name, idx)">\
Expand Down

0 comments on commit ade3d52

Please sign in to comment.