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.

896 lines
31 KiB

18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2011-2016 Wayne Stambaugh <stambaughw@verizon.net>
  6. * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. /**
  26. * @file erc.cpp
  27. * @brief Electrical Rules Check implementation.
  28. */
  29. #include <fctsys.h>
  30. #include <class_drawpanel.h>
  31. #include <kicad_string.h>
  32. #include <sch_edit_frame.h>
  33. #include <netlist.h>
  34. #include <netlist_object.h>
  35. #include <lib_pin.h>
  36. #include <erc.h>
  37. #include <sch_marker.h>
  38. #include <sch_sheet.h>
  39. #include <sch_reference_list.h>
  40. #include <wx/ffile.h>
  41. /* ERC tests :
  42. * 1 - conflicts between connected pins ( example: 2 connected outputs )
  43. * 2 - minimal connections requirements ( 1 input *must* be connected to an
  44. * output, or a passive pin )
  45. */
  46. /*
  47. * Electrical type of pins:
  48. * PIN_INPUT = usual pin input: must be connected
  49. * PIN_OUTPUT = usual output
  50. * PIN_BIDI = input or output (like port for a microprocessor)
  51. * PIN_TRISTATE = tris state bus pin
  52. * PIN_PASSIVE = pin for passive components: must be connected, and can be
  53. * connected to any pin
  54. * PIN_UNSPECIFIED = unknown electrical properties: creates always a warning
  55. * when connected
  56. * PIN_POWER_IN = power input (GND, VCC for ICs). Must be connected to a power
  57. * output.
  58. * PIN_POWER_OUT = output of a regulator: intended to be connected to power
  59. * input pins
  60. * PIN_OPENCOLLECTOR = pin type open collector
  61. * PIN_OPENEMITTER = pin type open emitter
  62. * PIN_NC = not connected (must be left open)
  63. *
  64. * Minimal requirements:
  65. * All pins *must* be connected (except PIN_NC).
  66. * When a pin is not connected in schematic, the user must place a "non
  67. * connected" symbol to this pin.
  68. * This ensures a forgotten connection will be detected.
  69. */
  70. /* Messages for conflicts :
  71. * PIN_INPUT, PIN_OUTPUT, PIN_BIDI, PIN_TRISTATE, PIN_PASSIVE,
  72. * PIN_UNSPECIFIED, PIN_POWER_IN, PIN_POWER_OUT, PIN_OPENCOLLECTOR,
  73. * PIN_OPENEMITTER, PIN_NC
  74. * These messages are used to show the ERC matrix in ERC dialog
  75. */
  76. // Messages for matrix rows:
  77. const wxString CommentERC_H[] =
  78. {
  79. _( "Input Pin" ),
  80. _( "Output Pin" ),
  81. _( "Bidirectional Pin" ),
  82. _( "Tri-State Pin" ),
  83. _( "Passive Pin" ),
  84. _( "Unspecified Pin" ),
  85. _( "Power Input Pin" ),
  86. _( "Power Output Pin" ),
  87. _( "Open Collector" ),
  88. _( "Open Emitter" ),
  89. _( "No Connection" )
  90. };
  91. // Messages for matrix columns
  92. const wxString CommentERC_V[] =
  93. {
  94. _( "Input Pin" ),
  95. _( "Output Pin" ),
  96. _( "Bidirectional Pin" ),
  97. _( "Tri-State Pin" ),
  98. _( "Passive Pin" ),
  99. _( "Unspecified Pin" ),
  100. _( "Power Input Pin" ),
  101. _( "Power Output Pin" ),
  102. _( "Open Collector" ),
  103. _( "Open Emitter" ),
  104. _( "No Connection" )
  105. };
  106. /* Look up table which gives the diag for a pair of connected pins
  107. * Can be modified by ERC options.
  108. * at start up: must be loaded by DefaultDiagErc
  109. * Can be modified in dialog ERC
  110. */
  111. int DiagErc[PINTYPE_COUNT][PINTYPE_COUNT];
  112. /**
  113. * Default Look up table which gives the ERC error level for a pair of connected pins
  114. * Same as DiagErc, but cannot be modified.
  115. * Used to init or reset DiagErc
  116. * note also, to avoid inconsistancy:
  117. * DefaultDiagErc[i][j] = DefaultDiagErc[j][i]
  118. */
  119. int DefaultDiagErc[PINTYPE_COUNT][PINTYPE_COUNT] =
  120. {
  121. /* I, O, Bi, 3S, Pas, UnS, PwrI, PwrO, OC, OE, NC */
  122. /* I */ { OK, OK, OK, OK, OK, WAR, OK, OK, OK, OK, ERR },
  123. /* O */ { OK, ERR, OK, WAR, OK, WAR, OK, ERR, ERR, ERR, ERR },
  124. /* Bi*/ { OK, OK, OK, OK, OK, WAR, OK, WAR, OK, WAR, ERR },
  125. /* 3S*/ { OK, WAR, OK, OK, OK, WAR, WAR, ERR, WAR, WAR, ERR },
  126. /*Pas*/ { OK, OK, OK, OK, OK, WAR, OK, OK, OK, OK, ERR },
  127. /*UnS */ { WAR, WAR, WAR, WAR, WAR, WAR, WAR, WAR, WAR, WAR, ERR },
  128. /*PwrI*/ { OK, OK, OK, WAR, OK, WAR, OK, OK, OK, OK, ERR },
  129. /*PwrO*/ { OK, ERR, WAR, ERR, OK, WAR, OK, ERR, ERR, ERR, ERR },
  130. /* OC */ { OK, ERR, OK, WAR, OK, WAR, OK, ERR, OK, OK, ERR },
  131. /* OE */ { OK, ERR, WAR, WAR, OK, WAR, OK, ERR, OK, OK, ERR },
  132. /* NC */ { ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR }
  133. };
  134. /**
  135. * Look up table which gives the minimal drive for a pair of connected pins on
  136. * a net.
  137. * <p>
  138. * The initial state of a net is NOC (Net with No Connection). It can be updated to
  139. * NPI (Pin Isolated), NET_NC (Net with a no connect symbol), NOD (Not Driven) or DRV
  140. * (DRIven). It can be updated to NET_NC with no error only if there is only one pin
  141. * in net. Nets are OK when their final state is NET_NC or DRV. Nets with the state
  142. * NOD have no valid source signal.
  143. */
  144. static int MinimalReq[PINTYPE_COUNT][PINTYPE_COUNT] =
  145. {
  146. /* In Out, Bi, 3S, Pas, UnS, PwrI,PwrO,OC, OE, NC */
  147. /* In*/ { NOD, DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, NPI },
  148. /*Out*/ { DRV, DRV, DRV, DRV, DRV, DRV, DRV, DRV, DRV, DRV, NPI },
  149. /* Bi*/ { DRV, DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, NPI },
  150. /* 3S*/ { DRV, DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, NPI },
  151. /*Pas*/ { DRV, DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, NPI },
  152. /*UnS*/ { DRV, DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, NPI },
  153. /*PwrI*/ { NOD, DRV, NOD, NOD, NOD, NOD, NOD, DRV, NOD, NOD, NPI },
  154. /*PwrO*/ { DRV, DRV, DRV, DRV, DRV, DRV, DRV, DRV, DRV, DRV, NPI },
  155. /* OC*/ { DRV, DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, NPI },
  156. /* OE*/ { DRV, DRV, DRV, DRV, DRV, DRV, NOD, DRV, DRV, DRV, NPI },
  157. /* NC*/ { NPI, NPI, NPI, NPI, NPI, NPI, NPI, NPI, NPI, NPI, NPI }
  158. };
  159. int TestDuplicateSheetNames( bool aCreateMarker )
  160. {
  161. SCH_SCREEN* screen;
  162. SCH_ITEM* item;
  163. SCH_ITEM* test_item;
  164. int err_count = 0;
  165. SCH_SCREENS screenList; // Created the list of screen
  166. for( screen = screenList.GetFirst(); screen != NULL; screen = screenList.GetNext() )
  167. {
  168. for( item = screen->GetDrawItems(); item != NULL; item = item->Next() )
  169. {
  170. // search for a sheet;
  171. if( item->Type() != SCH_SHEET_T )
  172. continue;
  173. for( test_item = item->Next(); test_item != NULL; test_item = test_item->Next() )
  174. {
  175. if( test_item->Type() != SCH_SHEET_T )
  176. continue;
  177. // We have found a second sheet: compare names
  178. // we are using case insensitive comparison to avoid mistakes between
  179. // similar names like Mysheet and mysheet
  180. if( ( (SCH_SHEET*) item )->GetName().CmpNoCase(
  181. ( ( SCH_SHEET* ) test_item )->GetName() ) == 0 )
  182. {
  183. if( aCreateMarker )
  184. {
  185. /* Create a new marker type ERC error*/
  186. SCH_MARKER* marker = new SCH_MARKER();
  187. marker->SetTimeStamp( GetNewTimeStamp() );
  188. marker->SetData( ERCE_DUPLICATE_SHEET_NAME,
  189. ( (SCH_SHEET*) test_item )->GetPosition(),
  190. _( "Duplicate sheet name" ),
  191. ( (SCH_SHEET*) test_item )->GetPosition() );
  192. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  193. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_ERROR );
  194. screen->Append( marker );
  195. }
  196. err_count++;
  197. }
  198. }
  199. }
  200. }
  201. return err_count;
  202. }
  203. int TestMultiunitFootprints( SCH_SHEET_LIST& aSheetList )
  204. {
  205. int errors = 0;
  206. std::map<wxString, LIB_ID> footprints;
  207. SCH_MULTI_UNIT_REFERENCE_MAP refMap;
  208. aSheetList.GetMultiUnitComponents( refMap, true );
  209. for( auto& component : refMap )
  210. {
  211. auto& refList = component.second;
  212. if( refList.GetCount() == 0 )
  213. {
  214. wxFAIL; // it should not happen
  215. continue;
  216. }
  217. // Reference footprint
  218. wxString fp;
  219. wxString unitName;
  220. for( unsigned i = 0; i < component.second.GetCount(); ++i )
  221. {
  222. SCH_COMPONENT* cmp = refList.GetItem( i ).GetComp();
  223. SCH_SHEET_PATH sheetPath = refList.GetItem( i ).GetSheetPath();
  224. fp = cmp->GetField( FOOTPRINT )->GetText();
  225. if( !fp.IsEmpty() )
  226. {
  227. unitName = cmp->GetRef( &sheetPath )
  228. + LIB_PART::SubReference( cmp->GetUnit(), false );
  229. break;
  230. }
  231. }
  232. for( unsigned i = 0; i < component.second.GetCount(); ++i )
  233. {
  234. SCH_REFERENCE& ref = refList.GetItem( i );
  235. SCH_COMPONENT* unit = ref.GetComp();
  236. SCH_SHEET_PATH sheetPath = refList.GetItem( i ).GetSheetPath();
  237. const wxString& curFp = unit->GetField( FOOTPRINT )->GetText();
  238. if( !curFp.IsEmpty() && fp != curFp )
  239. {
  240. wxString curUnitName = unit->GetRef( &sheetPath )
  241. + LIB_PART::SubReference( unit->GetUnit(), false );
  242. SCH_MARKER* marker = new SCH_MARKER();
  243. marker->SetTimeStamp( GetNewTimeStamp() );
  244. marker->SetData( ERCE_DIFFERENT_UNIT_FP, unit->GetPosition(),
  245. wxString::Format( _( "Unit %s has '%s' assigned, "
  246. "whereas unit %s has '%s' assigned" ), unitName, fp, curUnitName, curFp ),
  247. unit->GetPosition() );
  248. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  249. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
  250. ref.GetSheetPath().LastScreen()->Append( marker );
  251. ++errors;
  252. }
  253. }
  254. }
  255. return errors;
  256. }
  257. void Diagnose( NETLIST_OBJECT* aNetItemRef, NETLIST_OBJECT* aNetItemTst,
  258. int aMinConn, int aDiag )
  259. {
  260. SCH_MARKER* marker = NULL;
  261. SCH_SCREEN* screen;
  262. ELECTRICAL_PINTYPE ii, jj;
  263. if( aDiag == OK )
  264. return;
  265. /* Create new marker for ERC error. */
  266. marker = new SCH_MARKER();
  267. marker->SetTimeStamp( GetNewTimeStamp() );
  268. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  269. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
  270. screen = aNetItemRef->m_SheetPath.LastScreen();
  271. screen->Append( marker );
  272. wxString msg;
  273. if( aMinConn < 0 )
  274. {
  275. if( aNetItemRef->m_Type == NET_HIERLABEL || aNetItemRef->m_Type == NET_HIERBUSLABELMEMBER )
  276. {
  277. msg.Printf( _( "Hierarchical label %s is not connected to a sheet label." ),
  278. GetChars( aNetItemRef->m_Label ) );
  279. marker->SetData( ERCE_HIERACHICAL_LABEL,
  280. aNetItemRef->m_Start,
  281. msg,
  282. aNetItemRef->m_Start );
  283. }
  284. else if( aNetItemRef->m_Type == NET_GLOBLABEL )
  285. {
  286. msg.Printf( _( "Global label %s is not connected to any other global label." ),
  287. GetChars( aNetItemRef->m_Label ) );
  288. marker->SetData( ERCE_GLOBLABEL,
  289. aNetItemRef->m_Start,
  290. msg,
  291. aNetItemRef->m_Start );
  292. }
  293. else
  294. {
  295. msg.Printf( _( "Sheet label %s is not connected to a hierarchical label." ),
  296. GetChars( aNetItemRef->m_Label ) );
  297. marker->SetData( ERCE_HIERACHICAL_LABEL,
  298. aNetItemRef->m_Start,
  299. msg,
  300. aNetItemRef->m_Start );
  301. }
  302. return;
  303. }
  304. ii = aNetItemRef->m_ElectricalPinType;
  305. wxString cmp_ref( "?" );
  306. if( aNetItemRef->m_Type == NET_PIN && aNetItemRef->m_Link )
  307. cmp_ref = aNetItemRef->GetComponentParent()->GetRef( &aNetItemRef->m_SheetPath );
  308. if( aNetItemTst == NULL )
  309. {
  310. if( aMinConn == NOC ) /* Only 1 element in the net. */
  311. {
  312. msg.Printf( _( "Pin %s (%s) of component %s is unconnected." ),
  313. aNetItemRef->m_PinNum,
  314. GetChars( GetText( ii ) ),
  315. GetChars( cmp_ref ) );
  316. marker->SetData( ERCE_PIN_NOT_CONNECTED,
  317. aNetItemRef->m_Start,
  318. msg,
  319. aNetItemRef->m_Start );
  320. return;
  321. }
  322. if( aMinConn == NOD ) /* Nothing driving the net. */
  323. {
  324. if( aNetItemRef->m_Type == NET_PIN && aNetItemRef->m_Link )
  325. cmp_ref = aNetItemRef->GetComponentParent()->GetRef(
  326. &aNetItemRef->m_SheetPath );
  327. msg.Printf( _( "Pin %s (%s) of component %s is not driven (Net %d)." ),
  328. aNetItemRef->m_PinNum,
  329. GetChars( GetText( ii ) ),
  330. GetChars( cmp_ref ),
  331. aNetItemRef->GetNet() );
  332. marker->SetData( ERCE_PIN_NOT_DRIVEN,
  333. aNetItemRef->m_Start,
  334. msg,
  335. aNetItemRef->m_Start );
  336. return;
  337. }
  338. if( aDiag == UNC )
  339. {
  340. msg.Printf( _( "More than 1 pin connected to an UnConnect symbol." ) );
  341. marker->SetData( ERCE_NOCONNECT_CONNECTED,
  342. aNetItemRef->m_Start,
  343. msg,
  344. aNetItemRef->m_Start );
  345. return;
  346. }
  347. }
  348. if( aNetItemTst ) /* Error between 2 pins */
  349. {
  350. jj = aNetItemTst->m_ElectricalPinType;
  351. int errortype = ERCE_PIN_TO_PIN_WARNING;
  352. if( aDiag == ERR )
  353. {
  354. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_ERROR );
  355. errortype = ERCE_PIN_TO_PIN_ERROR;
  356. }
  357. wxString alt_cmp( "?" );
  358. if( aNetItemTst->m_Type == NET_PIN && aNetItemTst->m_Link )
  359. alt_cmp = aNetItemTst->GetComponentParent()->GetRef( &aNetItemTst->m_SheetPath );
  360. msg.Printf( _( "Pin %s (%s) of component %s is connected to " ),
  361. aNetItemRef->m_PinNum,
  362. GetChars( GetText( ii ) ),
  363. GetChars( cmp_ref ) );
  364. marker->SetData( errortype, aNetItemRef->m_Start, msg, aNetItemRef->m_Start );
  365. msg.Printf( _( "pin %s (%s) of component %s (net %d)." ),
  366. aNetItemTst->m_PinNum,
  367. GetChars( GetText( jj ) ),
  368. GetChars( alt_cmp ),
  369. aNetItemRef->GetNet() );
  370. marker->SetAuxiliaryData( msg, aNetItemTst->m_Start );
  371. }
  372. }
  373. void TestOthersItems( NETLIST_OBJECT_LIST* aList,
  374. unsigned aNetItemRef, unsigned aNetStart,
  375. int* aMinConnexion )
  376. {
  377. unsigned netItemTst = aNetStart;
  378. ELECTRICAL_PINTYPE jj;
  379. int erc = OK;
  380. /* Analysis of the table of connections. */
  381. ELECTRICAL_PINTYPE ref_elect_type = aList->GetItem( aNetItemRef )->m_ElectricalPinType;
  382. int local_minconn = NOC;
  383. if( ref_elect_type == PIN_NC )
  384. local_minconn = NPI;
  385. /* Test pins connected to NetItemRef */
  386. for( ; ; netItemTst++ )
  387. {
  388. if( aNetItemRef == netItemTst )
  389. continue;
  390. // We examine only a given net. We stop the search if the net changes
  391. if( ( netItemTst >= aList->size() ) // End of list
  392. || ( aList->GetItemNet( aNetItemRef ) !=
  393. aList->GetItemNet( netItemTst ) ) ) // End of net
  394. {
  395. /* End net code found: minimum connection test. */
  396. if( ( *aMinConnexion < NET_NC ) && ( local_minconn < NET_NC ) )
  397. {
  398. /* Not connected or not driven pin. */
  399. bool seterr = true;
  400. if( local_minconn == NOC &&
  401. aList->GetItemType( aNetItemRef ) == NET_PIN )
  402. {
  403. /* This pin is not connected: for multiple part per
  404. * package, and duplicated pin,
  405. * search for another instance of this pin
  406. * this will be flagged only if all instances of this pin
  407. * are not connected
  408. * TODO test also if instances connected are connected to
  409. * the same net
  410. */
  411. for( unsigned duplicate = 0; duplicate < aList->size(); duplicate++ )
  412. {
  413. if( aList->GetItemType( duplicate ) != NET_PIN )
  414. continue;
  415. if( duplicate == aNetItemRef )
  416. continue;
  417. if( aList->GetItem( aNetItemRef )->m_PinNum !=
  418. aList->GetItem( duplicate )->m_PinNum )
  419. continue;
  420. if( ( (SCH_COMPONENT*) aList->GetItem( aNetItemRef )->
  421. m_Link )->GetRef( &aList->GetItem( aNetItemRef )-> m_SheetPath ) !=
  422. ( (SCH_COMPONENT*) aList->GetItem( duplicate )->m_Link )
  423. ->GetRef( &aList->GetItem( duplicate )->m_SheetPath ) )
  424. continue;
  425. // Same component and same pin. Do dot create error for this pin
  426. // if the other pin is connected (i.e. if duplicate net has another
  427. // item)
  428. if( (duplicate > 0)
  429. && ( aList->GetItemNet( duplicate ) ==
  430. aList->GetItemNet( duplicate - 1 ) ) )
  431. seterr = false;
  432. if( (duplicate < aList->size() - 1)
  433. && ( aList->GetItemNet( duplicate ) ==
  434. aList->GetItemNet( duplicate + 1 ) ) )
  435. seterr = false;
  436. }
  437. }
  438. if( seterr )
  439. Diagnose( aList->GetItem( aNetItemRef ), NULL, local_minconn, WAR );
  440. *aMinConnexion = DRV; // inhibiting other messages of this
  441. // type for the net.
  442. }
  443. return;
  444. }
  445. switch( aList->GetItemType( netItemTst ) )
  446. {
  447. case NET_ITEM_UNSPECIFIED:
  448. case NET_SEGMENT:
  449. case NET_BUS:
  450. case NET_JUNCTION:
  451. case NET_LABEL:
  452. case NET_HIERLABEL:
  453. case NET_BUSLABELMEMBER:
  454. case NET_HIERBUSLABELMEMBER:
  455. case NET_SHEETBUSLABELMEMBER:
  456. case NET_SHEETLABEL:
  457. case NET_GLOBLABEL:
  458. case NET_GLOBBUSLABELMEMBER:
  459. case NET_PINLABEL:
  460. break;
  461. case NET_NOCONNECT:
  462. local_minconn = std::max( NET_NC, local_minconn );
  463. break;
  464. case NET_PIN:
  465. jj = aList->GetItem( netItemTst )->m_ElectricalPinType;
  466. local_minconn = std::max( MinimalReq[ref_elect_type][jj], local_minconn );
  467. if( netItemTst <= aNetItemRef )
  468. break;
  469. if( erc == OK )
  470. {
  471. erc = DiagErc[ref_elect_type][jj];
  472. if( erc != OK )
  473. {
  474. if( aList->GetConnectionType( netItemTst ) == UNCONNECTED )
  475. {
  476. Diagnose( aList->GetItem( aNetItemRef ),
  477. aList->GetItem( netItemTst ),
  478. 0, erc );
  479. aList->SetConnectionType( netItemTst, NOCONNECT_SYMBOL_PRESENT );
  480. }
  481. }
  482. }
  483. break;
  484. }
  485. }
  486. }
  487. int NETLIST_OBJECT_LIST::CountPinsInNet( unsigned aNetStart )
  488. {
  489. int count = 0;
  490. int curr_net = GetItemNet( aNetStart );
  491. /* Test pins connected to NetItemRef */
  492. for( unsigned item = aNetStart; item < size(); item++ )
  493. {
  494. // We examine only a given net. We stop the search if the net changes
  495. if( curr_net != GetItemNet( item ) ) // End of net
  496. break;
  497. if( GetItemType( item ) == NET_PIN )
  498. count++;
  499. }
  500. return count;
  501. }
  502. bool WriteDiagnosticERC( EDA_UNITS_T aUnits, const wxString& aFullFileName )
  503. {
  504. wxString msg;
  505. wxFFile file( aFullFileName, wxT( "wt" ) );
  506. if( !file.IsOpened() )
  507. return false;
  508. msg = _( "ERC report" );
  509. msg << wxT(" (") << DateAndTime() << wxT( ", " )
  510. << _( "Encoding UTF8" ) << wxT( " )\n" );
  511. int err_count = 0;
  512. int warn_count = 0;
  513. int total_count = 0;
  514. SCH_SHEET_LIST sheetList( g_RootSheet );
  515. for( unsigned i = 0; i < sheetList.size(); i++ )
  516. {
  517. msg << wxString::Format( _( "\n***** Sheet %s\n" ),
  518. GetChars( sheetList[i].PathHumanReadable() ) );
  519. for( SCH_ITEM* item = sheetList[i].LastDrawList(); item != NULL; item = item->Next() )
  520. {
  521. if( item->Type() != SCH_MARKER_T )
  522. continue;
  523. SCH_MARKER* marker = (SCH_MARKER*) item;
  524. if( marker->GetMarkerType() != MARKER_BASE::MARKER_ERC )
  525. continue;
  526. total_count++;
  527. if( marker->GetErrorLevel() == MARKER_BASE::MARKER_SEVERITY_ERROR )
  528. err_count++;
  529. if( marker->GetErrorLevel() == MARKER_BASE::MARKER_SEVERITY_WARNING )
  530. warn_count++;
  531. msg << marker->GetReporter().ShowReport( aUnits );
  532. }
  533. }
  534. msg << wxString::Format( _( "\n ** ERC messages: %d Errors %d Warnings %d\n" ),
  535. total_count, err_count, warn_count );
  536. // Currently: write report using UTF8 (as usual in Kicad).
  537. // TODO: see if we can use the current encoding page (mainly for Windows users),
  538. // Or other format (HTML?)
  539. file.Write( msg );
  540. // wxFFile dtor will close the file.
  541. return true;
  542. }
  543. void NETLIST_OBJECT_LIST::TestforNonOrphanLabel( unsigned aNetItemRef, unsigned aStartNet )
  544. {
  545. unsigned netItemTst = aStartNet;
  546. int erc = 1;
  547. // Review the list of labels connected to NetItemRef:
  548. for( ; ; netItemTst++ )
  549. {
  550. if( netItemTst == aNetItemRef )
  551. continue;
  552. /* Is always in the same net? */
  553. if( ( netItemTst == size() )
  554. || ( GetItemNet( aNetItemRef ) != GetItemNet( netItemTst ) ) )
  555. {
  556. /* End Netcode found. */
  557. if( erc )
  558. {
  559. /* Glabel or SheetLabel orphaned. */
  560. Diagnose( GetItem( aNetItemRef ), NULL, -1, WAR );
  561. }
  562. return;
  563. }
  564. if( GetItem( aNetItemRef )->IsLabelConnected( GetItem( netItemTst ) ) )
  565. erc = 0;
  566. //same thing, different order.
  567. if( GetItem( netItemTst )->IsLabelConnected( GetItem( aNetItemRef ) ) )
  568. erc = 0;
  569. }
  570. }
  571. // this code try to detect similar labels, i.e. labels which are identical
  572. // when they are compared using case insensitive coparisons.
  573. // A helper struct to compare NETLIST_OBJECT items by sheetpath and label texts
  574. // for a std::set<NETLIST_OBJECT*> container
  575. // the full text is "sheetpath+label" for local labels and "label" for global labels
  576. struct compare_labels
  577. {
  578. bool operator() ( const NETLIST_OBJECT* lab1, const NETLIST_OBJECT* lab2 )
  579. {
  580. wxString str1 = lab1->m_SheetPath.Path() + lab1->m_Label;
  581. wxString str2 = lab2->m_SheetPath.Path() + lab2->m_Label;
  582. return str1.Cmp( str2 ) < 0;
  583. }
  584. };
  585. struct compare_label_names
  586. {
  587. bool operator() ( const NETLIST_OBJECT* lab1, const NETLIST_OBJECT* lab2 )
  588. {
  589. return lab1->m_Label.Cmp( lab2->m_Label ) < 0;
  590. }
  591. };
  592. struct compare_paths
  593. {
  594. bool operator() ( const NETLIST_OBJECT* lab1, const NETLIST_OBJECT* lab2 )
  595. {
  596. return lab1->m_SheetPath.Path().Cmp( lab2->m_SheetPath.Path() ) < 0;
  597. }
  598. };
  599. // Helper functions to build the warning messages about Similar Labels:
  600. static int countIndenticalLabels( std::vector<NETLIST_OBJECT*>& aList, NETLIST_OBJECT* aLabel );
  601. static void SimilarLabelsDiagnose( NETLIST_OBJECT* aItemA, NETLIST_OBJECT* aItemB );
  602. void NETLIST_OBJECT_LIST::TestforSimilarLabels()
  603. {
  604. // Similar labels which are different when using case sensitive comparisons
  605. // but are equal when using case insensitive comparisons
  606. // list of all labels (used the better item to build diag messages)
  607. std::vector<NETLIST_OBJECT*> fullLabelList;
  608. // list of all labels , each label appears only once (used to to detect similar labels)
  609. std::set<NETLIST_OBJECT*, compare_labels> uniqueLabelList;
  610. wxString msg;
  611. // Build a list of differents labels. If inside a given sheet there are
  612. // more than one given label, only one label is stored.
  613. // not also the sheet labels are not taken in account for 2 reasons:
  614. // * they are in the root sheet but they are seen only from the child sheet
  615. // * any mismatch between child sheet hierarchical labels and the sheet label
  616. // already detected by ERC
  617. for( unsigned netItem = 0; netItem < size(); ++netItem )
  618. {
  619. switch( GetItemType( netItem ) )
  620. {
  621. case NET_LABEL:
  622. case NET_BUSLABELMEMBER:
  623. case NET_PINLABEL:
  624. case NET_GLOBBUSLABELMEMBER:
  625. case NET_HIERLABEL:
  626. case NET_HIERBUSLABELMEMBER:
  627. case NET_GLOBLABEL:
  628. // add this label in lists
  629. uniqueLabelList.insert( GetItem( netItem ) );
  630. fullLabelList.push_back( GetItem( netItem ) );
  631. break;
  632. case NET_SHEETLABEL:
  633. case NET_SHEETBUSLABELMEMBER:
  634. default:
  635. break;
  636. }
  637. }
  638. // build global labels and compare
  639. std::set<NETLIST_OBJECT*, compare_label_names> loc_labelList;
  640. std::set<NETLIST_OBJECT*>::const_iterator it;
  641. for( it = uniqueLabelList.begin(); it != uniqueLabelList.end(); ++it )
  642. {
  643. if( (*it)->IsLabelGlobal() )
  644. loc_labelList.insert( *it );
  645. }
  646. // compare global labels (same label names appears only once in list)
  647. for( it = loc_labelList.begin(); it != loc_labelList.end(); ++it )
  648. {
  649. std::set<NETLIST_OBJECT*>::const_iterator it_aux = it;
  650. for( ++it_aux; it_aux != loc_labelList.end(); ++it_aux )
  651. {
  652. if( (*it)->m_Label.CmpNoCase( (*it_aux)->m_Label ) == 0 )
  653. {
  654. // Create new marker for ERC.
  655. int cntA = countIndenticalLabels( fullLabelList, *it );
  656. int cntB = countIndenticalLabels( fullLabelList, *it_aux );
  657. if( cntA <= cntB )
  658. SimilarLabelsDiagnose( (*it), (*it_aux) );
  659. else
  660. SimilarLabelsDiagnose( (*it_aux), (*it) );
  661. }
  662. }
  663. }
  664. // Build paths list
  665. std::set<NETLIST_OBJECT*, compare_paths> pathsList;
  666. for( it = uniqueLabelList.begin(); it != uniqueLabelList.end(); ++it )
  667. pathsList.insert( *it );
  668. // Examine each label inside a sheet path:
  669. for( it = pathsList.begin(); it != pathsList.end(); ++it )
  670. {
  671. loc_labelList.clear();
  672. std::set<NETLIST_OBJECT*>::const_iterator it_aux = uniqueLabelList.begin();
  673. for( ; it_aux != uniqueLabelList.end(); ++it_aux )
  674. {
  675. if( (*it)->m_SheetPath.Path() == (*it_aux)->m_SheetPath.Path() )
  676. loc_labelList.insert( *it_aux );
  677. }
  678. // at this point, loc_labelList contains labels of the current sheet path.
  679. // Detect similar labels (same label names appears only once in list)
  680. std::set<NETLIST_OBJECT*>::const_iterator ref_it;
  681. for( ref_it = loc_labelList.begin(); ref_it != loc_labelList.end(); ++ref_it )
  682. {
  683. NETLIST_OBJECT* ref_item = *ref_it;
  684. it_aux = ref_it;
  685. for( ++it_aux; it_aux != loc_labelList.end(); ++it_aux )
  686. {
  687. // global label versus global label was already examined.
  688. // here, at least one label must be local
  689. if( ref_item->IsLabelGlobal() && (*it_aux)->IsLabelGlobal() )
  690. continue;
  691. if( ref_item->m_Label.CmpNoCase( (*it_aux)->m_Label ) == 0 )
  692. {
  693. // Create new marker for ERC.
  694. int cntA = countIndenticalLabels( fullLabelList, ref_item );
  695. int cntB = countIndenticalLabels( fullLabelList, *it_aux );
  696. if( cntA <= cntB )
  697. SimilarLabelsDiagnose( ref_item, (*it_aux) );
  698. else
  699. SimilarLabelsDiagnose( (*it_aux), ref_item );
  700. }
  701. }
  702. }
  703. }
  704. }
  705. // Helper function: count the number of labels identical to aLabel
  706. // for global label: global labels in the full project
  707. // for local label: all labels in the current sheet
  708. static int countIndenticalLabels( std::vector<NETLIST_OBJECT*>& aList, NETLIST_OBJECT* aLabel )
  709. {
  710. int count = 0;
  711. if( aLabel->IsLabelGlobal() )
  712. {
  713. for( unsigned netItem = 0; netItem < aList.size(); ++netItem )
  714. {
  715. NETLIST_OBJECT* item = aList[netItem];
  716. if( item->IsLabelGlobal() && item->m_Label == aLabel->m_Label )
  717. count++;
  718. }
  719. }
  720. else
  721. {
  722. for( unsigned netItem = 0; netItem < aList.size(); ++netItem )
  723. {
  724. NETLIST_OBJECT* item = aList[netItem];
  725. if( item->m_Label == aLabel->m_Label &&
  726. item->m_SheetPath.Path() == aLabel->m_SheetPath.Path() )
  727. count++;
  728. }
  729. }
  730. return count;
  731. }
  732. // Helper function: creates a marker for similar labels ERC warning
  733. static void SimilarLabelsDiagnose( NETLIST_OBJECT* aItemA, NETLIST_OBJECT* aItemB )
  734. {
  735. // Create new marker for ERC.
  736. SCH_MARKER* marker = new SCH_MARKER();
  737. marker->SetTimeStamp( GetNewTimeStamp() );
  738. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  739. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
  740. SCH_SCREEN* screen = aItemA->m_SheetPath.LastScreen();
  741. screen->Append( marker );
  742. wxString fmt = aItemA->IsLabelGlobal() ?
  743. _( "Global label \"%s\" (sheet \"%s\") looks like:" ) :
  744. _( "Local label \"%s\" (sheet \"%s\") looks like:" );
  745. wxString msg;
  746. msg.Printf( fmt, GetChars( aItemA->m_Label ), GetChars( aItemA->m_SheetPath.PathHumanReadable() ) );
  747. marker->SetData( aItemA->IsLabelGlobal() && aItemB->IsLabelGlobal() ?
  748. ERCE_SIMILAR_GLBL_LABELS : ERCE_SIMILAR_LABELS,
  749. aItemA->m_Start, msg, aItemA->m_Start );
  750. fmt = aItemB->IsLabelGlobal() ? _( "Global label \"%s\" (sheet \"%s\")" ) :
  751. _( "Local label \"%s\" (sheet \"%s\")" );
  752. msg.Printf( fmt, GetChars( aItemB->m_Label ), GetChars( aItemB->m_SheetPath.PathHumanReadable() ) );
  753. marker->SetAuxiliaryData( msg, aItemB->m_Start );
  754. }