diff --git a/board_config.zen b/board_config.zen index bfcd0f3..bdc036b 100644 --- a/board_config.zen +++ b/board_config.zen @@ -1,6 +1,9 @@ load("units.zen", "Impedance") +load("properties.zen", "Layout") +# Enum Types CopperWeight = enum("0.5oz", "1oz", "2oz") +CopperFinish = enum("HASL", "HASL Lead-free", "ENIG", "HAL SnPb", "HAL lead-free") # Copper constraints - all clearances and widths for copper features Copper = record( @@ -290,15 +293,91 @@ def merge_predefined_sizes( ) +def DefaultBoardConfig( + layers: int | None = None, + outer_copper_weight: CopperWeight = CopperWeight("1oz"), + copper_finish: CopperFinish = CopperFinish("ENIG"), + track_widths: list | None = None, + via_dimensions: list | None = None, + solder_mask_color: str | None = None, +) -> BoardConfig | None: + """Provide a default board configuration for the given layers and outer copper weight.""" + + stackup = { + (2, CopperWeight("1oz")): BASE_2L_STACKUP, + (4, CopperWeight("1oz")): BASE_4L_STACKUP, + (6, CopperWeight("1oz")): BASE_6L_STACKUP, + (8, CopperWeight("1oz")): BASE_8L_STACKUP, + (2, CopperWeight("2oz")): BASE_2L_STACKUP, # TODO: Add 2oz stackup + (4, CopperWeight("2oz")): BASE_4L_2OZ_STACKUP, + (6, CopperWeight("2oz")): BASE_6L_STACKUP, # TODO: Add 2oz stackup + (8, CopperWeight("2oz")): BASE_8L_STACKUP, # TODO: Add 2oz stackup + }.get((layers, outer_copper_weight)) + + netclasses = { + (2, CopperWeight("1oz")): BASE_2L_NETCLASSES, + (4, CopperWeight("1oz")): BASE_4L_NETCLASSES, + (6, CopperWeight("1oz")): BASE_6L_NETCLASSES, + (8, CopperWeight("1oz")): BASE_8L_NETCLASSES, + (2, CopperWeight("2oz")): BASE_2L_NETCLASSES, # TODO: Add 2oz netclasses + (4, CopperWeight("2oz")): BASE_4L_2OZ_NETCLASSES, + (6, CopperWeight("2oz")): BASE_6L_NETCLASSES, # TODO: Add 2oz netclasses + (8, CopperWeight("2oz")): BASE_8L_NETCLASSES, # TODO: Add 2oz netclasses + }.get((layers, outer_copper_weight)) + + constraints = { + CopperWeight("1oz"): BASE_CONSTRAINTS, + CopperWeight("2oz"): BASE_CONSTRAINTS_2OZ, + }.get(outer_copper_weight) + + predefined_sizes = { + CopperWeight("1oz"): BASE_PREDEFINED_SIZES, + CopperWeight("2oz"): BASE_PREDEFINED_SIZES_2OZ, + }.get(outer_copper_weight) + + if stackup == None: + print("Unsupported default stackup for layers: " + str(layers)) + return None + if netclasses == None: + print("Unsupported default netclasses for layers: " + str(layers)) + return None + if constraints == None: + print("Unsupported default constraints for copper weight: " + outer_copper_weight.value) + return None + if predefined_sizes == None: + print("Unsupported predefined sizes for copper weight: " + outer_copper_weight.value) + return None + + return BoardConfig( + stackup=deep_merge( + stackup, + Stackup( + solder_mask_color=solder_mask_color, + copper_finish=copper_finish.value, + ), + ), + design_rules=DesignRules( + constraints=constraints, + netclasses=netclasses, + predefined_sizes=merge_predefined_sizes( + predefined_sizes, + track_widths, + via_dimensions, + ), + ), + ) + + def Board( name: str, layout_path: str, config: BoardConfig | None = None, layers: int | None = None, - outer_copper_weight: CopperWeight = CopperWeight("1oz"), + outer_copper_weight: str | CopperWeight = "1oz", + copper_finish: str | CopperFinish = "ENIG", + solder_mask_color: str | None = None, track_widths: list | None = None, via_dimensions: list | None = None, - layout_hints: list | None = None, default: bool = False, ): """Define a PCB board configuration. @@ -329,76 +408,41 @@ def Board( - Custom track_widths and via_dimensions are appended to base configuration - All lists are deduplicated and sorted before use """ - # If config is not provided, construct it from layers - if config == None: - if layers == None: - error("Either 'config' or 'layers' must be provided") - - # Validate copper weight - if outer_copper_weight.value not in ["1oz", "2oz"]: - error("outer_copper_weight must be '1oz' or '2oz'") - - # Select configurations based on layers and copper weight - if layers == 2: - if outer_copper_weight.value == "2oz": - error("2oz outer copper not supported for 2-layer boards") - stackup = BASE_2L_STACKUP - netclasses = BASE_2L_NETCLASSES - constraints = BASE_CONSTRAINTS - predefined_sizes = BASE_PREDEFINED_SIZES - elif layers == 4: - if outer_copper_weight.value == "1oz": - stackup = BASE_4L_STACKUP - netclasses = BASE_4L_NETCLASSES - constraints = BASE_CONSTRAINTS - predefined_sizes = BASE_PREDEFINED_SIZES - else: # "2oz" - stackup = BASE_4L_2OZ_STACKUP - netclasses = BASE_4L_2OZ_NETCLASSES - constraints = BASE_CONSTRAINTS_2OZ - predefined_sizes = BASE_PREDEFINED_SIZES_2OZ - elif layers == 6: - if outer_copper_weight.value == "2oz": - error("2oz outer copper not supported for 6-layer boards") - stackup = BASE_6L_STACKUP - netclasses = BASE_6L_NETCLASSES - constraints = BASE_CONSTRAINTS - predefined_sizes = BASE_PREDEFINED_SIZES - elif layers == 8: - if outer_copper_weight.value == "2oz": - error("2oz outer copper not supported for 8-layer boards") - stackup = BASE_8L_STACKUP - netclasses = BASE_8L_NETCLASSES - constraints = BASE_CONSTRAINTS - predefined_sizes = BASE_PREDEFINED_SIZES - else: - error("layers must be 2, 4, 6, or 8") - # Merge user-provided sizes with base configuration - final_predefined_sizes = merge_predefined_sizes( - predefined_sizes, - track_widths, - via_dimensions, - ) + # Validate input units + if isinstance(outer_copper_weight, str): + outer_copper_weight = CopperWeight(outer_copper_weight) - design_rules = DesignRules( - constraints=constraints, - predefined_sizes=final_predefined_sizes, - netclasses=netclasses, - ) - config = BoardConfig( - design_rules=design_rules, - stackup=stackup, - ) + if isinstance(copper_finish, str): + copper_finish = CopperFinish(copper_finish) + + default_config = DefaultBoardConfig( + layers=layers, + outer_copper_weight=outer_copper_weight, + copper_finish=copper_finish, + solder_mask_color=solder_mask_color, + track_widths=track_widths, + via_dimensions=via_dimensions, + ) + if default_config: + config = deep_merge(default_config, config) + + if config == None: + error("Please provide a board config") + + # Add board config to the module builtin.add_board_config( name=name, default=default, config=config, ) - add_property("layout_path", Path(layout_path, allow_not_exist=True)) - if layout_hints: - add_property("layout_hints", layout_hints) + + # Add layout to the module + Layout( + name=name, + path=layout_path, + ) # Standard Base Configurations diff --git a/test/layout/test_BoardConfig/layout.kicad_pcb b/test/layout/test_BoardConfig/layout.kicad_pcb new file mode 100644 index 0000000..2750d8a --- /dev/null +++ b/test/layout/test_BoardConfig/layout.kicad_pcb @@ -0,0 +1,235 @@ +(kicad_pcb + (version 20241229) + (generator "pcbnew") + (generator_version "9.0") + (general + (thickness 1.6) + (legacy_teardrops no) + ) + (paper "A4") + (title_block + (title "${PCB_NAME}") + (date "${CURRENT_DATE}") + (rev "${PCB_VERSION}") + ) + (layers + (0 "F.Cu" mixed) + (4 "In1.Cu" power) + (6 "In2.Cu" power) + (8 "In3.Cu" mixed) + (10 "In4.Cu" power) + (12 "In5.Cu" mixed) + (14 "In6.Cu" power) + (2 "B.Cu" mixed) + (9 "F.Adhes" user "F.Adhesive") + (11 "B.Adhes" user "B.Adhesive") + (13 "F.Paste" user) + (15 "B.Paste" user) + (5 "F.SilkS" user "F.Silkscreen") + (7 "B.SilkS" user "B.Silkscreen") + (1 "F.Mask" user) + (3 "B.Mask" user) + (17 "Dwgs.User" user "User.Drawings") + (19 "Cmts.User" user "User.Comments") + (21 "Eco1.User" user "User.Eco1") + (23 "Eco2.User" user "User.Eco2") + (25 "Edge.Cuts" user) + (27 "Margin" user) + (31 "F.CrtYd" user "F.Courtyard") + (29 "B.CrtYd" user "B.Courtyard") + (35 "F.Fab" user) + (33 "B.Fab" user) + (39 "User.1" user) + (41 "User.2" user) + (43 "User.3" user) + (45 "User.4" user) + ) + (setup + (stackup + (layer + "F.SilkS" + (type "Top Silk Screen") + (color "White") + ) + (layer + "F.Paste" + (type "Top Solder Paste") + ) + (layer + "F.Mask" + (type "Top Solder Mask") + (color "Black") + (thickness 0.01) + ) + (layer + "F.Cu" + (type "copper") + (thickness 0.035) + ) + (layer + "dielectric 1" + (type "prepreg") + (thickness 0.1164) + (material "2116") + (epsilon_r 4.16) + (loss_tangent 0.025) + ) + (layer + "In1.Cu" + (type "copper") + (thickness 0.0152) + ) + (layer + "dielectric 2" + (type "core") + (thickness 0.3) + (material "FR4-Core") + (epsilon_r 4.6) + (loss_tangent 0.025) + ) + (layer + "In2.Cu" + (type "copper") + (thickness 0.0152) + ) + (layer + "dielectric 3" + (type "prepreg") + (thickness 0.0764) + (material "1080") + (epsilon_r 3.91) + (loss_tangent 0.025) + addsublayer + (type "prepreg") + (thickness 0.0764) + (material "1080") + (epsilon_r 3.91) + (loss_tangent 0.025) + ) + (layer + "In3.Cu" + (type "copper") + (thickness 0.0152) + ) + (layer + "dielectric 4" + (type "core") + (thickness 0.3) + (material "FR4-Core") + (epsilon_r 4.6) + (loss_tangent 0.025) + ) + (layer + "In4.Cu" + (type "copper") + (thickness 0.0152) + ) + (layer + "dielectric 5" + (type "prepreg") + (thickness 0.0764) + (material "1080") + (epsilon_r 3.91) + (loss_tangent 0.025) + addsublayer + (type "prepreg") + (thickness 0.0764) + (material "1080") + (epsilon_r 3.91) + (loss_tangent 0.025) + ) + (layer + "In5.Cu" + (type "copper") + (thickness 0.0152) + ) + (layer + "dielectric 6" + (type "core") + (thickness 0.3) + (material "FR4-Core") + (epsilon_r 4.6) + (loss_tangent 0.025) + ) + (layer + "In6.Cu" + (type "copper") + (thickness 0.0152) + ) + (layer + "dielectric 7" + (type "prepreg") + (thickness 0.1164) + (material "2116") + (epsilon_r 4.16) + (loss_tangent 0.025) + ) + (layer + "B.Cu" + (type "copper") + (thickness 0.035) + ) + (layer + "B.Mask" + (type "Bottom Solder Mask") + (color "Black") + (thickness 0.01) + ) + (layer + "B.Paste" + (type "Bottom Solder Paste") + ) + (layer + "B.SilkS" + (type "Bottom Silk Screen") + (color "White") + ) + (copper_finish "ENIG") + (dielectric_constraints no) +) + (pad_to_mask_clearance 0) + (allow_soldermask_bridges_in_footprints no) + (tenting front back) + (pcbplotparams + (layerselection 0x00000000_00000000_55555555_5755f5ff) + (plot_on_all_layers_selection 0x00000000_00000000_00000000_00000000) + (disableapertmacros no) + (usegerberextensions no) + (usegerberattributes yes) + (usegerberadvancedattributes yes) + (creategerberjobfile yes) + (dashed_line_dash_ratio 12.000000) + (dashed_line_gap_ratio 3.000000) + (svgprecision 4) + (plotframeref no) + (mode 1) + (useauxorigin no) + (hpglpennumber 1) + (hpglpenspeed 20) + (hpglpendiameter 15.000000) + (pdf_front_fp_property_popups yes) + (pdf_back_fp_property_popups yes) + (pdf_metadata yes) + (pdf_single_document no) + (dxfpolygonmode yes) + (dxfimperialunits yes) + (dxfusepcbnewfont yes) + (psnegative no) + (psa4output no) + (plot_black_and_white yes) + (sketchpadsonfab no) + (plotpadnumbers no) + (hidednponfab no) + (sketchdnponfab yes) + (crossoutdnponfab yes) + (subtractmaskfromsilk no) + (outputformat 1) + (mirror no) + (drillshape 1) + (scaleselection 1) + (outputdirectory "") + ) + ) + (net 0 "") + (embedded_fonts no) +) diff --git a/test/layout/test_BoardConfig/layout.kicad_pro b/test/layout/test_BoardConfig/layout.kicad_pro new file mode 100644 index 0000000..5e555b8 --- /dev/null +++ b/test/layout/test_BoardConfig/layout.kicad_pro @@ -0,0 +1,342 @@ +{ + "board": { + "3dviewports": [], + "design_settings": { + "defaults": { + "apply_defaults_to_fp_fields": false, + "apply_defaults_to_fp_shapes": false, + "apply_defaults_to_fp_text": false, + "board_outline_line_width": 0.05, + "copper_line_width": 0.2, + "copper_text_italic": false, + "copper_text_size_h": 0.0, + "copper_text_size_v": 0.0, + "copper_text_thickness": 0.3, + "copper_text_upright": false, + "courtyard_line_width": 0.05, + "dimension_precision": 4, + "dimension_units": 3, + "dimensions": { + "arrow_length": 1270000, + "extension_offset": 500000, + "keep_text_aligned": true, + "suppress_zeroes": true, + "text_position": 0, + "units_format": 0 + }, + "fab_line_width": 0.1, + "fab_text_italic": false, + "fab_text_size_h": 3.080203, + "fab_text_size_v": 1e-06, + "fab_text_thickness": 0.15, + "fab_text_upright": false, + "other_line_width": 0.1, + "other_text_italic": false, + "other_text_size_h": 165.901792, + "other_text_size_v": 1e-06, + "other_text_thickness": 0.15, + "other_text_upright": false, + "pads": { + "drill": 0.8, + "height": 1.27, + "width": 2.54 + }, + "silk_line_width": 0.1, + "silk_text_italic": false, + "silk_text_size_h": 0.8, + "silk_text_size_v": 0.8, + "silk_text_thickness": 0.1, + "silk_text_upright": false, + "zones": { + "min_clearance": 0.5 + } + }, + "diff_pair_dimensions": [], + "drc_exclusions": [], + "meta": { + "version": 2 + }, + "rule_severities": { + "annular_width": "error", + "clearance": "error", + "connection_width": "warning", + "copper_edge_clearance": "error", + "copper_sliver": "warning", + "courtyards_overlap": "error", + "creepage": "error", + "diff_pair_gap_out_of_range": "error", + "diff_pair_uncoupled_length_too_long": "error", + "drill_out_of_range": "error", + "duplicate_footprints": "warning", + "extra_footprint": "warning", + "footprint": "error", + "footprint_filters_mismatch": "ignore", + "footprint_symbol_mismatch": "warning", + "footprint_type_mismatch": "ignore", + "hole_clearance": "error", + "hole_to_hole": "warning", + "holes_co_located": "warning", + "invalid_outline": "error", + "isolated_copper": "warning", + "item_on_disabled_layer": "error", + "items_not_allowed": "error", + "length_out_of_range": "error", + "lib_footprint_issues": "warning", + "lib_footprint_mismatch": "warning", + "malformed_courtyard": "error", + "microvia_drill_out_of_range": "error", + "mirrored_text_on_front_layer": "warning", + "missing_courtyard": "ignore", + "missing_footprint": "warning", + "net_conflict": "warning", + "nonmirrored_text_on_back_layer": "warning", + "npth_inside_courtyard": "ignore", + "padstack": "warning", + "pth_inside_courtyard": "ignore", + "shorting_items": "error", + "silk_edge_clearance": "warning", + "silk_over_copper": "warning", + "silk_overlap": "warning", + "skew_out_of_range": "error", + "solder_mask_bridge": "error", + "starved_thermal": "error", + "text_height": "warning", + "text_on_edge_cuts": "error", + "text_thickness": "warning", + "through_hole_pad_without_hole": "error", + "too_many_vias": "error", + "track_angle": "error", + "track_dangling": "warning", + "track_segment_length": "error", + "track_width": "error", + "tracks_crossing": "error", + "unconnected_items": "error", + "unresolved_variable": "error", + "via_dangling": "warning", + "zones_intersect": "error" + }, + "rules": { + "max_error": 0.005, + "min_clearance": 0.09, + "min_connection": 0.0, + "min_copper_edge_clearance": 0.3, + "min_groove_width": 0.0, + "min_hole_clearance": 0.2, + "min_hole_to_hole": 0.2, + "min_microvia_diameter": 0.15, + "min_microvia_drill": 0.1, + "min_resolved_spokes": 2, + "min_silk_clearance": 0.1, + "min_text_height": 0.8, + "min_text_thickness": 0.08, + "min_through_hole_diameter": 0.15, + "min_track_width": 0.09, + "min_via_annular_width": 0.1, + "min_via_diameter": 0.25, + "solder_mask_to_copper_clearance": 0.0, + "use_height_for_length_calcs": true + }, + "teardrop_options": [ + { + "td_onpthpad": true, + "td_onroundshapesonly": false, + "td_onsmdpad": true, + "td_ontrackend": false, + "td_onvia": true + } + ], + "teardrop_parameters": [ + { + "td_allow_use_two_tracks": true, + "td_curve_segcount": 0, + "td_height_ratio": 1.0, + "td_length_ratio": 0.5, + "td_maxheight": 2.0, + "td_maxlen": 1.0, + "td_on_pad_in_zone": false, + "td_target_name": "td_round_shape", + "td_width_to_size_filter_ratio": 0.9 + }, + { + "td_allow_use_two_tracks": true, + "td_curve_segcount": 0, + "td_height_ratio": 1.0, + "td_length_ratio": 0.5, + "td_maxheight": 2.0, + "td_maxlen": 1.0, + "td_on_pad_in_zone": false, + "td_target_name": "td_rect_shape", + "td_width_to_size_filter_ratio": 0.9 + }, + { + "td_allow_use_two_tracks": true, + "td_curve_segcount": 0, + "td_height_ratio": 1.0, + "td_length_ratio": 0.5, + "td_maxheight": 2.0, + "td_maxlen": 1.0, + "td_on_pad_in_zone": false, + "td_target_name": "td_track_end", + "td_width_to_size_filter_ratio": 0.9 + } + ], + "track_widths": [ + 0.0, + 0.127, + 0.16, + 0.2, + 0.4, + 0.8 + ], + "tuning_pattern_settings": { + "diff_pair_defaults": { + "corner_radius_percentage": 80, + "corner_style": 1, + "max_amplitude": 1.0, + "min_amplitude": 0.2, + "single_sided": false, + "spacing": 1.0 + }, + "diff_pair_skew_defaults": { + "corner_radius_percentage": 80, + "corner_style": 1, + "max_amplitude": 1.0, + "min_amplitude": 0.2, + "single_sided": false, + "spacing": 0.6 + }, + "single_track_defaults": { + "corner_radius_percentage": 80, + "corner_style": 1, + "max_amplitude": 1.0, + "min_amplitude": 0.2, + "single_sided": false, + "spacing": 0.6 + } + }, + "via_dimensions": [ + { + "diameter": 0.0, + "drill": 0.0 + }, + { + "diameter": 0.4, + "drill": 0.2 + }, + { + "diameter": 0.5, + "drill": 0.3 + } + ], + "zones_allow_external_fillets": false + }, + "ipc2581": { + "dist": "", + "distpn": "", + "internal_id": "", + "mfg": "", + "mpn": "" + }, + "layer_pairs": [], + "layer_presets": [], + "viewports": [] + }, + "boards": [], + "cvpcb": { + "equivalence_files": [] + }, + "libraries": { + "pinned_footprint_libs": [], + "pinned_symbol_libs": [] + }, + "meta": { + "filename": "layout.kicad_pro", + "version": 3 + }, + "net_settings": { + "classes": [ + { + "bus_width": 12, + "clearance": 0.16, + "diff_pair_gap": 0.2, + "diff_pair_via_gap": 0.25, + "diff_pair_width": 0.2, + "line_style": 0, + "microvia_diameter": 0.3, + "microvia_drill": 0.1, + "name": "Default", + "pcb_color": "rgba(0, 0, 0, 0.000)", + "priority": 2147483647, + "schematic_color": "rgba(0, 0, 0, 0.000)", + "track_width": 0.16, + "via_diameter": 0.5, + "via_drill": 0.3, + "wire_width": 6 + }, + { + "clearance": 0.2, + "diff_pair_gap": 0.1778, + "diff_pair_width": 0.127, + "name": "100Ohm Diff", + "pcb_color": "rgb(194, 194, 0)", + "priority": -1, + "schematic_color": "rgba(0, 0, 0, 0.000)", + "track_width": 0.127 + }, + { + "clearance": 0.2, + "name": "50Ohm SE", + "pcb_color": "rgb(0, 0, 194)", + "priority": -1, + "schematic_color": "rgba(0, 0, 0, 0.000)", + "track_width": 0.1702 + }, + { + "clearance": 0.2, + "diff_pair_gap": 0.127, + "diff_pair_width": 0.1588, + "name": "85Ohm Diff", + "pcb_color": "rgb(194, 0, 194)", + "priority": -1, + "schematic_color": "rgba(0, 0, 0, 0.000)", + "track_width": 0.1588 + }, + { + "clearance": 0.2, + "diff_pair_gap": 0.127, + "diff_pair_width": 0.1384, + "name": "90Ohm Diff", + "pcb_color": "rgb(0, 194, 0)", + "priority": -1, + "schematic_color": "rgba(0, 0, 0, 0.000)", + "track_width": 0.1384 + } + ], + "meta": { + "version": 4 + }, + "net_colors": null, + "netclass_assignments": null, + "netclass_patterns": [] + }, + "pcbnew": { + "last_paths": { + "gencad": "", + "idf": "", + "netlist": "", + "plot": "", + "pos_files": "", + "specctra_dsn": "", + "step": "", + "svg": "", + "vrml": "" + }, + "page_layout_descr_file": "" + }, + "schematic": { + "legacy_lib_dir": "", + "legacy_lib_list": [] + }, + "sheets": [], + "text_variables": {} +} diff --git a/test/layout/test_BoardConfig/layout.log b/test/layout/test_BoardConfig/layout.log new file mode 100644 index 0000000..4cb44ff --- /dev/null +++ b/test/layout/test_BoardConfig/layout.log @@ -0,0 +1,70 @@ +./src/common/stdpbase.cpp(59): assert ""traits"" failed in Get(): create wxApp before calling this +INFO: Loading schematic data from /var/folders/89/b2bgn72x46x786r1_s0h2x4m0000gn/T/.tmp8GlLGg/netlist.json +INFO: Parsing JSON netlist from /var/folders/89/b2bgn72x46x786r1_s0h2x4m0000gn/T/.tmp8GlLGg/netlist.json +INFO: -------------------------------------------------------------------------------- +INFO: Running step: SetupBoard +INFO: -------------------------------------------------------------------------------- +INFO: Starting SetupBoard... +INFO: Configured title block with variable placeholders +INFO: Applying board configuration from /var/folders/89/b2bgn72x46x786r1_s0h2x4m0000gn/T/.tmp8GlLGg/board_config.json +INFO: Set minimum clearance: 0.09mm +INFO: Set minimum track width: 0.09mm +INFO: Set minimum via diameter: 0.25mm +INFO: Set copper to hole clearance: 0.2mm +INFO: Set copper to edge clearance: 0.3mm +INFO: Set minimum through hole: 0.15mm +INFO: Set hole to hole clearance: 0.2mm +INFO: Set minimum uvia diameter: 0.15mm +INFO: Set minimum uvia hole: 0.1mm +INFO: Set silkscreen minimum item clearance: 0.1mm +INFO: Set 6 pre-defined track widths (including 0.0 placeholder) +INFO: Set 3 pre-defined via dimensions (including 0.0/0.0 placeholder) +INFO: Updated internal m_DefaultNetclass with properties from 'Default' netclass +INFO: Added netclass '50Ohm SE' to board +INFO: Added netclass '85Ohm Diff' to board +INFO: Added netclass '90Ohm Diff' to board +INFO: Added netclass '100Ohm Diff' to board +INFO: Set 5 netclasses +INFO: Completed SetupBoard in 0.002 seconds +INFO: -------------------------------------------------------------------------------- +INFO: Running step: ApplyMovedPaths +INFO: -------------------------------------------------------------------------------- +INFO: Starting ApplyMovedPaths... +INFO: No moved paths to apply +INFO: Completed ApplyMovedPaths in 0.000 seconds +INFO: -------------------------------------------------------------------------------- +INFO: Running step: ImportNetlist +INFO: -------------------------------------------------------------------------------- +INFO: Starting ImportNetlist... +DEBUG: Environment setup took 0.000 seconds +DEBUG: Footprint library map loading took 0.002 seconds +INFO: Footprint synchronization took 0.000 seconds +INFO: Net synchronization took 0.000 seconds +INFO: Group synchronization took 0.000 seconds +DEBUG: Board refresh took 0.000 seconds +INFO: Virtual DOM building took 0.001 seconds +INFO: Virtual DOM structure: +INFO: Board None +INFO: Completed ImportNetlist in 0.003 seconds +INFO: -------------------------------------------------------------------------------- +INFO: Running step: SyncLayouts +INFO: -------------------------------------------------------------------------------- +INFO: Starting SyncLayouts... +INFO: Completed layout sync: synced 0 groups +INFO: Completed SyncLayouts in 0.000 seconds +INFO: -------------------------------------------------------------------------------- +INFO: Running step: PlaceComponents +INFO: -------------------------------------------------------------------------------- +INFO: Starting PlaceComponents... +INFO: Starting hierarchical component placement +INFO: No items were placed +INFO: Completed hierarchical component placement +INFO: Completed PlaceComponents in 0.000 seconds +INFO: -------------------------------------------------------------------------------- +INFO: Running step: FinalizeBoard +INFO: -------------------------------------------------------------------------------- +INFO: Starting FinalizeBoard... +INFO: Saved layout snapshot to /Users/davide/src/diodeinc/packages/stdlib/test/layout/test_BoardConfig/snapshot.layout.json +INFO: Snapshot export took 0.000 seconds +INFO: Board saving took 0.002 seconds +INFO: Completed FinalizeBoard in 0.002 seconds diff --git a/test/layout/test_BoardConfig/snapshot.layout.json b/test/layout/test_BoardConfig/snapshot.layout.json new file mode 100644 index 0000000..482d1f1 --- /dev/null +++ b/test/layout/test_BoardConfig/snapshot.layout.json @@ -0,0 +1,7 @@ +{ + "footprints": [], + "groups": [], + "tracks": [], + "vias": [], + "zones": [] +} \ No newline at end of file diff --git a/test/layout/test_BoardConfigColor/layout.kicad_pro b/test/layout/test_BoardConfigColor/layout.kicad_pro new file mode 100644 index 0000000..0d19549 --- /dev/null +++ b/test/layout/test_BoardConfigColor/layout.kicad_pro @@ -0,0 +1,342 @@ +{ + "board": { + "3dviewports": [], + "design_settings": { + "defaults": { + "apply_defaults_to_fp_fields": false, + "apply_defaults_to_fp_shapes": false, + "apply_defaults_to_fp_text": false, + "board_outline_line_width": 0.05, + "copper_line_width": 0.2, + "copper_text_italic": false, + "copper_text_size_h": 1.5, + "copper_text_size_v": 1.5, + "copper_text_thickness": 0.3, + "copper_text_upright": false, + "courtyard_line_width": 0.05, + "dimension_precision": 4, + "dimension_units": 3, + "dimensions": { + "arrow_length": 1270000, + "extension_offset": 500000, + "keep_text_aligned": true, + "suppress_zeroes": true, + "text_position": 0, + "units_format": 0 + }, + "fab_line_width": 0.1, + "fab_text_italic": false, + "fab_text_size_h": 1.0, + "fab_text_size_v": 1.0, + "fab_text_thickness": 0.15, + "fab_text_upright": false, + "other_line_width": 0.1, + "other_text_italic": false, + "other_text_size_h": 1.0, + "other_text_size_v": 1.0, + "other_text_thickness": 0.15, + "other_text_upright": false, + "pads": { + "drill": 0.8, + "height": 1.27, + "width": 2.54 + }, + "silk_line_width": 0.1, + "silk_text_italic": false, + "silk_text_size_h": 0.8, + "silk_text_size_v": 0.8, + "silk_text_thickness": 0.1, + "silk_text_upright": false, + "zones": { + "min_clearance": 0.5 + } + }, + "diff_pair_dimensions": [], + "drc_exclusions": [], + "meta": { + "version": 2 + }, + "rule_severities": { + "annular_width": "error", + "clearance": "error", + "connection_width": "warning", + "copper_edge_clearance": "error", + "copper_sliver": "warning", + "courtyards_overlap": "error", + "creepage": "error", + "diff_pair_gap_out_of_range": "error", + "diff_pair_uncoupled_length_too_long": "error", + "drill_out_of_range": "error", + "duplicate_footprints": "warning", + "extra_footprint": "warning", + "footprint": "error", + "footprint_filters_mismatch": "ignore", + "footprint_symbol_mismatch": "warning", + "footprint_type_mismatch": "ignore", + "hole_clearance": "error", + "hole_to_hole": "warning", + "holes_co_located": "warning", + "invalid_outline": "error", + "isolated_copper": "warning", + "item_on_disabled_layer": "error", + "items_not_allowed": "error", + "length_out_of_range": "error", + "lib_footprint_issues": "warning", + "lib_footprint_mismatch": "warning", + "malformed_courtyard": "error", + "microvia_drill_out_of_range": "error", + "mirrored_text_on_front_layer": "warning", + "missing_courtyard": "ignore", + "missing_footprint": "warning", + "net_conflict": "warning", + "nonmirrored_text_on_back_layer": "warning", + "npth_inside_courtyard": "ignore", + "padstack": "warning", + "pth_inside_courtyard": "ignore", + "shorting_items": "error", + "silk_edge_clearance": "warning", + "silk_over_copper": "warning", + "silk_overlap": "warning", + "skew_out_of_range": "error", + "solder_mask_bridge": "error", + "starved_thermal": "error", + "text_height": "warning", + "text_on_edge_cuts": "error", + "text_thickness": "warning", + "through_hole_pad_without_hole": "error", + "too_many_vias": "error", + "track_angle": "error", + "track_dangling": "warning", + "track_segment_length": "error", + "track_width": "error", + "tracks_crossing": "error", + "unconnected_items": "error", + "unresolved_variable": "error", + "via_dangling": "warning", + "zones_intersect": "error" + }, + "rules": { + "max_error": 0.005, + "min_clearance": 0.09, + "min_connection": 0.0, + "min_copper_edge_clearance": 0.3, + "min_groove_width": 0.0, + "min_hole_clearance": 0.2, + "min_hole_to_hole": 0.2, + "min_microvia_diameter": 0.15, + "min_microvia_drill": 0.1, + "min_resolved_spokes": 2, + "min_silk_clearance": 0.1, + "min_text_height": 0.8, + "min_text_thickness": 0.08, + "min_through_hole_diameter": 0.15, + "min_track_width": 0.09, + "min_via_annular_width": 0.1, + "min_via_diameter": 0.25, + "solder_mask_to_copper_clearance": 0.0, + "use_height_for_length_calcs": true + }, + "teardrop_options": [ + { + "td_onpthpad": true, + "td_onroundshapesonly": false, + "td_onsmdpad": true, + "td_ontrackend": false, + "td_onvia": true + } + ], + "teardrop_parameters": [ + { + "td_allow_use_two_tracks": true, + "td_curve_segcount": 0, + "td_height_ratio": 1.0, + "td_length_ratio": 0.5, + "td_maxheight": 2.0, + "td_maxlen": 1.0, + "td_on_pad_in_zone": false, + "td_target_name": "td_round_shape", + "td_width_to_size_filter_ratio": 0.9 + }, + { + "td_allow_use_two_tracks": true, + "td_curve_segcount": 0, + "td_height_ratio": 1.0, + "td_length_ratio": 0.5, + "td_maxheight": 2.0, + "td_maxlen": 1.0, + "td_on_pad_in_zone": false, + "td_target_name": "td_rect_shape", + "td_width_to_size_filter_ratio": 0.9 + }, + { + "td_allow_use_two_tracks": true, + "td_curve_segcount": 0, + "td_height_ratio": 1.0, + "td_length_ratio": 0.5, + "td_maxheight": 2.0, + "td_maxlen": 1.0, + "td_on_pad_in_zone": false, + "td_target_name": "td_track_end", + "td_width_to_size_filter_ratio": 0.9 + } + ], + "track_widths": [ + 0.0, + 0.127, + 0.16, + 0.2, + 0.4, + 0.8 + ], + "tuning_pattern_settings": { + "diff_pair_defaults": { + "corner_radius_percentage": 80, + "corner_style": 1, + "max_amplitude": 1.0, + "min_amplitude": 0.2, + "single_sided": false, + "spacing": 1.0 + }, + "diff_pair_skew_defaults": { + "corner_radius_percentage": 80, + "corner_style": 1, + "max_amplitude": 1.0, + "min_amplitude": 0.2, + "single_sided": false, + "spacing": 0.6 + }, + "single_track_defaults": { + "corner_radius_percentage": 80, + "corner_style": 1, + "max_amplitude": 1.0, + "min_amplitude": 0.2, + "single_sided": false, + "spacing": 0.6 + } + }, + "via_dimensions": [ + { + "diameter": 0.0, + "drill": 0.0 + }, + { + "diameter": 0.4, + "drill": 0.2 + }, + { + "diameter": 0.5, + "drill": 0.3 + } + ], + "zones_allow_external_fillets": false + }, + "ipc2581": { + "dist": "", + "distpn": "", + "internal_id": "", + "mfg": "", + "mpn": "" + }, + "layer_pairs": [], + "layer_presets": [], + "viewports": [] + }, + "boards": [], + "cvpcb": { + "equivalence_files": [] + }, + "libraries": { + "pinned_footprint_libs": [], + "pinned_symbol_libs": [] + }, + "meta": { + "filename": "layout.kicad_pro", + "version": 3 + }, + "net_settings": { + "classes": [ + { + "bus_width": 12, + "clearance": 0.16, + "diff_pair_gap": 0.2, + "diff_pair_via_gap": 0.25, + "diff_pair_width": 0.2, + "line_style": 0, + "microvia_diameter": 0.3, + "microvia_drill": 0.1, + "name": "Default", + "pcb_color": "rgba(0, 0, 0, 0.000)", + "priority": 2147483647, + "schematic_color": "rgba(0, 0, 0, 0.000)", + "track_width": 0.16, + "via_diameter": 0.5, + "via_drill": 0.3, + "wire_width": 6 + }, + { + "clearance": 0.2, + "diff_pair_gap": 0.127, + "diff_pair_width": 0.1334, + "name": "100Ohm Diff", + "pcb_color": "rgb(194, 194, 0)", + "priority": -1, + "schematic_color": "rgba(0, 0, 0, 0.000)", + "track_width": 0.1334 + }, + { + "clearance": 0.2, + "name": "50Ohm SE", + "pcb_color": "rgb(0, 0, 194)", + "priority": -1, + "schematic_color": "rgba(0, 0, 0, 0.000)", + "track_width": 0.2908 + }, + { + "clearance": 0.2, + "diff_pair_gap": 0.127, + "diff_pair_width": 0.2235, + "name": "85Ohm Diff", + "pcb_color": "rgb(194, 0, 194)", + "priority": -1, + "schematic_color": "rgba(0, 0, 0, 0.000)", + "track_width": 0.2235 + }, + { + "clearance": 0.2, + "diff_pair_gap": 0.127, + "diff_pair_width": 0.1867, + "name": "90Ohm Diff", + "pcb_color": "rgb(0, 194, 0)", + "priority": -1, + "schematic_color": "rgba(0, 0, 0, 0.000)", + "track_width": 0.1867 + } + ], + "meta": { + "version": 4 + }, + "net_colors": null, + "netclass_assignments": null, + "netclass_patterns": [] + }, + "pcbnew": { + "last_paths": { + "gencad": "", + "idf": "", + "netlist": "", + "plot": "", + "pos_files": "", + "specctra_dsn": "", + "step": "", + "svg": "", + "vrml": "" + }, + "page_layout_descr_file": "" + }, + "schematic": { + "legacy_lib_dir": "", + "legacy_lib_list": [] + }, + "sheets": [], + "text_variables": {} +} diff --git a/test/test_BoardConfig.zen b/test/test_BoardConfig.zen new file mode 100644 index 0000000..4147387 --- /dev/null +++ b/test/test_BoardConfig.zen @@ -0,0 +1,174 @@ +load("../board_config.zen", "Board", "BoardConfig", "Stackup") + +# Test common solder mask colors +Board( + name="GreenMask", + layout_path="layout/test_BoardConfig", + layers=4, + solder_mask_color="Green", + copper_finish="ENIG", +) + +Board( + name="BlueMask", + layout_path="layout/test_BoardConfig", + layers=4, + solder_mask_color="Blue", + copper_finish="ENIG", +) + +Board( + name="RedMask", + layout_path="layout/test_BoardConfig", + layers=4, + solder_mask_color="Red", + copper_finish="ENIG", +) + +Board( + name="BlackMask", + layout_path="layout/test_BoardConfig", + layers=4, + solder_mask_color="Black", + copper_finish="ENIG", +) + +Board( + name="WhiteMask", + layout_path="layout/test_BoardConfig", + layers=4, + solder_mask_color="White", + copper_finish="ENIG", +) + +Board( + name="YellowMask", + layout_path="layout/test_BoardConfig", + layers=4, + solder_mask_color="Yellow", + copper_finish="ENIG", +) + +Board( + name="PurpleMask", + layout_path="layout/test_BoardConfig", + layers=4, + solder_mask_color="Purple", + copper_finish="ENIG", +) + +Board( + name="MatteBlackMask", + layout_path="layout/test_BoardConfig", + layers=4, + solder_mask_color="Matte Black", + copper_finish="ENIG", +) + +# Test hex color codes +Board( + name="HexColorMask", + layout_path="layout/test_BoardConfig", + layers=4, + solder_mask_color="#FF5733", + copper_finish="ENIG", +) + +# Test with custom BoardConfig including both silk screen and solder mask colors +Board( + name="CustomColors", + layout_path="layout/test_BoardConfig", + layers=4, + config=BoardConfig( + stackup=Stackup( + silk_screen_color="White", + solder_mask_color="Blue", + ), + ), + copper_finish="ENIG", +) + +# Test different silk screen colors with solder mask +Board( + name="BlackSilkWhiteMask", + layout_path="layout/test_BoardConfig", + layers=4, + config=BoardConfig( + stackup=Stackup( + silk_screen_color="Black", + solder_mask_color="White", + ), + ), + copper_finish="ENIG", +) + +Board( + name="WhiteSilkBlackMask", + layout_path="layout/test_BoardConfig", + layers=4, + config=BoardConfig( + stackup=Stackup( + silk_screen_color="White", + solder_mask_color="Black", + ), + ), + copper_finish="ENIG", +) + +# Test hex colors for both silk screen and solder mask +Board( + name="HexBothColors", + layout_path="layout/test_BoardConfig", + layers=4, + config=BoardConfig( + stackup=Stackup( + silk_screen_color="#FFFFFF", + solder_mask_color="#191919E6", + ), + ), + copper_finish="ENIG", +) + +# Test with different copper finishes and colors +Board( + name="BlueMaskHALSnPb", + layout_path="layout/test_BoardConfig", + layers=4, + solder_mask_color="Blue", + copper_finish="HAL SnPb", +) + +Board( + name="GreenMaskLeadFree", + layout_path="layout/test_BoardConfig", + layers=4, + solder_mask_color="Green", + copper_finish="HAL lead-free", +) + +# Test on 2-layer board +Board( + name="TwoLayerBlue", + layout_path="layout/test_BoardConfig", + layers=2, + solder_mask_color="Blue", + copper_finish="ENIG", +) + +# Test on 6-layer board +Board( + name="SixLayerRed", + layout_path="layout/test_BoardConfig", + layers=6, + solder_mask_color="Red", + copper_finish="ENIG", +) + +# Test on 8-layer board +Board( + name="EightLayerBlack", + layout_path="layout/test_BoardConfig", + layers=8, + solder_mask_color="Black", + copper_finish="ENIG", +)