/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 CERN * @author Jon Evans * * 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 3 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, see . */ #include #include #include #include #include #include const int ercSettingsSchemaVersion = 0; #define OK PIN_ERROR::OK #define ERR PIN_ERROR::PP_ERROR #define WAR PIN_ERROR::WARNING /** * Default Look up table which gives the ERC error level for a pair of connected pins */ PIN_ERROR ERC_SETTINGS::m_defaultPinMap[ELECTRICAL_PINTYPES_TOTAL][ELECTRICAL_PINTYPES_TOTAL] = { /* I, O, Bi, 3S, Pas, UnS, PwrI, PwrO, OC, OE, NC */ /* I */ { OK, OK, OK, OK, OK, WAR, OK, OK, OK, OK, ERR }, /* O */ { OK, ERR, OK, WAR, OK, WAR, OK, ERR, ERR, ERR, ERR }, /* Bi*/ { OK, OK, OK, OK, OK, WAR, OK, WAR, OK, WAR, ERR }, /* 3S*/ { OK, WAR, OK, OK, OK, WAR, WAR, ERR, WAR, WAR, ERR }, /*Pas*/ { OK, OK, OK, OK, OK, WAR, OK, OK, OK, OK, ERR }, /*UnS */ { WAR, WAR, WAR, WAR, WAR, WAR, WAR, WAR, WAR, WAR, ERR }, /*PwrI*/ { OK, OK, OK, WAR, OK, WAR, OK, OK, OK, OK, ERR }, /*PwrO*/ { OK, ERR, WAR, ERR, OK, WAR, OK, ERR, ERR, ERR, ERR }, /* OC */ { OK, ERR, OK, WAR, OK, WAR, OK, ERR, OK, OK, ERR }, /* OE */ { OK, ERR, WAR, WAR, OK, WAR, OK, ERR, OK, OK, ERR }, /* NC */ { ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR } }; /** * Look up table which gives the minimal drive for a pair of connected pins on * a net. *

