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.

335 lines
12 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004-2022 KiCad Developers.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include <board.h>
  24. #include <pad.h>
  25. #include <drc/drc_engine.h>
  26. #include <drc/drc_item.h>
  27. #include <drc/drc_rule.h>
  28. #include <drc/drc_test_provider.h>
  29. #include <kiway.h>
  30. #include <netlist_reader/pcb_netlist.h>
  31. /*
  32. Schematic parity test.
  33. Errors generated:
  34. - DRCE_MISSING_FOOTPRINT
  35. - DRCE_DUPLICATE_FOOTPRINT
  36. - DRCE_EXTRA_FOOTPRINT
  37. - DRCE_SCHEMATIC_PARITY
  38. - DRCE_FOOTPRINT_FILTERS
  39. TODO:
  40. - cross-check PCB netlist against SCH netlist
  41. - cross-check PCB fields against SCH fields
  42. */
  43. class DRC_TEST_PROVIDER_SCHEMATIC_PARITY : public DRC_TEST_PROVIDER
  44. {
  45. public:
  46. DRC_TEST_PROVIDER_SCHEMATIC_PARITY()
  47. {
  48. m_isRuleDriven = false;
  49. }
  50. virtual ~DRC_TEST_PROVIDER_SCHEMATIC_PARITY()
  51. {
  52. }
  53. virtual bool Run() override;
  54. virtual const wxString GetName() const override
  55. {
  56. return wxT( "schematic_parity" );
  57. };
  58. virtual const wxString GetDescription() const override
  59. {
  60. return wxT( "Performs layout-vs-schematics integity check" );
  61. }
  62. private:
  63. void testNetlist( NETLIST& aNetlist );
  64. };
  65. void DRC_TEST_PROVIDER_SCHEMATIC_PARITY::testNetlist( NETLIST& aNetlist )
  66. {
  67. BOARD* board = m_drcEngine->GetBoard();
  68. auto compare = []( const FOOTPRINT* x, const FOOTPRINT* y )
  69. {
  70. return x->GetReference().CmpNoCase( y->GetReference() ) < 0;
  71. };
  72. auto footprints = std::set<FOOTPRINT*, decltype( compare )>( compare );
  73. // Search for duplicate footprints on the board
  74. for( FOOTPRINT* footprint : board->Footprints() )
  75. {
  76. if( m_drcEngine->IsErrorLimitExceeded( DRCE_DUPLICATE_FOOTPRINT ) )
  77. break;
  78. auto ins = footprints.insert( footprint );
  79. if( !ins.second && !( footprint->GetAttributes() & FP_BOARD_ONLY ) )
  80. {
  81. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_DUPLICATE_FOOTPRINT );
  82. drcItem->SetItems( footprint, *ins.first );
  83. reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
  84. }
  85. }
  86. // Search for component footprints in the netlist but not on the board.
  87. for( unsigned ii = 0; ii < aNetlist.GetCount(); ii++ )
  88. {
  89. COMPONENT* component = aNetlist.GetComponent( ii );
  90. FOOTPRINT* footprint = board->FindFootprintByReference( component->GetReference() );
  91. if( footprint == nullptr )
  92. {
  93. if( !m_drcEngine->IsErrorLimitExceeded( DRCE_MISSING_FOOTPRINT ) )
  94. {
  95. wxString msg;
  96. msg.Printf( _( "Missing footprint %s (%s)" ),
  97. component->GetReference(),
  98. component->GetValue() );
  99. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MISSING_FOOTPRINT );
  100. drcItem->SetErrorMessage( msg );
  101. reportViolation( drcItem, VECTOR2I(), UNDEFINED_LAYER );
  102. }
  103. }
  104. else
  105. {
  106. if( component->GetValue() != footprint->GetValue()
  107. && !m_drcEngine->IsErrorLimitExceeded( DRCE_SCHEMATIC_PARITY ) )
  108. {
  109. wxString msg;
  110. msg.Printf( _( "Value (%s) doesn't match symbol value (%s)." ),
  111. footprint->GetReference(), footprint->GetValue(),
  112. component->GetValue() );
  113. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SCHEMATIC_PARITY );
  114. drcItem->SetErrorMessage( msg );
  115. drcItem->SetItems( footprint );
  116. reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
  117. }
  118. if( component->GetFPID().GetUniStringLibId() != footprint->GetFPID().GetUniStringLibId()
  119. && !m_drcEngine->IsErrorLimitExceeded( DRCE_SCHEMATIC_PARITY ) )
  120. {
  121. wxString msg;
  122. msg.Printf( _( "%s doesn't match footprint given by symbol (%s)." ),
  123. footprint->GetFPID().GetUniStringLibId(),
  124. component->GetFPID().GetUniStringLibId() );
  125. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SCHEMATIC_PARITY );
  126. drcItem->SetErrorMessage( msg );
  127. drcItem->SetItems( footprint );
  128. reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
  129. }
  130. if( !m_drcEngine->IsErrorLimitExceeded( DRCE_FOOTPRINT_FILTERS ) )
  131. {
  132. wxString fpName = footprint->GetFPID().GetUniStringLibItemName();
  133. size_t filtercount = component->GetFootprintFilters().GetCount();
  134. bool found = ( 0 == filtercount ); // if no entries, do not filter
  135. for( size_t jj = 0; jj < filtercount && !found; jj++ )
  136. found = fpName.Matches( component->GetFootprintFilters()[jj] );
  137. if( !found )
  138. {
  139. wxString msg;
  140. msg.Printf( _( "%s doesn't match symbol's footprint filters (%s)." ),
  141. fpName,
  142. wxJoin( component->GetFootprintFilters(), ' ' ) );
  143. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_FOOTPRINT_FILTERS );
  144. drcItem->SetErrorMessage( msg );
  145. drcItem->SetItems( footprint );
  146. reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
  147. }
  148. }
  149. if( ( component->GetProperties().count( "dnp" ) > 0 )
  150. != ( ( footprint->GetAttributes() & FP_DNP ) > 0 )
  151. && !m_drcEngine->IsErrorLimitExceeded( DRCE_SCHEMATIC_PARITY ) )
  152. {
  153. wxString msg;
  154. msg.Printf( _( "'%s' settings differ." ), _( "Do not populate" ) );
  155. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SCHEMATIC_PARITY );
  156. drcItem->SetErrorMessage( drcItem->GetErrorMessage() + wxS( ": " ) + msg );
  157. drcItem->SetItems( footprint );
  158. reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
  159. }
  160. if( ( component->GetProperties().count( "exclude_from_bom" ) > 0 )
  161. != ( (footprint->GetAttributes() & FP_EXCLUDE_FROM_BOM ) > 0 )
  162. && !m_drcEngine->IsErrorLimitExceeded( DRCE_SCHEMATIC_PARITY ) )
  163. {
  164. wxString msg;
  165. msg.Printf( _( "'%s' settings differ." ), _( "Exclude from bill of materials" ) );
  166. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SCHEMATIC_PARITY );
  167. drcItem->SetErrorMessage( drcItem->GetErrorMessage() + wxS( ": " ) + msg );
  168. drcItem->SetItems( footprint );
  169. reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
  170. }
  171. for( PAD* pad : footprint->Pads() )
  172. {
  173. if( m_drcEngine->IsErrorLimitExceeded( DRCE_NET_CONFLICT ) )
  174. break;
  175. if( !pad->CanHaveNumber() )
  176. continue;
  177. const COMPONENT_NET& sch_net = component->GetNet( pad->GetNumber() );
  178. const wxString& pcb_netname = pad->GetNetname();
  179. if( !pcb_netname.IsEmpty() && sch_net.GetPinName().IsEmpty() )
  180. {
  181. wxString msg;
  182. msg.Printf( _( "No corresponding pin found in schematic." ) );
  183. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_NET_CONFLICT );
  184. drcItem->SetErrorMessage( msg );
  185. drcItem->SetItems( pad );
  186. reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
  187. }
  188. else if( pcb_netname.IsEmpty() && !sch_net.GetNetName().IsEmpty() )
  189. {
  190. wxString msg;
  191. msg.Printf( _( "Pad missing net given by schematic (%s)." ),
  192. sch_net.GetNetName() );
  193. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_NET_CONFLICT );
  194. drcItem->SetErrorMessage( msg );
  195. drcItem->SetItems( pad );
  196. reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
  197. }
  198. else if( pcb_netname != sch_net.GetNetName()
  199. && !( pcb_netname.starts_with(
  200. wxT( "unconnected-" ) )
  201. && pcb_netname.starts_with( sch_net.GetNetName() ) ) )
  202. {
  203. wxString msg;
  204. msg.Printf( _( "Pad net (%s) doesn't match net given by schematic (%s)." ),
  205. pcb_netname,
  206. sch_net.GetNetName() );
  207. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_NET_CONFLICT );
  208. drcItem->SetErrorMessage( msg );
  209. drcItem->SetItems( pad );
  210. reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
  211. }
  212. }
  213. for( unsigned jj = 0; jj < component->GetNetCount(); ++jj )
  214. {
  215. if( m_drcEngine->IsErrorLimitExceeded( DRCE_NET_CONFLICT ) )
  216. break;
  217. const COMPONENT_NET& sch_net = component->GetNet( jj );
  218. if( !footprint->FindPadByNumber( sch_net.GetPinName() ) )
  219. {
  220. wxString msg;
  221. if( sch_net.GetNetName().StartsWith( wxT( "unconnected-" ) ) )
  222. {
  223. msg = sch_net.GetPinName();
  224. }
  225. else
  226. {
  227. msg = wxString::Format( wxT( "%s (%s)" ),
  228. sch_net.GetPinName(),
  229. sch_net.GetNetName() );
  230. }
  231. msg.Printf( _( "No pad found for pin %s in schematic." ), msg );
  232. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_NET_CONFLICT );
  233. drcItem->SetErrorMessage( msg );
  234. drcItem->SetItems( footprint );
  235. reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
  236. }
  237. }
  238. }
  239. }
  240. // Search for component footprints found on board but not in netlist.
  241. for( FOOTPRINT* footprint : board->Footprints() )
  242. {
  243. if( m_drcEngine->IsErrorLimitExceeded( DRCE_EXTRA_FOOTPRINT ) )
  244. break;
  245. if( footprint->GetAttributes() & FP_BOARD_ONLY )
  246. continue;
  247. if( !aNetlist.GetComponentByReference( footprint->GetReference() ) )
  248. {
  249. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_EXTRA_FOOTPRINT );
  250. drcItem->SetItems( footprint );
  251. reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
  252. }
  253. }
  254. }
  255. bool DRC_TEST_PROVIDER_SCHEMATIC_PARITY::Run()
  256. {
  257. if( m_drcEngine->GetTestFootprints() )
  258. {
  259. if( !reportPhase( _( "Checking PCB to schematic parity..." ) ) )
  260. return false;
  261. auto netlist = m_drcEngine->GetSchematicNetlist();
  262. if( !netlist )
  263. {
  264. reportAux( wxT( "No netlist provided, skipping schematic parity tests." ) );
  265. return true;
  266. }
  267. testNetlist( *netlist );
  268. reportRuleStatistics();
  269. }
  270. return !m_drcEngine->IsCancelled();
  271. }
  272. namespace detail
  273. {
  274. static DRC_REGISTER_TEST_PROVIDER<DRC_TEST_PROVIDER_SCHEMATIC_PARITY> dummy;
  275. }