You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

390 lines
13 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020 CERN
  5. * @author Jon Evans <jon@craftyjon.com>
  6. *
  7. * This program is free software: you can redistribute it and/or modify it
  8. * under the terms of the GNU General Public License as published by the
  9. * Free Software Foundation, either version 3 of the License, or (at your
  10. * option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include <erc_item.h>
  21. #include <erc_settings.h>
  22. #include <schematic.h>
  23. #include <sch_marker.h>
  24. #include <sch_screen.h>
  25. #include <settings/json_settings_internals.h>
  26. #include <settings/parameters.h>
  27. const int ercSettingsSchemaVersion = 0;
  28. #define OK PIN_ERROR::OK
  29. #define ERR PIN_ERROR::PP_ERROR
  30. #define WAR PIN_ERROR::WARNING
  31. /**
  32. * Default Look up table which gives the ERC error level for a pair of connected pins
  33. */
  34. PIN_ERROR ERC_SETTINGS::m_defaultPinMap[ELECTRICAL_PINTYPES_TOTAL][ELECTRICAL_PINTYPES_TOTAL] =
  35. {
  36. /* I, O, Bi, 3S, Pas, NIC, UnS, PwrI, PwrO, OC, OE, NC */
  37. /* I */ { OK, OK, OK, OK, OK, OK, WAR, OK, OK, OK, OK, ERR },
  38. /* O */ { OK, ERR, OK, WAR, OK, OK, WAR, OK, ERR, ERR, ERR, ERR },
  39. /* Bi */ { OK, OK, OK, OK, OK, OK, WAR, OK, WAR, OK, WAR, ERR },
  40. /* 3S */ { OK, WAR, OK, OK, OK, OK, WAR, WAR, ERR, WAR, WAR, ERR },
  41. /*Pas */ { OK, OK, OK, OK, OK, OK, WAR, OK, OK, OK, OK, ERR },
  42. /*NIC */ { OK, OK, OK, OK, OK, OK, OK, OK, OK, OK, OK, ERR },
  43. /*UnS */ { WAR, WAR, WAR, WAR, WAR, OK, WAR, WAR, WAR, WAR, WAR, ERR },
  44. /*PwrI*/ { OK, OK, OK, WAR, OK, OK, WAR, OK, OK, OK, OK, ERR },
  45. /*PwrO*/ { OK, ERR, WAR, ERR, OK, OK, WAR, OK, ERR, ERR, ERR, ERR },
  46. /* OC */ { OK, ERR, OK, WAR, OK, OK, WAR, OK, ERR, OK, OK, ERR },
  47. /* OE */ { OK, ERR, WAR, WAR, OK, OK, WAR, OK, ERR, OK, OK, ERR },
  48. /* NC */ { ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR }
  49. };
  50. /**
  51. * Look up table which gives the minimal drive for a pair of connected pins on
  52. * a net.
  53. * <p>
  54. * The initial state of a net is NOC (Net with No Connection). It can be updated to
  55. * NPI (Pin Isolated), NET_NC (Net with a no connect symbol), NOD (Not Driven) or DRV
  56. * (DRIven). It can be updated to NET_NC with no error only if there is only one pin
  57. * in net. Nets are OK when their final state is NET_NC or DRV. Nets with the state
  58. * NOD have no valid source signal.
  59. */
  60. int ERC_SETTINGS::m_PinMinDrive[ELECTRICAL_PINTYPES_TOTAL][ELECTRICAL_PINTYPES_TOTAL] =
  61. {
  62. /* I, O, Bi, 3S, Pas, NIC, UnS, PwrI, PwrO, OC, OE, NC */
  63. /* I */ { NOD, DRV, DRV, DRV, DRV, NOD, DRV, NOD, DRV, DRV, DRV, NPI },
  64. /* O */ { DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, DRV, DRV, NPI },
  65. /* Bi */ { DRV, DRV, DRV, DRV, DRV, NOD, DRV, NOD, DRV, DRV, DRV, NPI },
  66. /* 3S */ { DRV, DRV, DRV, DRV, DRV, NOD, DRV, NOD, DRV, DRV, DRV, NPI },
  67. /*Pas */ { DRV, DRV, DRV, DRV, DRV, NOD, DRV, NOD, DRV, DRV, DRV, NPI },
  68. /*NIC */ { NOD, NOD, NOD, NOD, NOD, NOD, NOD, NOD, NOD, NOD, NOD, NPI },
  69. /*UnS */ { DRV, DRV, DRV, DRV, DRV, NOD, DRV, NOD, DRV, DRV, DRV, NPI },
  70. /*PwrI*/ { NOD, DRV, NOD, NOD, NOD, NOD, NOD, NOD, DRV, NOD, NOD, NPI },
  71. /*PwrO*/ { DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, DRV, DRV, NPI },
  72. /* OC */ { DRV, DRV, DRV, DRV, DRV, NOD, DRV, NOD, DRV, DRV, DRV, NPI },
  73. /* OE */ { DRV, DRV, DRV, DRV, DRV, NOD, DRV, NOD, DRV, DRV, DRV, NPI },
  74. /* NC */ { NPI, NPI, NPI, NPI, NPI, NPI, NPI, NPI, NPI, NPI, NPI, NPI }
  75. };
  76. ERC_SETTINGS::ERC_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ) :
  77. NESTED_SETTINGS( "erc", ercSettingsSchemaVersion, aParent, aPath )
  78. {
  79. ResetPinMap();
  80. for( int i = ERCE_FIRST; i <= ERCE_LAST; ++i )
  81. m_Severities[ i ] = RPT_SEVERITY_ERROR;
  82. // Error is the default setting so set non-error priorities here.
  83. m_Severities[ERCE_UNSPECIFIED] = RPT_SEVERITY_UNDEFINED;
  84. m_Severities[ERCE_PIN_TO_PIN_WARNING] = RPT_SEVERITY_WARNING;
  85. m_Severities[ERCE_SIMILAR_LABELS] = RPT_SEVERITY_WARNING;
  86. m_Severities[ERCE_GLOBLABEL] = RPT_SEVERITY_WARNING;
  87. m_Severities[ERCE_DRIVER_CONFLICT] = RPT_SEVERITY_WARNING;
  88. m_Severities[ERCE_BUS_ENTRY_CONFLICT] = RPT_SEVERITY_WARNING;
  89. m_Severities[ERCE_LIB_SYMBOL_ISSUES] = RPT_SEVERITY_WARNING;
  90. m_Severities[ERCE_NOCONNECT_CONNECTED] = RPT_SEVERITY_WARNING;
  91. m_Severities[ERCE_NOCONNECT_NOT_CONNECTED] = RPT_SEVERITY_WARNING;
  92. m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "rule_severities",
  93. [&]() -> nlohmann::json
  94. {
  95. nlohmann::json ret = {};
  96. for( const RC_ITEM& item : ERC_ITEM::GetItemsWithSeverities() )
  97. {
  98. wxString name = item.GetSettingsKey();
  99. int code = item.GetErrorCode();
  100. if( name.IsEmpty() || m_Severities.count( code ) == 0 )
  101. continue;
  102. ret[std::string( name.ToUTF8() )] = SeverityToString( m_Severities[code] );
  103. }
  104. return ret;
  105. },
  106. [&]( const nlohmann::json& aJson )
  107. {
  108. if( !aJson.is_object() )
  109. return;
  110. for( const RC_ITEM& item : ERC_ITEM::GetItemsWithSeverities() )
  111. {
  112. int code = item.GetErrorCode();
  113. wxString name = item.GetSettingsKey();
  114. std::string key( name.ToUTF8() );
  115. if( aJson.contains( key ) )
  116. m_Severities[code] = SeverityFromString( aJson[key] );
  117. }
  118. },
  119. {} ) );
  120. m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "erc_exclusions",
  121. [&]() -> nlohmann::json
  122. {
  123. nlohmann::json js = nlohmann::json::array();
  124. for( const auto& entry : m_ErcExclusions )
  125. js.push_back( entry );
  126. return js;
  127. },
  128. [&]( const nlohmann::json& aObj )
  129. {
  130. m_ErcExclusions.clear();
  131. if( !aObj.is_array() )
  132. return;
  133. for( const nlohmann::json& entry : aObj )
  134. {
  135. if( entry.empty() )
  136. continue;
  137. m_ErcExclusions.insert( entry.get<wxString>() );
  138. }
  139. },
  140. {} ) );
  141. m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "pin_map",
  142. [&]() -> nlohmann::json
  143. {
  144. nlohmann::json ret = nlohmann::json::array();
  145. for( int i = 0; i < ELECTRICAL_PINTYPES_TOTAL; i++ )
  146. {
  147. nlohmann::json inner = nlohmann::json::array();
  148. for( int j = 0; j < ELECTRICAL_PINTYPES_TOTAL; j++ )
  149. inner.push_back( static_cast<int>( GetPinMapValue( i, j ) ) );
  150. ret.push_back( inner );
  151. }
  152. return ret;
  153. },
  154. [&]( const nlohmann::json& aJson )
  155. {
  156. if( !aJson.is_array() || aJson.size() != ELECTRICAL_PINTYPES_TOTAL )
  157. return;
  158. for( size_t i = 0; i < ELECTRICAL_PINTYPES_TOTAL; i++ )
  159. {
  160. if( i > aJson.size() - 1 )
  161. break;
  162. nlohmann::json inner = aJson[i];
  163. if( !inner.is_array() || inner.size() != ELECTRICAL_PINTYPES_TOTAL )
  164. return;
  165. for( size_t j = 0; j < ELECTRICAL_PINTYPES_TOTAL; j++ )
  166. {
  167. if( inner[j].is_number_integer() )
  168. {
  169. int val = inner[j].get<int>();
  170. if( val >= 0 && val <= static_cast<int>( PIN_ERROR::UNCONNECTED ) )
  171. SetPinMapValue( i, j, static_cast<PIN_ERROR>( val ) );
  172. }
  173. }
  174. }
  175. },
  176. {} ) );
  177. }
  178. ERC_SETTINGS::~ERC_SETTINGS()
  179. {
  180. if( m_parent )
  181. {
  182. m_parent->ReleaseNestedSettings( this );
  183. m_parent = nullptr;
  184. }
  185. }
  186. SEVERITY ERC_SETTINGS::GetSeverity( int aErrorCode ) const
  187. {
  188. // Special-case pin-to-pin errors:
  189. // Ignore-or-not is controlled by ERCE_PIN_TO_PIN_WARNING (for both)
  190. // Warning-or-error is controlled by which errorCode it is
  191. if( aErrorCode == ERCE_PIN_TO_PIN_ERROR )
  192. {
  193. wxASSERT( m_Severities.count( ERCE_PIN_TO_PIN_WARNING ) );
  194. if( m_Severities.at( ERCE_PIN_TO_PIN_WARNING ) == RPT_SEVERITY_IGNORE )
  195. return RPT_SEVERITY_IGNORE;
  196. else
  197. return RPT_SEVERITY_ERROR;
  198. }
  199. else if( aErrorCode == ERCE_PIN_TO_PIN_WARNING )
  200. {
  201. wxASSERT( m_Severities.count( ERCE_PIN_TO_PIN_WARNING ) );
  202. if( m_Severities.at( ERCE_PIN_TO_PIN_WARNING ) == RPT_SEVERITY_IGNORE )
  203. return RPT_SEVERITY_IGNORE;
  204. else
  205. return RPT_SEVERITY_WARNING;
  206. }
  207. wxCHECK_MSG( m_Severities.count( aErrorCode ), RPT_SEVERITY_IGNORE,
  208. "Missing severity from map in ERC_SETTINGS!" );
  209. return m_Severities.at( aErrorCode );
  210. }
  211. void ERC_SETTINGS::SetSeverity( int aErrorCode, SEVERITY aSeverity )
  212. {
  213. m_Severities[ aErrorCode ] = aSeverity;
  214. }
  215. void ERC_SETTINGS::ResetPinMap()
  216. {
  217. memcpy( m_PinMap, m_defaultPinMap, sizeof( m_PinMap ) );
  218. }
  219. void SHEETLIST_ERC_ITEMS_PROVIDER::visitMarkers( std::function<void( SCH_MARKER* )> aVisitor ) const
  220. {
  221. SCH_SHEET_LIST sheetList = m_schematic->GetSheets();
  222. std::set<SCH_SCREEN*> seenScreens;
  223. for( unsigned i = 0; i < sheetList.size(); i++ )
  224. {
  225. bool firstTime = seenScreens.count( sheetList[i].LastScreen() ) == 0;
  226. if( firstTime )
  227. seenScreens.insert( sheetList[i].LastScreen() );
  228. for( SCH_ITEM* item : sheetList[i].LastScreen()->Items().OfType( SCH_MARKER_T ) )
  229. {
  230. SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
  231. if( marker->GetMarkerType() != MARKER_BASE::MARKER_ERC )
  232. continue;
  233. // Don't show non-specific markers more than once
  234. if( !firstTime &&
  235. !static_cast<const ERC_ITEM*>( marker->GetRCItem().get() )->IsSheetSpecific() )
  236. {
  237. continue;
  238. }
  239. aVisitor( marker );
  240. }
  241. }
  242. }
  243. void SHEETLIST_ERC_ITEMS_PROVIDER::SetSeverities( int aSeverities )
  244. {
  245. m_severities = aSeverities;
  246. m_filteredMarkers.clear();
  247. ERC_SETTINGS& settings = m_schematic->ErcSettings();
  248. visitMarkers(
  249. [&]( SCH_MARKER* aMarker )
  250. {
  251. SEVERITY markerSeverity;
  252. if( aMarker->IsExcluded() )
  253. markerSeverity = RPT_SEVERITY_EXCLUSION;
  254. else
  255. markerSeverity = settings.GetSeverity( aMarker->GetRCItem()->GetErrorCode() );
  256. if( markerSeverity & m_severities )
  257. m_filteredMarkers.push_back( aMarker );
  258. } );
  259. }
  260. int SHEETLIST_ERC_ITEMS_PROVIDER::GetCount( int aSeverity ) const
  261. {
  262. if( aSeverity < 0 )
  263. return m_filteredMarkers.size();
  264. int count = 0;
  265. const ERC_SETTINGS& settings = m_schematic->ErcSettings();
  266. visitMarkers(
  267. [&]( SCH_MARKER* aMarker )
  268. {
  269. SEVERITY markerSeverity;
  270. if( aMarker->IsExcluded() )
  271. markerSeverity = RPT_SEVERITY_EXCLUSION;
  272. else
  273. markerSeverity = settings.GetSeverity( aMarker->GetRCItem()->GetErrorCode() );
  274. if( markerSeverity == aSeverity )
  275. count++;
  276. } );
  277. return count;
  278. }
  279. std::shared_ptr<ERC_ITEM> SHEETLIST_ERC_ITEMS_PROVIDER::GetERCItem( int aIndex ) const
  280. {
  281. SCH_MARKER* marker = m_filteredMarkers[ aIndex ];
  282. return marker ? std::static_pointer_cast<ERC_ITEM>( marker->GetRCItem() ) : nullptr;
  283. }
  284. std::shared_ptr<RC_ITEM> SHEETLIST_ERC_ITEMS_PROVIDER::GetItem( int aIndex ) const
  285. {
  286. return GetERCItem( aIndex );
  287. }
  288. void SHEETLIST_ERC_ITEMS_PROVIDER::DeleteItem( int aIndex, bool aDeep )
  289. {
  290. SCH_MARKER* marker = m_filteredMarkers[ aIndex ];
  291. m_filteredMarkers.erase( m_filteredMarkers.begin() + aIndex );
  292. if( aDeep )
  293. {
  294. SCH_SCREENS screens( m_schematic->Root() );
  295. screens.DeleteMarker( marker );
  296. }
  297. }
  298. void SHEETLIST_ERC_ITEMS_PROVIDER::DeleteAllItems( bool aIncludeExclusions, bool aDeep )
  299. {
  300. // Filtered list was already handled through DeleteItem() by the tree control
  301. if( aDeep )
  302. {
  303. SCH_SCREENS screens( m_schematic->Root() );
  304. screens.DeleteAllMarkers( MARKER_BASE::MARKER_ERC, aIncludeExclusions );
  305. }
  306. }