Pcbnew: add layer mode/stackup checks to library parity DRC

This commit is contained in:
John Beard 2025-08-27 22:43:02 +08:00
parent c9476caebf
commit 262f1fdabb
5 changed files with 182 additions and 14 deletions

View File

@ -877,6 +877,7 @@ set( PCB_COMMON_SRCS
${CMAKE_SOURCE_DIR}/pcbnew/pcb_marker.cpp
${CMAKE_SOURCE_DIR}/pcbnew/footprint.cpp
${CMAKE_SOURCE_DIR}/pcbnew/fix_board_shape.cpp
${CMAKE_SOURCE_DIR}/pcbnew/layer_utils.cpp
${CMAKE_SOURCE_DIR}/pcbnew/netinfo_item.cpp
${CMAKE_SOURCE_DIR}/pcbnew/netinfo_list.cpp
${CMAKE_SOURCE_DIR}/pcbnew/pad.cpp

View File

@ -43,6 +43,7 @@
#include <footprint_edit_frame.h>
#include <footprint_editor_settings.h>
#include <grid_layer_box_helpers.h>
#include <layer_utils.h>
#include <kiplatform/ui.h>
#include <panel_embedded_files.h>
#include <panel_fp_properties_3d_model.h>
@ -575,19 +576,6 @@ static LSET GetAllUsedFootprintLayers( const FOOTPRINT& aFootprint )
}
static wxString GetLayerStringList( const BOARD& aBoard, const LSET& layers )
{
std::vector<wxString> layerNames;
for( PCB_LAYER_ID layer : layers.UIOrder() )
{
layerNames.push_back( aBoard.GetLayerName( layer ) );
}
return AccumulateDescriptions( layerNames );
}
bool DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR::Validate()
{
if( !m_itemsGrid->CommitPendingChanges() )
@ -692,7 +680,7 @@ bool DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR::Validate()
m_delayedErrorMessage =
wxString::Format( _( "You are trying to remove layers that are used by the footprint: %s.\n"
"Please remove the objects that use these layers first." ),
GetLayerStringList( *m_frame->GetBoard(), usedLayers ) );
LAYER_UTILS::AccumulateNames( usedLayers, m_frame->GetBoard() ) );
m_delayedFocusGrid = m_customUserLayersGrid;
m_delayedFocusColumn = 0;
m_delayedFocusRow = 0;

View File

