
Although a dir="oe" pin is generally equivalent to dir="io" pin with the i* signal(s) disconnected, they are not equivalent, because some pins may not be able to support input buffers at all, either because there are no input buffers, or because the input buffers are consumed by some other resource. E.g. this can happen on iCE40 when the input buffer is consumed by a PLL.
227 lines
8.6 KiB
Python
227 lines
8.6 KiB
Python
from .. import *
|
|
from ..hdl.rec import *
|
|
from ..lib.io import *
|
|
from ..build.dsl import *
|
|
from ..build.res import *
|
|
from .tools import *
|
|
|
|
|
|
class ConstraintManagerTestCase(FHDLTestCase):
|
|
def setUp(self):
|
|
self.resources = [
|
|
Resource("clk100", 0, DiffPairs("H1", "H2", dir="i")),
|
|
Resource("clk50", 0, Pins("K1")),
|
|
Resource("user_led", 0, Pins("A0", dir="o")),
|
|
Resource("i2c", 0,
|
|
Subsignal("scl", Pins("N10", dir="o")),
|
|
Subsignal("sda", Pins("N11"))
|
|
)
|
|
]
|
|
self.cm = ConstraintManager(self.resources, [])
|
|
|
|
def test_basic(self):
|
|
self.clocks = [
|
|
("clk100", 100),
|
|
(("clk50", 0), 50),
|
|
]
|
|
self.cm = ConstraintManager(self.resources, self.clocks)
|
|
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]
|
|
})
|
|
self.assertEqual(self.cm.clocks, {
|
|
("clk100", 0): 100,
|
|
("clk50", 0): 50,
|
|
})
|
|
|
|
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()), [
|
|
("user_led_0__io", ["A0"], {})
|
|
])
|
|
|
|
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)
|
|
scl, sda = ports
|
|
self.assertEqual(ports[1].name, "i2c_0__sda__io")
|
|
self.assertEqual(ports[1].nbits, 1)
|
|
|
|
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"], {}),
|
|
("i2c_0__sda__io", ["N11"], {})
|
|
])
|
|
|
|
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
|
|
self.assertEqual(p.name, "clk100_0__p")
|
|
self.assertEqual(p.nbits, clk100.width)
|
|
self.assertEqual(n.name, "clk100_0__n")
|
|
self.assertEqual(n.nbits, clk100.width)
|
|
|
|
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"], {}),
|
|
])
|
|
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_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)
|
|
|
|
def test_add_clock(self):
|
|
self.cm.add_clock("clk100", 0, 10e6)
|
|
self.assertEqual(self.cm.clocks["clk100", 0], 10e6)
|
|
self.cm.add_clock("clk50", 0, 5e6)
|
|
|
|
clk100 = self.cm.request("clk100", 0)
|
|
clk50 = self.cm.request("clk50", 0, dir="i")
|
|
self.assertEqual(list(sorted(self.cm.iter_clock_constraints())), [
|
|
("clk100_0__p", 10e6),
|
|
("clk50_0__io", 5e6)
|
|
])
|
|
|
|
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,
|
|
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"):
|
|
self.cm.add_resources([Resource("user_led", 0, Pins("A1", dir="o"))])
|
|
|
|
def test_wrong_lookup(self):
|
|
with self.assertRaises(NameError,
|
|
msg="Resource user_led#1 does not exist"):
|
|
r = self.cm.lookup("user_led", 1)
|
|
|
|
def test_wrong_frequency_subsignals(self):
|
|
with self.assertRaises(ConstraintError,
|
|
msg="Cannot constrain frequency of resource i2c#0 because "
|
|
"it has subsignals"):
|
|
self.cm.add_clock("i2c", 0, 10e6)
|
|
|
|
def test_wrong_frequency_tristate(self):
|
|
with self.assertRaises(ConstraintError,
|
|
msg="Cannot constrain frequency of resource clk50#0 because "
|
|
"it has been requested as a tristate buffer"):
|
|
self.cm.add_clock("clk50", 0, 20e6)
|
|
clk50 = self.cm.request("clk50", 0)
|
|
list(self.cm.iter_clock_constraints())
|
|
|
|
def test_wrong_frequency_duplicate(self):
|
|
with self.assertRaises(ConstraintError,
|
|
msg="Resource clk100#0 is already constrained to a frequency of 10.000000 MHz"):
|
|
self.cm.add_clock("clk100", 0, 10e6)
|
|
self.cm.add_clock("clk100", 0, 5e6)
|
|
|
|
def test_wrong_request_duplicate(self):
|
|
with self.assertRaises(ConstraintError,
|
|
msg="Resource user_led#0 has already been requested"):
|
|
self.cm.request("user_led", 0)
|
|
self.cm.request("user_led", 0)
|
|
|
|
def test_wrong_request_with_dir(self):
|
|
with self.assertRaises(TypeError,
|
|
msg="Direction must be one of \"i\", \"o\", \"oe\", \"io\", or \"-\", "
|
|
"not 'wrong'"):
|
|
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 "
|
|
"can be changed from \"io\" to \"i\", \"o\", or \"oe\", or from anything "
|
|
"to \"-\""):
|
|
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 "
|
|
"(pins o N10) ) (subsignal sda (pins io N11) ) ) has subsignals"):
|
|
i2c = self.cm.request("i2c", 0, dir="i")
|
|
|
|
def test_wrong_request_with_wrong_xdr(self):
|
|
with self.assertRaises(ValueError,
|
|
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)
|
|
|
|
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 "
|
|
"(pins o N10) ) (subsignal sda (pins io N11) ) ) has subsignals"):
|
|
i2c = self.cm.request("i2c", 0, xdr=2)
|