From 453344849d91b9bd6b4cad6f884557ee67dc1355 Mon Sep 17 00:00:00 2001 From: adamws Date: Fri, 30 Aug 2024 17:57:29 +0200 Subject: [PATCH] Add option to define order of pin matrix assignments --- .gitignore | 1 + src/kle2netlist/__main__.py | 14 +- src/kle2netlist/circuits/__init__.py | 13 +- src/kle2netlist/circuits/atmega32u4.py | 26 +- src/kle2netlist/netlist.py | 3 +- tests/test_netlist_generation.py | 55 ++-- .../2x2-with-uc-custom-order.net | 242 ++++++++++++++++++ 7 files changed, 321 insertions(+), 33 deletions(-) create mode 100644 tests/test_netlist_generation/2x2-with-uc-custom-order.net diff --git a/.gitignore b/.gitignore index f3ed051..d6faf73 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ fp-info-cache # Netlist files (exported from Eeschema) *.net +!tests/test_netlist_generation/*.net # Autorouter files (exported from Pcbnew) *.dsn diff --git a/src/kle2netlist/__main__.py b/src/kle2netlist/__main__.py index 9cf6fd1..93b0518 100644 --- a/src/kle2netlist/__main__.py +++ b/src/kle2netlist/__main__.py @@ -45,7 +45,8 @@ def main( diode_footprint: str = typer.Option( "Diode_SMD:D_SOD-123F", "-df", "--diode-footprint", help="Diode footprint" ), - lib_paths: Optional[List[str]] = typer.Option( + # use this feature https://github.com/fastapi/typer/pull/800 when merged: + lib_paths: Optional[str] = typer.Option( None, "-l", "--lib-path", help="Path to symbol library" ), controller_circuit: ControllerCircuit = typer.Option( @@ -53,6 +54,12 @@ def main( "--controller-circuit", help="Add microcontroller circuitry", ), + # use this feature https://github.com/fastapi/typer/pull/800 when merged: + row_column_pin_order: Optional[str] = typer.Option( + None, + "--row-column-pin-order", + help="Comma separated list of microcontroller pins defining order of row/column assignments", + ), version: bool = typer.Option( None, "-v", @@ -83,7 +90,10 @@ def main( stabilizer_footprint=stabilizer_footprint, diode_footprint=diode_footprint, controller_circuit=controller_circuit, - additional_search_path=lib_paths, + additional_search_path=lib_paths.split(",") if lib_paths else None, + row_column_pin_order=( + row_column_pin_order.split(",") if row_column_pin_order else None + ), ) generate_netlist(circuit, output) diff --git a/src/kle2netlist/circuits/__init__.py b/src/kle2netlist/circuits/__init__.py index 1c0d986..26eb46f 100644 --- a/src/kle2netlist/circuits/__init__.py +++ b/src/kle2netlist/circuits/__init__.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: MIT from dataclasses import dataclass, fields from enum import Enum -from typing import Dict, List +from typing import Dict, List, Optional from kbplacer.element_position import Side from skidl import Net @@ -76,9 +76,16 @@ class ControllerCircuit(str, Enum): NONE = "none" ATMEGA32U4_AU_V1 = "atmega32u4_au_v1" - def add(self, rows: Dict[str, Net], columns: Dict[str, Net]) -> None: + def add( + self, + rows: Dict[str, Net], + columns: Dict[str, Net], + row_column_pin_order: Optional[List[str]] = None, + ) -> None: if self == ControllerCircuit.ATMEGA32U4_AU_V1: - atmega32u4.circuit(rows, columns, "v1") + atmega32u4.circuit( + rows, columns, "v1", row_column_pin_order=row_column_pin_order + ) def positions(self) -> List[Footprint]: if self == ControllerCircuit.ATMEGA32U4_AU_V1: diff --git a/src/kle2netlist/circuits/atmega32u4.py b/src/kle2netlist/circuits/atmega32u4.py index 65b8110..4c4eac2 100644 --- a/src/kle2netlist/circuits/atmega32u4.py +++ b/src/kle2netlist/circuits/atmega32u4.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2024-present adamws # # SPDX-License-Identifier: MIT -from typing import Dict +from typing import Dict, List, Optional import skidl @@ -247,8 +247,13 @@ @skidl.subcircuit -def atmega32u4(rows: Dict[str, skidl.Net], columns: Dict[str, skidl.Net], footprints): - assignment_order = ATMEGA32U4AU_PIN_ASSIGN_ORDER[:] +def atmega32u4( + rows: Dict[str, skidl.Net], + columns: Dict[str, skidl.Net], + footprints, + row_column_pin_order: List[str], +): + assignment_order = row_column_pin_order[:] num_rows = len(rows) num_columns = len(columns) num_pins = len(assignment_order) @@ -369,13 +374,22 @@ def atmega32u4(rows: Dict[str, skidl.Net], columns: Dict[str, skidl.Net], footpr gnd += button[1] for _, row in rows.items(): - row += uc[assignment_order.pop(0)] + pin = assignment_order.pop(0) + row += uc[pin] for _, column in columns.items(): column += uc[assignment_order.pop(0)] -def circuit(rows: Dict[str, skidl.Net], columns: Dict[str, skidl.Net], variant: str): - atmega32u4(rows, columns, FOOTPRINTS[variant]) +def circuit( + rows: Dict[str, skidl.Net], + columns: Dict[str, skidl.Net], + variant: str, + *, + row_column_pin_order: Optional[List[str]] = None, +): + if not row_column_pin_order: + row_column_pin_order = ATMEGA32U4AU_PIN_ASSIGN_ORDER + atmega32u4(rows, columns, FOOTPRINTS[variant], row_column_pin_order) if __name__ == "__main__": diff --git a/src/kle2netlist/netlist.py b/src/kle2netlist/netlist.py index da4e52a..2eb6599 100644 --- a/src/kle2netlist/netlist.py +++ b/src/kle2netlist/netlist.py @@ -19,6 +19,7 @@ def build_circuit( stabilizer_footprint: str, diode_footprint: str, controller_circuit: ControllerCircuit = ControllerCircuit.NONE, + row_column_pin_order: Optional[List[str]] = None, additional_search_path: Optional[List[str]] = None, ) -> skidl.Circuit: set_skidl_search_path(additional_search_path) @@ -29,7 +30,7 @@ def build_circuit( rows, columns = handle_switch_matrix( keyboard, switch_footprint, diode_footprint, stabilizer_footprint ) - controller_circuit.add(rows, columns) + controller_circuit.add(rows, columns, row_column_pin_order=row_column_pin_order) return circuit diff --git a/tests/test_netlist_generation.py b/tests/test_netlist_generation.py index 9b32b23..3aa7791 100644 --- a/tests/test_netlist_generation.py +++ b/tests/test_netlist_generation.py @@ -38,15 +38,34 @@ def assert_netlist(netlist_template, result_file, template_dict): result_netlist.close() +@pytest.fixture +def file_isolation(tmpdir, request): + def _copy_files(layout_filename, netlist_template) -> None: + filename = request.module.__file__ + test_dir, _ = os.path.splitext(filename) + + if os.path.isdir(test_dir): + shutil.copy(f"{test_dir}/{layout_filename}", tmpdir) + shutil.copy(f"{test_dir}/{netlist_template}", tmpdir) + + return _copy_files + + @pytest.mark.parametrize( - ("layout_filename", "netlist_template", "controller_circuit"), + ( + "layout_filename", + "netlist_template", + "controller_circuit", + "row_column_pin_order", + ), [ # fmt: off - ("2x2.json", "2x2.net", ControllerCircuit.NONE), - ("2x2.json", "2x2-with-uc.net", ControllerCircuit.ATMEGA32U4_AU_V1), - ("2x2-with-alternative-layout.json", "2x2-with-alternative-layout.net", ControllerCircuit.NONE), - ("iso-enter.json", "iso-enter.net", ControllerCircuit.NONE), - ("empty.json", "empty-with-uc.net", ControllerCircuit.ATMEGA32U4_AU_V1), + ("2x2.json", "2x2.net", ControllerCircuit.NONE, None), + ("2x2.json", "2x2-with-uc.net", ControllerCircuit.ATMEGA32U4_AU_V1, None), + ("2x2.json", "2x2-with-uc-custom-order.net", ControllerCircuit.ATMEGA32U4_AU_V1, ["PD0", "PD1", "PF0", "PF1"]), + ("2x2-with-alternative-layout.json", "2x2-with-alternative-layout.net", ControllerCircuit.NONE, None), + ("iso-enter.json", "iso-enter.net", ControllerCircuit.NONE, None), + ("empty.json", "empty-with-uc.net", ControllerCircuit.ATMEGA32U4_AU_V1, None), # fmt: on ], ) @@ -58,23 +77,12 @@ class TestNetlistGeneration: "stabilizer_footprint_2u": "PCM_lib2:ST_2.00u", } - @pytest.fixture - def file_isolation(self, tmpdir, request): - def _copy_files(layout_filename, netlist_template) -> None: - filename = request.module.__file__ - test_dir, _ = os.path.splitext(filename) - - if os.path.isdir(test_dir): - shutil.copy(f"{test_dir}/{layout_filename}", tmpdir) - shutil.copy(f"{test_dir}/{netlist_template}", tmpdir) - - return _copy_files - def test_api( self, layout_filename, netlist_template, controller_circuit, + row_column_pin_order, file_isolation, tmpdir, ) -> None: @@ -87,6 +95,7 @@ def test_api( stabilizer_footprint="PCM_lib2:ST_{:.2f}u", diode_footprint="Diode_SMD:D_SOD-323F", controller_circuit=controller_circuit, + row_column_pin_order=row_column_pin_order, ) generate_netlist(circuit, result_netlist_path) @@ -99,6 +108,7 @@ def test_cli( layout_filename, netlist_template, controller_circuit, + row_column_pin_order, file_isolation, tmpdir, ) -> None: @@ -106,16 +116,19 @@ def test_cli( result_netlist_path = tmpdir.join("test.net") # fmt: off - result = runner.invoke(app, [ + args = [ "--layout", tmpdir.join(layout_filename), "--output", result_netlist_path, "--switch-footprint", "PCM_lib1:SW_{:.2f}u", "--stabilizer-footprint", "PCM_lib2:ST_{:.2f}u", "--diode-footprint", "Diode_SMD:D_SOD-323F", "--controller-circuit", controller_circuit, - ], - ) + ] # fmt: on + if row_column_pin_order: + args.append("--row-column-pin-order") + args.append(",".join(row_column_pin_order)) + result = runner.invoke(app, args) assert result.exit_code == 0 assert_netlist( diff --git a/tests/test_netlist_generation/2x2-with-uc-custom-order.net b/tests/test_netlist_generation/2x2-with-uc-custom-order.net new file mode 100644 index 0000000..7d91fb1 --- /dev/null +++ b/tests/test_netlist_generation/2x2-with-uc-custom-order.net @@ -0,0 +1,242 @@ +(export (version D) + (design +# (source "") +# (date "02/11/2021 08:02 PM") +# (tool "SKiDL (0.0.30)")) + (components + (comp (ref C1) + (value 22p) + (footprint Capacitor_SMD:C_0603_1608Metric) + (libsource (lib Device) (part C)) +# (sheetpath (names /top/18001832815836942775) (tstamps /top/18001832815836942775))) + (comp (ref C2) + (value 22p) + (footprint Capacitor_SMD:C_0603_1608Metric) + (libsource (lib Device) (part C)) +# (sheetpath (names /top/18188663817263142522) (tstamps /top/18188663817263142522))) + (comp (ref C3) + (value 0.1u) + (footprint Capacitor_SMD:C_0603_1608Metric) + (libsource (lib Device) (part C)) +# (sheetpath (names /top/8175458663902732966) (tstamps /top/8175458663902732966))) + (comp (ref C4) + (value 0.1u) + (footprint Capacitor_SMD:C_0603_1608Metric) + (libsource (lib Device) (part C)) +# (sheetpath (names /top/13237202579140692791) (tstamps /top/13237202579140692791))) + (comp (ref C5) + (value 0.1u) + (footprint Capacitor_SMD:C_0603_1608Metric) + (libsource (lib Device) (part C)) +# (sheetpath (names /top/8227448143397544909) (tstamps /top/8227448143397544909))) + (comp (ref C6) + (value 0.1u) + (footprint Capacitor_SMD:C_0603_1608Metric) + (libsource (lib Device) (part C)) +# (sheetpath (names /top/2409365322096952998) (tstamps /top/2409365322096952998))) + (comp (ref C7) + (value 4.7u) + (footprint Capacitor_SMD:C_0603_1608Metric) + (libsource (lib Device) (part C)) +# (sheetpath (names /top/5662103436809855345) (tstamps /top/5662103436809855345))) + (comp (ref C8) + (value 1u) + (footprint Capacitor_SMD:C_0603_1608Metric) + (libsource (lib Device) (part C)) +# (sheetpath (names /top/930912810921532981) (tstamps /top/930912810921532981))) + (comp (ref D1) + (value D) + (footprint Diode_SMD:D_SOD-323F) + (libsource (lib Device) (part D)) +# (sheetpath (names /top/18312722515296698309) (tstamps /top/18312722515296698309))) + (comp (ref D2) + (value D) + (footprint Diode_SMD:D_SOD-323F) + (libsource (lib Device) (part D)) +# (sheetpath (names /top/15978228252781608887) (tstamps /top/15978228252781608887))) + (comp (ref D3) + (value D) + (footprint Diode_SMD:D_SOD-323F) + (libsource (lib Device) (part D)) +# (sheetpath (names /top/4703575079412807575) (tstamps /top/4703575079412807575))) + (comp (ref D4) + (value D) + (footprint Diode_SMD:D_SOD-323F) + (libsource (lib Device) (part D)) +# (sheetpath (names /top/15882548998643781263) (tstamps /top/15882548998643781263))) + (comp (ref J1) + (value USB_C_Receptacle_USB2.0_14P) + (footprint Connector_USB:USB_C_Receptacle_XKB_U262-16XN-4BVC11) + (libsource (lib Connector) (part USB_C_Receptacle_USB2.0_14P)) +# (sheetpath (names /top/4844526346267810741) (tstamps /top/4844526346267810741))) + (comp (ref R1) + (value 22) + (footprint Resistor_SMD:R_0603_1608Metric) + (libsource (lib Device) (part R)) +# (sheetpath (names /top/7179373657408970856) (tstamps /top/7179373657408970856))) + (comp (ref R2) + (value 22) + (footprint Resistor_SMD:R_0603_1608Metric) + (libsource (lib Device) (part R)) +# (sheetpath (names /top/9978417715724002006) (tstamps /top/9978417715724002006))) + (comp (ref R3) + (value 10k) + (footprint Resistor_SMD:R_0603_1608Metric) + (libsource (lib Device) (part R)) +# (sheetpath (names /top/18434402957755578039) (tstamps /top/18434402957755578039))) + (comp (ref R4) + (value 10k) + (footprint Resistor_SMD:R_0603_1608Metric) + (libsource (lib Device) (part R)) +# (sheetpath (names /top/7166459574409252355) (tstamps /top/7166459574409252355))) + (comp (ref RST) + (value SW_SPST) + (footprint Button_Switch_SMD:SW_SPST_TL3342) + (libsource (lib Switch) (part SW_SPST)) +# (sheetpath (names /top/11567952041062639445) (tstamps /top/11567952041062639445))) + (comp (ref SW1) + (value SW_Push) + (footprint {{ switch_footprint_1u }}) + (libsource (lib Switch) (part SW_Push)) +# (sheetpath (names /top/5307434664195917066) (tstamps /top/5307434664195917066))) + (comp (ref SW2) + (value SW_Push) + (footprint {{ switch_footprint_1u }}) + (libsource (lib Switch) (part SW_Push)) +# (sheetpath (names /top/15254748721729782747) (tstamps /top/15254748721729782747))) + (comp (ref SW3) + (value SW_Push) + (footprint {{ switch_footprint_1u }}) + (libsource (lib Switch) (part SW_Push)) +# (sheetpath (names /top/15421197195437078572) (tstamps /top/15421197195437078572))) + (comp (ref SW4) + (value SW_Push) + (footprint {{ switch_footprint_1u }}) + (libsource (lib Switch) (part SW_Push)) +# (sheetpath (names /top/2152503131066222040) (tstamps /top/2152503131066222040))) + (comp (ref U1) + (value ATmega32U4-M) + (footprint Package_DFN_QFN:QFN-44-1EP_7x7mm_P0.5mm_EP5.2x5.2mm) + (libsource (lib MCU_Microchip_ATmega) (part ATmega16U4-M)) +# (sheetpath (names /top/14700442668264180207) (tstamps /top/14700442668264180207))) + (comp (ref U2) + (value TPD2S017) + (footprint Package_TO_SOT_SMD:SOT-23-6) + (libsource (lib Power_Protection) (part TPD2S017)) +# (sheetpath (names /top/4770598129058688152) (tstamps /top/4770598129058688152))) + (comp (ref Y1) + (value Crystal_GND24) + (footprint Crystal:Crystal_SMD_3225-4Pin_3.2x2.5mm) + (libsource (lib Device) (part Crystal_GND24)) +# (sheetpath (names /top/8563995125683780777) (tstamps /top/8563995125683780777)))) + (nets + (net (code 1) (name COL0) + (node (ref SW1) (pin 1)) + (node (ref SW3) (pin 1)) + (node (ref U1) (pin 41))) + (net (code 2) (name COL1) + (node (ref SW2) (pin 1)) + (node (ref SW4) (pin 1)) + (node (ref U1) (pin 40))) + (net (code 3) (name GND) + (node (ref C1) (pin 2)) + (node (ref C2) (pin 2)) + (node (ref C3) (pin 2)) + (node (ref C4) (pin 2)) + (node (ref C5) (pin 2)) + (node (ref C6) (pin 2)) + (node (ref C7) (pin 2)) + (node (ref C8) (pin 2)) + (node (ref J1) (pin A1)) + (node (ref J1) (pin A12)) + (node (ref J1) (pin B1)) + (node (ref J1) (pin B12)) + (node (ref J1) (pin S1)) + (node (ref R3) (pin 2)) + (node (ref RST) (pin 1)) + (node (ref U1) (pin 15)) + (node (ref U1) (pin 23)) + (node (ref U1) (pin 35)) + (node (ref U1) (pin 43)) + (node (ref U1) (pin 45)) + (node (ref U1) (pin 5)) + (node (ref U2) (pin 2)) + (node (ref Y1) (pin 2)) + (node (ref Y1) (pin 4))) + (net (code 4) (name N$1) + (node (ref D1) (pin 2)) + (node (ref SW1) (pin 2))) + (net (code 5) (name N$2) + (node (ref D2) (pin 2)) + (node (ref SW2) (pin 2))) + (net (code 6) (name N$3) + (node (ref D3) (pin 2)) + (node (ref SW3) (pin 2))) + (net (code 7) (name N$4) + (node (ref D4) (pin 2)) + (node (ref SW4) (pin 2))) + (net (code 8) (name ROW0) + (node (ref D1) (pin 1)) + (node (ref D2) (pin 1)) + (node (ref U1) (pin 18))) + (net (code 9) (name ROW1) + (node (ref D3) (pin 1)) + (node (ref D4) (pin 1)) + (node (ref U1) (pin 19))) + (net (code 10) (name VCC) + (node (ref C3) (pin 1)) + (node (ref C4) (pin 1)) + (node (ref C5) (pin 1)) + (node (ref C6) (pin 1)) + (node (ref C7) (pin 1)) + (node (ref J1) (pin A4)) + (node (ref J1) (pin A9)) + (node (ref J1) (pin B4)) + (node (ref J1) (pin B9)) + (node (ref R4) (pin 2)) + (node (ref U1) (pin 14)) + (node (ref U1) (pin 2)) + (node (ref U1) (pin 24)) + (node (ref U1) (pin 34)) + (node (ref U1) (pin 44)) + (node (ref U1) (pin 7)) + (node (ref U2) (pin 5))) + (net (code 11) (name mcu/D+) + (node (ref R2) (pin 1)) + (node (ref U1) (pin 4))) + (net (code 12) (name mcu/D-) + (node (ref R1) (pin 1)) + (node (ref U1) (pin 3))) + (net (code 13) (name mcu/UCAP) + (node (ref C8) (pin 1)) + (node (ref U1) (pin 6))) + (net (code 14) (name mcu/XTAL1) + (node (ref C1) (pin 1)) + (node (ref U1) (pin 17)) + (node (ref Y1) (pin 1))) + (net (code 15) (name mcu/XTAL2) + (node (ref C2) (pin 1)) + (node (ref U1) (pin 16)) + (node (ref Y1) (pin 3))) + (net (code 16) (name mcu/~{HWB}/PE2) + (node (ref R3) (pin 1)) + (node (ref U1) (pin 33))) + (net (code 17) (name mcu/~{RESET}) + (node (ref R4) (pin 1)) + (node (ref RST) (pin 2)) + (node (ref U1) (pin 13))) + (net (code 18) (name u2/D+) + (node (ref R2) (pin 2)) + (node (ref U2) (pin 6))) + (net (code 19) (name u2/D-) + (node (ref R1) (pin 2)) + (node (ref U2) (pin 1))) + (net (code 20) (name usb/D+) + (node (ref J1) (pin A6)) + (node (ref J1) (pin B6)) + (node (ref U2) (pin 4))) + (net (code 21) (name usb/D-) + (node (ref J1) (pin A7)) + (node (ref J1) (pin B7)) + (node (ref U2) (pin 3)))) +)