Skip to content

Commit

Permalink
[RACL] Implement mapping parsing and autoconnect
Browse files Browse the repository at this point in the history
Signed-off-by: Robert Schilling <[email protected]>
  • Loading branch information
Razer6 committed Dec 25, 2024
1 parent ced14ed commit 3ab664e
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 6 deletions.
4 changes: 4 additions & 0 deletions hw/top_earlgrey/templates/toplevel.sv.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,10 @@ max_intrwidth = (max(len(x.name) for x in block.interrupts)
%>\
% if m["param_list"] or block.alerts:
${m["type"]} #(
% if 'racl_mapping' in m:
.EnableRacl(1'b1),
.RaclErrorRsp(${"1'b1" if top['racl']['error_response'] else "1'b0"})
% endif
% if block.alerts:
<%
w = len(block.alerts)
Expand Down
56 changes: 53 additions & 3 deletions util/raclgen/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@

import hjson
import sys
from typing import Optional
from reggen.ip_block import IpBlock
from reggen.validate import check_keys
from typing import Dict

# Required fields for the RACL hjson
racl_required = {
'error_response': ['pb', 'When true, return TLUL error on denied RACL access, otherwise not'],
'roles': ['l', 'List, specifying all RACL roles'],
'policies': ['g', 'Dict, specifying the policies of all RACL groups']
}
Expand All @@ -22,13 +25,17 @@
}


def parse_racl_config(config_path: str) -> Dict[str, object]:
def _read_hjson(filename: str)-> Dict[str, object]:
try:
with open(config_path, 'r') as f_racl_config:
racl_config = hjson.load(f_racl_config)
with open(filename, 'r') as f_racl_config:
return hjson.load(f_racl_config)
except OSError:
raise SystemExit(sys.exc_info()[1])


def parse_racl_config(config_path: str) -> Dict[str, object]:
racl_config = _read_hjson(config_path)

# TODO(#25690) Further sanity checks on the parsed RACL config
error = check_keys(racl_config, racl_required, [], [], 'RACL Config')
if error:
Expand All @@ -50,3 +57,46 @@ def compute_policy_value(permission: str) -> int:
policy['rd_default'] = compute_policy_value('allowed_rd')
policy['wr_default'] = compute_policy_value('allowed_wr')
return racl_config


def parse_racl_mapping(racl_config: Dict[str, object], mapping_path: str,
if_name: Optional[str], ip_block: IpBlock):
mapping = _read_hjson(mapping_path)
parsed_register_mapping = {}

# Mapping must be a dict with a single entry:
# RACL_GROUP => register mapping
if len(mapping) != 1:
raise SystemExit('Mapping file must be a single-element dict mapping the RACL group to the '
'register mapping')
racl_group = list(mapping.keys())[0]
register_mapping = list(mapping.values())[0]

# Special handling of the all star assignment:
# "*": POLICY
# Assigns all registers to a given policy
if len(register_mapping) == 1 and list(register_mapping.keys())[0] == "*":
policy_name = list(register_mapping.values())[0]

if racl_group not in racl_config['policies']:
raise SystemExit(f'RACL group {racl_group} not defined in RACL config')

found_policy = None
for policy in racl_config['policies'][racl_group]:
if policy['name'] == policy_name:
found_policy = policy
break

if not found_policy:
raise SystemExit(f'RACL policy {policy_name} not defined in RACL config')

if if_name not in ip_block.reg_blocks:
raise SystemExit(f"Register interrface {if_name} not defined in in {ip_block['name']}")

for reg in ip_block.reg_blocks[if_name].flat_regs:
parsed_register_mapping[reg.name] = found_policy
else:
# General case not yet implemented
assert False

return parsed_register_mapping
17 changes: 14 additions & 3 deletions util/topgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
IpTemplate, TemplateRenderError)
from mako import exceptions
from mako.template import Template
from raclgen.lib import DEFAULT_RACL_CONFIG, parse_racl_config
from raclgen.lib import DEFAULT_RACL_CONFIG, parse_racl_config, parse_racl_mapping
from reggen import access, gen_rtl, gen_sec_cm_testplan, window
from reggen.countermeasure import CounterMeasure
from reggen.inter_signal import InterSignal
Expand Down Expand Up @@ -528,13 +528,24 @@ def generate_ac_range_check(topcfg: Dict[str, object], out_path: Path) -> None:


