parent
a013eb1f59
commit
ed64880cc4
|
@ -75,10 +75,11 @@ class BuildProducts:
|
||||||
|
|
||||||
class Platform(ConstraintManager, metaclass=ABCMeta):
|
class Platform(ConstraintManager, metaclass=ABCMeta):
|
||||||
resources = abstractproperty()
|
resources = abstractproperty()
|
||||||
|
connectors = abstractproperty()
|
||||||
clocks = abstractproperty()
|
clocks = abstractproperty()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(self.resources, self.clocks)
|
super().__init__(self.resources, self.connectors, self.clocks)
|
||||||
|
|
||||||
self.extra_files = OrderedDict()
|
self.extra_files = OrderedDict()
|
||||||
|
|
||||||
|
|
|
@ -15,26 +15,43 @@ class ConstraintError(Exception):
|
||||||
|
|
||||||
|
|
||||||
class ConstraintManager:
|
class ConstraintManager:
|
||||||
def __init__(self, resources, clocks):
|
def __init__(self, resources, connectors, clocks):
|
||||||
self.resources = OrderedDict()
|
self.resources = OrderedDict()
|
||||||
self.requested = OrderedDict()
|
self.connectors = OrderedDict()
|
||||||
self.clocks = OrderedDict()
|
self.clocks = OrderedDict()
|
||||||
|
|
||||||
|
self._mapping = OrderedDict()
|
||||||
|
self._requested = OrderedDict()
|
||||||
self._ports = []
|
self._ports = []
|
||||||
|
|
||||||
self.add_resources(resources)
|
self.add_resources(resources)
|
||||||
|
self.add_connectors(connectors)
|
||||||
for name_number, frequency in clocks:
|
for name_number, frequency in clocks:
|
||||||
if not isinstance(name_number, tuple):
|
if not isinstance(name_number, tuple):
|
||||||
name_number = (name_number, 0)
|
name_number = (name_number, 0)
|
||||||
self.add_clock(*name_number, frequency)
|
self.add_clock(*name_number, frequency)
|
||||||
|
|
||||||
def add_resources(self, resources):
|
def add_resources(self, resources):
|
||||||
for r in resources:
|
for res in resources:
|
||||||
if not isinstance(r, Resource):
|
if not isinstance(res, Resource):
|
||||||
raise TypeError("Object {!r} is not a Resource".format(r))
|
raise TypeError("Object {!r} is not a Resource".format(res))
|
||||||
if (r.name, r.number) in self.resources:
|
if (res.name, res.number) in self.resources:
|
||||||
raise NameError("Trying to add {!r}, but {!r} has the same name and number"
|
raise NameError("Trying to add {!r}, but {!r} has the same name and number"
|
||||||
.format(r, self.resources[r.name, r.number]))
|
.format(res, self.resources[res.name, res.number]))
|
||||||
self.resources[r.name, r.number] = r
|
self.resources[res.name, res.number] = res
|
||||||
|
|
||||||
|
def add_connectors(self, connectors):
|
||||||
|
for conn in connectors:
|
||||||
|
if not isinstance(conn, Connector):
|
||||||
|
raise TypeError("Object {!r} is not a Connector".format(conn))
|
||||||
|
if (conn.name, conn.number) in self.connectors:
|
||||||
|
raise NameError("Trying to add {!r}, but {!r} has the same name and number"
|
||||||
|
.format(conn, self.connectors[conn.name, conn.number]))
|
||||||
|
self.connectors[conn.name, conn.number] = conn
|
||||||
|
|
||||||
|
for conn_pin, plat_pin in conn:
|
||||||
|
assert conn_pin not in self._mapping
|
||||||
|
self._mapping[conn_pin] = plat_pin
|
||||||
|
|
||||||
def add_clock(self, name, number, frequency):
|
def add_clock(self, name, number, frequency):
|
||||||
resource = self.lookup(name, number)
|
resource = self.lookup(name, number)
|
||||||
|
@ -57,7 +74,7 @@ class ConstraintManager:
|
||||||
|
|
||||||
def request(self, name, number=0, *, dir=None, xdr=None):
|
def request(self, name, number=0, *, dir=None, xdr=None):
|
||||||
resource = self.lookup(name, number)
|
resource = self.lookup(name, number)
|
||||||
if (resource.name, resource.number) in self.requested:
|
if (resource.name, resource.number) in self._requested:
|
||||||
raise ConstraintError("Resource {}#{} has already been requested"
|
raise ConstraintError("Resource {}#{} has already been requested"
|
||||||
.format(name, number))
|
.format(name, number))
|
||||||
|
|
||||||
|
@ -129,48 +146,48 @@ class ConstraintManager:
|
||||||
value = resolve(resource,
|
value = resolve(resource,
|
||||||
*merge_options(resource, dir, xdr),
|
*merge_options(resource, dir, xdr),
|
||||||
name="{}_{}".format(resource.name, resource.number))
|
name="{}_{}".format(resource.name, resource.number))
|
||||||
self.requested[resource.name, resource.number] = value
|
self._requested[resource.name, resource.number] = value
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def iter_single_ended_pins(self):
|
def iter_single_ended_pins(self):
|
||||||
for resource, pin, port in self._ports:
|
for res, pin, port in self._ports:
|
||||||
if pin is None:
|
if pin is None:
|
||||||
continue
|
continue
|
||||||
if isinstance(resource.io[0], Pins):
|
if isinstance(res.io[0], Pins):
|
||||||
yield pin, port.io, resource.extras
|
yield pin, port.io, res.extras
|
||||||
|
|
||||||
def iter_differential_pins(self):
|
def iter_differential_pins(self):
|
||||||
for resource, pin, port in self._ports:
|
for res, pin, port in self._ports:
|
||||||
if pin is None:
|
if pin is None:
|
||||||
continue
|
continue
|
||||||
if isinstance(resource.io[0], DiffPairs):
|
if isinstance(res.io[0], DiffPairs):
|
||||||
yield pin, port.p, port.n, resource.extras
|
yield pin, port.p, port.n, res.extras
|
||||||
|
|
||||||
def iter_ports(self):
|
def iter_ports(self):
|
||||||
for resource, pin, port in self._ports:
|
for res, pin, port in self._ports:
|
||||||
if isinstance(resource.io[0], Pins):
|
if isinstance(res.io[0], Pins):
|
||||||
yield port.io
|
yield port.io
|
||||||
elif isinstance(resource.io[0], DiffPairs):
|
elif isinstance(res.io[0], DiffPairs):
|
||||||
yield port.p
|
yield port.p
|
||||||
yield port.n
|
yield port.n
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
def iter_port_constraints(self):
|
def iter_port_constraints(self):
|
||||||
for resource, pin, port in self._ports:
|
for res, pin, port in self._ports:
|
||||||
if isinstance(resource.io[0], Pins):
|
if isinstance(res.io[0], Pins):
|
||||||
yield port.io.name, resource.io[0].names, resource.extras
|
yield port.io.name, list(res.io[0].map_names(self._mapping, res)), res.extras
|
||||||
elif isinstance(resource.io[0], DiffPairs):
|
elif isinstance(res.io[0], DiffPairs):
|
||||||
yield port.p.name, resource.io[0].p.names, resource.extras
|
yield port.p.name, list(res.io[0].p.map_names(self._mapping, res)), res.extras
|
||||||
yield port.n.name, resource.io[0].n.names, resource.extras
|
yield port.n.name, list(res.io[0].n.map_names(self._mapping, res)), res.extras
|
||||||
else:
|
else:
|
||||||
assert False
|
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():
|
||||||
resource = self.resources[name, number]
|
resource = self.resources[name, number]
|
||||||
pin = self.requested[name, number]
|
|
||||||
period = self.clocks[name, number]
|
period = self.clocks[name, number]
|
||||||
|
pin = self._requested[name, number]
|
||||||
if pin.dir == "io":
|
if pin.dir == "io":
|
||||||
raise ConstraintError("Cannot constrain frequency of resource {}#{} because "
|
raise ConstraintError("Cannot constrain frequency of resource {}#{} because "
|
||||||
"it has been requested as a tristate buffer"
|
"it has been requested as a tristate buffer"
|
||||||
|
|
|
@ -17,20 +17,26 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
||||||
Subsignal("sda", Pins("N11"))
|
Subsignal("sda", Pins("N11"))
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
self.cm = ConstraintManager(self.resources, [])
|
self.connectors = [
|
||||||
|
Connector("pmod", 0, "B0 B1 B2 B3 - -"),
|
||||||
|
]
|
||||||
|
self.cm = ConstraintManager(self.resources, self.connectors, [])
|
||||||
|
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
self.clocks = [
|
self.clocks = [
|
||||||
("clk100", 100),
|
("clk100", 100),
|
||||||
(("clk50", 0), 50),
|
(("clk50", 0), 50),
|
||||||
]
|
]
|
||||||
self.cm = ConstraintManager(self.resources, self.clocks)
|
self.cm = ConstraintManager(self.resources, self.connectors, self.clocks)
|
||||||
self.assertEqual(self.cm.resources, {
|
self.assertEqual(self.cm.resources, {
|
||||||
("clk100", 0): self.resources[0],
|
("clk100", 0): self.resources[0],
|
||||||
("clk50", 0): self.resources[1],
|
("clk50", 0): self.resources[1],
|
||||||
("user_led", 0): self.resources[2],
|
("user_led", 0): self.resources[2],
|
||||||
("i2c", 0): self.resources[3]
|
("i2c", 0): self.resources[3]
|
||||||
})
|
})
|
||||||
|
self.assertEqual(self.cm.connectors, {
|
||||||
|
("pmod", 0): self.connectors[0],
|
||||||
|
})
|
||||||
self.assertEqual(self.cm.clocks, {
|
self.assertEqual(self.cm.clocks, {
|
||||||
("clk100", 0): 100,
|
("clk100", 0): 100,
|
||||||
("clk50", 0): 50,
|
("clk50", 0): 50,
|
||||||
|
@ -136,6 +142,23 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
||||||
self.assertIs(ports[0], clk100.p)
|
self.assertIs(ports[0], clk100.p)
|
||||||
self.assertIs(ports[1], clk100.n)
|
self.assertIs(ports[1], clk100.n)
|
||||||
|
|
||||||
|
def test_request_via_connector(self):
|
||||||
|
self.cm.add_resources([
|
||||||
|
Resource("spi", 0,
|
||||||
|
Subsignal("ss", Pins("1", conn=("pmod", 0))),
|
||||||
|
Subsignal("clk", Pins("2", conn=("pmod", 0))),
|
||||||
|
Subsignal("miso", Pins("3", conn=("pmod", 0))),
|
||||||
|
Subsignal("mosi", Pins("4", conn=("pmod", 0))),
|
||||||
|
)
|
||||||
|
])
|
||||||
|
spi0 = self.cm.request("spi", 0)
|
||||||
|
self.assertEqual(list(sorted(self.cm.iter_port_constraints())), [
|
||||||
|
("spi_0__clk__io", ["B1"], {}),
|
||||||
|
("spi_0__miso__io", ["B2"], {}),
|
||||||
|
("spi_0__mosi__io", ["B3"], {}),
|
||||||
|
("spi_0__ss__io", ["B0"], {}),
|
||||||
|
])
|
||||||
|
|
||||||
def test_add_clock(self):
|
def test_add_clock(self):
|
||||||
self.cm.add_clock("clk100", 0, 10e6)
|
self.cm.add_clock("clk100", 0, 10e6)
|
||||||
self.assertEqual(self.cm.clocks["clk100", 0], 10e6)
|
self.assertEqual(self.cm.clocks["clk100", 0], 10e6)
|
||||||
|
@ -158,6 +181,16 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
||||||
"(resource user_led 0 (pins o A0) ) has the same name and number"):
|
"(resource user_led 0 (pins o A0) ) has the same name and number"):
|
||||||
self.cm.add_resources([Resource("user_led", 0, Pins("A1", dir="o"))])
|
self.cm.add_resources([Resource("user_led", 0, Pins("A1", dir="o"))])
|
||||||
|
|
||||||
|
def test_wrong_connectors(self):
|
||||||
|
with self.assertRaises(TypeError, msg="Object 'wrong' is not a Connector"):
|
||||||
|
self.cm.add_connectors(['wrong'])
|
||||||
|
|
||||||
|
def test_wrong_connectors_duplicate(self):
|
||||||
|
with self.assertRaises(NameError,
|
||||||
|
msg="Trying to add (connector pmod 0 1=>1 2=>2), but "
|
||||||
|
"(connector pmod 0 1=>B0 2=>B1 3=>B2 4=>B3) has the same name and number"):
|
||||||
|
self.cm.add_connectors([Connector("pmod", 0, "1 2")])
|
||||||
|
|
||||||
def test_wrong_lookup(self):
|
def test_wrong_lookup(self):
|
||||||
with self.assertRaises(NameError,
|
with self.assertRaises(NameError,
|
||||||
msg="Resource user_led#1 does not exist"):
|
msg="Resource user_led#1 does not exist"):
|
||||||
|
|
24
nmigen/vendor/fpga/lattice_ice40.py
vendored
24
nmigen/vendor/fpga/lattice_ice40.py
vendored
|
@ -109,11 +109,11 @@ class LatticeICE40Platform(TemplatedPlatform):
|
||||||
]
|
]
|
||||||
|
|
||||||
def iter_ports(self):
|
def iter_ports(self):
|
||||||
for resource, pin, port in self._ports:
|
for res, pin, port in self._ports:
|
||||||
if isinstance(resource.io[0], Pins):
|
if isinstance(res.io[0], Pins):
|
||||||
yield port.io
|
yield port.io
|
||||||
elif isinstance(resource.io[0], DiffPairs):
|
elif isinstance(res.io[0], DiffPairs):
|
||||||
if resource.extras.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT":
|
if res.extras.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT":
|
||||||
yield port.p
|
yield port.p
|
||||||
else:
|
else:
|
||||||
yield port.p
|
yield port.p
|
||||||
|
@ -122,15 +122,15 @@ class LatticeICE40Platform(TemplatedPlatform):
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
def iter_port_constraints(self):
|
def iter_port_constraints(self):
|
||||||
for resource, pin, port in self._ports:
|
for res, pin, port in self._ports:
|
||||||
if isinstance(resource.io[0], Pins):
|
if isinstance(res.io[0], Pins):
|
||||||
yield port.io.name, resource.io[0].names, resource.extras
|
yield port.io.name, list(res.io[0].map_names(self._mapping, res)), res.extras
|
||||||
elif isinstance(resource.io[0], DiffPairs):
|
elif isinstance(res.io[0], DiffPairs):
|
||||||
if resource.extras.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT":
|
if res.extras.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT":
|
||||||
yield port.p.name, resource.io[0].p.names, resource.extras
|
yield port.p.name, list(res.io[0].p.map_names(self._mapping, res)), res.extras
|
||||||
else:
|
else:
|
||||||
yield port.p.name, resource.io[0].p.names, resource.extras
|
yield port.p.name, list(res.io[0].p.map_names(self._mapping, res)), res.extras
|
||||||
yield port.n.name, resource.io[0].n.names, resource.extras
|
yield port.n.name, list(res.io[0].n.map_names(self._mapping, res)), res.extras
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue