blob: 53413f462613b4e4f6ba641e75992635a749ad6b [file] [log] [blame]
lowRISC Contributors802543a2019-08-31 12:12:56 +01001# Copyright lowRISC contributors.
2# Licensed under the Apache License, Version 2.0, see LICENSE for details.
3# SPDX-License-Identifier: Apache-2.0
Michael Schaffner74c4ff22021-03-30 15:43:46 -07004import re
Eunchan Kim632c6f72019-09-30 11:11:51 -07005import logging as log
Eunchan Kim6a4b49e2020-02-18 10:33:39 -08006from collections import OrderedDict
Weicai Yang53b0d4d2020-11-30 15:28:33 -08007from enum import Enum
Michael Schaffner74c4ff22021-03-30 15:43:46 -07008from typing import Dict, List
lowRISC Contributors802543a2019-08-31 12:12:56 +01009
Timothy Chen0550d692020-04-20 17:19:35 -070010from reggen.validate import check_keys
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000011from reggen.ip_block import IpBlock
Timothy Chen80bd8aa2019-10-04 15:57:11 -070012
Eunchan Kim632c6f72019-09-30 11:11:51 -070013# For the reference
14# val_types = {
15# 'd': ["int", "integer (binary 0b, octal 0o, decimal, hex 0x)"],
16# 'x': ["xint", "x for undefined otherwise int"],
17# 'b': [
18# "bitrange", "bit number as decimal integer, \
19# or bit-range as decimal integers msb:lsb"
20# ],
21# 'l': ["list", "comma separated list enclosed in `[]`"],
22# 'ln': ["name list", 'comma separated list enclosed in `[]` of '\
23# 'one or more groups that have just name and dscr keys.'\
24# ' e.g. `{ name: "name", desc: "description"}`'],
25# 'lnw': ["name list+", 'name list that optionally contains a width'],
26# 'lp': ["parameter list", 'parameter list having default value optionally'],
27# 'g': ["group", "comma separated group of key:value enclosed in `{}`"],
28# 's': ["string", "string, typically short"],
29# 't': ["text", "string, may be multi-line enclosed in `'''` "\
30# "may use `**bold**`, `*italic*` or `!!Reg` markup"],
31# 'T': ["tuple", "tuple enclosed in ()"],
Philipp Wagner14a3fee2019-11-21 10:07:02 +000032# 'pi': ["python int", "Native Python type int (generated)"],
33# 'pb': ["python Bool", "Native Python type Bool (generated)"],
34# 'pl': ["python list", "Native Python type list (generated)"],
35# 'pe': ["python enum", "Native Python type enum (generated)"]
Eunchan Kim632c6f72019-09-30 11:11:51 -070036# }
37
38# Required/optional field in top hjson
39top_required = {
40 'name': ['s', 'Top name'],
41 'type': ['s', 'type of hjson. Shall be "top" always'],
Timothy Chen0550d692020-04-20 17:19:35 -070042 'clocks': ['g', 'group of clock properties'],
43 'resets': ['l', 'list of resets'],
Eunchan Kim632c6f72019-09-30 11:11:51 -070044 'module': ['l', 'list of modules to instantiate'],
Michael Schaffner74c4ff22021-03-30 15:43:46 -070045 'memory': ['l', 'list of memories. At least one memory '
46 'is needed to run the software'],
Eunchan Kim632c6f72019-09-30 11:11:51 -070047 'xbar': ['l', 'List of the xbar used in the top'],
Michael Schaffner7b0807d2020-10-27 19:54:52 -070048 'rnd_cnst_seed': ['int', "Seed for random netlist constant computation"],
Michael Schaffner74c4ff22021-03-30 15:43:46 -070049 'pinout': ['g', 'Pinout configuration'],
50 'targets': ['l', ' Target configurations'],
51 'pinmux': ['g', 'pinmux configuration'],
Eunchan Kim632c6f72019-09-30 11:11:51 -070052}
53
54top_optional = {
Michael Schaffner666dde12019-10-25 11:57:54 -070055 'alert_async': ['l', 'async alerts (generated)'],
Rupert Swarbrickddf01452021-02-02 09:28:01 +000056 'alert': ['lnw', 'alerts (generated)'],
57 'alert_module': [
58 'l',
59 'list of the modules that connects to alert_handler'
60 ],
Eunchan Kim1cf66af2020-04-30 11:31:53 -070061 'datawidth': ['pn', "default data width"],
Rupert Swarbrick26e304f2021-02-02 09:31:41 +000062 'exported_clks': ['g', 'clock signal routing rules'],
63 'host': ['g', 'list of host-only components in the system'],
Rupert Swarbrickddf01452021-02-02 09:28:01 +000064 'inter_module': ['g', 'define the signal connections between the modules'],
65 'interrupt': ['lnw', 'interrupts (generated)'],
66 'interrupt_module': ['l', 'list of the modules that connects to rv_plic'],
67 'num_cores': ['pn', "number of computing units"],
Rupert Swarbrick26e304f2021-02-02 09:31:41 +000068 'power': ['g', 'power domains supported by the design'],
Rupert Swarbrickef8cdad2021-02-18 14:41:38 +000069 'port': ['g', 'assign special attributes to specific ports']
Eunchan Kim632c6f72019-09-30 11:11:51 -070070}
71
72top_added = {}
73
74pinmux_required = {}
75pinmux_optional = {
Michael Schaffner74c4ff22021-03-30 15:43:46 -070076 'num_wkup_detect': [
77 'd', 'Number of wakeup detectors'
Eunchan Kim529134b2020-04-24 09:51:06 -070078 ],
Michael Schaffner74c4ff22021-03-30 15:43:46 -070079 'wkup_cnt_width': [
80 'd', 'Number of bits in wakeup detector counters'
81 ],
82 'signals': ['l', 'List of Dedicated IOs.'],
Eunchan Kim632c6f72019-09-30 11:11:51 -070083}
84pinmux_added = {
Michael Schaffner74c4ff22021-03-30 15:43:46 -070085 'ios': ['l', 'Full list of IO'],
Eunchan Kim632c6f72019-09-30 11:11:51 -070086}
87
Michael Schaffner74c4ff22021-03-30 15:43:46 -070088pinmux_sig_required = {
89 'instance': ['s', 'Module instance name'],
90 'connection': ['s', 'Specification of connection type, '
91 'can be direct, manual or muxed'],
Eunchan Kim632c6f72019-09-30 11:11:51 -070092}
Michael Schaffner74c4ff22021-03-30 15:43:46 -070093pinmux_sig_optional = {
94 'port': ['s', 'Port name of module'],
95 'pad': ['s', 'Pad name for direct connections'],
Michael Schaffnerc7dc5332021-04-09 16:30:26 -070096 'desc': ['s', 'Signal description'],
97 'attr': ['s', 'Pad type for generating the correct attribute CSR']
Michael Schaffner74c4ff22021-03-30 15:43:46 -070098}
99pinmux_sig_added = {}
100
101pinout_required = {
102 'banks': ['l', 'List of IO power banks'],
103 'pads': ['l', 'List of pads']
104}
105pinout_optional = {
106}
107pinout_added = {}
108
109pad_required = {
110 'name': ['l', 'Pad name'],
111 'type': ['s', 'Pad type'],
112 'bank': ['s', 'IO power bank for the pad'],
113 'connection': ['s', 'Specification of connection type, '
114 'can be direct, manual or muxed'],
115}
116pad_optional = {
117 'desc': ['s', 'Pad description'],
118}
119pad_added = {}
120
121target_required = {
122 'name': ['s', 'Name of target'],
123 'pinout': ['g', 'Target-specific pinout configuration'],
124 'pinmux': ['g', 'Target-specific pinmux configuration']
125}
126target_optional = {
127}
128target_added = {}
129
130target_pinmux_required = {
131 'special_signals': ['l', 'List of special signals and the pad they are mapped to.'],
132}
133target_pinmux_optional = {}
134target_pinmux_added = {}
135
136target_pinout_required = {
137 'remove_pads': ['l', 'List of pad names to remove and stub out'],
138 'add_pads': ['l', 'List of manual pads to add'],
139}
140target_pinout_optional = {}
141target_pinout_added = {}
142
143straps_required = {
144 'tap0': ['s', 'Name of tap0 pad'],
145 'tap1': ['s', 'Name of tap1 pad'],
146 'dft0': ['s', 'Name of dft0 pad'],
147 'dft1': ['s', 'Name of dft1 pad'],
148}
149straps_optional = {}
150straps_added = {}
151
152straps_required = {
153 'tap0': ['s', 'Name of tap0 pad'],
154 'tap1': ['s', 'Name of tap1 pad'],
155 'dft0': ['s', 'Name of dft0 pad'],
156 'dft1': ['s', 'Name of dft1 pad'],
157}
158straps_optional = {}
159straps_added = {}
160
161special_sig_required = {
162 'name': ['s', 'DIO name'],
163 'pad': ['s', 'Pad name'],
164}
165special_sig_optional = {
166 'desc': ['s', 'Description of signal connection'],
167}
168special_sig_added = {}
Eunchan Kim632c6f72019-09-30 11:11:51 -0700169
Timothy Chen1daf5822020-10-26 17:28:15 -0700170eflash_required = {
Timothy Chenfb8a7842021-08-20 00:23:47 -0700171 'type': ['s', 'string indicating type of memory'],
Timothy Chen1daf5822020-10-26 17:28:15 -0700172 'banks': ['d', 'number of flash banks'],
Rupert Swarbrick9a5b4e22021-02-02 09:38:09 +0000173 'pages_per_bank': ['d', 'number of data pages per flash bank'],
174 'program_resolution': ['d', 'maximum number of flash words allowed to program'],
Timothy Chen1daf5822020-10-26 17:28:15 -0700175}
176
177eflash_optional = {}
178
179eflash_added = {}
180
Michael Schaffner02e982f2021-07-09 17:40:34 -0700181module_required = {
182 'name': ['s', 'name of the instance'],
183 'type': ['s', 'comportable IP type'],
184 'clock_srcs': ['g', 'dict with clock sources'],
185 'clock_group': ['s', 'clock group'],
186 'reset_connections': ['g', 'dict with reset sources'],
187}
188
189module_optional = {
190 'domain': ['s', 'power domain, defaults to Domain0'],
191 'clock_reset_export': ['l', 'optional list with prefixes for exported '
192 'clocks and resets at the chip level'],
193 'attr': ['s', 'optional attribute indicating whether the IP is '
194 '"templated" or "reggen_only"'],
195 'base_addr': ['s', 'hex start address of the peripheral '
196 '(if the IP has only a single TL-UL interface)'],
197 'base_addrs': ['d', 'hex start addresses of the peripheral '
198 ' (if the IP has multiple TL-UL interfaces)'],
199 'memory': ['g', 'optional dict with memory region attributes'],
200 'param_decl': ['g', 'optional dict that allows to override instantiation parameters']
201}
202
203module_added = {
204 'clock_connections': ['g', 'generated clock connections']
205}
206
207memory_required = {
208 'label': ['s', 'region label for the linker script'],
Michael Schaffner0bedfa62021-07-27 15:54:15 -0700209 'swaccess': ['s', 'access attributes for the memory region (ro, rw)'],
210 'exec': ['pb', 'executable region indication for the linker script'],
211 'byte_write': ['pb', 'indicate whether the memory supports byte write accesses'],
Michael Schaffner02e982f2021-07-09 17:40:34 -0700212}
213
214memory_optional = {
Timothy Chenfb8a7842021-08-20 00:23:47 -0700215 'size': ['d', 'memory region size in bytes for the linker script, '
216 'xbar and RTL parameterisations'],
217 'config': ['d', 'Extra configuration for a particular memory'],
Michael Schaffner02e982f2021-07-09 17:40:34 -0700218}
219
220memory_added = {
221}
222
Eunchan Kim632c6f72019-09-30 11:11:51 -0700223
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700224# Supported PAD types.
225# Needs to coincide with enum definition in prim_pad_wrapper_pkg.sv
226class PadType(Enum):
227 INPUT_STD = 'InputStd'
228 BIDIR_STD = 'BidirStd'
229 BIDIR_TOL = 'BidirTol'
230 BIDIR_OD = 'BidirOd'
Michael Schaffner6c5f7a72021-04-09 11:51:10 -0700231 ANALOG_IN0 = 'AnalogIn0'
Arnon Sharline48c0012021-05-09 13:26:38 +0300232 ANALOG_IN1 = 'AnalogIn1'
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700233
234
235def is_valid_pad_type(obj):
236 try:
237 PadType(obj)
238 except ValueError:
239 return False
240 return True
241
242
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700243class TargetType(Enum):
244 MODULE = "module"
245 XBAR = "xbar"
246
247
248class Target:
249 """Target class informs the checkers if we are validating a module or xbar
250 """
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700251 def __init__(self, target_type):
Weicai Yang88ced022020-11-30 15:34:56 -0800252 # The type of this target
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700253 self.target_type = target_type
Weicai Yang88ced022020-11-30 15:34:56 -0800254 # The key to search against
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700255 if target_type == TargetType.MODULE:
256 self.key = "type"
257 else:
258 self.key = "name"
259
260
Timothy Chen1daf5822020-10-26 17:28:15 -0700261class Flash:
262 """Flash class contains information regarding parameter defaults.
263 For now, only expose banks / pages_per_bank for user configuration.
264 For now, also enforce power of 2 requiremnt.
265 """
266 max_banks = 4
267 max_pages_per_bank = 1024
268
Timothy Chen24798832021-08-20 16:12:12 -0700269 def __init__(self, mem, base_addr=0):
270 self.base_addr = int(base_addr, 16)
Timothy Chenfb8a7842021-08-20 00:23:47 -0700271 self.banks = mem.get('banks', 2)
272 self.pages_per_bank = mem.get('pages_per_bank', 8)
273 self.program_resolution = mem.get('program_resolution', 128)
Timothy Chene97e0b82020-12-11 17:18:43 -0800274 self.words_per_page = 256
Timothy Chen1daf5822020-10-26 17:28:15 -0700275 self.data_width = 64
276 self.metadata_width = 12
Timothy Chene97e0b82020-12-11 17:18:43 -0800277 self.info_types = 3
278 self.infos_per_bank = [10, 1, 2]
Timothy Chenfb8a7842021-08-20 00:23:47 -0700279 self.word_bytes = int(self.data_width / 8)
280 self.pgm_resolution_bytes = int(self.program_resolution * self.word_bytes)
281 self.check_values()
282
283 # populate size variable
284 self.bytes_per_page = self.word_bytes * self.words_per_page
285 self.bytes_per_bank = self.bytes_per_page * self.pages_per_bank
286 self.total_bytes = self.bytes_per_bank * self.banks
Timothy Chen24798832021-08-20 16:12:12 -0700287
288 size_int = int(self.total_bytes)
289 self.size = hex(size_int)
290 self.end_addr = self.base_addr + size_int
Timothy Chen1daf5822020-10-26 17:28:15 -0700291
292 def is_pow2(self, n):
293 return (n != 0) and (n & (n - 1) == 0)
294
295 def check_values(self):
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700296 pow2_check = (self.is_pow2(self.banks) and
297 self.is_pow2(self.pages_per_bank) and
298 self.is_pow2(self.program_resolution))
299 limit_check = ((self.banks <= Flash.max_banks) and
300 (self.pages_per_bank <= Flash.max_pages_per_bank))
Timothy Chen1daf5822020-10-26 17:28:15 -0700301
Timothy Chenfb8a7842021-08-20 00:23:47 -0700302 if not pow2_check:
303 raise ValueError(f'flash power of 2 check failed. A supplied parameter '
304 'is not power of 2')
Timothy Chen1daf5822020-10-26 17:28:15 -0700305
Timothy Chenfb8a7842021-08-20 00:23:47 -0700306 if not limit_check:
307 raise ValueError(f'flash number of banks and pages per bank too large')
Timothy Chen1daf5822020-10-26 17:28:15 -0700308
Timothy Chenfb8a7842021-08-20 00:23:47 -0700309 def _asdict(self):
310 return {
311 'banks': self.banks,
312 'pages_per_bank': self.pages_per_bank,
313 'program_resolution': self.pgm_resolution_bytes,
314 'bytes_per_page': self.bytes_per_page,
315 'bytes_per_bank': self.bytes_per_bank,
316 'size': self.size
317 }
Timothy Chen9b113e52020-11-06 18:48:05 -0800318
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700319# Check to see if each module/xbar defined in top.hjson exists as ip/xbar.hjson
320# Also check to make sure there are not multiple definitions of ip/xbar.hjson for each
321# top level definition
322# If it does, return a dictionary of instance names to index in ip/xbarobjs
323def check_target(top, objs, tgtobj):
324 error = 0
Eunchan Kim6a4b49e2020-02-18 10:33:39 -0800325 idxs = OrderedDict()
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700326
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000327 # Collect up counts of object names. We support entries of objs that are
328 # either dicts (for top-levels) or IpBlock objects.
329 name_indices = {}
330 for idx, obj in enumerate(objs):
331 if isinstance(obj, IpBlock):
332 name = obj.name.lower()
333 else:
334 name = obj['name'].lower()
335
336 log.info("%d Order is %s" % (idx, name))
337 name_indices.setdefault(name, []).append(idx)
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700338
339 tgt_type = tgtobj.target_type.value
340 inst_key = tgtobj.key
341
342 for cfg in top[tgt_type]:
343 cfg_name = cfg['name'].lower()
344 log.info("Checking target %s %s" % (tgt_type, cfg_name))
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000345
346 indices = name_indices.get(cfg[inst_key], [])
347 if not indices:
348 log.error("Could not find %s.hjson" % cfg_name)
349 error += 1
350 elif len(indices) > 1:
351 log.error("Duplicate %s.hjson" % cfg_name)
352 error += 1
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700353 else:
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000354 idxs[cfg_name] = indices[0]
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700355
356 log.info("Current state %s" % idxs)
357 return error, idxs
358
359
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700360def check_pad(top: Dict,
361 pad: Dict,
362 known_pad_names: Dict,
363 valid_connections: List[str],
364 prefix: str) -> int:
365 error = 0
366 error += check_keys(pad, pad_required, pad_optional,
367 pad_added, prefix)
368
369 # check name uniqueness
370 if pad['name'] in known_pad_names:
371 log.warning('Pad name {} is not unique'.format(pad['name']))
372 error += 1
373 known_pad_names[pad['name']] = 1
374
375 if not is_valid_pad_type(pad['type']):
376 log.warning('Unkown pad type {}'.format(pad['type']))
377 error += 1
378
379 if pad['bank'] not in top['pinout']['banks']:
380 log.warning('Unkown io power bank {}'.format(pad['bank']))
381 error += 1
382
383 if pad['connection'] not in valid_connections:
384 log.warning('Connection type {} of pad {} is invalid'
385 .format(pad['connection'], pad['name']))
386 error += 1
387
Eunchan Kim632c6f72019-09-30 11:11:51 -0700388 return error
389
390
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700391def check_pinout(top: Dict, prefix: str) -> int:
392 error = check_keys(top['pinout'], pinout_required, pinout_optional,
393 pinout_added, prefix + ' Pinout')
394
395 known_names = {}
396 for pad in top['pinout']['pads']:
397 error += check_keys(pad, pad_required, pad_optional,
398 pad_added, prefix + ' Pinout')
399
400 error += check_pad(top, pad, known_names,
401 ['direct', 'manual', 'muxed'],
402 prefix + ' Pad')
403
404 return error
405
406
407def check_pinmux(top: Dict, prefix: str) -> int:
408 error = check_keys(top['pinmux'], pinmux_required, pinmux_optional,
409 pinmux_added, prefix + ' Pinmux')
410
411 # This is used for the direct connection accounting below,
412 # where we tick off already connected direct pads.
413 known_direct_pads = {}
Michael Schaffnerc7dc5332021-04-09 16:30:26 -0700414 direct_pad_attr = {}
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700415 for pad in top['pinout']['pads']:
416 if pad['connection'] == 'direct':
417 known_direct_pads[pad['name']] = 1
Michael Schaffnerc7dc5332021-04-09 16:30:26 -0700418 direct_pad_attr[pad['name']] = pad['type']
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700419
420 # Note: the actual signal crosscheck is deferred until the merge stage,
421 # since we have no idea at this point which IOs comportable IPs expose.
422 for sig in top['pinmux']['signals']:
423 error += check_keys(sig, pinmux_sig_required, pinmux_sig_optional,
424 pinmux_sig_added, prefix + ' Pinmux signal')
425
426 if sig['connection'] not in ['direct', 'manual', 'muxed']:
427 log.warning('Invalid connection type {}'.format(sig['connection']))
428 error += 1
429
430 # The pad needs to refer to a valid pad name in the pinout that is of
431 # connection type "direct". We tick off all direct pads that have been
432 # referenced in order to make sure there are no double connections
433 # and unconnected direct pads.
434 padname = sig.setdefault('pad', '')
435 if padname != '':
436 if padname in known_direct_pads:
437 if known_direct_pads[padname] == 1:
438 known_direct_pads[padname] = 0
Michael Schaffnerc7dc5332021-04-09 16:30:26 -0700439 padattr = direct_pad_attr[padname]
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700440 else:
441 log.warning('Warning, direct pad {} is already connected'
442 .format(padname))
443 error += 1
444 else:
445 log.warning('Unknown direct pad {}'.format(padname))
446 error += 1
447
448 # Check port naming scheme.
449 port = sig.setdefault('port', '')
450 pattern = r'^[a-zA-Z0-9_]*(\[[0-9]*\]){0,1}'
451 matches = re.match(pattern, port)
452 if matches is None:
453 log.warning('Port name {} has wrong format'
454 .format(port))
455 error += 1
456
457 # Check that only direct connections have pad keys
458 if sig['connection'] == 'direct':
Michael Schaffnerc7dc5332021-04-09 16:30:26 -0700459 if sig.setdefault('attr', '') != '':
460 log.warning('Direct connection of instance {} port {} '
461 'must not have an associated pad attribute field'
462 .format(sig['instance'],
463 sig['port']))
464 error += 1
465 # Since the signal is directly connected, we can automatically infer
466 # the pad type needed to instantiate the correct attribute CSR WARL
467 # module inside the pinmux.
468 sig['attr'] = padattr
469
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700470 if padname == '':
471 log.warning('Instance {} port {} connection is of direct type '
472 'and therefore must have an associated pad name.'
473 .format(sig['instance'],
474 sig['port']))
475 error += 1
476 if port == '':
477 log.warning('Instance {} port {} connection is of direct type '
478 'and therefore must have an associated port name.'
479 .format(sig['instance'],
480 sig['port']))
481 error += 1
Michael Schaffnerc7dc5332021-04-09 16:30:26 -0700482 elif sig['connection'] == 'muxed':
483 # Muxed signals do not have a corresponding pad and attribute CSR,
484 # since they first go through the pinmux matrix.
485 if sig.setdefault('attr', '') != '':
486 log.warning('Muxed connection of instance {} port {} '
487 'must not have an associated pad attribute field'
488 .format(sig['instance'],
489 sig['port']))
490 error += 1
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700491 if padname != '':
Michael Schaffnerc7dc5332021-04-09 16:30:26 -0700492 log.warning('Muxed connection of instance {} port {} '
493 'must not have an associated pad'
494 .format(sig['instance'],
495 sig['port']))
496 error += 1
497 elif sig['connection'] == 'manual':
498 # This pad attr key is only allowed in the manual case,
499 # as there is no way to infer the pad type automatically.
500 sig.setdefault('attr', 'BidirStd')
501 if padname != '':
502 log.warning('Manual connection of instance {} port {} '
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700503 'must not have an associated pad'
504 .format(sig['instance'],
505 sig['port']))
506 error += 1
507
508 # At this point, all direct pads should have been ticked off.
509 for key, val in known_direct_pads.items():
510 if val == 1:
511 log.warning('Direct pad {} has not been connected'
512 .format(key))
513 error += 1
514
515 return error
516
517
518def check_implementation_targets(top: Dict, prefix: str) -> int:
519 error = 0
520 known_names = {}
521 for target in top['targets']:
522 error += check_keys(target, target_required, target_optional,
523 target_added, prefix + ' Targets')
524
525 # check name uniqueness
526 if target['name'] in known_names:
527 log.warning('Target name {} is not unique'.format(target['name']))
528 error += 1
529 known_names[target['name']] = 1
530
531 error += check_keys(target['pinmux'], target_pinmux_required, target_pinmux_optional,
532 target_pinmux_added, prefix + ' Target pinmux')
533
534 error += check_keys(target['pinout'], target_pinout_required, target_pinout_optional,
535 target_pinout_added, prefix + ' Target pinout')
536
537 # Check special pad signals
538 known_entry_names = {}
539 for entry in target['pinmux']['special_signals']:
540 error += check_keys(entry, special_sig_required, special_sig_optional,
541 special_sig_added, prefix + ' Special signal')
542
543 # check name uniqueness
544 if entry['name'] in known_entry_names:
545 log.warning('Special pad name {} is not unique'.format(entry['name']))
546 error += 1
547 known_entry_names[entry['name']] = 1
548
549 # The pad key needs to refer to a valid pad name.
550 is_muxed = False
551 for pad in top['pinout']['pads']:
552 if entry['pad'] == pad['name']:
553 is_muxed = pad['connection'] == 'muxed'
554 break
555 else:
556 log.warning('Unknown pad {}'.format(entry['pad']))
557 error += 1
558
559 if not is_muxed:
560 # If this is not a muxed pad, we need to make sure this refers to
561 # DIO that is NOT a manual pad.
562 for sig in top['pinmux']['signals']:
563 if entry['pad'] == sig['pad']:
564 break
565 else:
566 log.warning('Special pad {} cannot refer to a manual pad'.format(entry['pad']))
567 error += 1
568
569 # Check pads to remove and stub out
570 for entry in target['pinout']['remove_pads']:
571 # The pad key needs to refer to a valid pad name.
572 for pad in top['pinout']['pads']:
573 if entry == pad['name']:
574 break
575 else:
576 log.warning('Unknown pad {}'.format(entry))
577 error += 1
578
579 # Check pads to add
580 known_pad_names = {}
581 for pad in top['pinout']['pads']:
582 known_pad_names.update({pad['name']: 1})
583
584 for pad in target['pinout']['add_pads']:
585 error += check_pad(top, pad, known_pad_names, ['manual'],
586 prefix + ' Additional Pad')
587
588 return error
Eunchan Kim632c6f72019-09-30 11:11:51 -0700589
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700590
591def check_clocks_resets(top, ipobjs, ip_idxs, xbarobjs, xbar_idxs):
Timothy Chen33b3b9d2020-05-08 10:14:17 -0700592
Timothy Chenb63f3b82020-06-30 17:10:57 -0700593 error = 0
594
Timothy Chen92b526e2021-02-01 21:23:42 -0800595 # there should only be one each of pwrmgr/clkmgr/rstmgr
596 pwrmgrs = [m for m in top['module'] if m['type'] == 'pwrmgr']
597 clkmgrs = [m for m in top['module'] if m['type'] == 'clkmgr']
598 rstmgrs = [m for m in top['module'] if m['type'] == 'rstmgr']
599
600 if len(pwrmgrs) == 1 * len(clkmgrs) == 1 * len(rstmgrs) != 1:
601 log.error("Incorrect number of pwrmgr/clkmgr/rstmgr")
602 error += 1
603
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700604 # all defined clock/reset nets
Timothy Chenc6233932020-08-19 15:34:07 -0700605 reset_nets = [reset['name'] for reset in top['resets']['nodes']]
Rupert Swarbrick127b1092021-07-16 17:10:39 +0100606 clock_srcs = list(top['clocks'].all_srcs.keys())
Eunchan Kim632c6f72019-09-30 11:11:51 -0700607
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700608 # Check clock/reset port connection for all IPs
Timothy Chen3193b002019-10-04 16:56:05 -0700609 for ipcfg in top['module']:
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700610 ipcfg_name = ipcfg['name'].lower()
611 log.info("Checking clock/resets for %s" % ipcfg_name)
612 error += validate_reset(ipcfg, ipobjs[ip_idxs[ipcfg_name]], reset_nets)
Timothy Chen0550d692020-04-20 17:19:35 -0700613 error += validate_clock(ipcfg, ipobjs[ip_idxs[ipcfg_name]], clock_srcs)
Timothy Chen3193b002019-10-04 16:56:05 -0700614
615 if error:
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700616 log.error("module clock/reset checking failed")
Timothy Chen3193b002019-10-04 16:56:05 -0700617 break
618
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700619 # Check clock/reset port connection for all xbars
Timothy Chen3193b002019-10-04 16:56:05 -0700620 for xbarcfg in top['xbar']:
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700621 xbarcfg_name = xbarcfg['name'].lower()
622 log.info("Checking clock/resets for xbar %s" % xbarcfg_name)
623 error += validate_reset(xbarcfg, xbarobjs[xbar_idxs[xbarcfg_name]],
624 reset_nets, "xbar")
625 error += validate_clock(xbarcfg, xbarobjs[xbar_idxs[xbarcfg_name]],
Timothy Chen0550d692020-04-20 17:19:35 -0700626 clock_srcs, "xbar")
Timothy Chen3193b002019-10-04 16:56:05 -0700627
628 if error:
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700629 log.error("xbar clock/reset checking failed")
Timothy Chen3193b002019-10-04 16:56:05 -0700630 break
631
632 return error
633
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700634
Timothy Chen3193b002019-10-04 16:56:05 -0700635# Checks the following
636# For each defined reset connection in top*.hjson, there exists a defined port at the destination
637# and defined reset net
638# There are the same number of defined connections as there are ports
639def validate_reset(top, inst, reset_nets, prefix=""):
640 # Gather inst port list
641 error = 0
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000642
643 # Handle either an IpBlock (generated by reggen) or an OrderedDict
644 # (generated by topgen for a crossbar)
645 if isinstance(inst, IpBlock):
646 name = inst.name
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +0100647 reset_signals = inst.clocking.reset_signals()
Timothy Chen3193b002019-10-04 16:56:05 -0700648 else:
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000649 name = inst['name']
650 reset_signals = ([inst.get('reset_primary', 'rst_ni')] +
651 inst.get('other_reset_list', []))
Timothy Chen3193b002019-10-04 16:56:05 -0700652
Timothy Chen3193b002019-10-04 16:56:05 -0700653 log.info("%s %s resets are %s" %
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000654 (prefix, name, reset_signals))
Timothy Chen3193b002019-10-04 16:56:05 -0700655
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000656 if len(top['reset_connections']) != len(reset_signals):
Timothy Chen3193b002019-10-04 16:56:05 -0700657 error += 1
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700658 log.error("%s %s mismatched number of reset ports and nets" %
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000659 (prefix, name))
Timothy Chen3193b002019-10-04 16:56:05 -0700660
661 missing_port = [
662 port for port in top['reset_connections'].keys()
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000663 if port not in reset_signals
Timothy Chen3193b002019-10-04 16:56:05 -0700664 ]
665
666 if missing_port:
667 error += 1
668 log.error("%s %s Following reset ports do not exist:" %
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000669 (prefix, name))
Timothy Chen3193b002019-10-04 16:56:05 -0700670 [log.error("%s" % port) for port in missing_port]
671
672 missing_net = [
673 net for port, net in top['reset_connections'].items()
674 if net not in reset_nets
675 ]
676
677 if missing_net:
678 error += 1
679 log.error("%s %s Following reset nets do not exist:" %
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000680 (prefix, name))
Timothy Chen3193b002019-10-04 16:56:05 -0700681 [log.error("%s" % net) for net in missing_net]
682
683 return error
684
685
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700686# Checks the following
Timothy Chen0550d692020-04-20 17:19:35 -0700687# For each defined clock_src in top*.hjson, there exists a defined port at the destination
688# and defined clock source
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700689# There are the same number of defined connections as there are ports
Timothy Chen0550d692020-04-20 17:19:35 -0700690def validate_clock(top, inst, clock_srcs, prefix=""):
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700691 # Gather inst port list
692 error = 0
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000693
694 # Handle either an IpBlock (generated by reggen) or an OrderedDict
695 # (generated by topgen for a crossbar)
696 if isinstance(inst, IpBlock):
697 name = inst.name
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +0100698 clock_signals = inst.clocking.clock_signals()
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700699 else:
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000700 name = inst['name']
701 clock_signals = ([inst.get('clock_primary', 'rst_ni')] +
702 inst.get('other_clock_list', []))
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700703
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000704 if len(top['clock_srcs']) != len(clock_signals):
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700705 error += 1
706 log.error("%s %s mismatched number of clock ports and nets" %
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000707 (prefix, name))
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700708
709 missing_port = [
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000710 port for port in top['clock_srcs'].keys()
711 if port not in clock_signals
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700712 ]
713
714 if missing_port:
715 error += 1
716 log.error("%s %s Following clock ports do not exist:" %
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000717 (prefix, name))
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700718 [log.error("%s" % port) for port in missing_port]
719
720 missing_net = [
Eunchan Kim529134b2020-04-24 09:51:06 -0700721 net for port, net in top['clock_srcs'].items() if net not in clock_srcs
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700722 ]
723
724 if missing_net:
725 error += 1
726 log.error("%s %s Following clock nets do not exist:" %
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +0000727 (prefix, name))
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700728 [log.error("%s" % net) for net in missing_net]
729
730 return error
731
732
Timothy Chen1daf5822020-10-26 17:28:15 -0700733def check_flash(top):
Timothy Chen1daf5822020-10-26 17:28:15 -0700734
735 for mem in top['memory']:
736 if mem['type'] == "eflash":
Timothy Chen1daf5822020-10-26 17:28:15 -0700737
Timothy Chenfb8a7842021-08-20 00:23:47 -0700738 raise ValueError(f'top level flash memory definition not supported. Please use '
739 'the flash embedded inside flash_ctrl instead. If there is a '
740 'need for top level flash memory, please file an issue.')
Timothy Chen1daf5822020-10-26 17:28:15 -0700741
742
Timothy Chen7f8cc8e2020-11-11 13:15:57 -0800743def check_power_domains(top):
744 error = 0
745
746 # check that the default domain is valid
747 if top['power']['default'] not in top['power']['domains']:
748 error += 1
749 return error
750
Timothy Chen7f8cc8e2020-11-11 13:15:57 -0800751 # Check that each module, xbar, memory has a power domain defined.
752 # If not, give it a default.
753 # If there is one defined, check that it is a valid definition
754 for end_point in top['module'] + top['memory'] + top['xbar']:
755 if 'domain' not in end_point:
Timothy Chen7f8cc8e2020-11-11 13:15:57 -0800756 end_point['domain'] = top['power']['default']
Rupert Swarbrick1b4bfc52021-02-02 11:32:29 +0000757
758 if end_point['domain'] not in top['power']['domains']:
759 log.error("{} defined invalid domain {}"
760 .format(end_point['name'],
761 end_point['domain']))
Timothy Chen7f8cc8e2020-11-11 13:15:57 -0800762 error += 1
763 return error
764
765 # arrived without incident, return
766 return error
767
768
Michael Schaffner02e982f2021-07-09 17:40:34 -0700769def check_modules(top, prefix):
770 error = 0
771 for m in top['module']:
772 modname = m.get("name", "unnamed module")
773 error += check_keys(m, module_required, module_optional, module_added,
774 prefix + " " + modname)
775
776 # these fields are mutually exclusive
777 if 'base_addr' in m and 'base_addrs' in m:
778 log.error("{} {} a module cannot define both the 'base_addr' "
779 "and 'base_addrs' keys at the same time"
780 .format(prefix, modname))
781 error += 1
782
783 if 'base_addrs' in m and 'memory' in m:
784 for intf, value in m['memory'].items():
785 error += check_keys(value, memory_required,
786 memory_optional, memory_added,
787 prefix + " " + modname + " " + intf)
Timothy Chenfb8a7842021-08-20 00:23:47 -0700788
789 # if size is not declared, there must be extra config to determine it
790 if 'size' not in value and 'config' not in value:
791 raise ValueError(f'{m["name"]} memory declaration has neither size '
792 'nor extra configuration. Unable to determine '
793 'memory size')
794
795 if 'size' not in value:
796 mem_type = value['config'].get('type', "")
797
798 if mem_type == "flash":
799 check_keys(value['config'], eflash_required, eflash_optional,
800 eflash_added, "Eflash")
Timothy Chen24798832021-08-20 16:12:12 -0700801 flash = Flash(value['config'], m['base_addrs'][intf])
Timothy Chenfb8a7842021-08-20 00:23:47 -0700802 value['size'] = flash.size
803 value['config'] = flash
804 else:
805 raise ValueError(f'{m["name"]} memory config declaration does not have '
806 'a valid type')
807
Michael Schaffner02e982f2021-07-09 17:40:34 -0700808 # make sure the memory regions correspond to the TL-UL interfaces
809 if intf not in m['base_addrs']:
810 log.error("{} {} memory region {} does not "
811 "correspond to any of the defined "
812 "TL-UL interfaces".format(prefix, modname, intf))
813 error += 1
814 # make sure the linker region access attribute is valid
815 attr = value.get('swaccess', 'unknown attribute')
Michael Schaffner0bedfa62021-07-27 15:54:15 -0700816 if attr not in ['ro', 'rw']:
Michael Schaffner02e982f2021-07-09 17:40:34 -0700817 log.error('{} {} swaccess attribute {} of memory region {} '
818 'is not valid'.format(prefix, modname, attr, intf))
819 error += 1
820 return error
821
822
Timothy Chen3193b002019-10-04 16:56:05 -0700823def validate_top(top, ipobjs, xbarobjs):
lowRISC Contributors802543a2019-08-31 12:12:56 +0100824 # return as it is for now
Eunchan Kim632c6f72019-09-30 11:11:51 -0700825 error = check_keys(top, top_required, top_optional, top_added, "top")
826
827 if error != 0:
828 log.error("Top HJSON has top level errors. Aborting")
829 return top, error
830
831 component = top['name']
832
Michael Schaffner02e982f2021-07-09 17:40:34 -0700833 # Check module instantiations
834 error += check_modules(top, component)
835
836 # MODULE check
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700837 err, ip_idxs = check_target(top, ipobjs, Target(TargetType.MODULE))
838 error += err
839
Eunchan Kim529134b2020-04-24 09:51:06 -0700840 # XBAR check
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700841 err, xbar_idxs = check_target(top, xbarobjs, Target(TargetType.XBAR))
842 error += err
Eunchan Kim632c6f72019-09-30 11:11:51 -0700843
Eunchan Kim529134b2020-04-24 09:51:06 -0700844 # MEMORY check
Timothy Chenfb8a7842021-08-20 00:23:47 -0700845 check_flash(top)
Eunchan Kim632c6f72019-09-30 11:11:51 -0700846
Timothy Chen7f8cc8e2020-11-11 13:15:57 -0800847 # Power domain check
848 error += check_power_domains(top)
849
Eunchan Kim529134b2020-04-24 09:51:06 -0700850 # Clock / Reset check
Timothy Chen80bd8aa2019-10-04 15:57:11 -0700851 error += check_clocks_resets(top, ipobjs, ip_idxs, xbarobjs, xbar_idxs)
852
Eunchan Kim529134b2020-04-24 09:51:06 -0700853 # RV_PLIC check
Eunchan Kim632c6f72019-09-30 11:11:51 -0700854
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700855 # Pinout, pinmux and target checks
856 # Note that these checks must happen in this order, as
857 # the pinmux and target configs depend on the pinout.
858 error += check_pinout(top, component)
Eunchan Kim632c6f72019-09-30 11:11:51 -0700859 error += check_pinmux(top, component)
Michael Schaffner74c4ff22021-03-30 15:43:46 -0700860 error += check_implementation_targets(top, component)
Eunchan Kim632c6f72019-09-30 11:11:51 -0700861
Eunchan Kim632c6f72019-09-30 11:11:51 -0700862 return top, error