@@ -14,53 +14,76 @@ class PlacedBlock(NamedTuple):
1414 """A placement of a hierarchical block, including the coordinates of its immediate elements.
1515 Elements are placed in local space, with (0, 0) as the origin and elements moved as a group.
1616 Elements are indexed by name."""
17- elts : Dict [ str , Tuple [Union ['PlacedBlock' , TransformUtil .Path ], Tuple [float , float ]]] # name -> elt, (x, y)
17+ elts : List [ Tuple [Union ['PlacedBlock' , TransformUtil .Path ], Tuple [float , float ]]] # name -> elt, (x, y)
1818 height : float
1919 width : float
2020
2121
22- def arrange_netlist (netlist : Netlist ) -> PlacedBlock :
22+ class BlackBoxBlock (NamedTuple ):
23+ path : TransformUtil .Path
24+ bbox : Tuple [float , float , float , float ]
25+
26+
27+ def arrange_blocks (blocks : List [NetBlock ],
28+ additional_blocks : List [BlackBoxBlock ] = []) -> PlacedBlock :
2329 FOOTPRINT_BORDER = 1 # mm
2430 BLOCK_BORDER = 2 # mm
2531
2632 # create list of blocks by path
27- block_subblocks : Dict [Tuple [str , ...], Set [str ]] = {}
28- block_footprints : Dict [Tuple [str , ...], List [NetBlock ]] = {}
33+ block_subblocks : Dict [Tuple [str , ...], List [str ]] = {} # list to maintain sortedness
34+ block_footprints : Dict [Tuple [str , ...], List [Union [ NetBlock , BlackBoxBlock ] ]] = {}
2935
3036 # for here, we only group one level deep
31- for block in netlist . blocks :
37+ for block in blocks :
3238 containing_path = block .full_path .blocks [0 :min (len (block .full_path .blocks ) - 1 , 1 )]
3339 block_footprints .setdefault (containing_path , []).append (block )
3440 for i in range (len (containing_path )):
35- block_subblocks .setdefault (tuple (containing_path [:i ]), set ()).add (containing_path [i ])
41+ subblocks_list = block_subblocks .setdefault (tuple (containing_path [:i ]), list ())
42+ if containing_path [i ] not in subblocks_list :
43+ subblocks_list .append (containing_path [i ])
44+
45+ for blackbox in additional_blocks :
46+ containing_path = blackbox .path .blocks [0 :min (len (blackbox .path .blocks ) - 1 , 1 )]
47+ block_footprints .setdefault (containing_path , []).append (blackbox )
48+ for i in range (len (containing_path )):
49+ subblocks_list = block_subblocks .setdefault (tuple (containing_path [:i ]), list ())
50+ if containing_path [i ] not in subblocks_list :
51+ subblocks_list .append (containing_path [i ])
3652
3753 def arrange_hierarchy (root : Tuple [str , ...]) -> PlacedBlock :
3854 """Recursively arranges the immediate components of a hierarchy, treating each element
3955 as a bounding box rectangle, and trying to maintain some aspect ratio."""
4056 # TODO don't count borders as part of a block's width / height
4157 ASPECT_RATIO = 16 / 9
4258
43- sub_placed : List [Tuple [str , float , float , Union [PlacedBlock , NetBlock ]]] = [] # (name, width, height, PlacedBlock or footprint )
44- for subblock in block_subblocks .get (root , set ()):
59+ sub_placed : List [Tuple [float , float , Union [PlacedBlock , NetBlock , BlackBoxBlock ]]] = [] # (width, height, entry )
60+ for subblock in block_subblocks .get (root , list ()):
4561 subplaced = arrange_hierarchy (root + (subblock ,))
46- sub_placed .append ((subblock , subplaced .width + BLOCK_BORDER , subplaced .height + BLOCK_BORDER , subplaced ))
62+ sub_placed .append ((subplaced .width + BLOCK_BORDER , subplaced .height + BLOCK_BORDER , subplaced ))
4763
4864 for footprint in block_footprints .get (root , []):
49- bbox = FootprintDataTable .bbox_of (footprint .footprint ) or (1 , 1 , 1 , 1 )
65+ if isinstance (footprint , NetBlock ):
66+ bbox = FootprintDataTable .bbox_of (footprint .footprint ) or (1 , 1 , 1 , 1 )
67+ entry : Union [PlacedBlock , NetBlock , BlackBoxBlock ] = footprint
68+ elif isinstance (footprint , BlackBoxBlock ):
69+ bbox = footprint .bbox
70+ entry = footprint
71+ else :
72+ raise TypeError ()
5073 width = bbox [2 ] - bbox [0 ] + FOOTPRINT_BORDER
5174 height = bbox [3 ] - bbox [1 ] + FOOTPRINT_BORDER
5275 # use refdes as key so it's globally unique, for when this is run with blocks grouped together
53- sub_placed .append ((footprint . refdes , width , height , footprint ))
76+ sub_placed .append ((width , height , entry ))
5477
55- total_area = sum (width * height for _ , width , height , _ in sub_placed )
78+ total_area = sum (width * height for width , height , _ in sub_placed )
5679 max_width = math .sqrt (total_area * ASPECT_RATIO )
5780
5881 x_max = 0.0
5982 y_max = 0.0
6083 # track the y limits and y position of the prior elements
6184 x_stack : List [Tuple [float , float , float ]] = [] # [(x pos of next, y pos, y limit)]
62- elts : Dict [ str , Tuple [Union [PlacedBlock , TransformUtil .Path ], Tuple [float , float ]]] = {}
63- for name , width , height , entry in sorted (sub_placed , key = lambda x : - x [2 ]): # by height
85+ elts : List [ Tuple [Union [PlacedBlock , TransformUtil .Path ], Tuple [float , float ]]] = []
86+ for width , height , entry in sorted (sub_placed , key = lambda x : - x [1 ]): # by height
6487 if not x_stack : # only on first component
6588 next_y = 0.0
6689 else :
@@ -83,10 +106,13 @@ def arrange_hierarchy(root: Tuple[str, ...]) -> PlacedBlock:
83106 next_x = x_stack [- 1 ][0 ]
84107
85108 if isinstance (entry , PlacedBlock ): # assumed (0, 0) at top left
86- elts [ name ] = ( entry , (next_x , next_y ))
109+ elts . append (( entry , (next_x , next_y ) ))
87110 elif isinstance (entry , NetBlock ): # account for footprint origin, flipping y-axis
88111 bbox = FootprintDataTable .bbox_of (entry .footprint ) or (0 , 0 , 0 , 0 )
89- elts [name ] = (entry .full_path , (next_x - bbox [0 ], next_y + bbox [3 ]))
112+ elts .append ((entry .full_path , (next_x - bbox [0 ], next_y + bbox [3 ])))
113+ elif isinstance (entry , BlackBoxBlock ): # account for footprint origin, flipping y-axis
114+ bbox = entry .bbox
115+ elts .append ((entry .path , (next_x - bbox [0 ], next_y - bbox [0 ])))
90116 x_stack .append ((next_x + width , next_y , next_y + height ))
91117 x_max = max (x_max , next_x + width )
92118 y_max = max (y_max , next_y + height )
@@ -100,7 +126,7 @@ def flatten_packed_block(block: PlacedBlock) -> Dict[TransformUtil.Path, Tuple[f
100126 """Flatten a packed_block to a dict of individual components."""
101127 flattened : Dict [TransformUtil .Path , Tuple [float , float ]] = {}
102128 def walk_group (block : PlacedBlock , x_pos : float , y_pos : float ) -> None :
103- for _ , ( elt , (elt_x , elt_y )) in block .elts . items () :
129+ for elt , (elt_x , elt_y ) in block .elts :
104130 if isinstance (elt , PlacedBlock ):
105131 walk_group (elt , x_pos + elt_x , y_pos + elt_y )
106132 elif isinstance (elt , TransformUtil .Path ):
@@ -115,6 +141,7 @@ class SvgPcbGeneratedBlock(NamedTuple):
115141 path : TransformUtil .Path
116142 fn_name : str
117143 svgpcb_code : str
144+ bbox : Tuple [float , float , float , float ]
118145
119146
120147class SvgPcbTransform (TransformUtil .Transform ):
@@ -138,7 +165,7 @@ def visit_block(self, context: TransformUtil.TransformContext, block: edgir.Bloc
138165 generator_obj = cls ()
139166 generator_obj ._svgpcb_init (context .path , self .design , self .netlist )
140167 self ._svgpcb_blocks .append (SvgPcbGeneratedBlock (
141- context .path , generator_obj ._svgpcb_fn_name (), generator_obj ._svgpcb_template ()
168+ context .path , generator_obj ._svgpcb_fn_name (), generator_obj ._svgpcb_template (), generator_obj . _svgpcb_bbox ()
142169 ))
143170 else :
144171 pass
@@ -168,35 +195,51 @@ def filter_blocks_by_pathname(blocks: List[NetBlock], exclude_prefixes: List[Tup
168195 return [block for block in blocks
169196 if not block_matches_prefixes (block , exclude_prefixes )]
170197
198+ # handle blocks with svgpcb templates
171199 svgpcb_blocks = SvgPcbTransform (design , netlist ).run ()
172- svgpcb_block_prefixes = [block .path .to_tuple () for block in svgpcb_blocks ]
200+ svgpcb_block_bboxes = [BlackBoxBlock (block .path , block .bbox ) for block in svgpcb_blocks ]
201+
202+ # handle footprints
173203 netlist = NetlistTransform (design ).run ()
204+ svgpcb_block_prefixes = [block .path .to_tuple () for block in svgpcb_blocks ]
174205 other_blocks = filter_blocks_by_pathname (netlist .blocks , svgpcb_block_prefixes )
175- arranged_blocks = arrange_netlist ( netlist )
206+ arranged_blocks = arrange_blocks ( other_blocks , svgpcb_block_bboxes )
176207 pos_dict = flatten_packed_block (arranged_blocks )
177208
178- svgpcb_block_instantiations = [
179- f"const { SvgPcbTemplateBlock ._svgpcb_pathname_to_svgpcb (block .path )} = { block .fn_name } (pt(0, 0))"
180- for block in svgpcb_blocks
181- ]
209+ # note, dimensions in inches, divide by 25.4 to convert from mm
210+ svgpcb_block_instantiations = []
211+ for svgpcb_block in svgpcb_blocks :
212+ x_pos , y_pos = pos_dict .get (svgpcb_block .path , (0 , 0 )) # in mm, need to convert to in below
213+ block_code = f"const { SvgPcbTemplateBlock ._svgpcb_pathname_to_svgpcb (svgpcb_block .path )} = { svgpcb_block .fn_name } (pt({ x_pos / 25.4 :.3f} , { y_pos / 25.4 :.3f} ))"
214+ svgpcb_block_instantiations .append (block_code )
182215
183- # note, dimensions in inches
184216 other_block_instantiations = []
185- for block in other_blocks :
186- x_pos , y_pos = pos_dict .get (block .full_path , (0 , 0 )) # in mm, need to convert to in below
217+ for net_block in other_blocks :
218+ x_pos , y_pos = pos_dict .get (net_block .full_path , (0 , 0 )) # in mm, need to convert to in below
187219 block_code = f"""\
188- const { SvgPcbTemplateBlock ._svgpcb_pathname_to_svgpcb (block .full_path )} = board.add({ SvgPcbTemplateBlock ._svgpcb_footprint_to_svgpcb (block .footprint )} , {{
220+ // { net_block .full_path }
221+ const { net_block .refdes } = board.add({ SvgPcbTemplateBlock ._svgpcb_footprint_to_svgpcb (net_block .footprint )} , {{
189222 translate: pt({ x_pos / 25.4 :.3f} , { y_pos / 25.4 :.3f} ), rotate: 0,
190- id: '{ SvgPcbTemplateBlock . _svgpcb_pathname_to_svgpcb ( block . full_path ) } '
223+ id: '{ net_block . refdes } '
191224}})"""
192225 other_block_instantiations .append (block_code )
193226
227+ net_blocks_by_path = {net_block .full_path : net_block for net_block in netlist .blocks }
228+ netlist_code_entries = []
229+ for net in netlist .nets :
230+ pads_code = [f"""["{ net_blocks_by_path [pin .block_path ].refdes } ", "{ pin .pin_name } "]""" for pin in net .pins ]
231+ netlist_code_entries .append (f"""{{name: "{ net .name } ", pads: [{ ', ' .join (pads_code )} ]}}""" )
232+
194233 NEWLINE = '\n '
195234 full_code = f"""\
196235 const board = new PCB();
197236
198237{ NEWLINE .join (svgpcb_block_instantiations + other_block_instantiations )}
199238
239+ board.setNetlist([
240+ { ("," + NEWLINE + " " ).join (netlist_code_entries )}
241+ ])
242+
200243const limit0 = pt(-{ 2 / 25.4 } , -{ 2 / 25.4 } );
201244const limit1 = pt({ arranged_blocks .width / 25.4 } , { arranged_blocks .height / 25.4 } );
202245const xMin = Math.min(limit0[0], limit1[0]);
0 commit comments