mirror of https://github.com/llvm/circt.git
[pycde] Remove IbisClass
Bringing this internal for the time being
This commit is contained in:
parent
d3ebc7f19d
commit
839af9f39d
|
@ -1,75 +0,0 @@
|
|||
# REQUIRES: esi-runtime, esi-cosim, rtl-sim, questa, ibis
|
||||
# RUN: rm -rf %t
|
||||
# RUN: mkdir %t && cd %t
|
||||
# RUN: %PYTHON% %s %t 2>&1
|
||||
|
||||
# Ibis does not currently simulate with Verilator.
|
||||
# RUN: esi-cosim.py --sim questa -- %PYTHON% %S/test_software/ibis_foo.py cosim env
|
||||
|
||||
from symbol import func_type
|
||||
import pycde
|
||||
from pycde import Input, Module, generator, esi
|
||||
from pycde.module import Metadata
|
||||
from pycde.common import Clock
|
||||
from pycde.bsp import cosim
|
||||
from pycde.ibis import IbisClass, method
|
||||
from pycde.types import Array, Bits, UInt
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
__dirname__ = Path(__file__).parent
|
||||
|
||||
|
||||
class DemoTop(IbisClass):
|
||||
# ibis -t=sv --circt --write-circt-ir --no-inspection \
|
||||
# --no-control-inspection --no-wrapper --no-debug-view \
|
||||
# --base-library $IBIS_LIB/base.pd --import-dir $IBIS_LIB/
|
||||
# ibistool --lo --ir ibis_esi_demoDemoTop.mlir > ibis_esi_demoDemoTop.lo.mlir
|
||||
src_file = "ibis_esi_demoDemoTop.lo.mlir"
|
||||
support_files = "support_files.f"
|
||||
|
||||
metadata = Metadata(version="0.1",
|
||||
summary="A demonstration of ESI and Ibis",
|
||||
misc={
|
||||
"crcWidth": 64,
|
||||
"style": "stupid"
|
||||
})
|
||||
|
||||
@method
|
||||
def add(self, a: UInt(8), b: UInt(8), arr: Array(UInt(8), 16)) -> UInt(8):
|
||||
pass
|
||||
|
||||
@method
|
||||
def compute_crc(
|
||||
self, identifier: UInt(8), input: Array(UInt(8), 64),
|
||||
input_bytes: UInt(8), reset: UInt(8)
|
||||
) -> UInt(32):
|
||||
pass
|
||||
|
||||
|
||||
class IbisTestSystem(Module):
|
||||
clk = Clock()
|
||||
rst = Input(Bits(1))
|
||||
|
||||
@generator
|
||||
def build(ports):
|
||||
add = esi.FuncService.get(esi.AppID("add"), func_type=DemoTop.add.func_type)
|
||||
crc = esi.FuncService.get(esi.AppID("crc"),
|
||||
func_type=DemoTop.compute_crc.func_type)
|
||||
DemoTop(clk=ports.clk,
|
||||
rst=ports.rst,
|
||||
appid=esi.AppID("demo"),
|
||||
add=add,
|
||||
compute_crc=crc)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
s = pycde.System(cosim.CosimBSP(IbisTestSystem),
|
||||
name="IbisTest",
|
||||
output_directory=sys.argv[1])
|
||||
s.generate()
|
||||
s.print(file=open("ibis_test.mlir", "w"))
|
||||
s.compile()
|
||||
s.package()
|
|
@ -36,7 +36,6 @@ declare_mlir_python_sources(PyCDESources
|
|||
pycde/ndarray.py
|
||||
pycde/esi.py
|
||||
pycde/fsm.py
|
||||
pycde/ibis.py
|
||||
pycde/testing.py
|
||||
|
||||
pycde/bsp/__init__.py
|
||||
|
|
|
@ -1,249 +0,0 @@
|
|||
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
from .common import Clock, Input
|
||||
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, esi
|
||||
from .circt import ir
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Callable, Dict, List, Optional, get_type_hints
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
SvSupportPath = None
|
||||
if "IBIS_SVSUPPORT" in os.environ:
|
||||
SvSupportPath = Path(os.environ["IBIS_SVSUPPORT"])
|
||||
|
||||
|
||||
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],
|
||||
):
|
||||
self.name = name
|
||||
self.arg_types = 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),
|
||||
])
|
||||
|
||||
|
||||
def method(func: Callable):
|
||||
"""Decorator to mark a function as an Ibis function."""
|
||||
type_hints = get_type_hints(func)
|
||||
arg_types: Dict[str, Type] = {}
|
||||
return_type: Optional[Type] = None
|
||||
for name, type in type_hints.items():
|
||||
if not isinstance(type, Type):
|
||||
raise TypeError(
|
||||
f"Argument {name} of method {func.__name__} is not a CIRCT type")
|
||||
if name == "return":
|
||||
return_type = type
|
||||
else:
|
||||
arg_types[name] = type
|
||||
return IbisMethod(None, arg_types, return_type)
|
||||
|
||||
|
||||
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."""
|
||||
|
||||
SupportFiles: List[Path] = []
|
||||
|
||||
@staticmethod
|
||||
def package(sys: System):
|
||||
"""Copy in the necessary support files."""
|
||||
if SvSupportPath is None:
|
||||
raise RuntimeError(
|
||||
"IBIS_SVSUPPORT environment variable not set. Cannot copy in "
|
||||
"necessary support files.")
|
||||
|
||||
# TODO: make this configurable.
|
||||
platform = "SimOnly"
|
||||
outdir = sys.hw_output_dir
|
||||
|
||||
for idx, f in enumerate(IbisClassBuilder.SupportFiles):
|
||||
shutil.copy(SvSupportPath / f, outdir / f"{idx}_{f.name}")
|
||||
for f in (SvSupportPath / "HAL" / platform).glob("*.sv"):
|
||||
shutil.copy(f, outdir)
|
||||
|
||||
@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."""
|
||||
|
||||
from .system import System
|
||||
|
||||
sys: System = System.current()
|
||||
ret = sys._op_cache.get_circt_mod(self)
|
||||
if ret is None:
|
||||
return sys._create_circt_mod(self)
|
||||
return ret
|
||||
|
||||
def scan_cls(self) -> None:
|
||||
"""Scan the class for Ibis methods and register them."""
|
||||
|
||||
self.methods: List[IbisMethod] = []
|
||||
for name, value in self.cls_dct.items():
|
||||
if isinstance(value, IbisMethod):
|
||||
value.name = name
|
||||
self.methods.append(value)
|
||||
|
||||
self.src_file = None
|
||||
if hasattr(self.modcls, "src_file"):
|
||||
self.src_file = Path(self.modcls.src_file).resolve()
|
||||
if hasattr(self.modcls, "support_files"):
|
||||
IbisClassBuilder.SupportFiles = list([
|
||||
Path(f.strip())
|
||||
for f in Path(self.modcls.support_files).resolve().open().readlines()
|
||||
])
|
||||
|
||||
# Fixed ports -- clock and reset.
|
||||
self.clocks = {0}
|
||||
self.resets = {1}
|
||||
self.ports = [
|
||||
Clock("clk"),
|
||||
Input(Bits(1), "rst"),
|
||||
]
|
||||
# Method ports.
|
||||
self.ports.extend([
|
||||
Input(m.func_type, m.name) for m in self.methods if m.name is not None
|
||||
])
|
||||
# Add indexes to the ports.
|
||||
for idx, port in enumerate(self.ports):
|
||||
port.idx = idx
|
||||
# Ibis-specific generator.
|
||||
self.generators = {"default": generator(self.generate_wrapper)}
|
||||
|
||||
def create_op(self, sys, symbol):
|
||||
"""Creation callback for creating an Ibis method wrapper."""
|
||||
|
||||
# Add metadata to the manifest, if any.
|
||||
if hasattr(self.modcls, "metadata"):
|
||||
meta = self.modcls.metadata
|
||||
self.add_metadata(sys, symbol, meta)
|
||||
else:
|
||||
self.add_metadata(sys, symbol, None)
|
||||
|
||||
# Import the Ibis module to be wrapped.
|
||||
if self.src_file is None:
|
||||
raise RuntimeError(
|
||||
f"Could not find source file for module {self.modcls.name}")
|
||||
imported = sys.import_mlir(open(self.src_file).read())
|
||||
if self.modcls.__name__ not in imported:
|
||||
raise RuntimeError(
|
||||
f"Could not find module {self.modcls.name} in {self.src_file}")
|
||||
self.imported_mod = imported[self.modcls.__name__]
|
||||
|
||||
# Finally, create the module which this method is supposed to return.
|
||||
return hw.HWModuleOp(
|
||||
symbol,
|
||||
[(p.name, p.type._type) for p in self.inputs],
|
||||
[(p.name, p.type._type) for p in self.outputs],
|
||||
attributes=self.attributes,
|
||||
loc=self.loc,
|
||||
ip=sys._get_ip(),
|
||||
)
|
||||
|
||||
def generate_wrapper(self, ports):
|
||||
"""Instantiate and wrap an Ibis module."""
|
||||
System.current().add_packaging_step(IbisClassBuilder.package)
|
||||
|
||||
# Ports we shouldn't touch.
|
||||
exclude_ports = {
|
||||
"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.
|
||||
ibis_inputs = {
|
||||
p.name: Wire(p.type, p.name)
|
||||
for p in self.imported_mod.inputs()
|
||||
if p.name not in exclude_ports
|
||||
}
|
||||
ibis_instance = self.imported_mod(
|
||||
clk=ports.clk.to_bit(),
|
||||
clk1=ports.clk.to_bit(),
|
||||
rst_in=ports.rst,
|
||||
stall_rate_in=None,
|
||||
inspection_value_in=None,
|
||||
stall_rate_valid_in=None,
|
||||
**ibis_inputs,
|
||||
)
|
||||
inst_outputs = ibis_instance.outputs()
|
||||
|
||||
# For each method, connect the Ibis module to the ESI ports.
|
||||
for m in self.methods:
|
||||
assert m.name is not None
|
||||
mname = m.name + "_"
|
||||
|
||||
# Return side is FIFO.
|
||||
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)
|
||||
ibis_inputs[mname + "rden_in"].assign(rdy & ~empty_out)
|
||||
|
||||
# Pack the bundle and set it on my output port.
|
||||
arg_chan = getattr(ports, m.name).unpack(result=return_chan)["arg"]
|
||||
|
||||
# Call side is ready/valid.
|
||||
args_rdy = inst_outputs[mname + "rdy_out"]
|
||||
args, args_valid = arg_chan.unwrap(args_rdy)
|
||||
ibis_inputs[mname + "valid_in"].assign(args_valid)
|
||||
for name, _ in m.arg_types.items():
|
||||
input_wire = ibis_inputs[mname + name + "_in"]
|
||||
input_wire.assign(args[name].bitcast(input_wire.type))
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Name the wrapper module."""
|
||||
ibis_cls_name = super().name
|
||||
return ibis_cls_name + "_esi_wrapper"
|
||||
|
||||
|
||||
class IbisClass(Module):
|
||||
"""Base class to be extended for describing an Ibis class method."""
|
||||
|
||||
BuilderType = IbisClassBuilder
|
Loading…
Reference in New Issue