blob: 9474ad57fcdc9ff4f28179d022b67a623112bb2a [file] [log] [blame]
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +00001# Copyright lowRISC contributors.
2# Licensed under the Apache License, Version 2.0, see LICENSE for details.
3# SPDX-License-Identifier: Apache-2.0
4
5'''Code representing an IP block for reggen'''
6
Rupert Swarbrick200d8b42021-03-08 12:32:11 +00007from typing import Dict, List, Optional, Sequence, Set, Tuple
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +00008
9import hjson # type: ignore
10
11from .alert import Alert
Rupert Swarbrick6c831292021-02-25 17:08:53 +000012from .bus_interfaces import BusInterfaces
Timothy Chen7c3de6e2021-07-19 16:46:45 -070013from .clocking import Clocking, ClockingItem
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000014from .inter_signal import InterSignal
Michael Schaffnerbd430362022-05-09 18:13:23 -070015from .lib import (check_keys, check_name, check_int, check_bool, check_list)
Philipp Wagner78194c82021-03-10 10:08:17 +000016from .params import ReggenParams, LocalParam
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000017from .reg_block import RegBlock
18from .signal import Signal
Michael Schaffnere8976ff2021-11-15 16:12:17 -080019from .countermeasure import CounterMeasure
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000020
21
22REQUIRED_FIELDS = {
23 'name': ['s', "name of the component"],
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +010024 'clocking': ['l', "clocking for the device"],
Rupert Swarbrick6c831292021-02-25 17:08:53 +000025 'bus_interfaces': ['l', "bus interfaces for the device"],
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000026 'registers': [
27 'l',
28 "list of register definition groups and "
29 "offset control groups"
30 ]
31}
32
33OPTIONAL_FIELDS = {
34 'alert_list': ['lnw', "list of peripheral alerts"],
35 'available_inout_list': ['lnw', "list of available peripheral inouts"],
36 'available_input_list': ['lnw', "list of available peripheral inputs"],
37 'available_output_list': ['lnw', "list of available peripheral outputs"],
Rupert Swarbrickda5ed152021-06-08 14:14:40 +010038 'expose_reg_if': ['pb', 'if set, expose reg interface in reg2hw signal'],
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000039 'interrupt_list': ['lnw', "list of peripheral interrupts"],
40 'inter_signal_list': ['l', "list of inter-module signals"],
41 'no_auto_alert_regs': [
42 's', "Set to true to suppress automatic "
43 "generation of alert test registers. "
44 "Defaults to true if no alert_list is present. "
45 "Otherwise this defaults to false. "
46 ],
47 'no_auto_intr_regs': [
48 's', "Set to true to suppress automatic "
49 "generation of interrupt registers. "
50 "Defaults to true if no interrupt_list is present. "
51 "Otherwise this defaults to false. "
52 ],
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000053 'param_list': ['lp', "list of parameters of the IP"],
54 'regwidth': ['d', "width of registers in bits (default 32)"],
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000055 'reset_request_list': ['l', 'list of signals requesting reset'],
56 'scan': ['pb', 'Indicates the module have `scanmode_i`'],
Timothy Chen21e6a4f2021-04-30 03:47:01 -070057 'scan_reset': ['pb', 'Indicates the module have `scan_rst_ni`'],
58 'scan_en': ['pb', 'Indicates the module has `scan_en_i`'],
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000059 'SPDX-License-Identifier': [
60 's', "License ientifier (if using pure json) "
61 "Only use this if unable to put this "
62 "information in a comment at the top of the "
63 "file."
64 ],
Michael Schaffnere8976ff2021-11-15 16:12:17 -080065 'wakeup_list': ['lnw', "list of peripheral wakeups"],
66 'countermeasures': ["ln", "list of countermeasures in this block"]
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000067}
68
69
Rupert Swarbrick200d8b42021-03-08 12:32:11 +000070class IpBlock:
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000071 def __init__(self,
72 name: str,
73 regwidth: int,
Philipp Wagner78194c82021-03-10 10:08:17 +000074 params: ReggenParams,
Rupert Swarbrick200d8b42021-03-08 12:32:11 +000075 reg_blocks: Dict[Optional[str], RegBlock],
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000076 interrupts: Sequence[Signal],
77 no_auto_intr: bool,
78 alerts: List[Alert],
79 no_auto_alert: bool,
80 scan: bool,
81 inter_signals: List[InterSignal],
Rupert Swarbrick6c831292021-02-25 17:08:53 +000082 bus_interfaces: BusInterfaces,
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +010083 clocking: Clocking,
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000084 xputs: Tuple[Sequence[Signal],
85 Sequence[Signal],
86 Sequence[Signal]],
87 wakeups: Sequence[Signal],
88 reset_requests: Sequence[Signal],
Rupert Swarbrickda5ed152021-06-08 14:14:40 +010089 expose_reg_if: bool,
Timothy Chen21e6a4f2021-04-30 03:47:01 -070090 scan_reset: bool,
Michael Schaffnere8976ff2021-11-15 16:12:17 -080091 scan_en: bool,
92 countermeasures: List[CounterMeasure]):
Rupert Swarbrick200d8b42021-03-08 12:32:11 +000093 assert reg_blocks
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000094
Rupert Swarbrick200d8b42021-03-08 12:32:11 +000095 # Check that register blocks are in bijection with device interfaces
96 reg_block_names = reg_blocks.keys()
97 dev_if_names = [] # type: List[Optional[str]]
98 dev_if_names += bus_interfaces.named_devices
99 if bus_interfaces.has_unnamed_device:
100 dev_if_names.append(None)
101 assert set(reg_block_names) == set(dev_if_names)
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000102
Rupert Swarbrick200d8b42021-03-08 12:32:11 +0000103 self.name = name
104 self.regwidth = regwidth
105 self.reg_blocks = reg_blocks
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000106 self.params = params
107 self.interrupts = interrupts
108 self.no_auto_intr = no_auto_intr
109 self.alerts = alerts
110 self.no_auto_alert = no_auto_alert
111 self.scan = scan
112 self.inter_signals = inter_signals
Rupert Swarbrick6c831292021-02-25 17:08:53 +0000113 self.bus_interfaces = bus_interfaces
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +0100114 self.clocking = clocking
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000115 self.xputs = xputs
116 self.wakeups = wakeups
117 self.reset_requests = reset_requests
Rupert Swarbrickda5ed152021-06-08 14:14:40 +0100118 self.expose_reg_if = expose_reg_if
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000119 self.scan_reset = scan_reset
Timothy Chen21e6a4f2021-04-30 03:47:01 -0700120 self.scan_en = scan_en
Michael Schaffnere8976ff2021-11-15 16:12:17 -0800121 self.countermeasures = countermeasures
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000122
123 @staticmethod
124 def from_raw(param_defaults: List[Tuple[str, str]],
125 raw: object,
126 where: str) -> 'IpBlock':
127
128 rd = check_keys(raw, 'block at ' + where,
129 list(REQUIRED_FIELDS.keys()),
130 list(OPTIONAL_FIELDS.keys()))
131
132 name = check_name(rd['name'], 'name of block at ' + where)
133
134 what = '{} block at {}'.format(name, where)
135
136 r_regwidth = rd.get('regwidth')
137 if r_regwidth is None:
138 regwidth = 32
139 else:
140 regwidth = check_int(r_regwidth, 'regwidth field of ' + what)
141 if regwidth <= 0:
142 raise ValueError('Invalid regwidth field for {}: '
143 '{} is not positive.'
144 .format(what, regwidth))
145
Philipp Wagner78194c82021-03-10 10:08:17 +0000146 params = ReggenParams.from_raw('parameter list for ' + what,
147 rd.get('param_list', []))
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000148 try:
149 params.apply_defaults(param_defaults)
150 except (ValueError, KeyError) as err:
151 raise ValueError('Failed to apply defaults to params: {}'
152 .format(err)) from None
153
Rupert Swarbrick200d8b42021-03-08 12:32:11 +0000154 init_block = RegBlock(regwidth, params)
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000155
156 interrupts = Signal.from_raw_list('interrupt_list for block {}'
157 .format(name),
158 rd.get('interrupt_list', []))
159 alerts = Alert.from_raw_list('alert_list for block {}'
160 .format(name),
161 rd.get('alert_list', []))
Michael Schaffner02bfd842021-12-23 09:07:02 -0800162 known_cms = {}
163 raw_cms = rd.get('countermeasures', [])
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000164
Michael Schaffnere8976ff2021-11-15 16:12:17 -0800165 countermeasures = CounterMeasure.from_raw_list(
166 'countermeasure list for block {}'
Michael Schaffner02bfd842021-12-23 09:07:02 -0800167 .format(name), raw_cms)
168
169 # Ensure that the countermeasures are unique
170 for x in countermeasures:
171 if str(x) in known_cms:
172 raise RuntimeError(f"Duplicate countermeasure {str(x)}")
173 else:
174 known_cms.update({str(x): 1})
Michael Schaffnere8976ff2021-11-15 16:12:17 -0800175
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000176 no_auto_intr = check_bool(rd.get('no_auto_intr_regs', not interrupts),
177 'no_auto_intr_regs field of ' + what)
178
179 no_auto_alert = check_bool(rd.get('no_auto_alert_regs', not alerts),
180 'no_auto_alert_regs field of ' + what)
181
182 if interrupts and not no_auto_intr:
183 if interrupts[-1].bits.msb >= regwidth:
184 raise ValueError("Interrupt list for {} is too wide: "
185 "msb is {}, which doesn't fit with a "
186 "regwidth of {}."
187 .format(what,
188 interrupts[-1].bits.msb, regwidth))
Rupert Swarbrick200d8b42021-03-08 12:32:11 +0000189 init_block.make_intr_regs(interrupts)
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000190
191 if alerts:
192 if not no_auto_alert:
193 if len(alerts) > regwidth:
194 raise ValueError("Interrupt list for {} is too wide: "
195 "{} alerts don't fit with a regwidth of {}."
196 .format(what, len(alerts), regwidth))
Rupert Swarbrick200d8b42021-03-08 12:32:11 +0000197 init_block.make_alert_regs(alerts)
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000198
199 # Generate a NumAlerts parameter
200 existing_param = params.get('NumAlerts')
201 if existing_param is not None:
202 if ((not isinstance(existing_param, LocalParam) or
203 existing_param.param_type != 'int' or
204 existing_param.value != str(len(alerts)))):
205 raise ValueError('Conflicting definition of NumAlerts '
206 'parameter.')
207 else:
208 params.add(LocalParam(name='NumAlerts',
209 desc='Number of alerts',
210 param_type='int',
211 value=str(len(alerts))))
212
213 scan = check_bool(rd.get('scan', False), 'scan field of ' + what)
214
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000215 r_inter_signals = check_list(rd.get('inter_signal_list', []),
216 'inter_signal_list field')
217 inter_signals = [
218 InterSignal.from_raw('entry {} of the inter_signal_list field'
219 .format(idx + 1),
220 entry)
221 for idx, entry in enumerate(r_inter_signals)
222 ]
223
Rupert Swarbrick6c831292021-02-25 17:08:53 +0000224 bus_interfaces = (BusInterfaces.
225 from_raw(rd['bus_interfaces'],
226 'bus_interfaces field of ' + where))
227 inter_signals += bus_interfaces.inter_signals()
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000228
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +0100229 clocking = Clocking.from_raw(rd['clocking'],
230 'clocking field of ' + what)
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000231
Timothy Chena49ceb62021-07-13 14:59:09 -0700232 reg_blocks = RegBlock.build_blocks(init_block, rd['registers'],
233 bus_interfaces,
234 clocking)
235
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000236 xputs = (
237 Signal.from_raw_list('available_inout_list for block ' + name,
238 rd.get('available_inout_list', [])),
239 Signal.from_raw_list('available_input_list for block ' + name,
240 rd.get('available_input_list', [])),
241 Signal.from_raw_list('available_output_list for block ' + name,
242 rd.get('available_output_list', []))
243 )
244 wakeups = Signal.from_raw_list('wakeup_list for block ' + name,
245 rd.get('wakeup_list', []))
246 rst_reqs = Signal.from_raw_list('reset_request_list for block ' + name,
247 rd.get('reset_request_list', []))
248
Rupert Swarbrickda5ed152021-06-08 14:14:40 +0100249 expose_reg_if = check_bool(rd.get('expose_reg_if', False),
250 'expose_reg_if field of ' + what)
251
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000252 scan_reset = check_bool(rd.get('scan_reset', False),
253 'scan_reset field of ' + what)
254
Timothy Chen21e6a4f2021-04-30 03:47:01 -0700255 scan_en = check_bool(rd.get('scan_en', False),
Rupert Swarbrickba0bd322021-06-07 16:30:20 +0100256 'scan_en field of ' + what)
Timothy Chen21e6a4f2021-04-30 03:47:01 -0700257
Rupert Swarbrick200d8b42021-03-08 12:32:11 +0000258 # Check that register blocks are in bijection with device interfaces
259 reg_block_names = reg_blocks.keys()
260 dev_if_names = [] # type: List[Optional[str]]
261 dev_if_names += bus_interfaces.named_devices
262 if bus_interfaces.has_unnamed_device:
263 dev_if_names.append(None)
264 if set(reg_block_names) != set(dev_if_names):
265 raise ValueError("IP block {} defines device interfaces, named {} "
266 "but its registers don't match (they are keyed "
267 "by {})."
268 .format(name, dev_if_names,
269 list(reg_block_names)))
270
271 return IpBlock(name, regwidth, params, reg_blocks,
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000272 interrupts, no_auto_intr, alerts, no_auto_alert,
Michael Schaffnerbd430362022-05-09 18:13:23 -0700273 scan, inter_signals, bus_interfaces, clocking, xputs,
Michael Schaffnere8976ff2021-11-15 16:12:17 -0800274 wakeups, rst_reqs, expose_reg_if, scan_reset, scan_en,
275 countermeasures)
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000276
277 @staticmethod
278 def from_text(txt: str,
279 param_defaults: List[Tuple[str, str]],
280 where: str) -> 'IpBlock':
281 '''Load an IpBlock from an hjson description in txt'''
282 return IpBlock.from_raw(param_defaults,
283 hjson.loads(txt, use_decimal=True),
284 where)
285
286 @staticmethod
287 def from_path(path: str,
288 param_defaults: List[Tuple[str, str]]) -> 'IpBlock':
289 '''Load an IpBlock from an hjson description in a file at path'''
Srikrishna Iyer18b8ed92021-04-26 15:54:44 -0700290 with open(path, 'r', encoding='utf-8') as handle:
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000291 return IpBlock.from_text(handle.read(), param_defaults,
292 'file at {!r}'.format(path))
293
294 def _asdict(self) -> Dict[str, object]:
Rupert Swarbrick200d8b42021-03-08 12:32:11 +0000295 ret = {
296 'name': self.name,
297 'regwidth': self.regwidth
298 }
299 if len(self.reg_blocks) == 1 and None in self.reg_blocks:
300 ret['registers'] = self.reg_blocks[None].as_dicts()
301 else:
302 ret['registers'] = {k: v.as_dicts()
303 for k, v in self.reg_blocks.items()}
304
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000305 ret['param_list'] = self.params.as_dicts()
306 ret['interrupt_list'] = self.interrupts
307 ret['no_auto_intr_regs'] = self.no_auto_intr
308 ret['alert_list'] = self.alerts
309 ret['no_auto_alert_regs'] = self.no_auto_alert
310 ret['scan'] = self.scan
311 ret['inter_signal_list'] = self.inter_signals
Rupert Swarbrick6c831292021-02-25 17:08:53 +0000312 ret['bus_interfaces'] = self.bus_interfaces.as_dicts()
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000313
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +0100314 ret['clocking'] = self.clocking.items
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000315
316 inouts, inputs, outputs = self.xputs
317 if inouts:
318 ret['available_inout_list'] = inouts
319 if inputs:
320 ret['available_input_list'] = inputs
321 if outputs:
322 ret['available_output_list'] = outputs
323
324 if self.wakeups:
325 ret['wakeup_list'] = self.wakeups
326 if self.reset_requests:
327 ret['reset_request_list'] = self.reset_requests
328
329 ret['scan_reset'] = self.scan_reset
Timothy Chen21e6a4f2021-04-30 03:47:01 -0700330 ret['scan_en'] = self.scan_en
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000331
332 return ret
Rupert Swarbrick200d8b42021-03-08 12:32:11 +0000333
334 def get_rnames(self) -> Set[str]:
335 ret = set() # type: Set[str]
336 for rb in self.reg_blocks.values():
337 ret = ret.union(set(rb.name_to_offset.keys()))
338 return ret
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700339
Rupert Swarbrick0f6eeaf2021-05-28 15:24:14 +0100340 def get_signals_as_list_of_dicts(self) -> List[Dict[str, object]]:
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700341 '''Look up and return signal by name'''
342 result = []
343 for iodir, xput in zip(('inout', 'input', 'output'), self.xputs):
344 for sig in xput:
345 result.append(sig.as_nwt_dict(iodir))
346 return result
347
Rupert Swarbrick0f6eeaf2021-05-28 15:24:14 +0100348 def get_signal_by_name_as_dict(self, name: str) -> Dict[str, object]:
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700349 '''Look up and return signal by name'''
350 sig_list = self.get_signals_as_list_of_dicts()
351 for sig in sig_list:
352 if sig['name'] == name:
353 return sig
354 else:
355 raise ValueError("Signal {} does not exist in IP block {}"
356 .format(name, self.name))
Timothy Chen7c3de6e2021-07-19 16:46:45 -0700357
358 def has_shadowed_reg(self) -> bool:
359 '''Return boolean indication whether reg block contains shadowed registers'''
360
361 for rb in self.reg_blocks.values():
362 if rb.has_shadowed_reg():
363 return True
364
365 # if we are here, then no one has has a shadowed register
366 return False
367
368 def get_primary_clock(self) -> ClockingItem:
369 '''Return primary clock of an block'''
370
371 return self.clocking.primary
Michael Schaffner9af30532022-01-13 20:25:55 -0800372
373 def check_cm_annotations(self,
374 rtl_names: Dict[str, List[Tuple[str, int]]],
375 where: str) -> None:
376 '''Check RTL annotations against countermeasure list of this block'''
377
378 what = '{} block at {}'.format(self.name, where)
379 CounterMeasure.check_annotation_list(what,
380 rtl_names,
381 self.countermeasures)