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

View file

@ -19,10 +19,7 @@ class ConstraintManager:
self.resources = OrderedDict() self.resources = OrderedDict()
self.requested = OrderedDict() self.requested = OrderedDict()
self.clocks = OrderedDict() self.clocks = OrderedDict()
self._ports = [] self._ports = []
self._se_pins = []
self._dp_pins = []
self.add_resources(resources) self.add_resources(resources)
for name_number, frequency in clocks: for name_number, frequency in clocks:
@ -113,19 +110,12 @@ class ConstraintManager:
elif isinstance(subsignal.io[0], (Pins, DiffPairs)): elif isinstance(subsignal.io[0], (Pins, DiffPairs)):
phys = subsignal.io[0] phys = subsignal.io[0]
pin = Pin(len(phys), dir, xdr, name=name) pin = Pin(len(phys), dir, xdr, name=name)
if isinstance(phys, Pins): if isinstance(phys, Pins):
port = Signal(pin.width, name="{}_io".format(pin.name)) 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): if isinstance(phys, DiffPairs):
p_port = Signal(pin.width, name="{}_p".format(pin.name)) port = (Signal(pin.width, name="{}_p".format(pin.name)),
n_port = Signal(pin.width, name="{}_n".format(pin.name)) Signal(pin.width, name="{}_n".format(pin.name)))
self._dp_pins.append((pin, p_port, n_port)) self._ports.append((subsignal, pin, port))
self._ports.append((p_port, phys.p.names, subsignal.extras))
self._ports.append((n_port, phys.n.names, subsignal.extras))
return pin return pin
else: else:
assert False # :nocov: assert False # :nocov:
@ -136,13 +126,42 @@ class ConstraintManager:
self.requested[resource.name, resource.number] = value self.requested[resource.name, resource.number] = value
return value return value
def iter_ports(self): def iter_single_ended_pins(self):
for port, pins, extras in self._ports: for resource, pin, port in self._ports:
yield port if isinstance(resource.io[0], Pins):
yield pin, port, resource.extras
def iter_port_constraints(self): def iter_differential_pins(self):
for port, pins, extras in self._ports: for resource, pin, port in self._ports:
yield (port.name, pins, extras) 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): def iter_clock_constraints(self):
for name, number in self.clocks.keys() & self.requested.keys(): 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].name, "i2c_0__sda_io")
self.assertEqual(ports[1].nbits, 1) self.assertEqual(ports[1].nbits, 1)
self.assertEqual(self.cm._se_pins, [ self.assertEqual(list(self.cm.iter_single_ended_pins()), [
(i2c.scl, scl), (i2c.scl, scl, {}),
(i2c.sda, sda), (i2c.sda, sda, {}),
]) ])
self.assertEqual(list(self.cm.iter_port_constraints()), [ self.assertEqual(list(self.cm.iter_port_constraints()), [
("i2c_0__scl_io", ["N10"], {}), ("i2c_0__scl_io", ["N10"], {}),
@ -108,12 +108,18 @@ class ConstraintManagerTestCase(FHDLTestCase):
self.assertEqual(n.name, "clk100_0_n") self.assertEqual(n.name, "clk100_0_n")
self.assertEqual(n.nbits, clk100.width) self.assertEqual(n.nbits, clk100.width)
self.assertEqual(self.cm._dp_pins, [ self.assertEqual(list(self.cm.iter_differential_pins()), [
(clk100, p, n), (clk100, p, n, {}),
]) ])
self.assertEqual(list(self.cm.iter_port_constraints()), [ self.assertEqual(list(self.cm.iter_port_constraints()), [
("clk100_0_p", ["H1"], {}), ("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): def test_add_clock(self):