* The initial state of a net is NOC (Net with No Connection). It can be updated to * NPI (Pin Isolated), NET_NC (Net with a no connect symbol), NOD (Not Driven) or DRV * (DRIven). It can be updated to NET_NC with no error only if there is only one pin * in net. Nets are OK when their final state is NET_NC or DRV. Nets with the state * NOD have no valid source signal. */ int ERC_SETTINGS::m_PinMinDrive[ELECTRICAL_PINTYPES_TOTAL][ELECTRICAL_PINTYPES_TOTAL] = { /* In Out, Bi, 3S, Pas, UnS, PwrI,PwrO,OC, OE, NC */ /* In*/ { NOD, DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, NPI }, /*Out*/ { DRV, DRV, DRV, DRV, DRV, DRV, DRV, DRV, DRV, DRV, NPI }, /* Bi*/ { DRV, DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, NPI }, /* 3S*/ { DRV, DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, NPI }, /*Pas*/ { DRV, DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, NPI }, /*UnS*/ { DRV, DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, NPI }, /*PwrI*/ { NOD, DRV, NOD, NOD, NOD, NOD, NOD, DRV, NOD, NOD, NPI }, /*PwrO*/ { DRV, DRV, DRV, DRV, DRV, DRV, DRV, DRV, DRV, DRV, NPI }, /* OC*/ { DRV, DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, NPI }, /* OE*/ { DRV, DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, NPI }, /* NC*/ { NPI, NPI, NPI, NPI, NPI, NPI, NPI, NPI, NPI, NPI, NPI } }; ERC_SETTINGS::ERC_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ) : NESTED_SETTINGS( "erc", ercSettingsSchemaVersion, aParent, aPath ) { ResetPinMap(); for( int i = ERCE_FIRST; i <= ERCE_LAST; ++i ) m_Severities[ i ] = RPT_SEVERITY_ERROR; m_Severities[ERCE_UNSPECIFIED] = RPT_SEVERITY_UNDEFINED; m_Severities[ERCE_PIN_TO_PIN_WARNING] = RPT_SEVERITY_WARNING; m_params.emplace_back( new PARAM_LAMBDA( "rule_severities", [&]() -> nlohmann::json { nlohmann::json ret = {}; for( const RC_ITEM& item : ERC_ITEM::GetItemsWithSeverities() ) { int code = item.GetErrorCode(); if( !m_Severities.count( code ) ) continue; wxString name = item.GetSettingsKey(); ret[std::string( name.ToUTF8() )] = SeverityToString( static_cast( m_Severities[code] ) ); } return ret; }, [&]( const nlohmann::json& aJson ) { if( !aJson.is_object() ) return; for( const RC_ITEM& item : ERC_ITEM::GetItemsWithSeverities() ) { int code = item.GetErrorCode(); wxString name = item.GetSettingsKey(); std::string key( name.ToUTF8() ); if( aJson.contains( key ) ) m_Severities[code] = SeverityFromString( aJson[key] ); } }, {} ) ); m_params.emplace_back( new PARAM_LAMBDA( "pin_map", [&]() -> nlohmann::json { nlohmann::json ret = nlohmann::json::array(); for( int i = 0; i < ELECTRICAL_PINTYPES_TOTAL; i++ ) { nlohmann::json inner = nlohmann::json::array(); for( int j = 0; j < ELECTRICAL_PINTYPES_TOTAL; j++ ) inner.push_back( static_cast( GetPinMapValue( i, j ) ) ); ret.push_back( inner ); } return ret; }, [&]( const nlohmann::json& aJson ) { if( !aJson.is_array() || aJson.size() != ELECTRICAL_PINTYPES_TOTAL ) return; for( size_t i = 0; i < ELECTRICAL_PINTYPES_TOTAL; i++ ) { if( i > aJson.size() - 1 ) break; nlohmann::json inner = aJson[i]; if( !inner.is_array() || inner.size() != ELECTRICAL_PINTYPES_TOTAL ) return; for( size_t j = 0; j < ELECTRICAL_PINTYPES_TOTAL; j++ ) { if( inner[j].is_number_integer() ) { int val = inner[j].get(); if( val >= 0 && val <= static_cast( PIN_ERROR::UNCONNECTED ) ) SetPinMapValue( i, j, static_cast( val ) ); } } } }, {} ) ); } ERC_SETTINGS::~ERC_SETTINGS() { if( m_parent ) { m_parent->ReleaseNestedSettings( this ); m_parent = nullptr; } } int ERC_SETTINGS::GetSeverity( int aErrorCode ) const { // Special-case pin-to-pin errors: // Ignore-or-not is controlled by ERCE_PIN_TO_PIN_WARNING (for both) // Warning-or-error is controlled by which errorCode it is if( aErrorCode == ERCE_PIN_TO_PIN_ERROR ) { wxASSERT( m_Severities.count( ERCE_PIN_TO_PIN_WARNING ) ); if( m_Severities.at( ERCE_PIN_TO_PIN_WARNING ) == RPT_SEVERITY_IGNORE ) return RPT_SEVERITY_IGNORE; else return RPT_SEVERITY_ERROR; } else if( aErrorCode == ERCE_PIN_TO_PIN_WARNING ) { wxASSERT( m_Severities.count( ERCE_PIN_TO_PIN_WARNING ) ); if( m_Severities.at( ERCE_PIN_TO_PIN_WARNING ) == RPT_SEVERITY_IGNORE ) return RPT_SEVERITY_IGNORE; else return RPT_SEVERITY_WARNING; } wxCHECK_MSG( m_Severities.count( aErrorCode ), RPT_SEVERITY_IGNORE, "Missing severity from map in ERC_SETTINGS!" ); return m_Severities.at( aErrorCode ); } void ERC_SETTINGS::SetSeverity( int aErrorCode, int aSeverity ) { m_Severities[ aErrorCode ] = aSeverity; } void ERC_SETTINGS::ResetPinMap() { memcpy( m_PinMap, m_defaultPinMap, sizeof( m_PinMap ) ); } void SHEETLIST_ERC_ITEMS_PROVIDER::SetSeverities( int aSeverities ) { m_severities = aSeverities; m_filteredMarkers.clear(); SCH_SHEET_LIST sheetList = m_schematic->GetSheets(); ERC_SETTINGS& settings = m_schematic->ErcSettings(); for( unsigned i = 0; i < sheetList.size(); i++ ) { for( SCH_ITEM* aItem : sheetList[i].LastScreen()->Items().OfType( SCH_MARKER_T ) ) { SCH_MARKER* marker = static_cast( aItem ); int markerSeverity; if( marker->GetMarkerType() != MARKER_BASE::MARKER_ERC ) continue; if( marker->IsExcluded() ) markerSeverity = RPT_SEVERITY_EXCLUSION; else markerSeverity = settings.GetSeverity( marker->GetRCItem()->GetErrorCode() ); if( markerSeverity & m_severities ) m_filteredMarkers.push_back( marker ); } } } int SHEETLIST_ERC_ITEMS_PROVIDER::GetCount( int aSeverity ) { if( aSeverity < 0 ) return m_filteredMarkers.size(); int count = 0; SCH_SHEET_LIST sheetList = m_schematic->GetSheets(); ERC_SETTINGS& settings = m_schematic->ErcSettings(); for( unsigned i = 0; i < sheetList.size(); i++ ) { for( SCH_ITEM* aItem : sheetList[i].LastScreen()->Items().OfType( SCH_MARKER_T ) ) { SCH_MARKER* marker = static_cast( aItem ); int markerSeverity; if( marker->GetMarkerType() != MARKER_BASE::MARKER_ERC ) continue; if( marker->IsExcluded() ) markerSeverity = RPT_SEVERITY_EXCLUSION; else markerSeverity = settings.GetSeverity( marker->GetRCItem()->GetErrorCode() ); if( markerSeverity == aSeverity ) count++; } } return count; } ERC_ITEM* SHEETLIST_ERC_ITEMS_PROVIDER::GetItem( int aIndex ) { SCH_MARKER* marker = m_filteredMarkers[ aIndex ]; return marker ? static_cast( marker->GetRCItem() ) : nullptr; } void SHEETLIST_ERC_ITEMS_PROVIDER::DeleteItem( int aIndex, bool aDeep ) { SCH_MARKER* marker = m_filteredMarkers[ aIndex ]; m_filteredMarkers.erase( m_filteredMarkers.begin() + aIndex ); if( aDeep ) { SCH_SCREENS screens( m_schematic->Root() ); screens.DeleteMarker( marker ); } } void SHEETLIST_ERC_ITEMS_PROVIDER::DeleteAllItems( bool aIncludeExclusions, bool aDeep ) { // Filtered list was already handled through DeleteItem() by the tree control if( aDeep ) { SCH_SCREENS screens( m_schematic->Root() ); screens.DeleteAllMarkers( MARKER_BASE::MARKER_ERC, aIncludeExclusions ); } }