Skip to content

Commit 352b4de

Browse files
robtaylorclaude
andcommitted
Fix output ports to not have _oe signals
- Modified SiliconPlatformPort.__init__ to not create _oe signals for output ports - Changed IOBuffer.elaborate to only set oe for bidirectional ports - Updated wire method to check if _oe is None before setting it - Improved oe property error message to be more specific - Updated tests to verify output ports don't have oe signals - Fixed test_wire_output to work without an oe signal - Removed trailing whitespace for better code quality This fix addresses the hardware design principle that output ports should only have _o signals, not _oe (output enable) signals. Only bidirectional ports need _oe signals to control when they are driving vs. receiving. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 2113009 commit 352b4de

File tree

2 files changed

+73
-72
lines changed

2 files changed

+73
-72
lines changed

chipflow_lib/platforms/silicon.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,11 @@ def __init__(self,
7474
self._invert = invert
7575
self._options = port.options
7676
self._pins = port.pins
77-
77+
7878
# Initialize signal attributes to None
7979
self._i = None
8080
self._o = None
8181
self._oe = None
82-
8382
# Create signals based on direction
8483
if self._direction in (io.Direction.Input, io.Direction.Bidir):
8584
self._i = Signal(port.width, name=f"{component}_{name}__i")
@@ -90,19 +89,18 @@ def __init__(self,
9089
self._oe = Signal(port.width, name=f"{component}_{name}__oe", init=-1)
9190
else:
9291
self._oe = Signal(1, name=f"{component}_{name}__oe", init=-1)
93-
elif self._direction is io.Direction.Output:
94-
# Always create an _oe for output ports
95-
self._oe = Signal(1, name=f"{component}_{name}__oe", init=-1)
92+
# Output ports don't have _oe signals
9693

9794
logger.debug(f"Created SiliconPlatformPort {name}, width={len(port.pins)},dir{self._direction}")
9895

9996
def wire(self, m: Module, interface: PureInterface):
10097
assert self._direction == interface.signature.direction
10198
if hasattr(interface, 'i'):
10299
m.d.comb += interface.i.eq(self.i)
103-
for d in ['o', 'oe']:
104-
if hasattr(interface, d):
105-
m.d.comb += getattr(self, d).eq(getattr(interface, d))
100+
if hasattr(interface, 'o'):
101+
m.d.comb += self.o.eq(interface.o)
102+
if hasattr(interface, 'oe') and self._oe is not None:
103+
m.d.comb += self.oe.eq(interface.oe)
106104

107105
@property
108106

@@ -122,7 +120,7 @@ def o(self):
122120
@property
123121
def oe(self):
124122
if self._oe is None:
125-
raise AttributeError("SiliconPlatformPort with input direction does not have an "
123+
raise AttributeError("SiliconPlatformPort with output or input direction does not have an "
126124
"output enable signal")
127125
return self._oe
128126

@@ -217,7 +215,9 @@ def elaborate(self, platform):
217215
m.d.comb += i_inv.eq(self.port.i)
218216
if self.direction in (io.Direction.Output, io.Direction.Bidir):
219217
m.d.comb += self.port.o.eq(o_inv)
220-
m.d.comb += self.port.oe.eq(self.oe)
218+
# Only set oe for bidirectional ports
219+
if self.direction is io.Direction.Bidir:
220+
m.d.comb += self.port.oe.eq(self.oe)
221221

222222
return m
223223

tests/test_silicon_platform_port.py

Lines changed: 63 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
class TestSiliconPlatformPort(unittest.TestCase):
1515
def test_init_input_port(self):
1616
# Test initialization with input direction
17-
port_obj = Port(type="input", pins=["1", "2", "3"], port_name="test_input",
17+
port_obj = Port(type="input", pins=["1", "2", "3"], port_name="test_input",
1818
direction="i", options={})
1919
spp = SiliconPlatformPort("comp", "test_input", port_obj)
20-
20+
2121
self.assertEqual(spp.direction, io.Direction.Input)
2222
self.assertEqual(len(spp), 3) # Should match the port width
2323
self.assertFalse(spp.invert)
24-
24+
2525
# Test accessing properties
2626
_ = spp.i # Should not raise an error
2727
with self.assertRaises(AttributeError):
@@ -31,183 +31,184 @@ def test_init_input_port(self):
3131

3232
def test_init_output_port(self):
3333
# Test initialization with output direction
34-
port_obj = Port(type="output", pins=["1", "2"], port_name="test_output",
34+
port_obj = Port(type="output", pins=["1", "2"], port_name="test_output",
3535
direction="o", options={})
3636
spp = SiliconPlatformPort("comp", "test_output", port_obj)
37-
37+
3838
self.assertEqual(spp.direction, io.Direction.Output)
3939
self.assertEqual(len(spp), 2) # Should match the port width
4040
self.assertFalse(spp.invert)
41-
41+
4242
# Test accessing properties
4343
_ = spp.o # Should not raise an error
44-
_ = spp.oe # Should not raise an error since we now always have an _oe for outputs
44+
with self.assertRaises(AttributeError):
45+
_ = spp.oe # Should raise an error since output ports don't have oe signals
4546
with self.assertRaises(AttributeError):
4647
_ = spp.i # Should raise an error for output port
4748

4849
def test_init_bidir_port(self):
4950
# Test initialization with bidirectional direction
50-
port_obj = Port(type="bidir", pins=["1", "2", "3", "4"], port_name="test_bidir",
51+
port_obj = Port(type="bidir", pins=["1", "2", "3", "4"], port_name="test_bidir",
5152
direction="io", options={"all_have_oe": False})
5253
spp = SiliconPlatformPort("comp", "test_bidir", port_obj)
53-
54+
5455
self.assertEqual(spp.direction, io.Direction.Bidir)
5556
self.assertEqual(len(spp), 4) # Should match the port width
5657
self.assertFalse(spp.invert)
57-
58+
5859
# Check the signals have the correct widths
5960
self.assertEqual(len(spp.i), 4)
6061
self.assertEqual(len(spp.o), 4)
6162
self.assertEqual(len(spp.oe), 1) # Single OE for all pins
62-
63+
6364
# Test accessing properties
6465
_ = spp.i # Should not raise an error
6566
_ = spp.o # Should not raise an error
6667
_ = spp.oe # Should not raise an error
6768

6869
def test_init_bidir_port_all_have_oe(self):
6970
# Test initialization with bidirectional direction and all_have_oe=True
70-
port_obj = Port(type="bidir", pins=["1", "2", "3"], port_name="test_bidir",
71+
port_obj = Port(type="bidir", pins=["1", "2", "3"], port_name="test_bidir",
7172
direction="io", options={"all_have_oe": True})
7273
spp = SiliconPlatformPort("comp", "test_bidir", port_obj)
73-
74+
7475
self.assertEqual(spp.direction, io.Direction.Bidir)
7576
self.assertEqual(len(spp), 3) # Should match the port width
7677
self.assertFalse(spp.invert)
77-
78+
7879
# Check the signals have the correct widths
7980
self.assertEqual(len(spp.i), 3)
8081
self.assertEqual(len(spp.o), 3)
8182
self.assertEqual(len(spp.oe), 3) # One OE per pin
82-
83+
8384
def test_len_input_port(self):
8485
# Test __len__ with input direction
85-
port_obj = Port(type="input", pins=["1", "2", "3"], port_name="test_input",
86+
port_obj = Port(type="input", pins=["1", "2", "3"], port_name="test_input",
8687
direction="i", options={})
8788
spp = SiliconPlatformPort("comp", "test_input", port_obj)
88-
89+
8990
self.assertEqual(len(spp), 3) # Should match the port width
90-
91+
9192
def test_len_output_port(self):
9293
# Test __len__ with output direction
93-
port_obj = Port(type="output", pins=["1", "2"], port_name="test_output",
94+
port_obj = Port(type="output", pins=["1", "2"], port_name="test_output",
9495
direction="o", options={})
9596
spp = SiliconPlatformPort("comp", "test_output", port_obj)
96-
97+
9798
self.assertEqual(len(spp), 2) # Should match the port width
98-
99+
99100
def test_len_bidir_port(self):
100101
# Test __len__ with bidirectional direction
101-
port_obj = Port(type="bidir", pins=["1", "2", "3", "4"], port_name="test_bidir",
102+
port_obj = Port(type="bidir", pins=["1", "2", "3", "4"], port_name="test_bidir",
102103
direction="io", options={"all_have_oe": False})
103104
spp = SiliconPlatformPort("comp", "test_bidir", port_obj)
104-
105+
105106
self.assertEqual(len(spp), 4) # Should match the port width
106-
107+
107108
def test_len_bidir_port_all_have_oe(self):
108109
# Test __len__ with bidirectional direction and all_have_oe=True
109-
port_obj = Port(type="bidir", pins=["1", "2", "3"], port_name="test_bidir",
110+
port_obj = Port(type="bidir", pins=["1", "2", "3"], port_name="test_bidir",
110111
direction="io", options={"all_have_oe": True})
111112
spp = SiliconPlatformPort("comp", "test_bidir", port_obj)
112-
113+
113114
self.assertEqual(len(spp), 3) # Should match the port width
114115

115116
def test_getitem(self):
116117
# Test __getitem__
117-
port_obj = Port(type="bidir", pins=["1", "2", "3"], port_name="test_bidir",
118+
port_obj = Port(type="bidir", pins=["1", "2", "3"], port_name="test_bidir",
118119
direction="io", options={"all_have_oe": True})
119120
spp = SiliconPlatformPort("comp", "test_bidir", port_obj)
120-
121+
121122
# Get a slice of the port
122123
slice_port = spp[1]
123124
self.assertEqual(spp.direction, slice_port.direction)
124125
self.assertEqual(spp.invert, slice_port.invert)
125-
126+
126127
def test_invert(self):
127128
# Test __invert__ for a bidirectional port since it has all signal types
128-
port_obj = Port(type="bidir", pins=["1", "2", "3"], port_name="test_bidir",
129+
port_obj = Port(type="bidir", pins=["1", "2", "3"], port_name="test_bidir",
129130
direction="io", options={"all_have_oe": True})
130131
spp = SiliconPlatformPort("comp", "test_bidir", port_obj)
131-
132+
132133
inverted_port = ~spp
133134
self.assertEqual(spp.direction, inverted_port.direction)
134135
self.assertNotEqual(spp.invert, inverted_port.invert)
135136
self.assertTrue(inverted_port.invert)
136-
137+
137138
def test_add(self):
138139
# Test __add__
139-
port_obj1 = Port(type="input", pins=["1", "2"], port_name="test_input1",
140+
port_obj1 = Port(type="input", pins=["1", "2"], port_name="test_input1",
140141
direction="i", options={})
141-
port_obj2 = Port(type="input", pins=["3", "4"], port_name="test_input2",
142+
port_obj2 = Port(type="input", pins=["3", "4"], port_name="test_input2",
142143
direction="i", options={})
143144
spp1 = SiliconPlatformPort("comp", "test_input1", port_obj1)
144145
spp2 = SiliconPlatformPort("comp", "test_input2", port_obj2)
145-
146+
146147
combined_port = spp1 + spp2
147148
self.assertEqual(spp1.direction, combined_port.direction)
148149
self.assertEqual(len(combined_port), len(spp1) + len(spp2))
149-
150+
150151
def test_wire_input(self):
151152
# Test wire method with a mock input interface
152-
port_obj = Port(type="input", pins=["1", "2", "3"], port_name="test_input",
153+
port_obj = Port(type="input", pins=["1", "2", "3"], port_name="test_input",
153154
direction="i", options={})
154155
spp = SiliconPlatformPort("comp", "test_input", port_obj)
155-
156+
156157
# Create a mock interface
157158
class MockSignature(wiring.Signature):
158159
def __init__(self):
159160
super().__init__({"i": wiring.In(3)})
160161
self._direction = io.Direction.Input
161-
162+
162163
@property
163164
def direction(self):
164165
return self._direction
165-
166+
166167
class MockInterface(PureInterface):
167168
def __init__(self):
168169
self.signature = MockSignature()
169170
self.i = Signal(3)
170-
171+
171172
interface = MockInterface()
172173
m = Module()
173-
174+
174175
# Wire should not raise an exception
175176
spp.wire(m, interface)
176-
177+
177178
def test_wire_output(self):
178-
# Test wire method with a mock output interface to cover line 105
179-
port_obj = Port(type="output", pins=["1", "2"], port_name="test_output",
179+
# Test wire method with a mock output interface to cover line 102
180+
port_obj = Port(type="output", pins=["1", "2"], port_name="test_output",
180181
direction="o", options={})
181182
spp = SiliconPlatformPort("comp", "test_output", port_obj)
182-
183+
183184
# Create a mock interface
184185
class MockSignature(wiring.Signature):
185186
def __init__(self):
186187
super().__init__({"o": wiring.Out(2)})
187188
self._direction = io.Direction.Output
188-
189+
189190
@property
190191
def direction(self):
191192
return self._direction
192-
193+
193194
class MockInterface(PureInterface):
194195
def __init__(self):
195196
self.signature = MockSignature()
196197
self.o = Signal(2)
197-
self.oe = Signal(1)
198-
198+
# No oe signal for output ports
199+
199200
interface = MockInterface()
200201
m = Module()
201-
202+
202203
# Wire should not raise an exception
203204
spp.wire(m, interface)
204-
205+
205206
def test_wire_bidir(self):
206207
# Test wire method with a mock bidirectional interface to cover both cases
207-
port_obj = Port(type="bidir", pins=["1", "2", "3"], port_name="test_bidir",
208+
port_obj = Port(type="bidir", pins=["1", "2", "3"], port_name="test_bidir",
208209
direction="io", options={"all_have_oe": True})
209210
spp = SiliconPlatformPort("comp", "test_bidir", port_obj)
210-
211+
211212
# Create a mock interface
212213
class MockSignature(wiring.Signature):
213214
def __init__(self):
@@ -217,33 +218,33 @@ def __init__(self):
217218
"oe": wiring.Out(3),
218219
})
219220
self._direction = io.Direction.Bidir
220-
221+
221222
@property
222223
def direction(self):
223224
return self._direction
224-
225+
225226
class MockInterface(PureInterface):
226227
def __init__(self):
227228
self.signature = MockSignature()
228229
self.i = Signal(3)
229230
self.o = Signal(3)
230231
self.oe = Signal(3)
231-
232+
232233
interface = MockInterface()
233234
m = Module()
234-
235+
235236
# Wire should not raise an exception
236237
spp.wire(m, interface)
237-
238+
238239
def test_repr(self):
239240
# Test the __repr__ method for a bidirectional port
240-
port_obj = Port(type="bidir", pins=["1", "2", "3"], port_name="test_bidir",
241+
port_obj = Port(type="bidir", pins=["1", "2", "3"], port_name="test_bidir",
241242
direction="io", options={"all_have_oe": True})
242243
spp = SiliconPlatformPort("comp", "test_bidir", port_obj)
243-
244+
244245
# Get the representation
245246
repr_str = repr(spp)
246-
247+
247248
# Check that it contains expected elements
248249
self.assertIn("SiliconPlatformPort", repr_str)
249250
self.assertIn("direction", repr_str)

0 commit comments

Comments
 (0)