build.{dsl,plat,res}: allow dir="oe".
[nmigen.git] / nmigen / build / dsl.py
1 __all__ = ["Pins", "DiffPairs", "Subsignal", "Resource"]
2
3
4 class Pins:
5 def __init__(self, names, dir="io"):
6 if not isinstance(names, str):
7 raise TypeError("Names must be a whitespace-separated string, not {!r}"
8 .format(names))
9 self.names = names.split()
10
11 if dir not in ("i", "o", "io"):
12 raise TypeError("Direction must be one of \"i\", \"o\", \"oe\", or \"io\", not {!r}"
13 .format(dir))
14 self.dir = dir
15
16 def __len__(self):
17 return len(self.names)
18
19 def __iter__(self):
20 return iter(self.names)
21
22 def __repr__(self):
23 return "(pins {} {})".format(self.dir, " ".join(self.names))
24
25
26 class DiffPairs:
27 def __init__(self, p, n, dir="io"):
28 self.p = Pins(p, dir=dir)
29 self.n = Pins(n, dir=dir)
30
31 if len(self.p.names) != len(self.n.names):
32 raise TypeError("Positive and negative pins must have the same width, but {!r} "
33 "and {!r} do not"
34 .format(self.p, self.n))
35
36 self.dir = dir
37
38 def __len__(self):
39 return len(self.p.names)
40
41 def __iter__(self):
42 return zip(self.p.names, self.n.names)
43
44 def __repr__(self):
45 return "(diffpairs {} (p {}) (n {}))".format(
46 self.dir, " ".join(self.p.names), " ".join(self.n.names))
47
48
49 class Subsignal:
50 def __init__(self, name, *io, extras=None):
51 self.name = name
52
53 if not io:
54 raise TypeError("Missing I/O constraints")
55 for c in io:
56 if not isinstance(c, (Pins, DiffPairs, Subsignal)):
57 raise TypeError("I/O constraint must be one of Pins, DiffPairs or Subsignal, "
58 "not {!r}"
59 .format(c))
60 if isinstance(io[0], (Pins, DiffPairs)) and len(io) > 1:
61 raise TypeError("Pins and DiffPairs cannot be followed by more I/O constraints, but "
62 "{!r} is followed by {!r}"
63 .format(io[0], io[1]))
64 if isinstance(io[0], Subsignal):
65 for c in io[1:]:
66 if not isinstance(c, Subsignal):
67 raise TypeError("A Subsignal can only be followed by more Subsignals, but "
68 "{!r} is followed by {!r}"
69 .format(io[0], c))
70 self.io = io
71 self.extras = {}
72
73 if extras is not None:
74 if not isinstance(extras, dict):
75 raise TypeError("Extra constraints must be a dict, not {!r}"
76 .format(extras))
77 for extra_key, extra_value in extras.items():
78 if not isinstance(extra_key, str):
79 raise TypeError("Extra constraint key must be a string, not {!r}"
80 .format(extra_key))
81 if not isinstance(extra_value, str):
82 raise TypeError("Extra constraint value must be a string, not {!r}"
83 .format(extra_value))
84 self.extras[extra_key] = extra_value
85
86 if isinstance(self.io[0], Subsignal):
87 for sub in self.io:
88 sub.extras.update(self.extras)
89
90 def __repr__(self):
91 return "(subsignal {} {} {})".format(self.name,
92 " ".join(map(repr, self.io)),
93 " ".join("{}={}".format(k, v)
94 for k, v in self.extras.items()))
95
96
97 class Resource(Subsignal):
98 def __init__(self, name, number, *io, extras=None):
99 super().__init__(name, *io, extras=extras)
100
101 self.number = number
102
103 def __repr__(self):
104 return "(resource {} {} {} {})".format(self.name, self.number,
105 " ".join(map(repr, self.io)),
106 " ".join("{}={}".format(k, v)
107 for k, v in self.extras.items()))