build.{res,plat}: propagate extras to pin fragment factories.

This is necessary because on some platforms, like iCE40, extras
become parameters on an IO primitive, since the constraint file
format is not expressive enough for all of them.
This commit is contained in:
whitequark 2019-06-03 01:58:43 +00:00
parent 268fe6330e
commit fb01854372
3 changed files with 93 additions and 58 deletions

View file

@ -116,27 +116,27 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
fragment = Fragment.get(fragment, self)
pin_fragments = []
for pin, port in self._se_pins:
if pin.dir == "i":
pin_fragments.append((pin.name, self.get_input(pin, port)))
if pin.dir == "o":
pin_fragments.append((pin.name, self.get_output(pin, port)))
if pin.dir == "io":
pin_fragments.append((pin.name, self.get_tristate(pin, port)))
for pin, p_port, n_port in self._dp_pins:
if pin.dir == "i":
pin_fragments.append((pin.name, self.get_diff_input(pin, p_port, n_port)))
if pin.dir == "o":
pin_fragments.append((pin.name, self.get_diff_output(pin, p_port, n_port)))
if pin.dir == "io":
pin_fragments.append((pin.name, self.get_diff_tristate(pin, p_port, n_port)))
for pin_name, pin_fragment in pin_fragments:
def add_pin_fragment(pin, pin_fragment):
pin_fragment = Fragment.get(pin_fragment, self)
if not isinstance(pin_fragment, Instance):
pin_fragment.flatten = True
fragment.add_subfragment(pin_fragment, name="pin_{}".format(pin_name))
fragment.add_subfragment(pin_fragment, name="pin_{}".format(pin.name))
for pin, port, extras in self.iter_single_ended_pins():
if pin.dir == "i":
add_pin_fragment(pin, self.get_input(pin, port, extras))
if pin.dir == "o":
add_pin_fragment(pin, self.get_output(pin, port, extras))
if pin.dir == "io":
add_pin_fragment(pin, self.get_input(pin, port, extras))
for pin, p_port, n_port, extras in self.iter_differential_pins():
if pin.dir == "i":
add_pin_fragment(pin, self.get_diff_input(pin, p_port, n_port))
if pin.dir == "o":
add_pin_fragment(pin, self.get_diff_output(pin, p_port, n_port))
if pin.dir == "io":
add_pin_fragment(pin, self.get_diff_tristate(pin, p_port, n_port))
return self.toolchain_prepare(fragment, name, **kwargs)
@ -155,30 +155,37 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
raise NotImplementedError("Platform {} does not support programming"
.format(self.__class__.__name__))
def _check_feature(self, feature, pin, xdrs):
if not xdrs:
def _check_feature(self, feature, pin, extras, valid_xdrs, valid_extras):
if not valid_xdrs:
raise NotImplementedError("Platform {} does not support {}"
.format(self.__class__.__name__, feature))
elif pin.xdr not in xdrs:
elif pin.xdr not in valid_xdrs:
raise NotImplementedError("Platform {} does not support {} for XDR {}"
.format(self.__class__.__name__, feature, pin.xdr))
def get_input(self, pin, port):
self._check_feature("single-ended input", pin, xdrs=(1,))
if not valid_extras and extras:
raise NotImplementedError("Platform {} does not support extras for {}"
.format(self.__class__.__name__, feature))
def get_input(self, pin, port, extras):
self._check_feature("single-ended input", pin, extras,
valid_xdrs=(1,), valid_extras=None)
m = Module()
m.d.comb += pin.i.eq(port)
return m
def get_output(self, pin, port):
self._check_feature("single-ended output", pin, xdrs=(1,))
def get_output(self, pin, port, extras):
self._check_feature("single-ended output", pin, extras,
valid_xdrs=(1,), valid_extras=None)
m = Module()
m.d.comb += port.eq(pin.o)
return m
def get_tristate(self, pin, port):
self._check_feature("single-ended tristate", pin, xdrs=(1,))
def get_tristate(self, pin, port, extras):
self._check_feature("single-ended tristate", pin, extras,
valid_xdrs=(1,), valid_extras=None)
m = Module()
m.submodules += Instance("$tribuf",
@ -190,14 +197,17 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
m.d.comb += pin.i.eq(port)
return m
def get_diff_input(self, pin, p_port, n_port):
self._check_feature("differential input", pin, xdrs=())
def get_diff_input(self, pin, p_port, n_port, extras):
self._check_feature("differential input", pin, extras,
valid_xdrs=(), valid_extras=None)
def get_diff_output(self, pin, p_port, n_port):
self._check_feature("differential output", pin, xdrs=())
def get_diff_output(self, pin, p_port, n_port, extras):
self._check_feature("differential output", pin, extras,
valid_xdrs=(), valid_extras=None)
def get_diff_tristate(self, pin, p_port, n_port):
self._check_feature("differential tristate", pin, xdrs=())
def get_diff_tristate(self, pin, p_port, n_port, extras):
self._check_feature("differential tristate", pin, extras,
valid_xdrs=(), valid_extras=None)
class TemplatedPlatform(Platform):

View file

@ -19,10 +19,7 @@ class ConstraintManager:
self.resources = OrderedDict()
self.requested = OrderedDict()
self.clocks = OrderedDict()
self._ports = []
self._se_pins = []
self._dp_pins = []
self.add_resources(resources)
for name_number, frequency in clocks:
@ -113,19 +110,12 @@ class ConstraintManager:
elif isinstance(subsignal.io[0], (Pins, DiffPairs)):
phys = subsignal.io[0]
pin = Pin(len(phys), dir, xdr, name=name)
if isinstance(phys, Pins):
port = Signal(pin.width, name="{}_io".format(pin.name))
self._se_pins.append((pin, port))
self._ports.append((port, phys.names, subsignal.extras))
if isinstance(phys, DiffPairs):
p_port = Signal(pin.width, name="{}_p".format(pin.name))
n_port = Signal(pin.width, name="{}_n".format(pin.name))
self._dp_pins.append((pin, p_port, n_port))
self._ports.append((p_port, phys.p.names, subsignal.extras))
self._ports.append((n_port, phys.n.names, subsignal.extras))
port = (Signal(pin.width, name="{}_p".format(pin.name)),
Signal(pin.width, name="{}_n".format(pin.name)))
self._ports.append((subsignal, pin, port))
return pin
else:
assert False # :nocov:
@ -136,13 +126,42 @@ class ConstraintManager:
self.requested[resource.name, resource.number] = value
return value
def iter_ports(self):
for port, pins, extras in self._ports:
yield port
def iter_single_ended_pins(self):
for resource, pin, port in self._ports:
if isinstance(resource.io[0], Pins):
yield pin, port, resource.extras
def iter_port_constraints(self):
for port, pins, extras in self._ports:
yield (port.name, pins, extras)
def iter_differential_pins(self):
for resource, pin, port in self._ports:
if isinstance(resource.io[0], DiffPairs):
p_port, n_port = port
yield pin, p_port, n_port, resource.extras
def iter_ports(self):
for resource, pin, port in self._ports:
if isinstance(resource.io[0], Pins):
yield port
elif isinstance(resource.io[0], DiffPairs):
p_port, n_port = port
yield p_port
yield n_port
else:
assert False
def iter_port_constraints(self, diff_pins="pn"):
for resource, pin, port in self._ports:
if isinstance(resource.io[0], Pins):
yield port.name, resource.io[0].names, resource.extras
elif isinstance(resource.io[0], DiffPairs):
p_port, n_port = port
# On some FPGAs like iCE40, only one pin out of two in a differential pair may be
# constrained. The other has to be completely disconnected.
if "p" in diff_pins:
yield p_port.name, resource.io[0].p.names, resource.extras
if "n" in diff_pins:
yield n_port.name, resource.io[0].n.names, resource.extras
else:
assert False
def iter_clock_constraints(self):
for name, number in self.clocks.keys() & self.requested.keys():

View file

@ -85,9 +85,9 @@ class ConstraintManagerTestCase(FHDLTestCase):
self.assertEqual(ports[1].name, "i2c_0__sda_io")
self.assertEqual(ports[1].nbits, 1)
self.assertEqual(self.cm._se_pins, [
(i2c.scl, scl),
(i2c.sda, sda),
self.assertEqual(list(self.cm.iter_single_ended_pins()), [
(i2c.scl, scl, {}),
(i2c.sda, sda, {}),
])
self.assertEqual(list(self.cm.iter_port_constraints()), [
("i2c_0__scl_io", ["N10"], {}),
@ -108,12 +108,18 @@ class ConstraintManagerTestCase(FHDLTestCase):
self.assertEqual(n.name, "clk100_0_n")
self.assertEqual(n.nbits, clk100.width)
self.assertEqual(self.cm._dp_pins, [
(clk100, p, n),
self.assertEqual(list(self.cm.iter_differential_pins()), [
(clk100, p, n, {}),
])
self.assertEqual(list(self.cm.iter_port_constraints()), [
("clk100_0_p", ["H1"], {}),
("clk100_0_n", ["H2"], {})
("clk100_0_n", ["H2"], {}),
])
self.assertEqual(list(self.cm.iter_port_constraints(diff_pins="p")), [
("clk100_0_p", ["H1"], {}),
])
self.assertEqual(list(self.cm.iter_port_constraints(diff_pins="n")), [
("clk100_0_n", ["H2"], {}),
])
def test_add_clock(self):