From 533276e6b60870f98c63e31db687d5b25c7e2578 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Wed, 11 Aug 2021 14:01:30 +0100 Subject: [PATCH] Copper sliver checking. ADDED DRCE_COPPER_SLIVER DRC error. ADDED ADVANCED_CFG DRCSliverWidthTolerance and DRCSliverAngleTolerance. Fixes https://gitlab.com/kicad/code/kicad/issues/2183 --- common/advanced_config.cpp | 14 ++ include/advanced_config.h | 6 + pcbnew/CMakeLists.txt | 1 + pcbnew/board_design_settings.cpp | 2 + pcbnew/drc/drc_item.cpp | 6 + pcbnew/drc/drc_item.h | 6 +- .../drc/drc_test_provider_sliver_checker.cpp | 170 ++++++++++++++++++ 7 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 pcbnew/drc/drc_test_provider_sliver_checker.cpp diff --git a/common/advanced_config.cpp b/common/advanced_config.cpp index 362d03fb14..c2f9645c7e 100644 --- a/common/advanced_config.cpp +++ b/common/advanced_config.cpp @@ -75,6 +75,12 @@ static const wxChar ExtraFillMargin[] = wxT( "ExtraFillMargin" ); */ static const wxChar DRCEpsilon[] = wxT( "DRCEpsilon" ); +/** + * Angle and width tolerances for copper and solder mask sliver detection. + */ +static const wxChar DRCSliverWidthTolerance[] = wxT( "DRCSliverWidthTolerance" ); +static const wxChar DRCSliverAngleTolerance[] = wxT( "DRCSliverAngleTolerance" ); + /** * Used to calculate the actual hole size from the finish hole size. * IPC-6012 says 0.015-0.018mm; Cadence says at least 0.020mm for a Class 2 board and at least @@ -273,6 +279,8 @@ ADVANCED_CFG::ADVANCED_CFG() m_ExtraClearance = 0.0001; m_DRCEpsilon = 0.0001; // 0.1um is small enough not to materially violate // any constraints. + m_SliverWidthTolerance = 0.08; + m_SliverAngleTolerance = 20.0; m_HoleWallThickness = 0.020; // IPC-6012 says 15-18um; Cadence says at least // 0.020 for a Class 2 board and at least 0.025 @@ -342,6 +350,12 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg ) configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::DRCEpsilon, &m_DRCEpsilon, 0.0005, 0.0, 1.0 ) ); + configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::DRCSliverWidthTolerance, + &m_SliverWidthTolerance, 0.08, 0.01, 0.25 ) ); + + configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::DRCSliverAngleTolerance, + &m_SliverAngleTolerance, 20.0, 1.0, 90.0 ) ); + configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::HoleWallThickness, &m_HoleWallThickness, 0.020, 0.0, 1.0 ) ); diff --git a/include/advanced_config.h b/include/advanced_config.h index 6a32a41e63..521d862669 100644 --- a/include/advanced_config.h +++ b/include/advanced_config.h @@ -101,6 +101,12 @@ public: */ double m_DRCEpsilon; + /** + * Sliver tolerances for DRC. Units are mm and deg. + */ + double m_SliverWidthTolerance; + double m_SliverAngleTolerance; + /** * Hole wall plating thickness. Used to determine actual hole size from finish hole size. * Units are mm. diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 55ccde73a0..baaabbef14 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -249,6 +249,7 @@ set( PCBNEW_DRC_SRCS drc/drc_test_provider_silk_clearance.cpp drc/drc_test_provider_matched_length.cpp drc/drc_test_provider_diff_pair_coupling.cpp + drc/drc_test_provider_sliver_checker.cpp ) set( PCBNEW_NETLIST_SRCS diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp index 5b22e5370d..738b8a6f6b 100644 --- a/pcbnew/board_design_settings.cpp +++ b/pcbnew/board_design_settings.cpp @@ -167,6 +167,8 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS( JSON_SETTINGS* aParent, const std: m_DRCSeverities[ DRCE_DANGLING_TRACK ] = RPT_SEVERITY_WARNING; m_DRCSeverities[ DRCE_DANGLING_VIA ] = RPT_SEVERITY_WARNING; + m_DRCSeverities[ DRCE_COPPER_SLIVER ] = RPT_SEVERITY_WARNING; + m_DRCSeverities[ DRCE_MISSING_FOOTPRINT ] = RPT_SEVERITY_WARNING; m_DRCSeverities[ DRCE_DUPLICATE_FOOTPRINT ] = RPT_SEVERITY_WARNING; m_DRCSeverities[ DRCE_EXTRA_FOOTPRINT ] = RPT_SEVERITY_WARNING; diff --git a/pcbnew/drc/drc_item.cpp b/pcbnew/drc/drc_item.cpp index a80e02ce33..c530d4b626 100644 --- a/pcbnew/drc/drc_item.cpp +++ b/pcbnew/drc/drc_item.cpp @@ -183,6 +183,10 @@ DRC_ITEM DRC_ITEM::unresolvedVariable( DRCE_UNRESOLVED_VARIABLE, _( "Unresolved text variable" ), wxT( "unresolved_variable" ) ); +DRC_ITEM DRC_ITEM::copperSliver( DRCE_COPPER_SLIVER, + _( "Copper sliver" ), + wxT( "copper_sliver" ) ); + DRC_ITEM DRC_ITEM::silkMaskClearance( DRCE_SILK_MASK_CLEARANCE, _( "Silkscreen clipped by solder mask" ), wxT( "silk_over_copper" ) ); @@ -249,6 +253,7 @@ std::vector> DRC_ITEM::allItemTypes( { DRC_ITEM::missingCourtyard, DRC_ITEM::malformedCourtyard, DRC_ITEM::invalidOutline, + DRC_ITEM::copperSliver, DRC_ITEM::heading_schematic_parity, DRC_ITEM::duplicateFootprints, @@ -324,6 +329,7 @@ std::shared_ptr DRC_ITEM::Create( int aErrorCode ) case DRCE_EXTRA_FOOTPRINT: return std::make_shared( extraFootprint ); case DRCE_LIB_FOOTPRINT_ISSUES: return std::make_shared( libFootprintIssues ); case DRCE_UNRESOLVED_VARIABLE: return std::make_shared( unresolvedVariable ); + case DRCE_COPPER_SLIVER: return std::make_shared( copperSliver ); case DRCE_OVERLAPPING_SILK: return std::make_shared( silkOverlaps ); case DRCE_SILK_MASK_CLEARANCE: return std::make_shared( silkMaskClearance ); case DRCE_TEXT_HEIGHT: return std::make_shared( textHeightOutOfRange ); diff --git a/pcbnew/drc/drc_item.h b/pcbnew/drc/drc_item.h index f3cfc2eca8..1da47d9220 100644 --- a/pcbnew/drc/drc_item.h +++ b/pcbnew/drc/drc_item.h @@ -73,15 +73,18 @@ enum PCB_DRC_CODE { DRCE_PAD_TH_WITH_NO_HOLE, // footprint has Plated Through-Hole with no hole DRCE_UNRESOLVED_VARIABLE, + + DRCE_COPPER_SLIVER, + DRCE_SILK_MASK_CLEARANCE, // silkscreen clipped by mask (potentially leaving it // over pads, exposed copper, etc.) DRCE_TEXT_HEIGHT, DRCE_TEXT_THICKNESS, DRCE_OVERLAPPING_SILK, // silk to silk clearance error + DRCE_LENGTH_OUT_OF_RANGE, DRCE_SKEW_OUT_OF_RANGE, DRCE_TOO_MANY_VIAS, - DRCE_DIFF_PAIR_GAP_OUT_OF_RANGE, DRCE_DIFF_PAIR_UNCOUPLED_LENGTH_TOO_LONG, @@ -172,6 +175,7 @@ private: static DRC_ITEM netConflict; static DRC_ITEM libFootprintIssues; static DRC_ITEM unresolvedVariable; + static DRC_ITEM copperSliver; static DRC_ITEM silkMaskClearance; static DRC_ITEM silkOverlaps; static DRC_ITEM textHeightOutOfRange; diff --git a/pcbnew/drc/drc_test_provider_sliver_checker.cpp b/pcbnew/drc/drc_test_provider_sliver_checker.cpp new file mode 100644 index 0000000000..8025c37be7 --- /dev/null +++ b/pcbnew/drc/drc_test_provider_sliver_checker.cpp @@ -0,0 +1,170 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2021 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* + Checks for slivers in copper layers + + Errors generated: + - DRCE_COPPER_SLIVER +*/ + +class DRC_TEST_PROVIDER_SLIVER_CHECKER : public DRC_TEST_PROVIDER +{ +public: + DRC_TEST_PROVIDER_SLIVER_CHECKER() + { + } + + virtual ~DRC_TEST_PROVIDER_SLIVER_CHECKER() + { + } + + virtual bool Run() override; + + virtual const wxString GetName() const override + { + return "sliver checker"; + }; + + virtual const wxString GetDescription() const override + { + return "Checks copper layers for slivers"; + } + + virtual std::set GetConstraintTypes() const override + { + return {}; + } + +private: + wxString layerDesc( PCB_LAYER_ID aLayer ); +}; + + +wxString DRC_TEST_PROVIDER_SLIVER_CHECKER::layerDesc( PCB_LAYER_ID aLayer ) +{ + return wxString::Format( wxT( "(%s)" ), m_drcEngine->GetBoard()->GetLayerName( aLayer ) ); +} + + +bool DRC_TEST_PROVIDER_SLIVER_CHECKER::Run() +{ + const int delta = 250; // This is the number of tests between 2 calls to the progress bar + + if( m_drcEngine->IsErrorLimitExceeded( DRCE_COPPER_SLIVER ) ) + return true; // Continue with other tests + + if( !reportPhase( _( "Running sliver detection on copper layers..." ) ) ) + return false; // DRC cancelled + + int widthTolerance = Millimeter2iu( ADVANCED_CFG::GetCfg().m_SliverWidthTolerance ); + double angleTolerance = ADVANCED_CFG::GetCfg().m_SliverAngleTolerance; + int testLength = widthTolerance / ( 2 * sin( DEG2RAD( angleTolerance / 2 ) ) ); + + for( PCB_LAYER_ID layer : LSET::AllCuMask().Seq() ) + { + if( m_drcEngine->IsErrorLimitExceeded( DRCE_COPPER_SLIVER ) ) + continue; + + int itemCount = 0; + int itemIdx = 0; + SHAPE_POLY_SET poly; + + + forEachGeometryItem( s_allBasicItems, LSET().set( layer ), + [&]( BOARD_ITEM* item ) -> bool + { + ++itemCount; + return true; + } ); + + forEachGeometryItem( s_allBasicItems, LSET().set( layer ), + [&]( BOARD_ITEM* item ) -> bool + { + if( !reportProgress( itemIdx++, itemCount, delta ) ) + return false; + + if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T ) + { + ZONE* zone = static_cast( item ); + + poly.BooleanAdd( zone->GetFilledPolysList( layer ), + SHAPE_POLY_SET::PM_FAST ); + } + else + { + item->TransformShapeWithClearanceToPolygon( poly, layer, 0, ARC_LOW_DEF, + ERROR_OUTSIDE ); + } + + return true; + } ); + + poly.Simplify( SHAPE_POLY_SET::PM_FAST ); + + // Sharpen corners + poly.Deflate( widthTolerance / 2, ARC_LOW_DEF, SHAPE_POLY_SET::CHAMFER_ACUTE_CORNERS ); + + for( int jj = 0; jj < poly.OutlineCount(); ++jj ) + { + const std::vector& pts = poly.Outline( jj ).CPoints(); + int ptCount = pts.size(); + + for( int kk = 0; kk < ptCount; ++kk ) + { + VECTOR2I pt = pts[ kk ]; + VECTOR2I ptBefore = pts[ ( ptCount + kk - 1 ) % ptCount ]; + VECTOR2I ptAfter = pts[ ( kk + 1 ) % ptCount ]; + + VECTOR2I vBefore = ( ptBefore - pt ); + VECTOR2I vAfter = ( ptAfter - pt ); + VECTOR2I vIncluded = vBefore.Resize( testLength ) - vAfter.Resize( testLength ); + + if( vIncluded.SquaredEuclideanNorm() < SEG::Square( widthTolerance ) ) + { + std::shared_ptr drce = DRC_ITEM::Create( DRCE_COPPER_SLIVER ); + drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + layerDesc( layer ) ); + reportViolation( drce, (wxPoint) pt ); + } + } + } + } + + return true; +} + + +namespace detail +{ +static DRC_REGISTER_TEST_PROVIDER dummy; +}