hdl.ir: add IOBufferInstance.

This commit is contained in:
Wanda 2024-02-12 15:11:42 +01:00 committed by Catherine
parent 85bb5ee77c
commit c6bc9b47ef
4 changed files with 193 additions and 23 deletions

View file

@ -9,6 +9,7 @@ import jinja2
from .. import __version__
from .._toolchain import *
from ..hdl import *
from ..hdl._ir import IOBufferInstance
from ..hdl._xfrm import DomainLowerer
from ..lib.cdc import ResetSynchronizer
from ..back import rtlil, verilog
@ -221,11 +222,10 @@ class Platform(ResourceManager, metaclass=ABCMeta):
valid_xdrs=(0,), valid_attrs=None)
m = Module()
m.submodules += Instance("$tribuf",
p_WIDTH=pin.width,
i_EN=pin.oe,
i_A=self._invert_if(invert, pin.o),
o_Y=port,
m.submodules += IOBufferInstance(
pad=port,
o=self._invert_if(invert, pin.o),
oe=pin.oe,
)
return m
@ -234,13 +234,14 @@ class Platform(ResourceManager, metaclass=ABCMeta):
valid_xdrs=(0,), valid_attrs=None)
m = Module()
m.submodules += Instance("$tribuf",
p_WIDTH=pin.width,
i_EN=pin.oe,
i_A=self._invert_if(invert, pin.o),
o_Y=port,
i = Signal.like(pin.i)
m.submodules += IOBufferInstance(
pad=port,
i=i,
o=self._invert_if(invert, pin.o),
oe=pin.oe,
)
m.d.comb += pin.i.eq(self._invert_if(invert, port))
m.d.comb += pin.i.eq(self._invert_if(invert, i))
return m
def get_diff_input(self, pin, port, attrs, invert):

View file

@ -11,7 +11,7 @@ from . import _ast, _cd, _ir, _nir
__all__ = [
"UnusedElaboratable", "Elaboratable", "DriverConflict", "Fragment", "Instance",
"PortDirection", "Design", "build_netlist",
"IOBufferInstance", "PortDirection", "Design", "build_netlist",
]
@ -196,7 +196,7 @@ class Fragment:
# Always flatten subfragments that explicitly request it.
flatten_subfrags.add((subfrag, subfrag_hierarchy))
if isinstance(subfrag, (Instance, MemoryInstance)):
if isinstance(subfrag, (Instance, MemoryInstance, IOBufferInstance)):
# Never flatten instances.
continue
@ -460,6 +460,36 @@ class Instance(Fragment):
.format(kw, arg))
class IOBufferInstance(Fragment):
def __init__(self, pad, *, i=None, o=None, oe=None, src_loc_at=0, src_loc=None):
super().__init__()
self.pad = _ast.Value.cast(pad)
if i is None:
self.i = None
else:
self.i = _ast.Value.cast(i)
if len(self.pad) != len(self.i):
raise ValueError(f"`pad` length ({len(self.pad)}) doesn't match `i` length ({len(self.i)})")
if o is None:
if oe is not None:
raise ValueError("`oe` must not be used if `o` is not used")
self.o = _ast.Const(0, len(self.pad))
self.oe = _ast.Const(0)
else:
self.o = _ast.Value.cast(o)
if len(self.pad) != len(self.o):
raise ValueError(f"`pad` length ({len(self.pad)}) doesn't match `o` length ({len(self.o)})")
if oe is None:
self.oe = _ast.Const(1)
else:
self.oe = _ast.Value.cast(oe)
if len(self.oe) != 1:
raise ValueError(f"`oe` length ({len(self.oe)}) must be 1")
self.src_loc = src_loc or tracer.get_src_loc(src_loc_at)
class Design:
"""Represents a design ready for simulation or netlist building.
@ -943,13 +973,15 @@ class NetlistEmitter:
else:
assert False # :nocov:
def emit_tribuf(self, module_idx: int, instance: _ir.Instance):
pad = self.emit_lhs(instance.named_ports["Y"][0])
o, _signed = self.emit_rhs(module_idx, instance.named_ports["A"][0])
(oe,), _signed = self.emit_rhs(module_idx, instance.named_ports["EN"][0])
def emit_iobuffer(self, module_idx: int, instance: _ir.IOBufferInstance):
pad = self.emit_lhs(instance.pad)
o, _signed = self.emit_rhs(module_idx, instance.o)
(oe,), _signed = self.emit_rhs(module_idx, instance.oe)
assert len(pad) == len(o)
cell = _nir.IOBuffer(module_idx, pad=pad, o=o, oe=oe, src_loc=instance.src_loc)
self.netlist.add_cell(cell)
value = self.netlist.add_value_cell(len(pad), cell)
if instance.i is not None:
self.connect(self.emit_lhs(instance.i), value, src_loc=instance.src_loc)
def emit_memory(self, module_idx: int, fragment: '_mem.MemoryInstance', name: str):
cell = _nir.Memory(module_idx,
@ -1130,10 +1162,7 @@ class NetlistEmitter:
fragment_name = self.design.fragment_names[fragment]
if isinstance(fragment, _ir.Instance):
assert parent_module_idx is not None
if fragment.type == "$tribuf":
self.emit_tribuf(parent_module_idx, fragment)
else:
self.emit_instance(parent_module_idx, fragment, name=fragment_name[-1])
self.emit_instance(parent_module_idx, fragment, name=fragment_name[-1])
elif isinstance(fragment, _mem.MemoryInstance):
assert parent_module_idx is not None
memory = self.emit_memory(parent_module_idx, fragment, name=fragment_name[-1])
@ -1142,6 +1171,9 @@ class NetlistEmitter:
write_ports.append(self.emit_write_port(parent_module_idx, fragment, port, memory))
for port in fragment._read_ports:
self.emit_read_port(parent_module_idx, fragment, port, memory, write_ports)
elif isinstance(fragment, _ir.IOBufferInstance):
assert parent_module_idx is not None
self.emit_iobuffer(parent_module_idx, fragment)
elif type(fragment) is _ir.Fragment:
module_idx = self.netlist.add_module(parent_module_idx, fragment_name, src_loc=fragment.src_loc, cell_src_loc=cell_src_loc)
signal_names = self.design.signal_names[fragment]

View file

@ -277,6 +277,23 @@ class FragmentTransformer:
new_fragment = Instance(fragment.type, src_loc=fragment.src_loc)
new_fragment.parameters = OrderedDict(fragment.parameters)
self.map_named_ports(fragment, new_fragment)
elif isinstance(fragment, IOBufferInstance):
if hasattr(self, "on_value"):
new_fragment = IOBufferInstance(
pad=self.on_value(fragment.pad),
i=self.on_value(fragment.i) if fragment.i is not None else None,
o=self.on_value(fragment.o),
oe=self.on_value(fragment.oe),
src_loc=fragment.src_loc,
)
else:
new_fragment = IOBufferInstance(
pad=fragment.pad,
i=fragment.i,
o=fragment.o,
oe=fragment.oe,
src_loc=fragment.src_loc,
)
else:
new_fragment = Fragment(src_loc=fragment.src_loc)
new_fragment.flatten = fragment.flatten

View file

@ -938,4 +938,124 @@ class OriginsTestCase(FHDLTestCase):
del inst.origins
elab = ElaboratesTo(inst)
frag = Fragment.get(elab, platform=None)
self.assertFalse(hasattr(frag, "_origins"))
self.assertFalse(hasattr(frag, "_origins"))
class IOBufferTestCase(FHDLTestCase):
def test_nir_i(self):
pad = Signal(4)
i = Signal(4)
f = Fragment()
f.add_subfragment(IOBufferInstance(pad, i=i))
nl = build_netlist(f, ports=[pad, i])
self.assertRepr(nl, """
(
(module 0 None ('top')
(inout 'pad' 0.2:6)
(output 'i' 1.0:4)
)
(cell 0 0 (top
(output 'i' 1.0:4)
(inout 'pad' 2:6)
))
(cell 1 0 (iob 0.2:6 4'd0 0))
)
""")
def test_nir_o(self):
pad = Signal(4)
o = Signal(4)
f = Fragment()
f.add_subfragment(IOBufferInstance(pad, o=o))
nl = build_netlist(f, ports=[pad, o])
self.assertRepr(nl, """
(
(module 0 None ('top')
(input 'o' 0.6:10)
(inout 'pad' 0.2:6)
)
(cell 0 0 (top
(input 'o' 6:10)
(inout 'pad' 2:6)
))
(cell 1 0 (iob 0.2:6 0.6:10 1))
)
""")
def test_nir_oe(self):
pad = Signal(4)
o = Signal(4)
oe = Signal()
f = Fragment()
f.add_subfragment(IOBufferInstance(pad, o=o, oe=oe))
nl = build_netlist(f, ports=[pad, o, oe])
self.assertRepr(nl, """
(
(module 0 None ('top')
(input 'o' 0.6:10)
(input 'oe' 0.10)
(inout 'pad' 0.2:6)
)
(cell 0 0 (top
(input 'o' 6:10)
(input 'oe' 10:11)
(inout 'pad' 2:6)
))
(cell 1 0 (iob 0.2:6 0.6:10 0.10))
)
""")
def test_nir_io(self):
pad = Signal(4)
i = Signal(4)
o = Signal(4)
oe = Signal()
f = Fragment()
f.add_subfragment(IOBufferInstance(pad, i=i, o=o, oe=oe))
nl = build_netlist(f, ports=[pad, i, o, oe])
self.assertRepr(nl, """
(
(module 0 None ('top')
(input 'o' 0.6:10)
(input 'oe' 0.10)
(inout 'pad' 0.2:6)
(output 'i' 1.0:4)
)
(cell 0 0 (top
(output 'i' 1.0:4)
(input 'o' 6:10)
(input 'oe' 10:11)
(inout 'pad' 2:6)
))
(cell 1 0 (iob 0.2:6 0.6:10 0.10))
)
""")
def test_wrong_i(self):
pad = Signal(4)
i = Signal()
with self.assertRaisesRegex(ValueError,
r"^`pad` length \(4\) doesn't match `i` length \(1\)"):
IOBufferInstance(pad, i=i)
def test_wrong_o(self):
pad = Signal(4)
o = Signal()
with self.assertRaisesRegex(ValueError,
r"^`pad` length \(4\) doesn't match `o` length \(1\)"):
IOBufferInstance(pad, o=o)
def test_wrong_oe(self):
pad = Signal(4)
o = Signal(4)
oe = Signal(4)
with self.assertRaisesRegex(ValueError,
r"^`oe` length \(4\) must be 1"):
IOBufferInstance(pad, o=o, oe=oe)
def test_wrong_oe_without_o(self):
pad = Signal(4)
oe = Signal()
with self.assertRaisesRegex(ValueError,
r"^`oe` must not be used if `o` is not used"):
IOBufferInstance(pad, oe=oe)