tests: move out of the main package.
[nmigen.git] / tests / test_build_res.py
1 # nmigen: UnusedElaboratable=no
2
3 from nmigen import *
4 from nmigen.hdl.rec import *
5 from nmigen.lib.io import *
6 from nmigen.build.dsl import *
7 from nmigen.build.res import *
8
9 from .utils import *
10
11
12 class ResourceManagerTestCase(FHDLTestCase):
13 def setUp(self):
14 self.resources = [
15 Resource("clk100", 0, DiffPairs("H1", "H2", dir="i"), Clock(100e6)),
16 Resource("clk50", 0, Pins("K1"), Clock(50e6)),
17 Resource("user_led", 0, Pins("A0", dir="o")),
18 Resource("i2c", 0,
19 Subsignal("scl", Pins("N10", dir="o")),
20 Subsignal("sda", Pins("N11"))
21 )
22 ]
23 self.connectors = [
24 Connector("pmod", 0, "B0 B1 B2 B3 - -"),
25 ]
26 self.cm = ResourceManager(self.resources, self.connectors)
27
28 def test_basic(self):
29 self.cm = ResourceManager(self.resources, self.connectors)
30 self.assertEqual(self.cm.resources, {
31 ("clk100", 0): self.resources[0],
32 ("clk50", 0): self.resources[1],
33 ("user_led", 0): self.resources[2],
34 ("i2c", 0): self.resources[3]
35 })
36 self.assertEqual(self.cm.connectors, {
37 ("pmod", 0): self.connectors[0],
38 })
39
40 def test_add_resources(self):
41 new_resources = [
42 Resource("user_led", 1, Pins("A1", dir="o"))
43 ]
44 self.cm.add_resources(new_resources)
45 self.assertEqual(self.cm.resources, {
46 ("clk100", 0): self.resources[0],
47 ("clk50", 0): self.resources[1],
48 ("user_led", 0): self.resources[2],
49 ("i2c", 0): self.resources[3],
50 ("user_led", 1): new_resources[0]
51 })
52
53 def test_lookup(self):
54 r = self.cm.lookup("user_led", 0)
55 self.assertIs(r, self.cm.resources["user_led", 0])
56
57 def test_request_basic(self):
58 r = self.cm.lookup("user_led", 0)
59 user_led = self.cm.request("user_led", 0)
60
61 self.assertIsInstance(user_led, Pin)
62 self.assertEqual(user_led.name, "user_led_0")
63 self.assertEqual(user_led.width, 1)
64 self.assertEqual(user_led.dir, "o")
65
66 ports = list(self.cm.iter_ports())
67 self.assertEqual(len(ports), 1)
68
69 self.assertEqual(list(self.cm.iter_port_constraints()), [
70 ("user_led_0__io", ["A0"], {})
71 ])
72
73 def test_request_with_dir(self):
74 i2c = self.cm.request("i2c", 0, dir={"sda": "o"})
75 self.assertIsInstance(i2c, Record)
76 self.assertIsInstance(i2c.sda, Pin)
77 self.assertEqual(i2c.sda.dir, "o")
78
79 def test_request_tristate(self):
80 i2c = self.cm.request("i2c", 0)
81 self.assertEqual(i2c.sda.dir, "io")
82
83 ports = list(self.cm.iter_ports())
84 self.assertEqual(len(ports), 2)
85 scl, sda = ports
86 self.assertEqual(ports[1].name, "i2c_0__sda__io")
87 self.assertEqual(ports[1].width, 1)
88
89 scl_info, sda_info = self.cm.iter_single_ended_pins()
90 self.assertIs(scl_info[0], i2c.scl)
91 self.assertIs(scl_info[1].io, scl)
92 self.assertEqual(scl_info[2], {})
93 self.assertEqual(scl_info[3], False)
94 self.assertIs(sda_info[0], i2c.sda)
95 self.assertIs(sda_info[1].io, sda)
96
97 self.assertEqual(list(self.cm.iter_port_constraints()), [
98 ("i2c_0__scl__io", ["N10"], {}),
99 ("i2c_0__sda__io", ["N11"], {})
100 ])
101
102 def test_request_diffpairs(self):
103 clk100 = self.cm.request("clk100", 0)
104 self.assertIsInstance(clk100, Pin)
105 self.assertEqual(clk100.dir, "i")
106 self.assertEqual(clk100.width, 1)
107
108 ports = list(self.cm.iter_ports())
109 self.assertEqual(len(ports), 2)
110 p, n = ports
111 self.assertEqual(p.name, "clk100_0__p")
112 self.assertEqual(p.width, clk100.width)
113 self.assertEqual(n.name, "clk100_0__n")
114 self.assertEqual(n.width, clk100.width)
115
116 clk100_info, = self.cm.iter_differential_pins()
117 self.assertIs(clk100_info[0], clk100)
118 self.assertIs(clk100_info[1].p, p)
119 self.assertIs(clk100_info[1].n, n)
120 self.assertEqual(clk100_info[2], {})
121 self.assertEqual(clk100_info[3], False)
122
123 self.assertEqual(list(self.cm.iter_port_constraints()), [
124 ("clk100_0__p", ["H1"], {}),
125 ("clk100_0__n", ["H2"], {}),
126 ])
127
128 def test_request_inverted(self):
129 new_resources = [
130 Resource("cs", 0, PinsN("X0")),
131 Resource("clk", 0, DiffPairsN("Y0", "Y1")),
132 ]
133 self.cm.add_resources(new_resources)
134
135 cs = self.cm.request("cs")
136 clk = self.cm.request("clk")
137 cs_io, clk_p, clk_n = self.cm.iter_ports()
138
139 cs_info, = self.cm.iter_single_ended_pins()
140 self.assertIs(cs_info[0], cs)
141 self.assertIs(cs_info[1].io, cs_io)
142 self.assertEqual(cs_info[2], {})
143 self.assertEqual(cs_info[3], True)
144
145 clk_info, = self.cm.iter_differential_pins()
146 self.assertIs(clk_info[0], clk)
147 self.assertIs(clk_info[1].p, clk_p)
148 self.assertIs(clk_info[1].n, clk_n)
149 self.assertEqual(clk_info[2], {})
150 self.assertEqual(clk_info[3], True)
151
152 def test_request_raw(self):
153 clk50 = self.cm.request("clk50", 0, dir="-")
154 self.assertIsInstance(clk50, Record)
155 self.assertIsInstance(clk50.io, Signal)
156
157 ports = list(self.cm.iter_ports())
158 self.assertEqual(len(ports), 1)
159 self.assertIs(ports[0], clk50.io)
160
161 def test_request_raw_diffpairs(self):
162 clk100 = self.cm.request("clk100", 0, dir="-")
163 self.assertIsInstance(clk100, Record)
164 self.assertIsInstance(clk100.p, Signal)
165 self.assertIsInstance(clk100.n, Signal)
166
167 ports = list(self.cm.iter_ports())
168 self.assertEqual(len(ports), 2)
169 self.assertIs(ports[0], clk100.p)
170 self.assertIs(ports[1], clk100.n)
171
172 def test_request_via_connector(self):
173 self.cm.add_resources([
174 Resource("spi", 0,
175 Subsignal("ss", Pins("1", conn=("pmod", 0))),
176 Subsignal("clk", Pins("2", conn=("pmod", 0))),
177 Subsignal("miso", Pins("3", conn=("pmod", 0))),
178 Subsignal("mosi", Pins("4", conn=("pmod", 0))),
179 )
180 ])
181 spi0 = self.cm.request("spi", 0)
182 self.assertEqual(list(self.cm.iter_port_constraints()), [
183 ("spi_0__ss__io", ["B0"], {}),
184 ("spi_0__clk__io", ["B1"], {}),
185 ("spi_0__miso__io", ["B2"], {}),
186 ("spi_0__mosi__io", ["B3"], {}),
187 ])
188
189 def test_request_via_nested_connector(self):
190 new_connectors = [
191 Connector("pmod_extension", 0, "1 2 3 4 - -", conn=("pmod", 0)),
192 ]
193 self.cm.add_connectors(new_connectors)
194 self.cm.add_resources([
195 Resource("spi", 0,
196 Subsignal("ss", Pins("1", conn=("pmod_extension", 0))),
197 Subsignal("clk", Pins("2", conn=("pmod_extension", 0))),
198 Subsignal("miso", Pins("3", conn=("pmod_extension", 0))),
199 Subsignal("mosi", Pins("4", conn=("pmod_extension", 0))),
200 )
201 ])
202 spi0 = self.cm.request("spi", 0)
203 self.assertEqual(list(self.cm.iter_port_constraints()), [
204 ("spi_0__ss__io", ["B0"], {}),
205 ("spi_0__clk__io", ["B1"], {}),
206 ("spi_0__miso__io", ["B2"], {}),
207 ("spi_0__mosi__io", ["B3"], {}),
208 ])
209
210 def test_request_clock(self):
211 clk100 = self.cm.request("clk100", 0)
212 clk50 = self.cm.request("clk50", 0, dir="i")
213 clk100_port_p, clk100_port_n, clk50_port = self.cm.iter_ports()
214 self.assertEqual(list(self.cm.iter_clock_constraints()), [
215 (clk100.i, clk100_port_p, 100e6),
216 (clk50.i, clk50_port, 50e6)
217 ])
218
219 def test_add_clock(self):
220 i2c = self.cm.request("i2c")
221 self.cm.add_clock_constraint(i2c.scl.o, 100e3)
222 self.assertEqual(list(self.cm.iter_clock_constraints()), [
223 (i2c.scl.o, None, 100e3)
224 ])
225
226 def test_wrong_resources(self):
227 with self.assertRaisesRegex(TypeError, r"^Object 'wrong' is not a Resource$"):
228 self.cm.add_resources(['wrong'])
229
230 def test_wrong_resources_duplicate(self):
231 with self.assertRaisesRegex(NameError,
232 (r"^Trying to add \(resource user_led 0 \(pins o A1\)\), but "
233 r"\(resource user_led 0 \(pins o A0\)\) has the same name and number$")):
234 self.cm.add_resources([Resource("user_led", 0, Pins("A1", dir="o"))])
235
236 def test_wrong_connectors(self):
237 with self.assertRaisesRegex(TypeError, r"^Object 'wrong' is not a Connector$"):
238 self.cm.add_connectors(['wrong'])
239
240 def test_wrong_connectors_duplicate(self):
241 with self.assertRaisesRegex(NameError,
242 (r"^Trying to add \(connector pmod 0 1=>1 2=>2\), but "
243 r"\(connector pmod 0 1=>B0 2=>B1 3=>B2 4=>B3\) has the same name and number$")):
244 self.cm.add_connectors([Connector("pmod", 0, "1 2")])
245
246 def test_wrong_lookup(self):
247 with self.assertRaisesRegex(ResourceError,
248 r"^Resource user_led#1 does not exist$"):
249 r = self.cm.lookup("user_led", 1)
250
251 def test_wrong_clock_signal(self):
252 with self.assertRaisesRegex(TypeError,
253 r"^Object None is not a Signal$"):
254 self.cm.add_clock_constraint(None, 10e6)
255
256 def test_wrong_clock_frequency(self):
257 with self.assertRaisesRegex(TypeError,
258 r"^Frequency must be a number, not None$"):
259 self.cm.add_clock_constraint(Signal(), None)
260
261 def test_wrong_request_duplicate(self):
262 with self.assertRaisesRegex(ResourceError,
263 r"^Resource user_led#0 has already been requested$"):
264 self.cm.request("user_led", 0)
265 self.cm.request("user_led", 0)
266
267 def test_wrong_request_duplicate_physical(self):
268 self.cm.add_resources([
269 Resource("clk20", 0, Pins("H1", dir="i")),
270 ])
271 self.cm.request("clk100", 0)
272 with self.assertRaisesRegex(ResourceError,
273 (r"^Resource component clk20_0 uses physical pin H1, but it is already "
274 r"used by resource component clk100_0 that was requested earlier$")):
275 self.cm.request("clk20", 0)
276
277 def test_wrong_request_with_dir(self):
278 with self.assertRaisesRegex(TypeError,
279 (r"^Direction must be one of \"i\", \"o\", \"oe\", \"io\", or \"-\", "
280 r"not 'wrong'$")):
281 user_led = self.cm.request("user_led", 0, dir="wrong")
282
283 def test_wrong_request_with_dir_io(self):
284 with self.assertRaisesRegex(ValueError,
285 (r"^Direction of \(pins o A0\) cannot be changed from \"o\" to \"i\"; direction "
286 r"can be changed from \"io\" to \"i\", \"o\", or \"oe\", or from anything "
287 r"to \"-\"$")):
288 user_led = self.cm.request("user_led", 0, dir="i")
289
290 def test_wrong_request_with_dir_dict(self):
291 with self.assertRaisesRegex(TypeError,
292 (r"^Directions must be a dict, not 'i', because \(resource i2c 0 \(subsignal scl "
293 r"\(pins o N10\)\) \(subsignal sda \(pins io N11\)\)\) "
294 r"has subsignals$")):
295 i2c = self.cm.request("i2c", 0, dir="i")
296
297 def test_wrong_request_with_wrong_xdr(self):
298 with self.assertRaisesRegex(ValueError,
299 r"^Data rate of \(pins o A0\) must be a non-negative integer, not -1$"):
300 user_led = self.cm.request("user_led", 0, xdr=-1)
301
302 def test_wrong_request_with_xdr_dict(self):
303 with self.assertRaisesRegex(TypeError,
304 r"^Data rate must be a dict, not 2, because \(resource i2c 0 \(subsignal scl "
305 r"\(pins o N10\)\) \(subsignal sda \(pins io N11\)\)\) "
306 r"has subsignals$"):
307 i2c = self.cm.request("i2c", 0, xdr=2)
308
309 def test_wrong_clock_constraint_twice(self):
310 clk100 = self.cm.request("clk100")
311 with self.assertRaisesRegex(ValueError,
312 (r"^Cannot add clock constraint on \(sig clk100_0__i\), which is already "
313 r"constrained to 100000000\.0 Hz$")):
314 self.cm.add_clock_constraint(clk100.i, 1e6)