@@ -35,362 +35,6 @@ def allocate(self, available, width):
3535 return allocated
3636
3737
38- @mock .patch ('chipflow_lib.pin_lock._parse_config' )
39- class TestLockPins (unittest .TestCase ):
40- def setUp (self ):
41- """Set up test environment with temporary directory"""
42- self .temp_dir = tempfile .TemporaryDirectory ()
43- self .original_cwd = os .getcwd ()
44- os .chdir (self .temp_dir .name )
45-
46- # Set up mock environment variables
47- self .env_patcher = mock .patch .dict (os .environ , {
48- "CHIPFLOW_ROOT" : self .temp_dir .name
49- })
50- self .env_patcher .start ()
51-
52- # Create test configuration
53- self .test_config = {
54- "chipflow" : {
55- "silicon" : {
56- "package" : "test_package" ,
57- "pads" : {
58- "clk" : {"type" : "clock" , "loc" : "1" },
59- "rst" : {"type" : "reset" , "loc" : "2" },
60- "led" : {"type" : "io" , "loc" : "3" }
61- },
62- "power" : {
63- "vdd" : {"type" : "power" , "loc" : "4" },
64- "vss" : {"type" : "ground" , "loc" : "5" }
65- }
66- },
67- "clocks" : {
68- "default" : "clk"
69- },
70- "resets" : {
71- "default" : "rst"
72- },
73- "top" : {
74- "soc" : "module:SoC"
75- }
76- }
77- }
78-
79- # Create mock interfaces
80- self .mock_interfaces = {
81- "soc" : {
82- "interface" : {
83- "members" : {
84- "uart" : {
85- "type" : "interface" ,
86- "members" : {
87- "tx" : {"type" : "port" , "width" : 1 , "dir" : "o" },
88- "rx" : {"type" : "port" , "width" : 1 , "dir" : "i" }
89- }
90- },
91- "gpio" : {
92- "type" : "interface" ,
93- "members" : {
94- "pins" : {"type" : "port" , "width" : 4 , "dir" : "io" }
95- }
96- }
97- }
98- }
99- }
100- }
101-
102- def tearDown (self ):
103- """Clean up after tests"""
104- self .env_patcher .stop ()
105- os .chdir (self .original_cwd )
106- self .temp_dir .cleanup ()
107-
108- @mock .patch ('chipflow_lib.pin_lock.PACKAGE_DEFINITIONS' )
109- @mock .patch ('chipflow_lib.pin_lock.top_interfaces' )
110- @mock .patch ('builtins.print' )
111- def test_lock_pins_new_lockfile (self , mock_print , mock_top_interfaces , mock_package_defs , mock_parse_config ):
112- """Test lock_pins when no lockfile exists"""
113- # Setup mocks
114- mock_parse_config .return_value = self .test_config
115- mock_top_interfaces .return_value = ({}, self .mock_interfaces )
116-
117- # Create mock package type
118- mock_package_type = MockPackageType ()
119- mock_package_defs .__getitem__ .return_value = mock_package_type
120- mock_package_defs .__contains__ .return_value = True
121-
122- # Execute lock_pins
123- with mock .patch ('chipflow_lib.pin_lock.logger' ) as mock_logger :
124- lock_pins ()
125-
126- # Verify print and logger calls
127- mock_print .assert_called_once_with ("Locking pins: " )
128- mock_logger .debug .assert_any_call (f"Checking [chipflow.silicon.pads]:" )
129- mock_logger .debug .assert_any_call (f"Checking [chipflow.silicon.power]:" )
130-
131- # Verify lockfile was created
132- lockfile_path = Path ('pins.lock' )
133- self .assertTrue (lockfile_path .exists ())
134-
135- # Check content of lockfile
136- with open (lockfile_path , 'r' ) as f :
137- lock_data = json .load (f )
138-
139- # Check that pins were allocated for interfaces
140- self .assertIn ("port_map" , lock_data )
141- self .assertIn ("soc" , lock_data ["port_map" ])
142- self .assertIn ("uart" , lock_data ["port_map" ]["soc" ])
143- self .assertIn ("gpio" , lock_data ["port_map" ]["soc" ])
144-
145- # Check that pin allocations make sense
146- self .assertEqual (len (lock_data ["port_map" ]["soc" ]["uart" ]["tx" ]["pins" ]), 1 )
147- self .assertEqual (len (lock_data ["port_map" ]["soc" ]["uart" ]["rx" ]["pins" ]), 1 )
148- self .assertEqual (len (lock_data ["port_map" ]["soc" ]["gpio" ]["pins" ]["pins" ]), 4 )
149-
150- @mock .patch ('chipflow_lib.pin_lock.PACKAGE_DEFINITIONS' )
151- @mock .patch ('chipflow_lib.pin_lock.top_interfaces' )
152- @mock .patch ('builtins.print' )
153- def test_lock_pins_existing_lockfile (self , mock_print , mock_top_interfaces , mock_package_defs , mock_parse_config ):
154- """Test lock_pins when lockfile exists"""
155- # Setup mocks
156- mock_parse_config .return_value = self .test_config
157- mock_top_interfaces .return_value = ({}, self .mock_interfaces )
158-
159- # Create mock package type
160- mock_package_type = MockPackageType ()
161- mock_package_defs .__getitem__ .return_value = mock_package_type
162- mock_package_defs .__contains__ .return_value = True
163-
164- # Create existing lockfile with predefined pin allocations
165- existing_lock = {
166- "package" : {
167- "package_type" : {
168- "type" : "_QuadPackageDef" ,
169- "name" : "test_package" ,
170- "width" : 36 ,
171- "height" : 36
172- },
173- "power" : {
174- "vdd" : {"type" : "power" , "pins" : ["4" ], "port_name" : "vdd" },
175- "vss" : {"type" : "ground" , "pins" : ["5" ], "port_name" : "vss" }
176- },
177- "clocks" : {
178- "clk" : {"type" : "clock" , "pins" : ["1" ], "direction" : "i" , "port_name" : "clk" }
179- },
180- "resets" : {
181- "rst" : {"type" : "reset" , "pins" : ["2" ], "direction" : "i" , "port_name" : "rst" }
182- }
183- },
184- "port_map" : {
185- "soc" : {
186- "uart" : {
187- "tx" : {"type" : "output" , "pins" : ["10" ], "port_name" : "soc_uart_tx" , "direction" : "o" },
188- "rx" : {"type" : "input" , "pins" : ["11" ], "port_name" : "soc_uart_rx" , "direction" : "i" }
189- },
190- "gpio" : {
191- "pins" : {"type" : "bidir" , "pins" : ["12" , "13" , "14" , "15" ], "port_name" : "soc_gpio_pins" , "direction" : "io" }
192- }
193- }
194- },
195- "metadata" : self .mock_interfaces
196- }
197-
198- with open ('pins.lock' , 'w' ) as f :
199- json .dump (existing_lock , f )
200-
201- # Execute lock_pins
202- with mock .patch ('chipflow_lib.pin_lock.logger' ) as mock_logger :
203- lock_pins ()
204-
205- # Verify print and logger calls
206- mock_print .assert_called_once_with ("Locking pins: using pins.lock" )
207-
208- # Verify lockfile was updated
209- lockfile_path = Path ('pins.lock' )
210- self .assertTrue (lockfile_path .exists ())
211-
212- # Check content of lockfile
213- with open (lockfile_path , 'r' ) as f :
214- lock_data = json .load (f )
215-
216- # Check that pins were preserved from existing lock
217- self .assertEqual (lock_data ["port_map" ]["soc" ]["uart" ]["tx" ]["pins" ], ["10" ])
218- self .assertEqual (lock_data ["port_map" ]["soc" ]["uart" ]["rx" ]["pins" ], ["11" ])
219- self .assertEqual (lock_data ["port_map" ]["soc" ]["gpio" ]["pins" ]["pins" ], ["12" , "13" , "14" , "15" ])
220-
221- @mock .patch ('chipflow_lib.pin_lock.PACKAGE_DEFINITIONS' )
222- @mock .patch ('chipflow_lib.pin_lock.top_interfaces' )
223- def test_lock_pins_out_of_pins (self , mock_top_interfaces , mock_package_defs , mock_parse_config ):
224- """Test lock_pins when we run out of pins"""
225- # Setup mocks
226- mock_parse_config .return_value = self .test_config
227- mock_top_interfaces .return_value = ({}, self .mock_interfaces )
228-
229- # Create mock package type with limited pins
230- limited_package = MockPackageType ()
231- limited_package .pins = set (["1" , "2" , "3" , "4" , "5" ]) # Only enough for the fixed pins
232- mock_package_defs .__getitem__ .return_value = limited_package
233- mock_package_defs .__contains__ .return_value = True
234-
235- # Execute lock_pins - should raise an error
236- with self .assertRaises (ChipFlowError ) as cm :
237- lock_pins ()
238-
239- self .assertIn ("No pins were allocated" , str (cm .exception ))
240-
241- @mock .patch ('chipflow_lib.pin_lock.PACKAGE_DEFINITIONS' )
242- @mock .patch ('chipflow_lib.pin_lock.top_interfaces' )
243- def test_lock_pins_pin_conflict (self , mock_top_interfaces , mock_package_defs , mock_parse_config ):
244- """Test lock_pins when there's a pin conflict with existing lock"""
245- # Setup mocks
246- # Change the loc of a pin in the config
247- conflicting_config = self .test_config .copy ()
248- conflicting_config ["chipflow" ]["silicon" ]["pads" ]["clk" ]["loc" ] = "99"
249-
250- mock_parse_config .return_value = conflicting_config
251- mock_top_interfaces .return_value = ({}, self .mock_interfaces )
252-
253- # Create mock package type
254- mock_package_type = MockPackageType ()
255- mock_package_defs .__getitem__ .return_value = mock_package_type
256- mock_package_defs .__contains__ .return_value = True
257-
258- # Create existing lockfile with clk on pin 1
259- existing_lock = {
260- "package" : {
261- "package_type" : {
262- "type" : "_QuadPackageDef" ,
263- "name" : "test_package" ,
264- "width" : 36 ,
265- "height" : 36
266- },
267- "power" : {
268- "vdd" : {"type" : "power" , "pins" : ["4" ], "port_name" : "vdd" },
269- "vss" : {"type" : "ground" , "pins" : ["5" ], "port_name" : "vss" }
270- },
271- "clocks" : {
272- "clk" : {"type" : "clock" , "pins" : ["1" ], "direction" : "i" , "port_name" : "clk" }
273- },
274- "resets" : {
275- "rst" : {"type" : "reset" , "pins" : ["2" ], "direction" : "i" , "port_name" : "rst" }
276- }
277- },
278- "port_map" : {},
279- "metadata" : {}
280- }
281-
282- with open ('pins.lock' , 'w' ) as f :
283- json .dump (existing_lock , f )
284-
285- # Execute lock_pins - should raise an error
286- with self .assertRaises (ChipFlowError ) as cm :
287- lock_pins ()
288-
289- self .assertIn ("conflicts with pins.lock" , str (cm .exception ))
290-
291- @mock .patch ('chipflow_lib.pin_lock.PACKAGE_DEFINITIONS' )
292- @mock .patch ('chipflow_lib.pin_lock.top_interfaces' )
293- def test_lock_pins_interface_size_change (self , mock_top_interfaces , mock_package_defs , mock_parse_config ):
294- """Test lock_pins when an interface changes size"""
295- # Setup mocks
296- mock_parse_config .return_value = self .test_config
297-
298- # Create interfaces with larger gpio width
299- modified_interfaces = {
300- "soc" : {
301- "interface" : {
302- "members" : {
303- "uart" : {
304- "type" : "interface" ,
305- "members" : {
306- "tx" : {"type" : "port" , "width" : 1 , "dir" : "o" },
307- "rx" : {"type" : "port" , "width" : 1 , "dir" : "i" }
308- }
309- },
310- "gpio" : {
311- "type" : "interface" ,
312- "members" : {
313- "pins" : {"type" : "port" , "width" : 8 , "dir" : "io" } # Changed from 4 to 8
314- }
315- }
316- }
317- }
318- }
319- }
320-
321- mock_top_interfaces .return_value = ({}, modified_interfaces )
322-
323- # Create mock package type
324- mock_package_type = MockPackageType ()
325- mock_package_defs .__getitem__ .return_value = mock_package_type
326- mock_package_defs .__contains__ .return_value = True
327-
328- # Create existing lockfile with gpio width 4
329- existing_lock = {
330- "package" : {
331- "package_type" : {
332- "type" : "_QuadPackageDef" ,
333- "name" : "test_package" ,
334- "width" : 36 ,
335- "height" : 36
336- },
337- "power" : {
338- "vdd" : {"type" : "power" , "pins" : ["4" ], "port_name" : "vdd" },
339- "vss" : {"type" : "ground" , "pins" : ["5" ], "port_name" : "vss" }
340- },
341- "clocks" : {
342- "clk" : {"type" : "clock" , "pins" : ["1" ], "direction" : "i" , "port_name" : "clk" }
343- },
344- "resets" : {
345- "rst" : {"type" : "reset" , "pins" : ["2" ], "direction" : "i" , "port_name" : "rst" }
346- }
347- },
348- "port_map" : {
349- "soc" : {
350- "uart" : {
351- "tx" : {"type" : "output" , "pins" : ["10" ], "port_name" : "soc_uart_tx" , "direction" : "o" },
352- "rx" : {"type" : "input" , "pins" : ["11" ], "port_name" : "soc_uart_rx" , "direction" : "i" }
353- },
354- "gpio" : {
355- "pins" : {"type" : "bidir" , "pins" : ["12" , "13" , "14" , "15" ], "port_name" : "soc_gpio_pins" , "direction" : "io" }
356- }
357- }
358- },
359- "metadata" : {}
360- }
361-
362- with open ('pins.lock' , 'w' ) as f :
363- json .dump (existing_lock , f )
364-
365- # Execute lock_pins - should raise an error
366- with self .assertRaises (ChipFlowError ) as cm :
367- lock_pins ()
368-
369- self .assertIn ("has changed size" , str (cm .exception ))
370-
371- @mock .patch ('chipflow_lib.pin_lock.PACKAGE_DEFINITIONS' )
372- @mock .patch ('chipflow_lib.pin_lock.top_interfaces' )
373- @mock .patch ('builtins.print' )
374- def test_lock_pins_unknown_package (self , mock_print , mock_top_interfaces , mock_package_defs , mock_parse_config ):
375- """Test lock_pins with an unknown package"""
376- # Setup config with unknown package
377- unknown_config = self .test_config .copy ()
378- unknown_config ["chipflow" ]["silicon" ]["package" ] = "unknown_package"
379- mock_parse_config .return_value = unknown_config
380-
381- # Create mock interfaces
382- mock_top_interfaces .return_value = ({}, {})
383-
384- # Set up package defs
385- mock_package_defs .__contains__ .return_value = False
386-
387- # Execute lock_pins
388- with mock .patch ('chipflow_lib.pin_lock.logger' ) as mock_logger :
389- with self .assertRaises (KeyError ) as cm :
390- lock_pins ()
391-
392- # Verify logger warning
393- mock_logger .debug .assert_any_call ("Package 'unknown_package is unknown" )
39438
39539
39640class TestPinLockUtilities (unittest .TestCase ):
0 commit comments