hdl.ir: add IOBufferInstance.
This commit is contained in:
parent
85bb5ee77c
commit
c6bc9b47ef
|
@ -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):
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue