KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_backannotate_unit_swap.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <tools/backannotate.h>
23
24#include <map>
25#include <vector>
26
27
29make_candidate( const char* aRef, int aUnit, const std::vector<const char*>& aPins,
30 const std::initializer_list<std::pair<const char*, const char*>>& aNets )
31{
33 candidate.m_ref = wxString::FromUTF8( aRef );
34 candidate.m_currentUnit = aUnit;
35
36 for( const char* pin : aPins )
37 candidate.m_unitPinNumbers.push_back( wxString::FromUTF8( pin ) );
38
39 for( const auto& [pin, net] : aNets )
40 candidate.m_schNetsByPin[wxString::FromUTF8( pin )] = wxString::FromUTF8( net );
41
42 return candidate;
43}
44
45
46static std::map<wxString, wxString>
47make_pin_map( const std::initializer_list<std::pair<const char*, const char*>>& aNets )
48{
49 std::map<wxString, wxString> pinMap;
50
51 for( const auto& [pin, net] : aNets )
52 pinMap[wxString::FromUTF8( pin )] = wxString::FromUTF8( net );
53
54 return pinMap;
55}
56
57
58// aMappingOk = every candidate should be matched to exactly one destination unit pattern
59// aIdentity = each candidate should map to itself
60// aExpectedSwappedCandidateIndices = list of units that are going to be swapped
61
62static void check_plan( const BACKANNOTATE_UNIT_SWAP_PLAN& aPlan, bool aMappingOk, bool aIdentity,
63 std::initializer_list<size_t> aExpectedSwappedCandidateIndices )
64{
65 BOOST_CHECK_EQUAL( aPlan.m_mappingOk, aMappingOk );
66 BOOST_CHECK_EQUAL( aPlan.m_identity, aIdentity );
67 BOOST_CHECK_EQUAL( aPlan.m_swappedCandidateIndices.size(), aExpectedSwappedCandidateIndices.size() );
68
69 for( size_t candidateIdx : aExpectedSwappedCandidateIndices )
70 BOOST_CHECK_EQUAL( aPlan.m_swappedCandidateIndices.count( candidateIdx ), 1U );
71}
72
73
74// Check that the final unit assignments for the candidates match the expected units after applying the swap plan.
75// aExpectedUnits = rearranged list of which units positions the swapping should apply, e.g. if you
76// swapped 1 and 2 in the candidates list, and the original units were { 1, 2 }, then the expected units would be { 2, 1 }.
77static void check_final_units( const std::vector<BACKANNOTATE_UNIT_SWAP_CANDIDATE>& aCandidates,
78 const BACKANNOTATE_UNIT_SWAP_PLAN& aPlan,
79 std::initializer_list<int> aExpectedUnits )
80{
81 std::vector<int> finalUnits;
82
83 for( const BACKANNOTATE_UNIT_SWAP_CANDIDATE& candidate : aCandidates )
84 finalUnits.push_back( candidate.m_currentUnit );
85
86 for( const BACKANNOTATE_UNIT_SWAP_STEP& step : aPlan.m_steps )
87 std::swap( finalUnits[step.m_firstIndex], finalUnits[step.m_secondIndex] );
88
89 BOOST_REQUIRE_EQUAL( finalUnits.size(), aExpectedUnits.size() );
90
91 size_t idx = 0;
92
93 for( int expectedUnit : aExpectedUnits )
94 BOOST_CHECK_EQUAL( finalUnits[idx++], expectedUnit );
95}
96
97
98BOOST_AUTO_TEST_SUITE( BackannotateUnitSwapPlanner )
99
100
101BOOST_AUTO_TEST_CASE( BackannotateUnitSwapPlanner_DetectsIdentityMapping )
102{
103 std::vector<BACKANNOTATE_UNIT_SWAP_CANDIDATE> candidates =
104 {
105 make_candidate( "U1A", 1, { "1", "2", "3" },
106 { { "1", "Aout" }, { "2", "Net-(U1A--)" }, { "3", "A+" } } ),
107 make_candidate( "U1B", 2, { "5", "6", "7" },
108 { { "5", "B+" }, { "6", "B-" }, { "7", "Bout" } } )
109 };
110
112 PlanBackannotateUnitSwaps( candidates,
113 make_pin_map( { { "1", "Aout" }, { "2", "Net-(U1A--)" }, { "3", "A+" },
114 { "5", "B+" }, { "6", "B-" }, { "7", "Bout" } } ) );
115
116 check_plan( plan, true, true, {} );
117 check_final_units( candidates, plan, { 1, 2 } );
118}
119
120
121BOOST_AUTO_TEST_CASE( BackannotateUnitSwapPlanner_DetectsSimpleUnitSwap )
122{
123 std::vector<BACKANNOTATE_UNIT_SWAP_CANDIDATE> candidates =
124 {
125 make_candidate( "U1A", 1, { "1", "2", "3" },
126 { { "1", "A1" }, { "2", "A2" }, { "3", "A3" } } ),
127 make_candidate( "U1B", 2, { "4", "5", "6" },
128 { { "4", "B1" }, { "5", "B2" }, { "6", "B3" } } )
129 };
130
132 PlanBackannotateUnitSwaps( candidates,
133 make_pin_map( { { "1", "B1" }, { "2", "B2" }, { "3", "B3" },
134 { "4", "A1" }, { "5", "A2" }, { "6", "A3" } } ) );
135
136 check_plan( plan, true, false, { 0, 1 } );
137 check_final_units( candidates, plan, { 2, 1 } );
138}
139
140
141BOOST_AUTO_TEST_CASE( BackannotateUnitSwapPlanner_DetectsBAS16TWGateRotation )
142{
143 std::vector<BACKANNOTATE_UNIT_SWAP_CANDIDATE> candidates = {
144 make_candidate( "D1", 1, { "6", "1" }, { { "6", "K1" }, { "1", "A1" } } ),
145 make_candidate( "D1", 2, { "5", "2" }, { { "5", "K2" }, { "2", "A2" } } ),
146 make_candidate( "D1", 3, { "4", "3" }, { { "4", "K3" }, { "3", "A3" } } )
147 };
148
150 candidates,
152 { { "6", "K2" }, { "1", "A2" }, { "5", "K3" }, { "2", "A3" }, { "4", "K1" }, { "3", "A1" } } ) );
153
154 check_plan( plan, true, false, { 0, 1, 2 } );
155 check_final_units( candidates, plan, { 3, 1, 2 } );
156}
157
158
159BOOST_AUTO_TEST_CASE( BackannotateUnitSwapPlanner_DetectsRN1GateRotationWithSharedCommonPin )
160{
161 std::vector<BACKANNOTATE_UNIT_SWAP_CANDIDATE> candidates = {
162 make_candidate( "RN1", 1, { "1", "2" }, { { "1", "R1" }, { "2", "R1.2" } } ),
163 make_candidate( "RN1", 2, { "1", "3" }, { { "1", "R1" }, { "3", "R2.2" } } ),
164 make_candidate( "RN1", 3, { "1", "4" }, { { "1", "R1" }, { "4", "R3.2" } } )
165 };
166
168 candidates,
170 { { "1", "R1" }, { "2", "R2.2" }, { "3", "R3.2" }, { "4", "R1.2" } } ) );
171
172 check_plan( plan, true, false, { 0, 1, 2 } );
173 check_final_units( candidates, plan, { 3, 1, 2 } );
174}
175
176
177BOOST_AUTO_TEST_CASE( BackannotateUnitSwapPlanner_DoesNotTreatHybridBAS16TWGateAndPinSwapAsPureUnitRotation )
178{
179 std::vector<BACKANNOTATE_UNIT_SWAP_CANDIDATE> candidates = {
180 make_candidate( "D1", 1, { "6", "1" }, { { "6", "K1" }, { "1", "A1" } } ),
181 make_candidate( "D1", 2, { "5", "2" }, { { "5", "K2" }, { "2", "A2" } } ),
182 make_candidate( "D1", 3, { "4", "3" }, { { "4", "K3" }, { "3", "A3" } } )
183 };
184
185 // Simulate these PCB operations in order:
186 // 1. Swap diode gates B and C, so the B/C unit positions exchange their net pairs.
187 // 2. After that gate swap, swap the two pads on the gate now sitting in slot B.
188 //
189 // The resulting footprint pad-to-net assignment is:
190 // 1 -> A1
191 // 2 -> K3
192 // 3 -> A2
193 // 4 -> K2
194 // 5 -> A3
195 // 6 -> K1
196 //
197 // This is not a pure unit rotation anymore. Unit A still matches, unit C now matches
198 // the old B gate, but unit B has its A/K pins inverted. The unit-swap planner should
199 // reject this as a complete unit mapping instead of treating it as a valid gate-only swap.
201 candidates,
203 { { "1", "A1" }, { "2", "K3" }, { "3", "A2" }, { "4", "K2" }, { "5", "A3" }, { "6", "K1" } } ) );
204
205 BOOST_CHECK( !plan.m_mappingOk );
206 BOOST_CHECK( plan.m_steps.empty() );
207 BOOST_CHECK( plan.m_swappedCandidateIndices.empty() );
208}
209
BACKANNOTATE_UNIT_SWAP_PLAN PlanBackannotateUnitSwaps(const std::vector< BACKANNOTATE_UNIT_SWAP_CANDIDATE > &aCandidates, const std::map< wxString, wxString > &aPcbPinMap)
Compute a pure unit-swap plan from schematic-side unit definitions and the final PCB pin map.
std::vector< wxString > m_unitPinNumbers
std::map< wxString, wxString > m_schNetsByPin
std::vector< BACKANNOTATE_UNIT_SWAP_STEP > m_steps
std::set< size_t > m_swappedCandidateIndices
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_CASE(BackannotateUnitSwapPlanner_DetectsIdentityMapping)
static void check_final_units(const std::vector< BACKANNOTATE_UNIT_SWAP_CANDIDATE > &aCandidates, const BACKANNOTATE_UNIT_SWAP_PLAN &aPlan, std::initializer_list< int > aExpectedUnits)
static BACKANNOTATE_UNIT_SWAP_CANDIDATE make_candidate(const char *aRef, int aUnit, const std::vector< const char * > &aPins, const std::initializer_list< std::pair< const char *, const char * > > &aNets)
static std::map< wxString, wxString > make_pin_map(const std::initializer_list< std::pair< const char *, const char * > > &aNets)
static void check_plan(const BACKANNOTATE_UNIT_SWAP_PLAN &aPlan, bool aMappingOk, bool aIdentity, std::initializer_list< size_t > aExpectedSwappedCandidateIndices)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
KIBIS_PIN * pin
BOOST_CHECK_EQUAL(result, "25.4")