@ -22,6 +22,7 @@
*/
#include <layer_range.h>
#include <layer_utils.h>
#include <kiway.h>
#include <macros.h>
#include <netlist_reader/pcb_netlist.h>
@ -581,6 +582,74 @@ bool zoneNeedsUpdate( const ZONE* a, const ZONE* b, REPORTER* aReporter )
}
/**
* Compare the stackup related settings of two footprints.
*
* Returns true if they differ.
*/
bool stackupNeedsUpdate( const FOOTPRINT& a, const FOOTPRINT& b, REPORTER* aReporter )
{
bool diff = false;
TEST( a.GetStackupMode(), b.GetStackupMode(),
wxString::Format( _( "Footprint stackup mode differs." ) ) );
const LSET& aLayers = a.GetLayerSet();
const LSET& bLayers = b.GetLayerSet();
TEST( aLayers, bLayers,
wxString::Format( _( "Footprint layers differ." ) ) );
return diff;
}
/**
* Report board->footprint stackup differences.
*
* This is not necessarily a comparison failure, but may be useful information
* for the user to see.
*
* @return true if there is
*/
bool footprintVsBoardStackup( const FOOTPRINT& aFp, const BOARD& aBoard, REPORTER* aReporter )
{
if( aFp.GetStackupMode() == FOOTPRINT_STACKUP::EXPAND_INNER_LAYERS )
return false;
// Filter only layers that can differ between footprint and board
const LSET& fpLayers = aFp.GetStackupLayers();
const LSET& brdLayers = aBoard.GetEnabledLayers() & ( LSET::AllCuMask() | LSET::UserDefinedLayersMask() );
bool mismatch = false;
// Any layer in the FP and not on the board is flagged
const LSET onlyInFp = fpLayers & ~brdLayers;
if( onlyInFp.count() )
{
mismatch = true;
if( aReporter )
aReporter->Report( wxString::Format( _( "Footprint has %lu layers not on board: %s" ), onlyInFp.count(),
LAYER_UTILS::AccumulateNames( onlyInFp, &aBoard ) ) );
}
// Only look at copper layers here: user layers on the board and not in the FP is normal
const LSET cuOnlyInBoard = ( brdLayers & ~fpLayers ) & LSET::AllCuMask();
if( cuOnlyInBoard.count() )
{
mismatch = true;
if( aReporter )
aReporter->Report( wxString::Format( _( "Board has %lu copper layers not in footprint: %s" ),
cuOnlyInBoard.count(),
LAYER_UTILS::AccumulateNames( cuOnlyInBoard, &aBoard ) ) );
}
return mismatch;
}
bool FOOTPRINT::FootprintNeedsUpdate( const FOOTPRINT* aLibFP, int aCompareFlags,
REPORTER* aReporter )
{
@ -617,6 +686,9 @@ bool FOOTPRINT::FootprintNeedsUpdate( const FOOTPRINT* aLibFP, int aCompareFlags
aLibFP = temp.get();
// These checks don't set off errors, they're just informational
footprintVsBoardStackup( *this, *GetBoard(), aReporter );
#define TEST_ATTR( a, b, attr, msg ) TEST( ( a & attr ), ( b & attr ), msg )
TEST_ATTR( GetAttributes(), aLibFP->GetAttributes(), (FP_THROUGH_HOLE | FP_SMD),
@ -649,6 +721,12 @@ bool FOOTPRINT::FootprintNeedsUpdate( const FOOTPRINT* aLibFP, int aCompareFlags
#define REPORT( msg ) { if( aReporter ) aReporter->Report( msg ); }
#define CHECKPOINT { if( diff && !aReporter ) return diff; }
if( stackupNeedsUpdate( *this, *aLibFP, aReporter ) )
{
diff = true;
REPORT( _( "Footprint stackup differs." ) );
}
// Clearance and zone connection overrides are as likely to be set at the board level as in
// the library.
//

40
pcbnew/layer_utils.cpp Normal file
View File

@ -0,0 +1,40 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "layer_utils.h"
wxString LAYER_UTILS::AccumulateNames( const LSEQ& aLayers, const BOARD* aBoard )
{
wxString result;
for( const PCB_LAYER_ID layer : aLayers )
{
if( !result.IsEmpty() )
result += ", ";
result += aBoard ? aBoard->GetLayerName( layer ) : LayerName( layer );
}
return result;
}

61
pcbnew/layer_utils.h Normal file
View File

@ -0,0 +1,61 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#pragma once
#include <wx/string.h>
#include <board.h>
#include <lseq.h>
#include <lset.h>
/**
* Utility functions for dealing with layers in the context of a PCB board.s
*
* This includes functions that need access to the board to get layer names,
* and other more complex, but reusable operations that either shouldn't be in LSET/LSEQ's
* interface or need access to Pcbnew types.
*/
namespace LAYER_UTILS
{
/**
* Accumulate layer names from a layer set into a comma separated string.
*
* @param aLayers is the list of layers to accumulate.
* @param aBoard is the board to get layer names from, if null the default names
* are used.
*/
wxString AccumulateNames( const LSEQ& aLayers, const BOARD* aBoard );
/**
* Accumulate layer names from a layer set into a comma separated string,
* in UI order.
*/
inline wxString AccumulateNames( const LSET& aLayers, const BOARD* aBoard )
{
return AccumulateNames( aLayers.UIOrder(), aBoard );
}
} // namespace LAYER_UTILS