CIRCT 20.0.0git
Loading...
Searching...
No Matches
hw.py
Go to the documentation of this file.
1# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
2# See https://llvm.org/LICENSE.txt for license information.
3# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5from __future__ import annotations
6
7from . import hw
8from .. import support
9from .._mlir_libs._circt._hw import *
10from ..dialects._ods_common import _cext as _ods_cext
11from ..ir import *
12from ._hw_ops_gen import *
13from ._hw_ops_gen import _Dialect
14from typing import Dict, Type
15
16
17def create_parameters(parameters: dict[str, Attribute], module: ModuleLike):
18 # Compute mapping from parameter name to index, and initialize array.
19 mod_param_decls = module.parameters
20 mod_param_decls_idxs = {
21 decl.name: idx for (idx, decl) in enumerate(mod_param_decls)
22 }
23 inst_param_array = [None] * len(module.parameters)
24
25 # Fill in all the parameters specified.
26 if isinstance(parameters, DictAttr):
27 parameters = {i.name: i.attr for i in parameters}
28 for (pname, pval) in parameters.items():
29 if pname not in mod_param_decls_idxs:
30 raise ValueError(
31 f"Could not find parameter '{pname}' in module parameter decls")
32 idx = mod_param_decls_idxs[pname]
33 param_decl = mod_param_decls[idx]
34 inst_param_array[idx] = hw.ParamDeclAttr.get(pname, param_decl.param_type,
35 pval)
36
37 # Fill in the defaults from the module param decl.
38 for (idx, pval) in enumerate(inst_param_array):
39 if pval is not None:
40 continue
41 inst_param_array[idx] = mod_param_decls[idx]
42
43 return inst_param_array
44
45
46class InstanceBuilder(support.NamedValueOpView):
47 """Helper class to incrementally construct an instance of a module."""
48
49 def __init__(self,
50 module,
51 name,
52 input_port_mapping,
53 *,
54 results=None,
55 parameters={},
56 sym_name=None,
57 loc=None,
58 ip=None):
59 self.module = module
60 instance_name = StringAttr.get(name)
61 module_name = FlatSymbolRefAttr.get(StringAttr(module.name).value)
62 inst_param_array = create_parameters(parameters, module)
63 if sym_name:
64 inner_sym = hw.InnerSymAttr.get(StringAttr.get(sym_name))
65 else:
66 inner_sym = None
67 pre_args = [instance_name, module_name]
68 post_args = [
69 ArrayAttr.get([StringAttr.get(x) for x in self.operand_names()]),
70 ArrayAttr.get([StringAttr.get(x) for x in self.result_names()]),
71 ArrayAttr.get(inst_param_array)
72 ]
73 if results is None:
74 results = module.type.output_types
75
76 if not isinstance(module, hw.HWModuleExternOp):
77 input_name_type_lookup = {
78 name: support.type_to_pytype(ty)
79 for name, ty in zip(self.operand_names(), module.type.input_types)
80 }
81 for input_name, input_value in input_port_mapping.items():
82 if input_name not in input_name_type_lookup:
83 continue # This error gets caught and raised later.
84 mod_input_type = input_name_type_lookup[input_name]
85 if support.type_to_pytype(input_value.type) != mod_input_type:
86 raise TypeError(f"Input '{input_name}' has type '{input_value.type}' "
87 f"but expected '{mod_input_type}'")
88
89 super().__init__(hw.InstanceOp,
90 results,
91 input_port_mapping,
92 pre_args,
93 post_args,
94 needs_result_type=True,
95 inner_sym=inner_sym,
96 loc=loc,
97 ip=ip)
98
99 def create_default_value(self, index, data_type, arg_name):
100 type = self.module.type.input_types[index]
101 return support.BackedgeBuilder.create(type,
102 arg_name,
103 self,
104 instance_of=self.module)
105
106 def operand_names(self):
107 return self.module.type.input_names
108
109 def result_names(self):
110 return self.module.type.output_names
111
112
114 """Custom Python helper class for module-like operations."""
115
116 @staticmethod
117 def init(
118 op,
119 name,
120 input_ports=[],
121 output_ports=[],
122 *,
123 parameters=[],
124 attributes={},
125 body_builder=None,
126 loc=None,
127 ip=None,
128 ):
129 """
130 Create a module-like with the provided `name`, `input_ports`, and
131 `output_ports`.
132 - `name` is a string representing the module name.
133 - `input_ports` is a list of pairs of string names and mlir.ir types.
134 - `output_ports` is a list of pairs of string names and mlir.ir types.
135 - `body_builder` is an optional callback, when provided a new entry block
136 is created and the callback is invoked with the new op as argument within
137 an InsertionPoint context already set for the block. The callback is
138 expected to insert a terminator in the block.
139 """
140 # Copy the mutable default arguments. 'Cause python.
141 input_ports = list(input_ports)
142 output_ports = list(output_ports)
143 parameters = list(parameters)
144 attributes = dict(attributes)
145
146 operands = []
147 results = []
148 attributes["sym_name"] = StringAttr.get(str(name))
149
150 module_ports = []
151 input_names = []
152 unknownLoc = Location.unknown().attr
153 for (i, (port_name, port_type)) in enumerate(input_ports):
154 input_name = StringAttr.get(str(port_name))
155 input_dir = hw.ModulePortDirection.INPUT
156 input_port = hw.ModulePort(input_name, port_type, input_dir)
157 module_ports.append(input_port)
158 input_names.append(input_name)
159
160 output_types = []
161 output_names = []
162 for (i, (port_name, port_type)) in enumerate(output_ports):
163 output_name = StringAttr.get(str(port_name))
164 output_dir = hw.ModulePortDirection.OUTPUT
165 output_port = hw.ModulePort(output_name, port_type, output_dir)
166 module_ports.append(output_port)
167 output_names.append(output_name)
168 attributes["per_port_attrs"] = ArrayAttr.get([])
169
170 if len(parameters) > 0 or "parameters" not in attributes:
171 attributes["parameters"] = ArrayAttr.get(parameters)
172
173 attributes["module_type"] = TypeAttr.get(hw.ModuleType.get(module_ports))
174
175 _ods_cext.ir.OpView.__init__(
176 op,
177 op.build_generic(attributes=attributes,
178 results=results,
179 operands=operands,
180 loc=loc,
181 ip=ip))
182
183 if body_builder:
184 entry_block = op.add_entry_block()
185
186 with InsertionPoint(entry_block):
187 with support.BackedgeBuilder(str(name)):
188 outputs = body_builder(op)
189 _create_output_op(name, output_ports, entry_block, outputs)
190
191 @staticmethod
192 def type(op):
193 return hw.ModuleType(TypeAttr(op.attributes["module_type"]).value)
194
195 @staticmethod
196 def name(op):
197 return op.attributes["sym_name"]
198
199 @staticmethod
200 def is_external(op):
201 return len(op.regions[0].blocks) == 0
202
203 @staticmethod
204 def parameters(op) -> list[ParamDeclAttr]:
205 return [hw.ParamDeclAttr(a) for a in ArrayAttr(op.attributes["parameters"])]
206
207 @staticmethod
209 name: str,
210 parameters: Dict[str, object] = {},
211 results=None,
212 sym_name=None,
213 loc=None,
214 ip=None,
215 **kwargs):
216 return InstanceBuilder(op,
217 name,
218 kwargs,
219 parameters=parameters,
220 results=results,
221 sym_name=sym_name,
222 loc=loc,
223 ip=ip)
224
225
226def _create_output_op(cls_name, output_ports, entry_block, bb_ret):
227 """Create the hw.OutputOp from the body_builder return."""
228
229 # Determine if the body already has an output op.
230 block_len = len(entry_block.operations)
231 if block_len > 0:
232 last_op = entry_block.operations[block_len - 1]
233 if isinstance(last_op, hw.OutputOp):
234 # If it does, the return from body_builder must be None.
235 if bb_ret is not None and bb_ret != last_op:
236 raise support.ConnectionError(
237 f"In {cls_name}, cannot return value from body_builder and "
238 "create hw.OutputOp")
239 return
240
241 # If builder didn't create an output op and didn't return anything, this op
242 # mustn't have any outputs.
243 if bb_ret is None:
244 if len(output_ports) == 0:
245 hw.OutputOp([])
246 return
247 raise support.ConnectionError(
248 f"In {cls_name}, must return module output values")
249
250 # Now create the output op depending on the object type returned
251 outputs: list[Value] = list()
252
253 # Only acceptable return is a dict of port, value mappings.
254 if not isinstance(bb_ret, dict):
255 raise support.ConnectionError(
256 f"In {cls_name}, can only return a dict of port, value mappings "
257 "from body_builder.")
258
259 # A dict of `OutputPortName` -> ValueLike must be converted to a list in port
260 # order.
261 unconnected_ports = []
262 for (name, port_type) in output_ports:
263 if name not in bb_ret:
264 unconnected_ports.append(name)
265 outputs.append(None)
266 else:
267 val = support.get_value(bb_ret[name])
268 if val is None:
269 field_type = type(bb_ret[name])
270 raise TypeError(
271 f"In {cls_name}, body_builder return doesn't support type "
272 f"'{field_type}'")
273 if val.type != port_type:
274 if isinstance(port_type, hw.TypeAliasType) and \
275 port_type.inner_type == val.type:
276 val = hw.BitcastOp.create(port_type, val).result
277 else:
278 raise TypeError(
279 f"In {cls_name}, output port '{name}' type ({val.type}) doesn't "
280 f"match declared type ({port_type})")
281 outputs.append(val)
282 bb_ret.pop(name)
283 if len(unconnected_ports) > 0:
284 raise support.UnconnectedSignalError(cls_name, unconnected_ports)
285 if len(bb_ret) > 0:
286 raise support.ConnectionError(
287 f"Could not map the following to output ports in {cls_name}: " +
288 ",".join(bb_ret.keys()))
289
290 hw.OutputOp(outputs)
291
292
293@_ods_cext.register_operation(_Dialect, replace=True)
295 """Specialization for the HW module op class."""
296
298 self,
299 name,
300 input_ports=[],
301 output_ports=[],
302 *,
303 parameters=[],
304 attributes={},
305 body_builder=None,
306 loc=None,
307 ip=None,
308 ):
309 if "comment" not in attributes:
310 attributes["comment"] = StringAttr.get("")
311 ModuleLike.init(self,
312 name,
313 input_ports,
314 output_ports,
315 parameters=parameters,
316 attributes=attributes,
317 body_builder=body_builder,
318 loc=loc,
319 ip=ip)
320
321 def instantiate(self, *args, **kwargs):
322 return ModuleLike.instantiate(self, *args, **kwargs)
323
324 @property
325 def type(self):
326 return ModuleLike.type(self)
327
328 @property
329 def name(self):
330 return ModuleLike.name(self)
331
332 @property
333 def is_external(self):
334 return ModuleLike.is_external(self)
335
336 @property
337 def parameters(self) -> list[ParamDeclAttr]:
338 return ModuleLike.parameters(self)
339
340 @property
341 def body(self):
342 return self.regions[0]
343
344 @property
345 def entry_block(self):
346 return self.regions[0].blocks[0]
347
348 @property
349 def input_indices(self):
350 indices: dict[int, str] = {}
351 op_names = self.type.input_names
352 for idx, name in enumerate(op_names):
353 indices[name] = idx
354 return indices
355
356 # Support attribute access to block arguments by name
357 def __getattr__(self, name):
358 if name in self.input_indices:
359 index = self.input_indices[name]
360 return self.entry_block.arguments[index]
361 raise AttributeError(f"unknown input port name {name}")
362
363 def inputs(self) -> dict[str:Value]:
364 ret = {}
365 for (name, idx) in self.input_indices.items():
366 ret[name] = self.entry_block.arguments[idx]
367 return ret
368
369 def outputs(self) -> dict[str:Type]:
370 result_names = self.type.output_names
371 result_types = self.type.output_types
372 return dict(zip(result_names, result_types))
373
375 if not self.is_external:
376 raise IndexError('The module already has an entry block')
377 self.body.blocks.append(*self.type.input_types)
378 return self.body.blocks[0]
379
380
381@_ods_cext.register_operation(_Dialect, replace=True)
383 """Specialization for the HW module op class."""
384
386 self,
387 name,
388 input_ports=[],
389 output_ports=[],
390 *,
391 parameters=[],
392 attributes={},
393 body_builder=None,
394 loc=None,
395 ip=None,
396 ):
397 if "comment" not in attributes:
398 attributes["comment"] = StringAttr.get("")
399 ModuleLike.init(self,
400 name,
401 input_ports,
402 output_ports,
403 parameters=parameters,
404 attributes=attributes,
405 body_builder=body_builder,
406 loc=loc,
407 ip=ip)
408
409 def instantiate(self, *args, **kwargs):
410 return ModuleLike.instantiate(self, *args, **kwargs)
411
412 @property
413 def type(self):
414 return ModuleLike.type(self)
415
416 @property
417 def name(self):
418 return ModuleLike.name(self)
419
420 @property
421 def is_external(self):
422 return ModuleLike.is_external(self)
423
424 @property
425 def parameters(self) -> list[ParamDeclAttr]:
426 return ModuleLike.parameters(self)
427
428
429@_ods_cext.register_operation(_Dialect, replace=True)
431
432 @staticmethod
433 def create(data_type, value):
434 return hw.ConstantOp(IntegerAttr.get(data_type, value))
435
436
437@_ods_cext.register_operation(_Dialect, replace=True)
439
440 @staticmethod
441 def create(data_type, value):
442 value = support.get_value(value)
443 return hw.BitcastOp(data_type, value)
444
445
446@_ods_cext.register_operation(_Dialect, replace=True)
448
449 @staticmethod
450 def create(array_value, idx):
451 array_value = support.get_value(array_value)
452 array_type = support.get_self_or_inner(array_value.type)
453 if isinstance(idx, int):
454 idx_width = (array_type.size - 1).bit_length()
455 idx_val = ConstantOp.create(IntegerType.get_signless(idx_width),
456 idx).result
457 else:
458 idx_val = support.get_value(idx)
459 return hw.ArrayGetOp(array_value, idx_val)
460
461
462@_ods_cext.register_operation(_Dialect, replace=True)
464
465 @staticmethod
466 def create(array_value, low_index, ret_type):
467 array_value = support.get_value(array_value)
468 array_type = support.get_self_or_inner(array_value.type)
469 if isinstance(low_index, int):
470 idx_width = (array_type.size - 1).bit_length()
471 idx_width = max(1, idx_width) # hw.constant cannot produce i0.
472 idx_val = ConstantOp.create(IntegerType.get_signless(idx_width),
473 low_index).result
474 else:
475 idx_val = support.get_value(low_index)
476 return hw.ArraySliceOp(ret_type, array_value, idx_val)
477
478
479@_ods_cext.register_operation(_Dialect, replace=True)
481
482 @staticmethod
483 def create(elements):
484 if not elements:
485 raise ValueError("Cannot 'create' an array of length zero")
486 vals = []
487 type = None
488 for i, arg in enumerate(elements):
489 arg_val = support.get_value(arg)
490 vals.append(arg_val)
491 if type is None:
492 type = arg_val.type
493 elif type != arg_val.type:
494 raise TypeError(
495 f"Argument {i} has a different element type ({arg_val.type}) than the element type of the array ({type})"
496 )
497 return hw.ArrayCreateOp(hw.ArrayType.get(type, len(vals)), vals)
498
499
500@_ods_cext.register_operation(_Dialect, replace=True)
502
503 @staticmethod
504 def create(*sub_arrays):
505 vals = []
506 types = []
507 element_type = None
508 for i, array in enumerate(sub_arrays):
509 array_value = support.get_value(array)
510 array_type = support.type_to_pytype(array_value.type)
511 if array_value is None or not isinstance(array_type, hw.ArrayType):
512 raise TypeError(f"Cannot concatenate {array_value}")
513 if element_type is None:
514 element_type = array_type.element_type
515 elif element_type != array_type.element_type:
516 raise TypeError(
517 f"Argument {i} has a different element type ({element_type}) than the element type of the array ({array_type.element_type})"
518 )
519
520 vals.append(array_value)
521 types.append(array_type)
522
523 size = sum(t.size for t in types)
524 combined_type = hw.ArrayType.get(element_type, size)
525 return hw.ArrayConcatOp(combined_type, vals)
526
527
528@_ods_cext.register_operation(_Dialect, replace=True)
530
531 @staticmethod
532 def create(elements, result_type: Type = None):
533 elem_name_values = [
534 (name, support.get_value(value)) for (name, value) in elements
535 ]
536 struct_fields = [(name, value.type) for (name, value) in elem_name_values]
537 struct_type = hw.StructType.get(struct_fields)
538
539 if result_type is None:
540 result_type = struct_type
541 else:
542 result_type_inner = support.get_self_or_inner(result_type)
543 if result_type_inner != struct_type:
544 raise TypeError(
545 f"result type:\n\t{result_type_inner}\nmust match generated struct type:\n\t{struct_type}"
546 )
547
548 return hw.StructCreateOp(result_type,
549 [value for (_, value) in elem_name_values])
550
551
552@_ods_cext.register_operation(_Dialect, replace=True)
554
555 @staticmethod
556 def create(struct_value, field_name: str):
557 struct_value = support.get_value(struct_value)
558 struct_type = support.get_self_or_inner(struct_value.type)
559 field_type = struct_type.get_field(field_name)
560 field_index = struct_type.get_field_index(field_name)
561 if field_index == UnitAttr.get():
562 raise TypeError(
563 f"field '{field_name}' not element of struct type {struct_type}")
564 return hw.StructExtractOp(field_type, struct_value, field_index)
565
566
567@_ods_cext.register_operation(_Dialect, replace=True)
569
570 @staticmethod
571 def create(sym_name: str, type: Type, verilog_name: str = None):
572 return hw.TypedeclOp(StringAttr.get(sym_name),
573 TypeAttr.get(type),
574 verilogName=verilog_name)
575
576
577@_ods_cext.register_operation(_Dialect, replace=True)
579
580 @staticmethod
581 def create(sym_name: str):
582 op = hw.TypeScopeOp(StringAttr.get(sym_name))
583 op.regions[0].blocks.append()
584 return op
585
586 @property
587 def body(self):
588 return self.regions[0].blocks[0]
create(*sub_arrays)
Definition hw.py:504
create(elements)
Definition hw.py:483
create(array_value, idx)
Definition hw.py:450
create(array_value, low_index, ret_type)
Definition hw.py:466
create(data_type, value)
Definition hw.py:441
create(data_type, value)
Definition hw.py:433
is_external(self)
Definition hw.py:421
__init__(self, name, input_ports=[], output_ports=[], *parameters=[], attributes={}, body_builder=None, loc=None, ip=None)
Definition hw.py:396
instantiate(self, *args, **kwargs)
Definition hw.py:409
list[ParamDeclAttr] parameters(self)
Definition hw.py:425
body(self)
Definition hw.py:341
__getattr__(self, name)
Definition hw.py:357
is_external(self)
Definition hw.py:333
dict[str:Value] inputs(self)
Definition hw.py:363
instantiate(self, *args, **kwargs)
Definition hw.py:321
name(self)
Definition hw.py:329
list[ParamDeclAttr] parameters(self)
Definition hw.py:337
dict[str:Type] outputs(self)
Definition hw.py:369
__init__(self, name, input_ports=[], output_ports=[], *parameters=[], attributes={}, body_builder=None, loc=None, ip=None)
Definition hw.py:308
input_indices(self)
Definition hw.py:349
type(self)
Definition hw.py:325
add_entry_block(self)
Definition hw.py:374
entry_block(self)
Definition hw.py:345
result_names(self)
Definition hw.py:109
__init__(self, module, name, input_port_mapping, *results=None, parameters={}, sym_name=None, loc=None, ip=None)
Definition hw.py:58
create_default_value(self, index, data_type, arg_name)
Definition hw.py:99
operand_names(self)
Definition hw.py:106
is_external(op)
Definition hw.py:200
type(op)
Definition hw.py:192
name(op)
Definition hw.py:196
list[ParamDeclAttr] parameters(op)
Definition hw.py:204
instantiate(op, str name, Dict[str, object] parameters={}, results=None, sym_name=None, loc=None, ip=None, **kwargs)
Definition hw.py:215
init(op, name, input_ports=[], output_ports=[], *parameters=[], attributes={}, body_builder=None, loc=None, ip=None)
Definition hw.py:128
create(elements, Type result_type=None)
Definition hw.py:532
create(struct_value, str field_name)
Definition hw.py:556
body(self)
Definition hw.py:587
create(str sym_name)
Definition hw.py:581
create(str sym_name, Type type, str verilog_name=None)
Definition hw.py:571
_create_output_op(cls_name, output_ports, entry_block, bb_ret)
Definition hw.py:226
create_parameters(dict[str, Attribute] parameters, ModuleLike module)
Definition hw.py:17