Skip to content

Commit

Permalink
[pycde] Disallow structs with zero fields (#7000)
Browse files Browse the repository at this point in the history
Also, fix IbisClass for functions with zero arguments.

Co-authored-by: Morten Borup Petersen <morten_bp@live.dk>
  • Loading branch information
teqdruid and mortbopet committed May 8, 2024
1 parent ac66ece commit 7cd0ea9
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 19 deletions.
63 changes: 44 additions & 19 deletions frontends/PyCDE/src/pycde/ibis.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@
from .constructs import Wire
from .module import Module, ModuleBuilder, generator
from .system import System
from .types import (Bits, Bundle, BundledChannel, ChannelDirection, Channel,
StructType, Type)

from .circt.dialects import hw
from .types import (
Bits,
Bundle,
BundledChannel,
ChannelDirection,
Channel,
StructType,
Type,
)

from .circt.dialects import hw, esi
from .circt import ir

from pathlib import Path
from typing import Callable, Dict, List, Optional, get_type_hints
Expand All @@ -24,21 +32,29 @@

class IbisMethod:
"""Replace a decorated method with this class as a record. Used during class
scanning to identify Ibis methods."""

def __init__(self, name: Optional[str], arg_types: Dict[str, Type],
return_type: Optional[Type]):
scanning to identify Ibis methods."""

def __init__(
self,
name: Optional[str],
arg_types: Dict[str, Type],
return_type: Optional[Type],
):
self.name = name
self.arg_types = arg_types
# Assemble the args into a single struct type.
self.arg_type = StructType(self.arg_types)
if self.arg_types:
# Assemble the args into a single struct type.
self.arg_type = StructType(self.arg_types)
else:
self.arg_type = Bits(0)

# i0 is used for void.
if return_type is None:
return_type = Bits(0)
self.return_type = return_type
self.func_type = Bundle([
BundledChannel("arg", ChannelDirection.TO, self.arg_type),
BundledChannel("result", ChannelDirection.FROM, self.return_type)
BundledChannel("result", ChannelDirection.FROM, self.return_type),
])


Expand All @@ -60,7 +76,7 @@ def method(func: Callable):

class IbisClassBuilder(ModuleBuilder):
"""Ibis-specific module builder. This is used to scan a class for Ibis
methods, import the Ibis module, and create the wrapper module."""
methods, import the Ibis module, and create the wrapper module."""

SupportFiles: List[Path] = []

Expand All @@ -84,11 +100,12 @@ def package(sys: System):
@property
def circt_mod(self):
"""Get the raw CIRCT operation for the module definition. DO NOT store the
returned value!!! It needs to get reaped after the current action (e.g.
instantiation, generation). Memory safety when interacting with native code
can be painful."""
returned value!!! It needs to get reaped after the current action (e.g.
instantiation, generation). Memory safety when interacting with native code
can be painful."""

from .system import System

sys: System = System.current()
ret = sys._op_cache.get_circt_mod(self)
if ret is None:
Expand Down Expand Up @@ -166,8 +183,12 @@ def generate_wrapper(self, ports):

# Ports we shouldn't touch.
exclude_ports = {
"clk", "clk1", "rst_in", "stall_rate_in", "inspection_value_in",
"stall_rate_valid_in"
"clk",
"clk1",
"rst_in",
"stall_rate_in",
"inspection_value_in",
"stall_rate_valid_in",
}

# Create wires for all the inputs and instantiate the Ibis module.
Expand All @@ -193,8 +214,12 @@ def generate_wrapper(self, ports):
mname = m.name + "_"

# Return side is FIFO.
return_out_untyped = inst_outputs[mname + "result_out"]
return_out = return_out_untyped.bitcast(m.return_type)
if m.return_type == Bits(0):
# Void return type; hw.constant 0 : i0
return_out = Bits(0)(0)
else:
return_out_untyped = inst_outputs[mname + "result_out"]
return_out = return_out_untyped.bitcast(m.return_type)

empty_out = inst_outputs[mname + "empty_out"]
return_chan, rdy = Channel(m.return_type).wrap(return_out, ~empty_out)
Expand Down
2 changes: 2 additions & 0 deletions frontends/PyCDE/src/pycde/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ def __new__(
cls, fields: typing.Union[typing.List[typing.Tuple[str, Type]],
typing.Dict[str, Type]]
) -> StructType:
if len(fields) == 0:
raise ValueError("Structs must have at least one field.")
if isinstance(fields, dict):
fields = list(fields.items())
if not isinstance(fields, list):
Expand Down
7 changes: 7 additions & 0 deletions frontends/PyCDE/test/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from pycde import Clock, Input, types, System
from pycde.module import AppID, generator, Module, modparams
from pycde.testing import unittestmodule
from pycde.types import StructType


# CHECK: TypeError: Module parameter definitions cannot have *args
Expand Down Expand Up @@ -91,3 +92,9 @@ class OperatorError2(Module):
def build(ports):
# CHECK: Comparisons of signed/unsigned integers to Bits<32> not supported. RHS operand should be cast .as_sint()/.as_uint() if possible.
ports.b == ports.a


# -----

# CHECK: Structs must have at least one field
StructType([])

0 comments on commit 7cd0ea9

Please sign in to comment.