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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25
26#include <tools/backannotate.h>
27
28#include <map>
29#include <vector>
30
31
33make_candidate( const char* aRef, int aUnit, const std::vector<const char*>& aPins,
34 const std::initializer_list<std::pair<const char*, const char*>>& aNets )
35{
37 candidate.m_ref = wxString::FromUTF8( aRef );
38 candidate.m_currentUnit = aUnit;
39
40 for( const char* pin : aPins )
41 candidate.m_unitPinNumbers.push_back( wxString::FromUTF8( pin ) );
42
43 for( const auto& [pin, net] : aNets )
44 candidate.m_schNetsByPin[wxString::FromUTF8( pin )] = wxString::FromUTF8( net );
45
46 return candidate;
47}
48
49
50static std::map<wxString, wxString>
51make_pin_map( const std::initializer_list<std::pair<const char*, const char*>>& aNets )
52{
53 std::map<wxString, wxString> pinMap;
54
55 for( const auto& [pin, net] : aNets )
56 pinMap[wxString::FromUTF8( pin )] = wxString::FromUTF8( net );
57
58 return pinMap;
59}
60
61
62// aMappingOk = every candidate should be matched to exactly one destination unit pattern
63// aIdentity = each candidate should map to itself
64// aExpectedSwappedCandidateIndices = list of units that are going to be swapped
65
66static void check_plan( const BACKANNOTATE_UNIT_SWAP_PLAN& aPlan, bool aMappingOk, bool aIdentity,
67 std::initializer_list<size_t> aExpectedSwappedCandidateIndices )
68{
69 BOOST_CHECK_EQUAL( aPlan.m_mappingOk, aMappingOk );
70 BOOST_CHECK_EQUAL( aPlan.m_identity, aIdentity );
71 BOOST_CHECK_EQUAL( aPlan.m_swappedCandidateIndices.size(), aExpectedSwappedCandidateIndices.size() );
72
73 for( size_t candidateIdx : aExpectedSwappedCandidateIndices )
74 BOOST_CHECK_EQUAL( aPlan.m_swappedCandidateIndices.count( candidateIdx ), 1U );
75}
76
77
78// Check that the final unit assignments for the candidates match the expected units after applying the swap plan.
79// aExpectedUnits = rearranged list of which units positions the swapping should apply, e.g. if you
80// swapped 1 and 2 in the candidates list, and the original units were { 1, 2 }, then the expected units would be { 2, 1 }.
81static void check_final_units( const std::vector<BACKANNOTATE_UNIT_SWAP_CANDIDATE>& aCandidates,
82 const BACKANNOTATE_UNIT_SWAP_PLAN& aPlan,
83 std::initializer_list<int> aExpectedUnits )
84{
85 std::vector<int> finalUnits;
86
87 for( const BACKANNOTATE_UNIT_SWAP_CANDIDATE& candidate : aCandidates )
88 finalUnits.push_back( candidate.m_currentUnit );
89
90 for( const BACKANNOTATE_UNIT_SWAP_STEP& step : aPlan.m_steps )
91 std::swap( finalUnits[step.m_firstIndex], finalUnits[step.m_secondIndex] );
92
93 BOOST_REQUIRE_EQUAL( finalUnits.size(), aExpectedUnits.size() );
94
95 size_t idx = 0;
96
97 for( int expectedUnit : aExpectedUnits )
98 BOOST_CHECK_EQUAL( finalUnits[idx++], expectedUnit );
99}
100
101
102BOOST_AUTO_TEST_SUITE( BackannotateUnitSwapPlanner )
103
104
105BOOST_AUTO_TEST_CASE( BackannotateUnitSwapPlanner_DetectsIdentityMapping )
106{
107 std::vector<BACKANNOTATE_UNIT_SWAP_CANDIDATE> candidates =
108 {
109 make_candidate( "U1A", 1, { "1", "2", "3" },
110 { { "1", "Aout" }, { "2", "Net-(U1A--)" }, { "3", "A+" } } ),
111 make_candidate( "U1B", 2, { "5", "6", "7" },
112 { { "5", "B+" }, { "6", "B-" }, { "7", "Bout" } } )
113 };
114
116 PlanBackannotateUnitSwaps( candidates,
117 make_pin_map( { { "1", "Aout" }, { "2", "Net-(U1A--)" }, { "3", "A+" },
118 { "5", "B+" }, { "6", "B-" }, { "7", "Bout" } } ) );
119
120 check_plan( plan, true, true, {} );
121 check_final_units( candidates, plan, { 1, 2 } );
122}
123
124
125BOOST_AUTO_TEST_CASE( BackannotateUnitSwapPlanner_DetectsSimpleUnitSwap )
126{
127 std::vector<BACKANNOTATE_UNIT_SWAP_CANDIDATE> candidates =
128 {
129 make_candidate( "U1A", 1, { "1", "2", "3" },
130 { { "1", "A1" }, { "2", "A2" }, { "3", "A3" } } ),
131 make_candidate( "U1B", 2, { "4", "5", "6" },
132 { { "4", "B1" }, { "5", "B2" }, { "6", "B3" } } )
133 };
134
136 PlanBackannotateUnitSwaps( candidates,
137 make_pin_map( { { "1", "B1" }, { "2", "B2" }, { "3", "B3" },
138 { "4", "A1" }, { "5", "A2" }, { "6", "A3" } } ) );
139
140 check_plan( plan, true, false, { 0, 1 } );
141 check_final_units( candidates, plan, { 2, 1 } );
142}
143
144
145BOOST_AUTO_TEST_CASE( BackannotateUnitSwapPlanner_DetectsBAS16TWGateRotation )
146{
147 std::vector<BACKANNOTATE_UNIT_SWAP_CANDIDATE> candidates = {
148 make_candidate( "D1", 1, { "6", "1" }, { { "6", "K1" }, { "1", "A1" } } ),
149 make_candidate( "D1", 2, { "5", "2" }, { { "5", "K2" }, { "2", "A2" } } ),
150 make_candidate( "D1", 3, { "4", "3" }, { { "4", "K3" }, { "3", "A3" } } )
151 };
152
154 candidates,
156 { { "6", "K2" }, { "1", "A2" }, { "5", "K3" }, { "2", "A3" }, { "4", "K1" }, { "3", "A1" } } ) );
157
158 check_plan( plan, true, false, { 0, 1, 2 } );
159 check_final_units( candidates, plan, { 3, 1, 2 } );
160}
161
162
163BOOST_AUTO_TEST_CASE( BackannotateUnitSwapPlanner_DetectsRN1GateRotationWithSharedCommonPin )
164{
165 std::vector<BACKANNOTATE_UNIT_SWAP_CANDIDATE> candidates = {
166 make_candidate( "RN1", 1, { "1", "2" }, { { "1", "R1" }, { "2", "R1.2" } } ),
167 make_candidate( "RN1", 2, { "1", "3" }, { { "1", "R1" }, { "3", "R2.2" } } ),
168 make_candidate( "RN1", 3, { "1", "4" }, { { "1", "R1" }, { "4", "R3.2" } } )
169 };
170
172 candidates,
174 { { "1", "R1" }, { "2", "R2.2" }, { "3", "R3.2" }, { "4", "R1.2" } } ) );
175
176 check_plan( plan, true, false, { 0, 1, 2 } );
177 check_final_units( candidates, plan, { 3, 1, 2 } );
178}
179
180
181BOOST_AUTO_TEST_CASE( BackannotateUnitSwapPlanner_DoesNotTreatHybridBAS16TWGateAndPinSwapAsPureUnitRotation )
182{
183 std::vector<BACKANNOTATE_UNIT_SWAP_CANDIDATE> candidates = {
184 make_candidate( "D1", 1, { "6", "1" }, { { "6", "K1" }, { "1", "A1" } } ),
185 make_candidate( "D1", 2, { "5", "2" }, { { "5", "K2" }, { "2", "A2" } } ),
186 make_candidate( "D1", 3, { "4", "3" }, { { "4", "K3" }, { "3", "A3" } } )
187 };
188
189 // Simulate these PCB operations in order:
190 // 1. Swap diode gates B and C, so the B/C unit positions exchange their net pairs.
191 // 2. After that gate swap, swap the two pads on the gate now sitting in slot B.
192 //
193 // The resulting footprint pad-to-net assignment is:
194 // 1 -> A1
195 // 2 -> K3
196 // 3 -> A2
197 // 4 -> K2
198 // 5 -> A3
199 // 6 -> K1
200 //
201 // This is not a pure unit rotation anymore. Unit A still matches, unit C now matches
202 // the old B gate, but unit B has its A/K pins inverted. The unit-swap planner should
203 // reject this as a complete unit mapping instead of treating it as a valid gate-only swap.
205 candidates,
207 { { "1", "A1" }, { "2", "K3" }, { "3", "A2" }, { "4", "K2" }, { "5", "A3" }, { "6", "K1" } } ) );
208
209 BOOST_CHECK( !plan.m_mappingOk );
210 BOOST_CHECK( plan.m_steps.empty() );
211 BOOST_CHECK( plan.m_swappedCandidateIndices.empty() );
212}
213
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")