2019-10-26 00:36:54 -06:00
|
|
|
# nmigen: UnusedElaboratable=no
|
|
|
|
|
2019-04-26 06:37:08 -06:00
|
|
|
from .. import *
|
|
|
|
from ..hdl.rec import *
|
|
|
|
from ..lib.io import *
|
|
|
|
from ..build.dsl import *
|
|
|
|
from ..build.res import *
|
2019-10-13 12:53:38 -06:00
|
|
|
from .utils import *
|
2019-04-26 06:37:08 -06:00
|
|
|
|
|
|
|
|
2019-06-05 01:02:08 -06:00
|
|
|
class ResourceManagerTestCase(FHDLTestCase):
|
2019-04-26 06:37:08 -06:00
|
|
|
def setUp(self):
|
|
|
|
self.resources = [
|
2019-06-05 02:48:36 -06:00
|
|
|
Resource("clk100", 0, DiffPairs("H1", "H2", dir="i"), Clock(100e6)),
|
|
|
|
Resource("clk50", 0, Pins("K1"), Clock(50e6)),
|
2019-04-26 06:37:08 -06:00
|
|
|
Resource("user_led", 0, Pins("A0", dir="o")),
|
|
|
|
Resource("i2c", 0,
|
|
|
|
Subsignal("scl", Pins("N10", dir="o")),
|
|
|
|
Subsignal("sda", Pins("N11"))
|
|
|
|
)
|
|
|
|
]
|
2019-06-03 09:02:15 -06:00
|
|
|
self.connectors = [
|
|
|
|
Connector("pmod", 0, "B0 B1 B2 B3 - -"),
|
|
|
|
]
|
2019-06-05 02:48:36 -06:00
|
|
|
self.cm = ResourceManager(self.resources, self.connectors)
|
2019-04-26 06:37:08 -06:00
|
|
|
|
|
|
|
def test_basic(self):
|
2019-06-05 02:48:36 -06:00
|
|
|
self.cm = ResourceManager(self.resources, self.connectors)
|
2019-04-26 06:37:08 -06:00
|
|
|
self.assertEqual(self.cm.resources, {
|
|
|
|
("clk100", 0): self.resources[0],
|
|
|
|
("clk50", 0): self.resources[1],
|
|
|
|
("user_led", 0): self.resources[2],
|
|
|
|
("i2c", 0): self.resources[3]
|
|
|
|
})
|
2019-06-03 09:02:15 -06:00
|
|
|
self.assertEqual(self.cm.connectors, {
|
|
|
|
("pmod", 0): self.connectors[0],
|
|
|
|
})
|
2019-04-26 06:37:08 -06:00
|
|
|
|
|
|
|
def test_add_resources(self):
|
|
|
|
new_resources = [
|
|
|
|
Resource("user_led", 1, Pins("A1", dir="o"))
|
|
|
|
]
|
|
|
|
self.cm.add_resources(new_resources)
|
|
|
|
self.assertEqual(self.cm.resources, {
|
|
|
|
("clk100", 0): self.resources[0],
|
|
|
|
("clk50", 0): self.resources[1],
|
|
|
|
("user_led", 0): self.resources[2],
|
|
|
|
("i2c", 0): self.resources[3],
|
|
|
|
("user_led", 1): new_resources[0]
|
|
|
|
})
|
|
|
|
|
|
|
|
def test_lookup(self):
|
|
|
|
r = self.cm.lookup("user_led", 0)
|
|
|
|
self.assertIs(r, self.cm.resources["user_led", 0])
|
|
|
|
|
|
|
|
def test_request_basic(self):
|
|
|
|
r = self.cm.lookup("user_led", 0)
|
|
|
|
user_led = self.cm.request("user_led", 0)
|
|
|
|
|
|
|
|
self.assertIsInstance(user_led, Pin)
|
|
|
|
self.assertEqual(user_led.name, "user_led_0")
|
|
|
|
self.assertEqual(user_led.width, 1)
|
|
|
|
self.assertEqual(user_led.dir, "o")
|
|
|
|
|
|
|
|
ports = list(self.cm.iter_ports())
|
|
|
|
self.assertEqual(len(ports), 1)
|
|
|
|
|
|
|
|
self.assertEqual(list(self.cm.iter_port_constraints()), [
|
2019-06-02 20:48:55 -06:00
|
|
|
("user_led_0__io", ["A0"], {})
|
2019-04-26 06:37:08 -06:00
|
|
|
])
|
|
|
|
|
|
|
|
def test_request_with_dir(self):
|
|
|
|
i2c = self.cm.request("i2c", 0, dir={"sda": "o"})
|
|
|
|
self.assertIsInstance(i2c, Record)
|
|
|
|
self.assertIsInstance(i2c.sda, Pin)
|
|
|
|
self.assertEqual(i2c.sda.dir, "o")
|
|
|
|
|
|
|
|
def test_request_tristate(self):
|
|
|
|
i2c = self.cm.request("i2c", 0)
|
|
|
|
self.assertEqual(i2c.sda.dir, "io")
|
|
|
|
|
|
|
|
ports = list(self.cm.iter_ports())
|
|
|
|
self.assertEqual(len(ports), 2)
|
2019-06-01 10:41:30 -06:00
|
|
|
scl, sda = ports
|
2019-06-02 20:48:55 -06:00
|
|
|
self.assertEqual(ports[1].name, "i2c_0__sda__io")
|
2019-09-20 09:35:55 -06:00
|
|
|
self.assertEqual(ports[1].width, 1)
|
2019-04-26 06:37:08 -06:00
|
|
|
|
2019-06-02 19:58:43 -06:00
|
|
|
self.assertEqual(list(self.cm.iter_single_ended_pins()), [
|
2019-06-12 08:42:39 -06:00
|
|
|
(i2c.scl, scl, {}, False),
|
|
|
|
(i2c.sda, sda, {}, False),
|
2019-06-01 10:41:30 -06:00
|
|
|
])
|
2019-04-26 06:37:08 -06:00
|
|
|
self.assertEqual(list(self.cm.iter_port_constraints()), [
|
2019-06-02 20:48:55 -06:00
|
|
|
("i2c_0__scl__io", ["N10"], {}),
|
|
|
|
("i2c_0__sda__io", ["N11"], {})
|
2019-04-26 06:37:08 -06:00
|
|
|
])
|
|
|
|
|
|
|
|
def test_request_diffpairs(self):
|
|
|
|
clk100 = self.cm.request("clk100", 0)
|
|
|
|
self.assertIsInstance(clk100, Pin)
|
|
|
|
self.assertEqual(clk100.dir, "i")
|
|
|
|
self.assertEqual(clk100.width, 1)
|
|
|
|
|
|
|
|
ports = list(self.cm.iter_ports())
|
|
|
|
self.assertEqual(len(ports), 2)
|
|
|
|
p, n = ports
|
2019-06-02 20:48:55 -06:00
|
|
|
self.assertEqual(p.name, "clk100_0__p")
|
2019-09-20 09:35:55 -06:00
|
|
|
self.assertEqual(p.width, clk100.width)
|
2019-06-02 20:48:55 -06:00
|
|
|
self.assertEqual(n.name, "clk100_0__n")
|
2019-09-20 09:35:55 -06:00
|
|
|
self.assertEqual(n.width, clk100.width)
|
2019-04-26 06:37:08 -06:00
|
|
|
|
2019-06-02 19:58:43 -06:00
|
|
|
self.assertEqual(list(self.cm.iter_differential_pins()), [
|
2019-06-12 08:42:39 -06:00
|
|
|
(clk100, p, n, {}, False),
|
2019-06-01 10:41:30 -06:00
|
|
|
])
|
2019-04-26 06:37:08 -06:00
|
|
|
self.assertEqual(list(self.cm.iter_port_constraints()), [
|
2019-06-02 20:48:55 -06:00
|
|
|
("clk100_0__p", ["H1"], {}),
|
|
|
|
("clk100_0__n", ["H2"], {}),
|
2019-06-02 19:58:43 -06:00
|
|
|
])
|
2019-04-26 06:37:08 -06:00
|
|
|
|
2019-06-12 08:42:39 -06:00
|
|
|
def test_request_inverted(self):
|
|
|
|
new_resources = [
|
|
|
|
Resource("cs", 0, PinsN("X0")),
|
|
|
|
Resource("clk", 0, DiffPairsN("Y0", "Y1")),
|
|
|
|
]
|
|
|
|
self.cm.add_resources(new_resources)
|
|
|
|
|
|
|
|
sig_cs = self.cm.request("cs")
|
|
|
|
sig_clk = self.cm.request("clk")
|
|
|
|
port_cs, port_clk_p, port_clk_n = self.cm.iter_ports()
|
|
|
|
self.assertEqual(list(self.cm.iter_single_ended_pins()), [
|
|
|
|
(sig_cs, port_cs, {}, True),
|
|
|
|
])
|
|
|
|
self.assertEqual(list(self.cm.iter_differential_pins()), [
|
|
|
|
(sig_clk, port_clk_p, port_clk_n, {}, True),
|
|
|
|
])
|
|
|
|
|
2019-06-02 21:17:20 -06:00
|
|
|
def test_request_raw(self):
|
|
|
|
clk50 = self.cm.request("clk50", 0, dir="-")
|
|
|
|
self.assertIsInstance(clk50, Record)
|
|
|
|
self.assertIsInstance(clk50.io, Signal)
|
|
|
|
|
|
|
|
ports = list(self.cm.iter_ports())
|
|
|
|
self.assertEqual(len(ports), 1)
|
|
|
|
self.assertIs(ports[0], clk50.io)
|
|
|
|
|
|
|
|
def test_request_raw_diffpairs(self):
|
|
|
|
clk100 = self.cm.request("clk100", 0, dir="-")
|
|
|
|
self.assertIsInstance(clk100, Record)
|
|
|
|
self.assertIsInstance(clk100.p, Signal)
|
|
|
|
self.assertIsInstance(clk100.n, Signal)
|
|
|
|
|
|
|
|
ports = list(self.cm.iter_ports())
|
|
|
|
self.assertEqual(len(ports), 2)
|
|
|
|
self.assertIs(ports[0], clk100.p)
|
|
|
|
self.assertIs(ports[1], clk100.n)
|
|
|
|
|
2019-06-03 09:02:15 -06:00
|
|
|
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)
|
2019-06-05 02:48:36 -06:00
|
|
|
self.assertEqual(list(self.cm.iter_port_constraints()), [
|
|
|
|
("spi_0__ss__io", ["B0"], {}),
|
2019-06-03 09:02:15 -06:00
|
|
|
("spi_0__clk__io", ["B1"], {}),
|
|
|
|
("spi_0__miso__io", ["B2"], {}),
|
|
|
|
("spi_0__mosi__io", ["B3"], {}),
|
|
|
|
])
|
|
|
|
|
2019-08-18 13:56:25 -06:00
|
|
|
def test_request_via_nested_connector(self):
|
|
|
|
new_connectors = [
|
|
|
|
Connector("pmod_extension", 0, "1 2 3 4 - -", conn=("pmod", 0)),
|
|
|
|
]
|
|
|
|
self.cm.add_connectors(new_connectors)
|
|
|
|
self.cm.add_resources([
|
|
|
|
Resource("spi", 0,
|
|
|
|
Subsignal("ss", Pins("1", conn=("pmod_extension", 0))),
|
|
|
|
Subsignal("clk", Pins("2", conn=("pmod_extension", 0))),
|
|
|
|
Subsignal("miso", Pins("3", conn=("pmod_extension", 0))),
|
|
|
|
Subsignal("mosi", Pins("4", conn=("pmod_extension", 0))),
|
|
|
|
)
|
|
|
|
])
|
|
|
|
spi0 = self.cm.request("spi", 0)
|
|
|
|
self.assertEqual(list(self.cm.iter_port_constraints()), [
|
|
|
|
("spi_0__ss__io", ["B0"], {}),
|
|
|
|
("spi_0__clk__io", ["B1"], {}),
|
|
|
|
("spi_0__miso__io", ["B2"], {}),
|
|
|
|
("spi_0__mosi__io", ["B3"], {}),
|
|
|
|
])
|
|
|
|
|
2019-06-05 02:48:36 -06:00
|
|
|
def test_request_clock(self):
|
2019-04-26 06:37:08 -06:00
|
|
|
clk100 = self.cm.request("clk100", 0)
|
|
|
|
clk50 = self.cm.request("clk50", 0, dir="i")
|
2019-06-05 02:48:36 -06:00
|
|
|
clk100_port_p, clk100_port_n, clk50_port = self.cm.iter_ports()
|
|
|
|
self.assertEqual(list(self.cm.iter_clock_constraints()), [
|
2020-02-06 17:07:19 -07:00
|
|
|
(clk100.i, clk100_port_p, 100e6),
|
|
|
|
(clk50.i, clk50_port, 50e6)
|
2019-06-05 02:48:36 -06:00
|
|
|
])
|
|
|
|
|
|
|
|
def test_add_clock(self):
|
|
|
|
i2c = self.cm.request("i2c")
|
build.res: simplify clock constraints.
Before this commit, it was possible to set and get clock constraints
placed on Pin objects. This was not a very good implementation, since
it relied on matching the identity of the provided Pin object to
a previously requested one. The only reason it worked like that is
deficiencies in nextpnr.
Since then, nextpnr has been fixed to allow setting constraints on
arbitrary nets. Correspondingly, backends that are using Synplify
were changed to use [get_nets] instead of [get_ports] in SDC files.
However, in some situations, Synplify does not allow specifying
ports in [get_nets]. (In fact, nextpnr had a similar problem, but
it has also been fixed.)
The simplest way to address this is to refer to the interior net
(after the input buffer), which always works. The only downside
of this is that requesting a clock as a raw pin using
platform.request("clk", dir="-")
and directly applying a constraint to it could fail in some cases.
This is not a significant issue.
2019-09-21 08:12:29 -06:00
|
|
|
self.cm.add_clock_constraint(i2c.scl.o, 100e3)
|
2019-06-05 02:48:36 -06:00
|
|
|
self.assertEqual(list(self.cm.iter_clock_constraints()), [
|
2020-02-06 17:07:19 -07:00
|
|
|
(i2c.scl.o, None, 100e3)
|
2019-04-26 06:37:08 -06:00
|
|
|
])
|
|
|
|
|
|
|
|
def test_wrong_resources(self):
|
|
|
|
with self.assertRaises(TypeError, msg="Object 'wrong' is not a Resource"):
|
|
|
|
self.cm.add_resources(['wrong'])
|
|
|
|
|
|
|
|
def test_wrong_resources_duplicate(self):
|
|
|
|
with self.assertRaises(NameError,
|
2019-06-05 02:48:36 -06:00
|
|
|
msg="Trying to add (resource user_led 0 (pins o A1)), but "
|
|
|
|
"(resource user_led 0 (pins o A0)) has the same name and number"):
|
2019-04-26 06:37:08 -06:00
|
|
|
self.cm.add_resources([Resource("user_led", 0, Pins("A1", dir="o"))])
|
|
|
|
|
2019-06-03 09:02:15 -06:00
|
|
|
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")])
|
|
|
|
|
2019-04-26 06:37:08 -06:00
|
|
|
def test_wrong_lookup(self):
|
2019-06-05 01:02:08 -06:00
|
|
|
with self.assertRaises(ResourceError,
|
2019-04-26 06:37:08 -06:00
|
|
|
msg="Resource user_led#1 does not exist"):
|
|
|
|
r = self.cm.lookup("user_led", 1)
|
|
|
|
|
2019-06-05 02:48:36 -06:00
|
|
|
def test_wrong_clock_signal(self):
|
2019-06-04 04:23:27 -06:00
|
|
|
with self.assertRaises(TypeError,
|
build.res: simplify clock constraints.
Before this commit, it was possible to set and get clock constraints
placed on Pin objects. This was not a very good implementation, since
it relied on matching the identity of the provided Pin object to
a previously requested one. The only reason it worked like that is
deficiencies in nextpnr.
Since then, nextpnr has been fixed to allow setting constraints on
arbitrary nets. Correspondingly, backends that are using Synplify
were changed to use [get_nets] instead of [get_ports] in SDC files.
However, in some situations, Synplify does not allow specifying
ports in [get_nets]. (In fact, nextpnr had a similar problem, but
it has also been fixed.)
The simplest way to address this is to refer to the interior net
(after the input buffer), which always works. The only downside
of this is that requesting a clock as a raw pin using
platform.request("clk", dir="-")
and directly applying a constraint to it could fail in some cases.
This is not a significant issue.
2019-09-21 08:12:29 -06:00
|
|
|
msg="Object None is not a Signal"):
|
2019-06-05 02:48:36 -06:00
|
|
|
self.cm.add_clock_constraint(None, 10e6)
|
2019-04-26 06:37:08 -06:00
|
|
|
|
2019-06-05 02:48:36 -06:00
|
|
|
def test_wrong_clock_frequency(self):
|
|
|
|
with self.assertRaises(TypeError,
|
|
|
|
msg="Frequency must be a number, not None"):
|
|
|
|
self.cm.add_clock_constraint(Signal(), None)
|
2019-04-26 06:37:08 -06:00
|
|
|
|
|
|
|
def test_wrong_request_duplicate(self):
|
2019-06-05 01:02:08 -06:00
|
|
|
with self.assertRaises(ResourceError,
|
2019-04-26 06:37:08 -06:00
|
|
|
msg="Resource user_led#0 has already been requested"):
|
|
|
|
self.cm.request("user_led", 0)
|
|
|
|
self.cm.request("user_led", 0)
|
|
|
|
|
2019-07-03 09:07:44 -06:00
|
|
|
def test_wrong_request_duplicate_physical(self):
|
|
|
|
self.cm.add_resources([
|
|
|
|
Resource("clk20", 0, Pins("H1", dir="i")),
|
|
|
|
])
|
|
|
|
self.cm.request("clk100", 0)
|
|
|
|
with self.assertRaises(ResourceError,
|
|
|
|
msg="Resource component clk20_0 uses physical pin H1, but it is already "
|
|
|
|
"used by resource component clk100_0 that was requested earlier"):
|
|
|
|
self.cm.request("clk20", 0)
|
|
|
|
|
2019-04-26 06:37:08 -06:00
|
|
|
def test_wrong_request_with_dir(self):
|
|
|
|
with self.assertRaises(TypeError,
|
2019-06-02 22:39:05 -06:00
|
|
|
msg="Direction must be one of \"i\", \"o\", \"oe\", \"io\", or \"-\", "
|
|
|
|
"not 'wrong'"):
|
2019-04-26 06:37:08 -06:00
|
|
|
user_led = self.cm.request("user_led", 0, dir="wrong")
|
|
|
|
|
|
|
|
def test_wrong_request_with_dir_io(self):
|
|
|
|
with self.assertRaises(ValueError,
|
|
|
|
msg="Direction of (pins o A0) cannot be changed from \"o\" to \"i\"; direction "
|
2019-06-02 22:39:05 -06:00
|
|
|
"can be changed from \"io\" to \"i\", \"o\", or \"oe\", or from anything "
|
2019-06-02 21:17:20 -06:00
|
|
|
"to \"-\""):
|
2019-04-26 06:37:08 -06:00
|
|
|
user_led = self.cm.request("user_led", 0, dir="i")
|
|
|
|
|
|
|
|
def test_wrong_request_with_dir_dict(self):
|
|
|
|
with self.assertRaises(TypeError,
|
|
|
|
msg="Directions must be a dict, not 'i', because (resource i2c 0 (subsignal scl "
|
2019-06-05 02:48:36 -06:00
|
|
|
"(pins o N10)) (subsignal sda (pins io N11))) "
|
2019-06-05 01:02:08 -06:00
|
|
|
"has subsignals"):
|
2019-04-26 06:37:08 -06:00
|
|
|
i2c = self.cm.request("i2c", 0, dir="i")
|
|
|
|
|
|
|
|
def test_wrong_request_with_wrong_xdr(self):
|
|
|
|
with self.assertRaises(ValueError,
|
2019-06-02 21:32:30 -06:00
|
|
|
msg="Data rate of (pins o A0) must be a non-negative integer, not -1"):
|
|
|
|
user_led = self.cm.request("user_led", 0, xdr=-1)
|
2019-04-26 06:37:08 -06:00
|
|
|
|
|
|
|
def test_wrong_request_with_xdr_dict(self):
|
|
|
|
with self.assertRaises(TypeError,
|
|
|
|
msg="Data rate must be a dict, not 2, because (resource i2c 0 (subsignal scl "
|
2019-06-05 02:48:36 -06:00
|
|
|
"(pins o N10)) (subsignal sda (pins io N11))) "
|
2019-06-05 01:02:08 -06:00
|
|
|
"has subsignals"):
|
2019-04-26 06:37:08 -06:00
|
|
|
i2c = self.cm.request("i2c", 0, xdr=2)
|
2019-06-05 02:48:36 -06:00
|
|
|
|
|
|
|
def test_wrong_clock_constraint_twice(self):
|
|
|
|
clk100 = self.cm.request("clk100")
|
|
|
|
with self.assertRaises(ValueError,
|
build.res: simplify clock constraints.
Before this commit, it was possible to set and get clock constraints
placed on Pin objects. This was not a very good implementation, since
it relied on matching the identity of the provided Pin object to
a previously requested one. The only reason it worked like that is
deficiencies in nextpnr.
Since then, nextpnr has been fixed to allow setting constraints on
arbitrary nets. Correspondingly, backends that are using Synplify
were changed to use [get_nets] instead of [get_ports] in SDC files.
However, in some situations, Synplify does not allow specifying
ports in [get_nets]. (In fact, nextpnr had a similar problem, but
it has also been fixed.)
The simplest way to address this is to refer to the interior net
(after the input buffer), which always works. The only downside
of this is that requesting a clock as a raw pin using
platform.request("clk", dir="-")
and directly applying a constraint to it could fail in some cases.
This is not a significant issue.
2019-09-21 08:12:29 -06:00
|
|
|
msg="Cannot add clock constraint on (sig clk100_0__i), which is already "
|
2019-06-05 02:48:36 -06:00
|
|
|
"constrained to 100000000.0 Hz"):
|
build.res: simplify clock constraints.
Before this commit, it was possible to set and get clock constraints
placed on Pin objects. This was not a very good implementation, since
it relied on matching the identity of the provided Pin object to
a previously requested one. The only reason it worked like that is
deficiencies in nextpnr.
Since then, nextpnr has been fixed to allow setting constraints on
arbitrary nets. Correspondingly, backends that are using Synplify
were changed to use [get_nets] instead of [get_ports] in SDC files.
However, in some situations, Synplify does not allow specifying
ports in [get_nets]. (In fact, nextpnr had a similar problem, but
it has also been fixed.)
The simplest way to address this is to refer to the interior net
(after the input buffer), which always works. The only downside
of this is that requesting a clock as a raw pin using
platform.request("clk", dir="-")
and directly applying a constraint to it could fail in some cases.
This is not a significant issue.
2019-09-21 08:12:29 -06:00
|
|
|
self.cm.add_clock_constraint(clk100.i, 1e6)
|