-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtool_rsn_util.py
More file actions
335 lines (286 loc) · 15.5 KB
/
Copy pathtool_rsn_util.py
File metadata and controls
335 lines (286 loc) · 15.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
import argparse
import json
from py_verilog_parser import parse_verilog
from py_verilog_parser import generate_verilog
from py_verilog_parser.verilog import *
from termcolor import colored
from steps_rsn import *
TRIM_NAME_LENGTH = 40
STRATEGY_STRUCTURES = {
'skip': { 'boundary': None, 'internal': None, 'sibs': None },
'route-single': { 'boundary': None, 'internal': None, 'sibs': None },
'route-sib': { 'boundary': None, 'internal': None, 'sibs': 'yes' },
'boundary-scan-single': { 'boundary': 'combined', 'internal': None, 'sibs': None },
'boundary-scan-sib': { 'boundary': 'combined', 'internal': None, 'sibs': 'yes' },
'sboundary-scan-single': { 'boundary': 'split', 'internal': None, 'sibs': None },
'sboundary-scan-sib': { 'boundary': 'split', 'internal': None, 'sibs': 'yes' },
'internal-scan-single': { 'boundary': None, 'internal': 'yes', 'sibs': None },
'internal-scan-sib': { 'boundary': None, 'internal': 'yes', 'sibs': 'yes' },
'full-scan-single': { 'boundary': 'combined', 'internal': 'yes', 'sibs': None },
'full-scan-sib': { 'boundary': 'combined', 'internal': 'yes', 'sibs': 'yes' },
'sfull-scan-single': { 'boundary': 'split', 'internal': 'yes', 'sibs': None },
'sfull-scan-sib': { 'boundary': 'split', 'internal': 'yes', 'sibs': 'yes' },
}
STRATEGY_VISUALIZATION = {
'skip': { 'color': 'red', 'marker': '[ ]' },
'route-single': { 'color': 'yellow', 'marker': '[R ]' },
'route-sib': { 'color': 'yellow', 'marker': '[RS ]' },
'boundary-scan-single': { 'color': 'yellow', 'marker': '[R B ]' },
'boundary-scan-sib': { 'color': 'yellow', 'marker': '[RSB ]' },
'sboundary-scan-single': { 'color': 'yellow', 'marker': '[R b ]' },
'sboundary-scan-sib': { 'color': 'yellow', 'marker': '[RSb ]' },
'internal-scan-single': { 'color': 'yellow', 'marker': '[R I]' },
'internal-scan-sib': { 'color': 'yellow', 'marker': '[RS I]' },
'full-scan-single': { 'color': 'green', 'marker': '[R BI]' },
'full-scan-sib': { 'color': 'green', 'marker': '[RSBI]' },
'sfull-scan-single': { 'color': 'green', 'marker': '[R bI]' },
'sfull-scan-sib': { 'color': 'green', 'marker': '[RSbI]' },
}
def _visualize_module(instance: ModuleInstance, configuration: Dict, indent: int=0, lines: int=0):
def _trim_name(module: str) -> str:
return module if len(module) <= TRIM_NAME_LENGTH \
else module[0:TRIM_NAME_LENGTH//2] + '...' + module[-TRIM_NAME_LENGTH//2:]
name = instance.module_name.name
inst = instance.instance_name.name
module_config = configuration.get(name, None)
if module_config is None:
return
sub_modules = [module.module_name.name for module in module.module_instances]
gates = sum([1 for module in sub_modules if module in library.get(module, {}).get('combinational', False)])
flipflops = sum([1 for module in sub_modules if module in library.get(module, {}).get('sequential', False)])
transistors = sum([library[module]['transistors'] for module in sub_modules if module in library])
indentation = ''
for index in range(indent):
if index + 1 == indent:
indentation += '+-'
elif lines & (1 << index):
indentation += '| '
else:
indentation += ' '
visuals = STRATEGY_VISUALIZATION.get(module_config['strategy'], None)
marker = colored(visuals['marker'], visuals['color'], attrs=['bold'])
print(f'{indentation}{marker} {_trim_name(inst)} [{gates} G {flipflops} FF {transistors} T] ({_trim_name(name)})')
for index, instance in enumerate(module.module_instances):
inst_lines = lines if index + 1 == len(module.module_instances) else (lines | (1 << indent))
_visualize_module(instance, configuration, indent + 1, inst_lines)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='RSN generation utility')
parser.add_argument('--input', required=True, dest='input_file', action='store', help='The Verilog input file')
parser.add_argument('--output', dest='output_file', action='store', help='The Verilog output file')
parser.add_argument('--target', required=True, dest='target', action='store', help='Target for this script (create-config, visualize-config, generate-rsn)')
parser.add_argument('--cell-library', dest='library', action='store', required=True, help='Metadata for cell library')
parser.add_argument('--cell-transistors', dest='transistors', action='store', required=True, help='Metadata for cell transistor counts')
parser.add_argument('--configuration', dest='configuration', action='store', required=True, help='RSN configuration file')
parser.add_argument('--top-module', required=True, action='store', dest='top_module', help='SoC top level Verilog module')
parser.add_argument('--exclude-port', action='append', dest='excluded_ports', default=[], help='Port to exclude from boundary scan creation e.g. clock, reset port')
options = parser.parse_args()
with open(options.library, 'rt', encoding='utf-8') as stream:
library = json.load(stream)
# Annotate cells with transistor counts
with open(options.transistors, 'rt', encoding='utf-8') as stream:
transistors = json.load(stream)
for cell_name, transistor_count in transistors.items():
library[cell_name]['transistors'] = transistor_count
print(f'Info: Reading {options.input_file}')
with open(options.input_file, 'rt', encoding='utf-8') as input:
ast = parse_verilog(input.read())
if options.target == 'create-config':
configuration = {
'implementation': {
'host-interface': {
'name': 'rsn_host_port',
'rsn-ports': RsnHostInterface('placeholder', None).get_rsn_ports()
},
'client-interface': {
'name': 'rsn_client_port',
'rsn-ports': RsnClientInterface('placeholder', None).get_rsn_ports()
},
'data-register': {
'name': 'rsn_data_register',
'rsn-ports': RsnDataRegister('placeholder', None).get_rsn_ports()
},
'boundary-scan': {
'input': {
'name': 'rsn_boundary_scan',
'rsn-ports': RsnBoundaryCell('placeholder', 'placeholder', 'none', 'input').get_rsn_ports()
},
'output': {
'name': 'rsn_boundary_scan',
'rsn-ports': RsnBoundaryCell('placeholder', 'placeholder', 'none', 'output').get_rsn_ports()
}
},
'internal-scan': dict([
(name, {
'name': 'rsn_internal_scan',
'rsn-ports': RsnInternalScanCell('placeholder', 'placeholder', 'none', 'full').get_rsn_ports(),
'ff-ports': RsnInternalScanCell('placeholder', 'placeholder', 'none', 'full').get_target_ports(),
}) for name, cell in library.items() if cell.get('flipflop', False)
]),
'sib': {
'name': 'rsn_sib',
'rsn-ports': RsnSib('placeholder', 'placeholder').get_rsn_ports()
}
},
'modules': {}
}
for module in ast.modules:
is_top = (module.module_name.name == options.top_module)
strategy = 'sfull-scan-sib' if is_top else 'internal-scan-sib'
configuration['modules'][module.module_name.name] = {
'name': module.module_name.name,
'excluded-ports': options.excluded_ports,
'strategy': strategy,
'is-top-level': is_top
}
print(f'Info: Creating configuration {options.configuration}')
with open(options.configuration, 'wt', encoding='utf-8') as output:
json.dump(configuration, output, indent=4)
elif options.target == 'visualize-config':
with open(options.configuration, 'rt', encoding='utf-8') as input:
configuration = json.load(input)
print(f'Info: Visualization configuration')
_visualize_module(ModuleInstance(options.top_module, '', {}), configuration.get('modules', {}))
elif options.target == 'apply-config':
with open(options.configuration, 'rt', encoding='utf-8') as input:
configuration = json.load(input)
module_configurations = configuration.get('modules', {})
implementations = configuration.get('implementation', {})
data_register_impl = implementations.get('data-register', None)
host_interface_impl = implementations.get('host-interface', None)
client_interface_impl = implementations.get('client-interface', None)
boundary_scan_impl = implementations.get('boundary-scan', None)
internal_scan_impl = implementations.get('internal-scan', None)
sib_impl = implementations.get('sib', None)
rsn = Rsn()
for module in ast.modules:
strategy_name = module_configurations.get(module.module_name.name, {}).get('strategy', None)
if strategy_name is None or strategy_name == 'skip':
continue
strategy = STRATEGY_STRUCTURES.get(strategy_name, None)
print(f'Info: Applying {strategy_name} strategy to module {module.module_name.name}')
if strategy is None:
raise ValueError(f'Strategy {strategy_name} not found')
rsn_module = RsnModule(module.module_name.name)
rsn.add_module(rsn_module)
# Host interface
if host_interface_impl is None:
raise ValueError('No implementations for host-interface module specified')
impl = host_interface_impl.get('name', 'rsn_host_port')
host_port = rsn_module.add_host_interface('rsn_interface', impl, module)
host_port.update_rsn_port_names(host_interface_impl.get('rsn-ports', {}))
# Boundary Scan
boundary_scan_conf = strategy['boundary']
if boundary_scan_conf is not None:
if boundary_scan_impl is None:
raise ValueError('No implementations for boundary-scan module specified')
def _create_boundary(targets: List[IdentifierIndexed], direction: str, start_index: int = 1):
result = []
for target, index in zip(targets, range(start_index, start_index + len(targets))):
if direction not in boundary_scan_impl:
raise ValueError(f'No implementation for {direction} boundary-scan module specified')
impl = boundary_scan_impl[direction].get('name', 'rsn_boundary_scan')
cell = rsn_module.add_boundary_scan_cell(f'boundary_ff_{index}', impl, target, direction)
cell.update_rsn_port_names(boundary_scan_impl[direction].get('rsn-ports', {}))
result += [cell]
return result
boundary_inputs = []
boundary_outputs = []
for port_identifier in module.port_list:
port_name = port_identifier if isinstance(port_identifier, Identifier) else port_identifier[0]
port = module.get_port(port_name.name)
port_indices = ([0] if port.range is None else port.range.to_indices())
if isinstance(port, InputDeclaration) or isinstance(port, InOutDeclaration):
boundary_inputs += [IdentifierIndexed(name.name, Number(None, None, f'{index}')) \
for index in port_indices for name in port.net_names]
if isinstance(port, OutputDeclaration) or isinstance(port, InOutDeclaration):
boundary_outputs += [IdentifierIndexed(name.name, Number(None, None, f'{index}')) \
for index in port_indices for name in port.net_names]
if boundary_inputs and 'input' not in boundary_scan_impl:
raise ValueError('No implementations for \'input\' boundary-scan module specified')
if boundary_outputs and 'output' not in boundary_scan_impl:
raise ValueError('No implementations for \'output\' boundary-scan module specified')
input_scan = _create_boundary(boundary_inputs, 'input', start_index=1)
output_scan = _create_boundary(boundary_outputs, 'output', start_index=len(input_scan))
# Internal Scan
internal_scan_conf = strategy['internal']
if internal_scan_conf is not None:
if internal_scan_impl is None:
raise ValueError('No implementation for internal-scan module specified')
def _create_internal(targets: List[ModuleInstance], start_index: int = 1):
result = []
for target, index in zip(targets, range(start_index, start_index + len(targets))):
if target.module_name.name not in internal_scan_impl:
raise ValueError(f'No implementation for {target.module_name.name} internal-scan module specified')
impl = internal_scan_impl[target.module_name.name].get('name', 'rsn_internal_scan')
cell = rsn_module.add_internal_scan_cell(f'internal_ff_{index}', impl, target)
cell.update_rsn_port_names(internal_scan_impl[target.module_name.name].get('rsn-ports', {}))
cell.update_target_port_names(internal_scan_impl[target.module_name.name].get('ff-ports', {}))
result += [cell]
return result
flipflops = [instance for instance in module.module_instances if library.get(instance.module_name.name, {}).get('flipflop', False)]
internal_scan = _create_internal(flipflops, start_index=1)
# Submodules
submodules = []
for instance in module.module_instances:
strategy_name = module_configurations.get(instance.module_name.name, {}).get('strategy', None)
if strategy_name is None or strategy_name == 'skip':
continue
submodules += [instance]
def _create_submodule(targets: List[ModuleInstance], start_index: int = 1):
result = []
for target, index in zip(targets, range(start_index, start_index + len(targets))):
if client_interface_impl is None:
raise ValueError('No implementations for client-interface module specified')
impl = client_interface_impl.get('name', 'rsn_client_port')
client_port = rsn_module.add_client_interface(f'submodule_{index}', impl, target)
client_port.update_rsn_port_names(client_interface_impl.get('rsn-ports', {}))
result += [client_port]
return result
submodule_scans = _create_submodule(submodules)
# Plan chain and SIBs according to the module's strategy.
# The RSN elements are stored as list. SIBs in the chain are stored
# as tuples with the second element being the connected chain.
rsn_elements = []
sib_conf = strategy['sibs']
if sib_conf == None:
if boundary_scan_impl == 'combined':
rsn_elements += input_scan + output_scan
elif boundary_scan_conf == 'split':
rsn_elements += [('sib_boundary_input', input_scan)] if input_scan else []
rsn_elements += [('sib_boundary_output', output_scan)] if output_scan else []
rsn_elements += internal_scan
rsn_elements += submodule_scans
elif sib_conf == 'yes':
if boundary_scan_impl == 'combined':
rsn_elements += [('sib_boundary_combined', input_scan + output_scan)] if input_scan + output_scan else []
elif boundary_scan_conf == 'split':
rsn_elements += [('sib_boundary_input', input_scan)] if input_scan else []
rsn_elements += [('sib_boundary_output', output_scan)] if output_scan else []
rsn_elements += [('sib_internal', internal_scan)] if internal_scan else []
rsn_elements += [('sib_submodules', submodule_scans)] if submodule_scans else []
# Connect the RSN and form the planned chains.
def _build_scan_chain(host: Union[RsnHostInterface, RsnSib], chain: List[RsnElement]) -> None:
chain_end = host
for index, element in enumerate(chain):
interface = ('host' if index == 0 else 'client')
# A tuple signals a SIB with the first element being the name
# and the second element being the scan chain connected to the SIB.
if isinstance(element, tuple):
if sib_impl is None:
raise ValueError(f'No implementation for sib module specified')
sib_name, sib_chain = element
impl = sib_impl.get('name', 'rsn_sib')
sib = rsn_module.add_sib(sib_name, impl)
sib.update_rsn_port_names(sib_impl.get('rsn-ports', {}))
_build_scan_chain(sib, sib_chain)
element = sib
rsn_module.link_rsn_elements(chain_end, element, interface)
chain_end = element
_build_scan_chain(host_port, rsn_elements)
print(f'Info: Applying RSN')
rsn.apply_rsn(ast.modules)
print(f'Info: Writing {options.output_file}')
with open(options.output_file, 'wt', encoding='utf-8') as output:
output.write(generate_verilog(ast, collapsed_modules=list(library.keys())))
else:
print(f'Error: Target {options.target} is not known')