hdl.ir: add IOBufferInstance.
This commit is contained in:
parent
85bb5ee77c
commit
c6bc9b47ef
|
@ -9,6 +9,7 @@ import jinja2
|
||||||
from .. import __version__
|
from .. import __version__
|
||||||
from .._toolchain import *
|
from .._toolchain import *
|
||||||
from ..hdl import *
|
from ..hdl import *
|
||||||
|
from ..hdl._ir import IOBufferInstance
|
||||||
from ..hdl._xfrm import DomainLowerer
|
from ..hdl._xfrm import DomainLowerer
|
||||||
from ..lib.cdc import ResetSynchronizer
|
from ..lib.cdc import ResetSynchronizer
|
||||||
from ..back import rtlil, verilog
|
from ..back import rtlil, verilog
|
||||||
|
@ -221,11 +222,10 @@ class Platform(ResourceManager, metaclass=ABCMeta):
|
||||||
valid_xdrs=(0,), valid_attrs=None)
|
valid_xdrs=(0,), valid_attrs=None)
|
||||||
|
|
||||||
m = Module()
|
m = Module()
|
||||||
m.submodules += Instance("$tribuf",
|
m.submodules += IOBufferInstance(
|
||||||
p_WIDTH=pin.width,
|
pad=port,
|
||||||
i_EN=pin.oe,
|
o=self._invert_if(invert, pin.o),
|
||||||
i_A=self._invert_if(invert, pin.o),
|
oe=pin.oe,
|
||||||
o_Y=port,
|
|
||||||
)
|
)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
@ -234,13 +234,14 @@ class Platform(ResourceManager, metaclass=ABCMeta):
|
||||||
valid_xdrs=(0,), valid_attrs=None)
|
valid_xdrs=(0,), valid_attrs=None)
|
||||||
|
|
||||||
m = Module()
|
m = Module()
|
||||||
m.submodules += Instance("$tribuf",
|
i = Signal.like(pin.i)
|
||||||
p_WIDTH=pin.width,
|
m.submodules += IOBufferInstance(
|
||||||
i_EN=pin.oe,
|
pad=port,
|
||||||
i_A=self._invert_if(invert, pin.o),
|
i=i,
|
||||||
o_Y=port,
|
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
|
return m
|
||||||
|
|
||||||
def get_diff_input(self, pin, port, attrs, invert):
|
def get_diff_input(self, pin, port, attrs, invert):
|
||||||
|
|
|
@ -11,7 +11,7 @@ from . import _ast, _cd, _ir, _nir
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"UnusedElaboratable", "Elaboratable", "DriverConflict", "Fragment", "Instance",
|
"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.
|
# Always flatten subfragments that explicitly request it.
|
||||||
flatten_subfrags.add((subfrag, subfrag_hierarchy))
|
flatten_subfrags.add((subfrag, subfrag_hierarchy))
|
||||||
|
|
||||||
if isinstance(subfrag, (Instance, MemoryInstance)):
|
if isinstance(subfrag, (Instance, MemoryInstance, IOBufferInstance)):
|
||||||
# Never flatten instances.
|
# Never flatten instances.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -460,6 +460,36 @@ class Instance(Fragment):
|
||||||
.format(kw, arg))
|
.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:
|
class Design:
|
||||||
"""Represents a design ready for simulation or netlist building.
|
"""Represents a design ready for simulation or netlist building.
|
||||||
|
|
||||||
|
@ -943,13 +973,15 @@ class NetlistEmitter:
|
||||||
else:
|
else:
|
||||||
assert False # :nocov:
|
assert False # :nocov:
|
||||||
|
|
||||||
def emit_tribuf(self, module_idx: int, instance: _ir.Instance):
|
def emit_iobuffer(self, module_idx: int, instance: _ir.IOBufferInstance):
|
||||||
pad = self.emit_lhs(instance.named_ports["Y"][0])
|
pad = self.emit_lhs(instance.pad)
|
||||||
o, _signed = self.emit_rhs(module_idx, instance.named_ports["A"][0])
|
o, _signed = self.emit_rhs(module_idx, instance.o)
|
||||||
(oe,), _signed = self.emit_rhs(module_idx, instance.named_ports["EN"][0])
|
(oe,), _signed = self.emit_rhs(module_idx, instance.oe)
|
||||||
assert len(pad) == len(o)
|
assert len(pad) == len(o)
|
||||||
cell = _nir.IOBuffer(module_idx, pad=pad, o=o, oe=oe, src_loc=instance.src_loc)
|
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):
|
def emit_memory(self, module_idx: int, fragment: '_mem.MemoryInstance', name: str):
|
||||||
cell = _nir.Memory(module_idx,
|
cell = _nir.Memory(module_idx,
|
||||||
|
@ -1130,10 +1162,7 @@ class NetlistEmitter:
|
||||||
fragment_name = self.design.fragment_names[fragment]
|
fragment_name = self.design.fragment_names[fragment]
|
||||||
if isinstance(fragment, _ir.Instance):
|
if isinstance(fragment, _ir.Instance):
|
||||||
assert parent_module_idx is not None
|
assert parent_module_idx is not None
|
||||||
if fragment.type == "$tribuf":
|
self.emit_instance(parent_module_idx, fragment, name=fragment_name[-1])
|
||||||
self.emit_tribuf(parent_module_idx, fragment)
|
|
||||||
else:
|
|
||||||
self.emit_instance(parent_module_idx, fragment, name=fragment_name[-1])
|
|
||||||
elif isinstance(fragment, _mem.MemoryInstance):
|
elif isinstance(fragment, _mem.MemoryInstance):
|
||||||
assert parent_module_idx is not None
|
assert parent_module_idx is not None
|
||||||
memory = self.emit_memory(parent_module_idx, fragment, name=fragment_name[-1])
|
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))
|
write_ports.append(self.emit_write_port(parent_module_idx, fragment, port, memory))
|
||||||
for port in fragment._read_ports:
|
for port in fragment._read_ports:
|
||||||
self.emit_read_port(parent_module_idx, fragment, port, memory, write_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:
|
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)
|
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]
|
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 = Instance(fragment.type, src_loc=fragment.src_loc)
|
||||||
new_fragment.parameters = OrderedDict(fragment.parameters)
|
new_fragment.parameters = OrderedDict(fragment.parameters)
|
||||||
self.map_named_ports(fragment, new_fragment)
|
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:
|
else:
|
||||||
new_fragment = Fragment(src_loc=fragment.src_loc)
|
new_fragment = Fragment(src_loc=fragment.src_loc)
|
||||||
new_fragment.flatten = fragment.flatten
|
new_fragment.flatten = fragment.flatten
|
||||||
|
|
|
@ -938,4 +938,124 @@ class OriginsTestCase(FHDLTestCase):
|
||||||
del inst.origins
|
del inst.origins
|
||||||
elab = ElaboratesTo(inst)
|
elab = ElaboratesTo(inst)
|
||||||
frag = Fragment.get(elab, platform=None)
|
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