# Generate RACL collateral
def generate_racl(topcfg: Dict[str, object], out_path: Path) -> None:
def generate_racl(topcfg: Dict[str, object], name_to_block: Dict[str, IpBlock],
out_path: Path) -> None:
# Not all tops use RACL
if 'racl_config' not in topcfg:
return

# Read the top-level RACL information
topcfg['racl'] = parse_racl_config(topcfg['racl_config'])

# Generate the RACL mappings for all subscribing IPs
for m in topcfg['module']:
if 'racl_mappings' in m:
for if_name, mapping_path in m['racl_mappings'].items():
m['racl_mappings'][if_name] = parse_racl_mapping(topcfg['racl'],
mapping_path,
if_name,
name_to_block[m['type']])

log.info('Generating RACL Control IP with ipgen')
topname = topcfg['name']

Expand Down Expand Up @@ -904,7 +915,7 @@ def _process_top(
generate_ac_range_check(completecfg, out_path)

# Generate RACL collateral
generate_racl(completecfg, out_path)
generate_racl(completecfg, name_to_block, out_path)

# Generate top only modules
# These modules are not ipgen, but are not in hw/ip
Expand Down
26 changes: 26 additions & 0 deletions util/topgen/intermodule.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,32 @@ def autoconnect(topcfg: OrderedDict, name_to_block: Dict[str, IpBlock]):
for xbar in topcfg["xbar"]:
autoconnect_xbar(topcfg, name_to_block, xbar)

# Auto connect RACL subscribing IPs to the assocated racl_ctrl IP
if 'racl' in topcfg:
# Find the RACL control of the defined group. This currently only works for the default
# RACL ctrl module when there is a single instance. This limitiation currently comes from
# ipgen and is tracked in #25673
racl_ctrl = lib.find_module(topcfg['module'], 'racl_ctrl')

# Determine all subscribing RACL modules
for m in topcfg['module']:
for racl_group in m.get('racl_mappings', {}).keys():
add_intermodule_connection(obj=topcfg,
req_m=racl_ctrl['name'],
req_s="racl_policies",
rsp_m='pwm_aon',
rsp_s="racl_policies")
add_intermodule_connection(obj=topcfg,
req_m=racl_ctrl['name'],
req_s="racl_error",
rsp_m='pwm_aon',
rsp_s="racl_error")
add_intermodule_connection(obj=topcfg,
req_m=racl_ctrl['name'],
req_s="racl_error_log",
rsp_m='pwm_aon',
rsp_s="racl_error_log")


def _get_default_name(sig, suffix):
"""Generate default for a net if one does not already exist.
Expand Down
23 changes: 23 additions & 0 deletions util/topgen/merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,29 @@ def elaborate_instance(instance, block: IpBlock):
raise ValueError(f'generate_dif contains invalid value {instance["generate_dif"]}')
instance['generate_dif'] = converted_value

# An instance can either have a 'racl_mapping' or 'racl_mappings' but can't have both.
# 'racl_mapping' is used when the device only has a single register interface and
# 'racl_mappings' there are more.
racl_mapping = instance.get('racl_mapping')
racl_mappings = instance.get('racl_mappings')

if racl_mapping and racl_mappings:
raise ValueError(f"Cannot specify both 'racl_mapping' and 'racl_mappings'")

# Map both variants to the 'racl_mappings' for a unified code. At this point, we cannot parse
# the RACL config as the top_config with all the RACL information is not yet available
racl_mapping_dict = {}
if racl_mapping:
racl_mapping_dict[None] = racl_mapping

if racl_mappings:
for if_name, racl_mapping in racl_mappings.items():
racl_mapping_dict[if_name] = racl_mapping

if racl_mapping_dict:
del instance['racl_mapping']
instance['racl_mappings'] = racl_mapping_dict


# TODO: Replace this part to be configurable from Hjson or template
predefined_modules = {
Expand Down
3 changes: 3 additions & 0 deletions util/topgen/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@
'outgoing_alert': ['s', 'optional string to indicate alerts are routed externally to the named '
'group'],
'incoming_interrupt': ['g', 'Parsed incoming interrupts (generated)'],
'racl_mapping': ['s', 'optional RACL mapping for that IP'],
'racl_mappings': ['g', 'dict of RACL mappings for all registers interfaces if there are more '
'than one'],
}

module_added = {
Expand Down

0 comments on commit 3ab664e

Please sign in to comment.