CIRCT  18.0.0git
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 
5 from __future__ import annotations
6 
7 from . import hw
8 from .. import support
9 from .._mlir_libs._circt._hw import *
10 from ..dialects._ods_common import _cext as _ods_cext
11 from ..ir import *
12 from ._hw_ops_gen import *
13 from ._hw_ops_gen import _Dialect
14 from typing import Dict, Type
15 
16 
17 def 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 
46 class 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.modulemodule = 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_namesoperand_names()]),
70  ArrayAttr.get([StringAttr.get(x) for x in self.result_namesresult_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_namesoperand_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.modulemodule.type.input_types[index]
101  return support.BackedgeBuilder.create(type,
102  arg_name,
103  self,
104  instance_of=self.modulemodule)
105 
106  def operand_names(self):
107  return self.modulemodule.type.input_names
108 
109  def result_names(self):
110  return self.modulemodule.type.output_names
111 
112 
114  """Custom Python base class for module-like operations."""
115 
116  def __init__(
117  self,
118  name,
119  input_ports=[],
120  output_ports=[],
121  *,
122  parameters=[],
123  attributes={},
124  body_builder=None,
125  loc=None,
126  ip=None,
127  ):
128  """
129  Create a module-like with the provided `name`, `input_ports`, and
130  `output_ports`.
131  - `name` is a string representing the module name.
132  - `input_ports` is a list of pairs of string names and mlir.ir types.
133  - `output_ports` is a list of pairs of string names and mlir.ir types.
134  - `body_builder` is an optional callback, when provided a new entry block
135  is created and the callback is invoked with the new op as argument within
136  an InsertionPoint context already set for the block. The callback is
137  expected to insert a terminator in the block.
138  """
139  # Copy the mutable default arguments. 'Cause python.
140  input_ports = list(input_ports)
141  output_ports = list(output_ports)
142  parameters = list(parameters)
143  attributes = dict(attributes)
144 
145  operands = []
146  results = []
147  attributes["sym_name"] = StringAttr.get(str(name))
148 
149  module_ports = []
150  input_names = []
151  port_locs = []
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  port_locs.append(unknownLoc)
160 
161  output_types = []
162  output_names = []
163  for (i, (port_name, port_type)) in enumerate(output_ports):
164  output_name = StringAttr.get(str(port_name))
165  output_dir = hw.ModulePortDirection.OUTPUT
166  output_port = hw.ModulePort(output_name, port_type, output_dir)
167  module_ports.append(output_port)
168  output_names.append(output_name)
169  port_locs.append(unknownLoc)
170  attributes["port_locs"] = ArrayAttr.get(port_locs)
171  attributes["per_port_attrs"] = ArrayAttr.get([])
172 
173  if len(parameters) > 0 or "parameters" not in attributes:
174  attributes["parameters"] = ArrayAttr.get(parameters)
175 
176  attributes["module_type"] = TypeAttr.get(hw.ModuleType.get(module_ports))
177 
178  _ods_cext.ir.OpView.__init__(
179  self,
180  self.build_generic(attributes=attributes,
181  results=results,
182  operands=operands,
183  loc=loc,
184  ip=ip))
185 
186  if body_builder:
187  entry_block = self.add_entry_block()
188 
189  with InsertionPoint(entry_block):
190  with support.BackedgeBuilder():
191  outputs = body_builder(self)
192  _create_output_op(name, output_ports, entry_block, outputs)
193 
194  @property
195  def type(self):
196  return hw.ModuleType(TypeAttr(self.attributes["module_type"]).value)
197 
198  @property
199  def name(self):
200  return self.attributes["sym_name"]
201 
202  @property
203  def is_external(self):
204  return len(self.regions[0].blocks) == 0
205 
206  @property
207  def parameters(self) -> list[ParamDeclAttr]:
208  return [
209  hw.ParamDeclAttr(a) for a in ArrayAttr(self.attributes["parameters"])
210  ]
211 
212  def instantiate(self,
213  name: str,
214  parameters: Dict[str, object] = {},
215  results=None,
216  sym_name=None,
217  loc=None,
218  ip=None,
219  **kwargs):
220  return InstanceBuilder(self,
221  name,
222  kwargs,
223  parameters=parameters,
224  results=results,
225  sym_name=sym_name,
226  loc=loc,
227  ip=ip)
228 
229 
230 def _create_output_op(cls_name, output_ports, entry_block, bb_ret):
231  """Create the hw.OutputOp from the body_builder return."""
232 
233  # Determine if the body already has an output op.
234  block_len = len(entry_block.operations)
235  if block_len > 0:
236  last_op = entry_block.operations[block_len - 1]
237  if isinstance(last_op, hw.OutputOp):
238  # If it does, the return from body_builder must be None.
239  if bb_ret is not None and bb_ret != last_op:
240  raise support.ConnectionError(
241  f"In {cls_name}, cannot return value from body_builder and "
242  "create hw.OutputOp")
243  return
244 
245  # If builder didn't create an output op and didn't return anything, this op
246  # mustn't have any outputs.
247  if bb_ret is None:
248  if len(output_ports) == 0:
249  hw.OutputOp([])
250  return
251  raise support.ConnectionError(
252  f"In {cls_name}, must return module output values")
253 
254  # Now create the output op depending on the object type returned
255  outputs: list[Value] = list()
256 
257  # Only acceptable return is a dict of port, value mappings.
258  if not isinstance(bb_ret, dict):
259  raise support.ConnectionError(
260  f"In {cls_name}, can only return a dict of port, value mappings "
261  "from body_builder.")
262 
263  # A dict of `OutputPortName` -> ValueLike must be converted to a list in port
264  # order.
265  unconnected_ports = []
266  for (name, port_type) in output_ports:
267  if name not in bb_ret:
268  unconnected_ports.append(name)
269  outputs.append(None)
270  else:
271  val = support.get_value(bb_ret[name])
272  if val is None:
273  field_type = type(bb_ret[name])
274  raise TypeError(
275  f"In {cls_name}, body_builder return doesn't support type "
276  f"'{field_type}'")
277  if val.type != port_type:
278  if isinstance(port_type, hw.TypeAliasType) and \
279  port_type.inner_type == val.type:
280  val = hw.BitcastOp.create(port_type, val).result
281  else:
282  raise TypeError(
283  f"In {cls_name}, output port '{name}' type ({val.type}) doesn't "
284  f"match declared type ({port_type})")
285  outputs.append(val)
286  bb_ret.pop(name)
287  if len(unconnected_ports) > 0:
288  raise support.UnconnectedSignalError(cls_name, unconnected_ports)
289  if len(bb_ret) > 0:
290  raise support.ConnectionError(
291  f"Could not map the following to output ports in {cls_name}: " +
292  ",".join(bb_ret.keys()))
293 
294  hw.OutputOp(outputs)
295 
296 
297 @_ods_cext.register_operation(_Dialect, replace=True)
299  """Specialization for the HW module op class."""
300 
301  def __init__(
302  self,
303  name,
304  input_ports=[],
305  output_ports=[],
306  *,
307  parameters=[],
308  attributes={},
309  body_builder=None,
310  loc=None,
311  ip=None,
312  ):
313  if "comment" not in attributes:
314  attributes["comment"] = StringAttr.get("")
315  super().__init__(name,
316  input_ports,
317  output_ports,
318  parameters=parameters,
319  attributes=attributes,
320  body_builder=body_builder,
321  loc=loc,
322  ip=ip)
323 
324  @property
325  def body(self):
326  return self.regions[0]
327 
328  @property
329  def entry_block(self):
330  return self.regions[0].blocks[0]
331 
332  @property
333  def input_indices(self):
334  indices: dict[int, str] = {}
335  op_names = self.typetype.input_names
336  for idx, name in enumerate(op_names):
337  indices[name] = idx
338  return indices
339 
340  # Support attribute access to block arguments by name
341  def __getattr__(self, name):
342  if name in self.input_indicesinput_indices:
343  index = self.input_indicesinput_indices[name]
344  return self.entry_blockentry_block.arguments[index]
345  raise AttributeError(f"unknown input port name {name}")
346 
347  def inputs(self) -> dict[str:Value]:
348  ret = {}
349  for (name, idx) in self.input_indicesinput_indices.items():
350  ret[name] = self.entry_blockentry_block.arguments[idx]
351  return ret
352 
353  def outputs(self) -> dict[str:Type]:
354  result_names = self.typetype.output_names
355  result_types = self.typetype.output_types
356  return dict(zip(result_names, result_types))
357 
358  def add_entry_block(self):
359  if not self.is_externalis_external:
360  raise IndexError('The module already has an entry block')
361  self.bodybody.blocks.append(*self.typetype.input_types)
362  return self.bodybody.blocks[0]
363 
364 
365 @_ods_cext.register_operation(_Dialect, replace=True)
367  """Specialization for the HW module op class."""
368 
369  def __init__(
370  self,
371  name,
372  input_ports=[],
373  output_ports=[],
374  *,
375  parameters=[],
376  attributes={},
377  body_builder=None,
378  loc=None,
379  ip=None,
380  ):
381  if "comment" not in attributes:
382  attributes["comment"] = StringAttr.get("")
383  super().__init__(name,
384  input_ports,
385  output_ports,
386  parameters=parameters,
387  attributes=attributes,
388  body_builder=body_builder,
389  loc=loc,
390  ip=ip)
391 
392 
393 @_ods_cext.register_operation(_Dialect, replace=True)
395 
396  @staticmethod
397  def create(data_type, value):
398  return hw.ConstantOp(IntegerAttr.get(data_type, value))
399 
400 
401 @_ods_cext.register_operation(_Dialect, replace=True)
403 
404  @staticmethod
405  def create(data_type, value):
406  value = support.get_value(value)
407  return hw.BitcastOp(data_type, value)
408 
409 
410 @_ods_cext.register_operation(_Dialect, replace=True)
412 
413  @staticmethod
414  def create(array_value, idx):
415  array_value = support.get_value(array_value)
416  array_type = support.get_self_or_inner(array_value.type)
417  if isinstance(idx, int):
418  idx_width = (array_type.size - 1).bit_length()
419  idx_val = ConstantOp.create(IntegerType.get_signless(idx_width),
420  idx).result
421  else:
422  idx_val = support.get_value(idx)
423  return hw.ArrayGetOp(array_value, idx_val)
424 
425 
426 @_ods_cext.register_operation(_Dialect, replace=True)
428 
429  @staticmethod
430  def create(array_value, low_index, ret_type):
431  array_value = support.get_value(array_value)
432  array_type = support.get_self_or_inner(array_value.type)
433  if isinstance(low_index, int):
434  idx_width = (array_type.size - 1).bit_length()
435  idx_width = max(1, idx_width) # hw.constant cannot produce i0.
436  idx_val = ConstantOp.create(IntegerType.get_signless(idx_width),
437  low_index).result
438  else:
439  idx_val = support.get_value(low_index)
440  return hw.ArraySliceOp(ret_type, array_value, idx_val)
441 
442 
443 @_ods_cext.register_operation(_Dialect, replace=True)
445 
446  @staticmethod
447  def create(elements):
448  if not elements:
449  raise ValueError("Cannot 'create' an array of length zero")
450  vals = []
451  type = None
452  for i, arg in enumerate(elements):
453  arg_val = support.get_value(arg)
454  vals.append(arg_val)
455  if type is None:
456  type = arg_val.type
457  elif type != arg_val.type:
458  raise TypeError(
459  f"Argument {i} has a different element type ({arg_val.type}) than the element type of the array ({type})"
460  )
461  return hw.ArrayCreateOp(hw.ArrayType.get(type, len(vals)), vals)
462 
463 
464 @_ods_cext.register_operation(_Dialect, replace=True)
466 
467  @staticmethod
468  def create(*sub_arrays):
469  vals = []
470  types = []
471  element_type = None
472  for i, array in enumerate(sub_arrays):
473  array_value = support.get_value(array)
474  array_type = support.type_to_pytype(array_value.type)
475  if array_value is None or not isinstance(array_type, hw.ArrayType):
476  raise TypeError(f"Cannot concatenate {array_value}")
477  if element_type is None:
478  element_type = array_type.element_type
479  elif element_type != array_type.element_type:
480  raise TypeError(
481  f"Argument {i} has a different element type ({element_type}) than the element type of the array ({array_type.element_type})"
482  )
483 
484  vals.append(array_value)
485  types.append(array_type)
486 
487  size = sum(t.size for t in types)
488  combined_type = hw.ArrayType.get(element_type, size)
489  return hw.ArrayConcatOp(combined_type, vals)
490 
491 
492 @_ods_cext.register_operation(_Dialect, replace=True)
494 
495  @staticmethod
496  def create(elements, result_type: Type = None):
497  elem_name_values = [
498  (name, support.get_value(value)) for (name, value) in elements
499  ]
500  struct_fields = [(name, value.type) for (name, value) in elem_name_values]
501  struct_type = hw.StructType.get(struct_fields)
502 
503  if result_type is None:
504  result_type = struct_type
505  else:
506  result_type_inner = support.get_self_or_inner(result_type)
507  if result_type_inner != struct_type:
508  raise TypeError(
509  f"result type:\n\t{result_type_inner}\nmust match generated struct type:\n\t{struct_type}"
510  )
511 
512  return hw.StructCreateOp(result_type,
513  [value for (_, value) in elem_name_values])
514 
515 
516 @_ods_cext.register_operation(_Dialect, replace=True)
518 
519  @staticmethod
520  def create(struct_value, field_name: str):
521  struct_value = support.get_value(struct_value)
522  struct_type = support.get_self_or_inner(struct_value.type)
523  field_type = struct_type.get_field(field_name)
524  field_index = struct_type.get_field_index(field_name)
525  if field_index == UnitAttr.get():
526  raise TypeError(
527  f"field '{field_name}' not element of struct type {struct_type}")
528  return hw.StructExtractOp(field_type, struct_value, field_index)
529 
530 
531 @_ods_cext.register_operation(_Dialect, replace=True)
533 
534  @staticmethod
535  def create(sym_name: str, type: Type, verilog_name: str = None):
536  return hw.TypedeclOp(StringAttr.get(sym_name),
537  TypeAttr.get(type),
538  verilogName=verilog_name)
539 
540 
541 @_ods_cext.register_operation(_Dialect, replace=True)
543 
544  @staticmethod
545  def create(sym_name: str):
546  op = hw.TypeScopeOp(StringAttr.get(sym_name))
547  op.regions[0].blocks.append()
548  return op
549 
550  @property
551  def body(self):
552  return self.regions[0].blocks[0]
def create(*sub_arrays)
Definition: hw.py:468
def create(elements)
Definition: hw.py:447
def create(array_value, idx)
Definition: hw.py:414
def create(array_value, low_index, ret_type)
Definition: hw.py:430
def create(data_type, value)
Definition: hw.py:405
def create(data_type, value)
Definition: hw.py:397
def __init__(self, name, input_ports=[], output_ports=[], *parameters=[], attributes={}, body_builder=None, loc=None, ip=None)
Definition: hw.py:380
def body(self)
Definition: hw.py:325
def add_entry_block(self)
Definition: hw.py:358
dict[str:Value] inputs(self)
Definition: hw.py:347
def entry_block(self)
Definition: hw.py:329
dict[str:Type] outputs(self)
Definition: hw.py:353
def __init__(self, name, input_ports=[], output_ports=[], *parameters=[], attributes={}, body_builder=None, loc=None, ip=None)
Definition: hw.py:312
def input_indices(self)
Definition: hw.py:333
def __getattr__(self, name)
Definition: hw.py:341
def create_default_value(self, index, data_type, arg_name)
Definition: hw.py:99
def __init__(self, module, name, input_port_mapping, *results=None, parameters={}, sym_name=None, loc=None, ip=None)
Definition: hw.py:58
def operand_names(self)
Definition: hw.py:106
def result_names(self)
Definition: hw.py:109
def is_external(self)
Definition: hw.py:203
list[ParamDeclAttr] parameters(self)
Definition: hw.py:207
def name(self)
Definition: hw.py:199
def type(self)
Definition: hw.py:195
def instantiate(self, str name, Dict[str, object] parameters={}, results=None, sym_name=None, loc=None, ip=None, **kwargs)
Definition: hw.py:219
def __init__(self, name, input_ports=[], output_ports=[], *parameters=[], attributes={}, body_builder=None, loc=None, ip=None)
Definition: hw.py:127
def create(elements, Type result_type=None)
Definition: hw.py:496
def create(struct_value, str field_name)
Definition: hw.py:520
def create(str sym_name)
Definition: hw.py:545
def body(self)
Definition: hw.py:551
def create(str sym_name, Type type, str verilog_name=None)
Definition: hw.py:535
def _create_output_op(cls_name, output_ports, entry_block, bb_ret)
Definition: hw.py:230
def create_parameters(dict[str, Attribute] parameters, ModuleLike module)
Definition: hw.py:17