@@ -58,7 +58,6 @@ def empty(cls, path: TransformUtil.Path) -> "BoardScope": # returns a fresh, em
5858 return BoardScope (path , {}, {}, {}, [])
5959
6060
61- Scopes = Dict [TransformUtil .Path , Optional [BoardScope ]] # Block -> board scope (reference, aliased across entries)
6261ClassPaths = Dict [
6362 TransformUtil .Path , List [edgir .LibraryPath ]
6463] # Path -> class names corresponding to shortened path name
@@ -82,8 +81,9 @@ def flatten_port(path: TransformUtil.Path, port: edgir.PortLike) -> Iterable[Tra
8281 def __init__ (self , design : CompiledDesign ):
8382 super ().__init__ (design )
8483
85- self .all_scopes = [BoardScope .empty (TransformUtil .Path .empty ())] # list of unique scopes
86- self .scopes : Scopes = {TransformUtil .Path .empty (): self .all_scopes [0 ]}
84+ self .scopes : Dict [TransformUtil .Path , Optional [BoardScope ]] = {
85+ TransformUtil .Path .empty (): BoardScope .empty (TransformUtil .Path .empty ())
86+ } # board scope path to scope object
8787 self .class_paths : ClassPaths = {TransformUtil .Path .empty (): []} # seed root
8888 self .path_traverse_order : List [TransformUtil .Path ] = []
8989
@@ -98,6 +98,16 @@ def process_blocklike(
9898 else :
9999 scope_obj = None
100100
101+ if isinstance (block , edgir .HierarchyBlock ) and "fp_subboard" in block .meta .members .node :
102+ # only valid for sub-board blocks, where some things happen in the parent scope
103+ parent_scope = self ._board_parent_scopes [path ]
104+ if parent_scope is not None :
105+ parent_scope_obj : Optional [BoardScope ] = self .scopes [parent_scope ]
106+ else :
107+ parent_scope_obj = None
108+ else :
109+ parent_scope_obj = None
110+
101111 if isinstance (block , edgir .HierarchyBlock ):
102112 class_path = self .class_paths [path ]
103113 for block_pair in block .blocks :
@@ -171,66 +181,85 @@ def process_blocklike(
171181 scope_obj .edges .setdefault (src_path , []) # make sure there is a port entry so single-pin nets are named
172182 scope_obj .pins .setdefault (src_path , []).append (NetPin (path , pin_name ))
173183
174- for constraint_pair in block .constraints :
175- if scope_obj is not None :
184+ # for blocks with mixed scope, connections happen in both scopes, since blocks may be in either
185+ # this may cause inner connections to leach out into the parent scope, or
186+ # result in extraneous connections in either scope,
187+ # but is much simpler implementation-wise
188+ all_scopes = []
189+ if scope_obj is not None :
190+ all_scopes .append (scope_obj )
191+ if parent_scope_obj is not None and parent_scope_obj is not scope_obj :
192+ all_scopes .append (parent_scope_obj )
193+
194+ if all_scopes :
195+ for constraint_pair in block .constraints :
176196 if constraint_pair .value .HasField ("connected" ):
177- self .process_connected (path , block , scope_obj , constraint_pair .value .connected )
197+ self .process_connected (path , block , all_scopes , constraint_pair .value .connected )
178198 elif constraint_pair .value .HasField ("connectedArray" ):
179199 for expanded_connect in constraint_pair .value .connectedArray .expanded :
180- self .process_connected (path , block , scope_obj , expanded_connect )
200+ self .process_connected (path , block , all_scopes , expanded_connect )
181201 elif constraint_pair .value .HasField ("exported" ):
182- self .process_exported (path , block , scope_obj , constraint_pair .value .exported )
202+ self .process_exported (path , block , all_scopes , constraint_pair .value .exported )
183203 elif constraint_pair .value .HasField ("exportedArray" ):
184204 for expanded_export in constraint_pair .value .exportedArray .expanded :
185- self .process_exported (path , block , scope_obj , expanded_export )
205+ self .process_exported (path , block , all_scopes , expanded_export )
186206 elif constraint_pair .value .HasField ("exportedTunnel" ):
187- self .process_exported (path , block , scope_obj , constraint_pair .value .exportedTunnel )
207+ self .process_exported (path , block , all_scopes , constraint_pair .value .exportedTunnel )
188208
189209 def process_connected (
190- self , path : TransformUtil .Path , current : edgir .EltTypes , scope : BoardScope , constraint : edgir .ConnectedExpr
210+ self ,
211+ path : TransformUtil .Path ,
212+ current : edgir .EltTypes ,
213+ scopes : List [BoardScope ],
214+ constraint : edgir .ConnectedExpr ,
191215 ) -> None :
192216 if constraint .expanded :
193217 assert len (constraint .expanded ) == 1
194- self .process_connected (path , current , scope , constraint .expanded [0 ])
218+ self .process_connected (path , current , scopes , constraint .expanded [0 ])
195219 return
196220 assert constraint .block_port .HasField ("ref" )
197221 assert constraint .link_port .HasField ("ref" )
198222 self .connect_ports (
199- scope , path .follow (constraint .block_port .ref , current ), path .follow (constraint .link_port .ref , current )
223+ scopes , path .follow (constraint .block_port .ref , current ), path .follow (constraint .link_port .ref , current )
200224 )
201225
202226 def process_exported (
203- self , path : TransformUtil .Path , current : edgir .EltTypes , scope : BoardScope , constraint : edgir .ExportedExpr
227+ self ,
228+ path : TransformUtil .Path ,
229+ current : edgir .EltTypes ,
230+ scopes : List [BoardScope ],
231+ constraint : edgir .ExportedExpr ,
204232 ) -> None :
205233 if constraint .expanded :
206234 assert len (constraint .expanded ) == 1
207- self .process_exported (path , current , scope , constraint .expanded [0 ])
235+ self .process_exported (path , current , scopes , constraint .expanded [0 ])
208236 return
209237 assert constraint .internal_block_port .HasField ("ref" )
210238 assert constraint .exterior_port .HasField ("ref" )
211239 self .connect_ports (
212- scope ,
240+ scopes ,
213241 path .follow (constraint .internal_block_port .ref , current ),
214242 path .follow (constraint .exterior_port .ref , current ),
215243 )
216244
217245 def connect_ports (
218246 self ,
219- scope : BoardScope ,
247+ scopes : List [ BoardScope ] ,
220248 elt1 : Tuple [TransformUtil .Path , edgir .EltTypes ],
221249 elt2 : Tuple [TransformUtil .Path , edgir .EltTypes ],
222250 ) -> None :
223251 """Recursively connect ports, including containers and leaf ports. Net-ness is ignored here."""
224252 if isinstance (elt1 [1 ], edgir .Port ) and isinstance (elt2 [1 ], edgir .Port ):
225- scope .edges .setdefault (elt1 [0 ], []).append (elt2 [0 ])
226- scope .edges .setdefault (elt2 [0 ], []).append (elt1 [0 ])
253+ for scope in scopes :
254+ scope .edges .setdefault (elt1 [0 ], []).append (elt2 [0 ])
255+ scope .edges .setdefault (elt2 [0 ], []).append (elt1 [0 ])
227256
228257 elt1_names = list (map (lambda pair : pair .name , elt1 [1 ].ports ))
229258 elt2_names = list (map (lambda pair : pair .name , elt2 [1 ].ports ))
230259 assert elt1_names == elt2_names , f"mismatched port sub-ports in types { elt1 } , { elt2 } "
231260 for key in elt2_names :
232261 self .connect_ports (
233- scope ,
262+ scopes ,
234263 (elt1 [0 ].append_port (key ), edgir .resolve_portlike (edgir .pair_get (elt1 [1 ].ports , key ))),
235264 (elt2 [0 ].append_port (key ), edgir .resolve_portlike (edgir .pair_get (elt2 [1 ].ports , key ))),
236265 )
@@ -371,11 +400,10 @@ def port_ignored_paths(path: TransformUtil.Path) -> bool: # ignore link ports f
371400
372401 return Netlist (netlist_footprints , netlist_nets )
373402
374- def run (self ) -> Netlist :
403+ def run (self ) -> Dict [ TransformUtil . Path , Netlist ] :
375404 self .transform_design (self ._design .design )
376405
377- assert len (self .all_scopes ) == 1 , "TODO: support multiple boards"
378- return self .scope_to_netlist (self .all_scopes [0 ])
406+ return {path : self .scope_to_netlist (scope ) for path , scope in self .scopes .items () if scope is not None }
379407
380408
381409class PathShortener :
0 commit comments