1 /* Author: Matt Liberty */
2 /*
3  * Copyright (c) 2020, The Regents of the University of California
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *     * Redistributions of source code must retain the above copyright
9  *       notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above copyright
11  *       notice, this list of conditions and the following disclaimer in the
12  *       documentation and/or other materials provided with the distribution.
13  *     * Neither the name of the University nor the
14  *       names of its contributors may be used to endorse or promote products
15  *       derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS BE LIABLE FOR ANY DIRECT,
21  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #define BOOST_TEST_MODULE gc
30 
31 #ifdef HAS_BOOST_UNIT_TEST_LIBRARY
32 // Shared library version
33 #define BOOST_TEST_DYN_LINK
34 #include <boost/test/unit_test.hpp>
35 #else
36 // Header only version
37 #include <boost/test/included/unit_test.hpp>
38 #endif
39 
40 #include <boost/test/data/test_case.hpp>
41 #include <iostream>
42 
43 #include "fixture.h"
44 #include "frDesign.h"
45 #include "gc/FlexGC.h"
46 
47 using namespace fr;
48 namespace bdata = boost::unit_test::data;
49 
50 // Fixture for GC tests
51 struct GCFixture : public Fixture
52 {
GCFixtureGCFixture53   GCFixture() : worker(design->getTech(), logger.get()) {}
54 
testMarkerGCFixture55   void testMarker(frMarker* marker,
56                   frLayerNum layer_num,
57                   frConstraintTypeEnum type,
58                   const frBox& expected_bbox)
59   {
60     frBox bbox;
61     marker->getBBox(bbox);
62 
63     BOOST_TEST(marker->getLayerNum() == layer_num);
64     BOOST_TEST(marker->getConstraint());
65     TEST_ENUM_EQUAL(marker->getConstraint()->typeId(), type);
66     BOOST_TEST(bbox == expected_bbox);
67   }
68 
runGCGCFixture69   void runGC()
70   {
71     // Needs to be run after all the objects are created but before gc
72     initRegionQuery();
73 
74     // Run the GC engine
75     const frBox work(0, 0, 2000, 2000);
76     worker.setExtBox(work);
77     worker.setDrcBox(work);
78 
79     worker.init(design.get());
80     worker.main();
81     worker.end();
82   }
83 
84   FlexGCWorker worker;
85 };
86 
87 BOOST_FIXTURE_TEST_SUITE(gc, GCFixture);
88 
89 // Two touching metal shape from different nets generate a short
BOOST_AUTO_TEST_CASE(metal_short)90 BOOST_AUTO_TEST_CASE(metal_short)
91 {
92   // Setup
93   frNet* n1 = makeNet("n1");
94   frNet* n2 = makeNet("n2");
95 
96   makePathseg(n1, 2, {0, 0}, {500, 0});
97   makePathseg(n2, 2, {500, 0}, {1000, 0});
98 
99   runGC();
100 
101   // Test the results
102   auto& markers = worker.getMarkers();
103 
104   BOOST_TEST(markers.size() == 1);
105   testMarker(markers[0].get(),
106              2,
107              frConstraintTypeEnum::frcShortConstraint,
108              frBox(500, -50, 500, 50));
109 }
110 
111 /*
112  *
113  *                     |---------------|(750,200)
114  *                     |               |
115  *                     |               |
116  *                     |     i1        |
117  *                     |     OBS       |
118  *                     |               |
119  *                     |****|(550,90)  |
120  *                     | in |          |
121  * --------------------|----|--(600,50)|
122  * |           (450,40)|****| |        |
123  * |         n1        |      |        |
124  * --------------------|---------------|
125  * (0,-50)        (450,-50)
126  */
127 // short with obs
BOOST_AUTO_TEST_CASE(metal_short_obs)128 BOOST_AUTO_TEST_CASE(metal_short_obs)
129 {
130   // Setup
131   frNet* n1 = makeNet("n1");
132 
133   makePathseg(n1, 2, {0, 0}, {600, 0});
134   auto block = makeMacro("OBS");
135   makeMacroObs(block, 450, -50, 750, 200, 2);
136   auto pin = makeMacroPin(block, "in", 450, 40, 550, 90, 2);
137   auto i1 = makeInst("i1", block, 0, 0);
138   auto instTerm = i1->getInstTerms()[0].get();
139   instTerm->addToNet(n1);
140 
141   n1->addInstTerm(instTerm);
142   auto instTermNode = make_unique<frNode>();
143   instTermNode->setPin(instTerm);
144   instTermNode->setType(frNodeTypeEnum::frcPin);
145   n1->addNode(instTermNode);
146   runGC();
147 
148   // Test the results
149   auto& markers = worker.getMarkers();
150 
151   BOOST_TEST(markers.size() == 3);
152   // short of pin+net (450,-50), (550,90)
153   // with obs 450,-50), (750,200)
154   testMarker(markers[0].get(),
155              2,
156              frConstraintTypeEnum::frcShortConstraint,
157              frBox(450, -50, 550, 40));
158 
159   // shorts of net (0,-50), (600,50)
160   // with obs (450,-50), (750,200)
161   // 2 max rectangles generated
162   testMarker(markers[1].get(),
163              2,
164              frConstraintTypeEnum::frcShortConstraint,
165              frBox(550, -50, 600, 50));
166   testMarker(markers[2].get(),
167              2,
168              frConstraintTypeEnum::frcShortConstraint,
169              frBox(450, -50, 600, 40));
170 }
171 
172 // Two touching metal shape from the same net must have sufficient
173 // overlap
BOOST_AUTO_TEST_CASE(metal_non_sufficient)174 BOOST_AUTO_TEST_CASE(metal_non_sufficient)
175 {
176   // Setup
177   frNet* n1 = makeNet("n1");
178 
179   makePathseg(n1, 2, {0, 0}, {0, 500});
180   makePathseg(n1, 2, {0, 0}, {500, 0});
181 
182   runGC();
183 
184   // Test the results
185   auto& markers = worker.getMarkers();
186 
187   BOOST_TEST(markers.size() == 1);
188   testMarker(markers[0].get(),
189              2,
190              frConstraintTypeEnum::frcNonSufficientMetalConstraint,
191              frBox(0, 0, 50, 50));
192 }
193 
194 // Path seg less than min width flags a violation
BOOST_AUTO_TEST_CASE(min_width)195 BOOST_AUTO_TEST_CASE(min_width)
196 {
197   // Setup
198   frNet* n1 = makeNet("n1");
199 
200   makePathseg(n1, 2, {0, 0}, {500, 0}, 60);
201 
202   runGC();
203 
204   // Test the results
205   auto& markers = worker.getMarkers();
206 
207   BOOST_TEST(markers.size() == 1);
208   testMarker(markers[0].get(),
209              2,
210              frConstraintTypeEnum::frcMinWidthConstraint,
211              frBox(0, -30, 500, 30));
212 }
213 
214 // Abutting Path seg less than min width don't flag a violation
215 // as their combined width is ok
BOOST_AUTO_TEST_CASE(min_width_combines_shapes)216 BOOST_AUTO_TEST_CASE(min_width_combines_shapes)
217 {
218   // Setup
219   frNet* n1 = makeNet("n1");
220 
221   makePathseg(n1, 2, {0, 0}, {500, 0}, 60);
222   makePathseg(n1, 2, {0, 60}, {500, 60}, 60);
223 
224   runGC();
225 
226   // Test the results
227   BOOST_TEST(worker.getMarkers().size() == 0);
228 }
229 
230 // Check violation for off-grid points
BOOST_AUTO_TEST_CASE(off_grid)231 BOOST_AUTO_TEST_CASE(off_grid)
232 {
233   // Setup
234   frNet* n1 = makeNet("n1");
235 
236   makePathseg(n1, 2, {1, 1}, {501, 1});
237 
238   runGC();
239 
240   // Test the results
241   auto& markers = worker.getMarkers();
242 
243   BOOST_TEST(worker.getMarkers().size() == 1);
244   testMarker(markers[0].get(),
245              2,
246              frConstraintTypeEnum::frcOffGridConstraint,
247              frBox(1, -49, 501, 51));
248 }
249 
250 // Check violation for corner spacing
BOOST_AUTO_TEST_CASE(corner_basic)251 BOOST_AUTO_TEST_CASE(corner_basic)
252 {
253   // Setup
254   makeCornerConstraint(2);
255 
256   frNet* n1 = makeNet("n1");
257 
258   makePathseg(n1, 2, {0, 0}, {500, 0});
259   makePathseg(n1, 2, {500, 200}, {1000, 200});
260 
261   runGC();
262 
263   // Test the results
264   auto& markers = worker.getMarkers();
265 
266   BOOST_TEST(worker.getMarkers().size() == 1);
267   testMarker(markers[0].get(),
268              2,
269              frConstraintTypeEnum::frcLef58CornerSpacingConstraint,
270              frBox(500, 50, 500, 150));
271 }
272 
273 // Check no violation for corner spacing with EOL spacing
274 // (same as corner_basic but for eol)
BOOST_AUTO_TEST_CASE(corner_eol_no_violation)275 BOOST_AUTO_TEST_CASE(corner_eol_no_violation)
276 {
277   // Setup
278   makeCornerConstraint(2, 200);
279 
280   frNet* n1 = makeNet("n1");
281 
282   makePathseg(n1, 2, {0, 0}, {500, 0});
283   makePathseg(n1, 2, {500, 200}, {1000, 200});
284 
285   runGC();
286 
287   // Test the results
288   auto& markers = worker.getMarkers();
289 
290   BOOST_TEST(worker.getMarkers().size() == 0);
291 }
292 
293 // Check no violation for corner spacing with PRL > 0
294 // (same as corner_basic but for n2's pathseg begin pt)
BOOST_AUTO_TEST_CASE(corner_prl_no_violation)295 BOOST_AUTO_TEST_CASE(corner_prl_no_violation)
296 {
297   // Setup
298   makeCornerConstraint(2);
299 
300   frNet* n1 = makeNet("n1");
301 
302   makePathseg(n1, 2, {0, 0}, {500, 0});
303   makePathseg(n1, 2, {400, 200}, {1000, 200});
304 
305   runGC();
306 
307   // Test the results
308   auto& markers = worker.getMarkers();
309 
310   BOOST_TEST(worker.getMarkers().size() == 0);
311 }
312 
313 // Check violation for corner spacing on a concave corner
314 BOOST_AUTO_TEST_CASE(corner_concave, *boost::unit_test::disabled())
315 {
316   // Setup
317   makeCornerConstraint(2, /* no eol */ -1, frCornerTypeEnum::CONCAVE);
318 
319   frNet* n1 = makeNet("n1");
320 
321   makePathsegExt(n1, 2, {0, 0}, {500, 0});
322   makePathsegExt(n1, 2, {0, 0}, {0, 500});
323   makePathseg(n1, 2, {200, 200}, {1000, 200});
324 
325   runGC();
326 
327   // Test the results
328   auto& markers = worker.getMarkers();
329 
330   BOOST_TEST(worker.getMarkers().size() == 1);
331   testMarker(markers[0].get(),
332              2,
333              frConstraintTypeEnum::frcLef58CornerSpacingConstraint,
334              frBox(50, 50, 200, 200));
335 }
336 
337 // Check violation for parallel-run-length (PRL) spacing tables
338 // This test runs over a variety of width / prl / spacing values
339 // where the spacing is both legal or illegal.
340 BOOST_DATA_TEST_CASE(spacing_prl,
341                      (bdata::make({100, 220}) * bdata::make({300, 500})
342                       ^ bdata::make({100, 200, 300, 400}))
343                          * bdata::make({true, false}),
344                      width,
345                      prl,
346                      spacing,
347                      legal)
348 {
349   // Setup
350   makeSpacingConstraint(2);
351 
352   frNet* n1 = makeNet("n1");
353   frNet* n2 = makeNet("n2");
354 
355   frCoord y = /* n2_width / 2 */ 50 + spacing + width / 2;
356   if (!legal) {
357     /* move too close making a violation */
358     y -= 10;
359   }
360   makePathseg(n1, 2, {0, y}, {prl, y}, width);
361   makePathseg(n2, 2, {0, 0}, {500, 0}, 100);
362 
363   runGC();
364 
365   // Test the results
366   auto& markers = worker.getMarkers();
367 
368   if (legal) {
369     BOOST_TEST(worker.getMarkers().size() == 0);
370   } else {
371     BOOST_TEST(worker.getMarkers().size() == 1);
372     testMarker(markers[0].get(),
373                2,
374                frConstraintTypeEnum::frcSpacingTablePrlConstraint,
375                frBox(0, 50, prl, y - width / 2));
376   }
377 }
378 
379 // Check violation for spacing two widths with design rule width on macro
380 // obstruction
381 BOOST_DATA_TEST_CASE(design_rule_width, bdata::make({true, false}), legal)
382 {
383   // Setup
384   makeSpacingTableTwConstraint(2, {90, 190}, {-1, -1}, {{0, 50}, {50, 100}});
385   /*
386   WIDTH  90     0      50
387   WIDTH 190     50    150
388   */
389   frNet* n1 = makeNet("n1");
390 
391   makePathseg(n1, 2, {0, 50}, {500, 50}, 100);
392   auto block = makeMacro("DRW");
393   makeMacroObs(block, 0, 140, 500, 340, 2, legal ? 100 : -1);
394   makeInst("i1", block, 0, 0);
395   /*
396   If DESIGNRULEWIDTH is 100
397     width(n1) = 100      width(obs) = 100 : reqSpcVal = 0
398   legal
399 
400   if DESIGNRULEWIDTH is -1 (undefined)
401     width(n1) = 100      width(obs) = 200 : reqSpcVal = 100
402   illegal
403   */
404   runGC();
405 
406   // Test the results
407   auto& markers = worker.getMarkers();
408   if (legal)
409     BOOST_TEST(markers.size() == 0);
410   else {
411     BOOST_TEST(markers.size() == 1);
412     testMarker(markers[0].get(),
413                2,
414                frConstraintTypeEnum::frcSpacingTableTwConstraint,
415                frBox(0, 100, 500, 140));
416   }
417 }
418 
419 // Check for a min step violation.  The checker seems broken
420 // so this test is disabled.
421 BOOST_AUTO_TEST_CASE(min_step, *boost::unit_test::disabled())
422 {
423   // Setup
424   makeMinStepConstraint(2);
425 
426   frNet* n1 = makeNet("n1");
427 
428   makePathseg(n1, 2, {0, 0}, {200, 0});
429   makePathseg(n1, 2, {100, 20}, {200, 20});
430 
431   runGC();
432 
433   // Test the results
434   BOOST_TEST(worker.getMarkers().size() == 1);
435 }
436 
437 // Check for a lef58 style min step violation.  The checker is very
438 // limited and just supports NOBETWEENEOL style.
BOOST_AUTO_TEST_CASE(min_step58)439 BOOST_AUTO_TEST_CASE(min_step58)
440 {
441   // Setup
442   makeMinStep58Constraint(2);
443 
444   frNet* n1 = makeNet("n1");
445 
446   makePathseg(n1, 2, {0, 0}, {500, 0});
447   makePathseg(n1, 2, {200, 20}, {300, 20});
448 
449   runGC();
450 
451   // Test the results
452   auto& markers = worker.getMarkers();
453   BOOST_TEST(markers.size() == 1);
454   testMarker(markers[0].get(),
455              2,
456              frConstraintTypeEnum::frcLef58MinStepConstraint,
457              frBox(200, 50, 300, 70));
458 }
459 
460 // Check for a lef58 rect only violation.  The markers are
461 // the concave corners expanded by min-width and intersected
462 // with the metal shapes.
BOOST_AUTO_TEST_CASE(rect_only)463 BOOST_AUTO_TEST_CASE(rect_only)
464 {
465   // Setup
466   makeRectOnlyConstraint(2);
467 
468   frNet* n1 = makeNet("n1");
469 
470   makePathseg(n1, 2, {0, 0}, {500, 0});
471   makePathseg(n1, 2, {200, 0}, {200, 100});
472 
473   runGC();
474 
475   // Test the results
476   auto& markers = worker.getMarkers();
477   BOOST_TEST(markers.size() == 3);
478   testMarker(markers[0].get(),
479              2,
480              frConstraintTypeEnum::frcLef58RectOnlyConstraint,
481              frBox(150, -50, 250, 100));
482   testMarker(markers[1].get(),
483              2,
484              frConstraintTypeEnum::frcLef58RectOnlyConstraint,
485              frBox(150, -50, 350, 50));
486   testMarker(markers[2].get(),
487              2,
488              frConstraintTypeEnum::frcLef58RectOnlyConstraint,
489              frBox(50, -50, 250, 50));
490 }
491 
492 // Check for a min enclosed area violation.
BOOST_AUTO_TEST_CASE(min_enclosed_area)493 BOOST_AUTO_TEST_CASE(min_enclosed_area)
494 {
495   // Setup
496   makeMinEnclosedAreaConstraint(2);
497 
498   frNet* n1 = makeNet("n1");
499 
500   makePathsegExt(n1, 2, {0, 0}, {200, 0});
501   makePathsegExt(n1, 2, {0, 0}, {0, 200});
502   makePathsegExt(n1, 2, {0, 200}, {200, 200});
503   makePathsegExt(n1, 2, {200, 0}, {200, 200});
504 
505   runGC();
506 
507   // Test the results
508   auto& markers = worker.getMarkers();
509   BOOST_TEST(markers.size() == 1);
510   testMarker(markers[0].get(),
511              2,
512              frConstraintTypeEnum::frcMinEnclosedAreaConstraint,
513              frBox(50, 50, 150, 150));
514 }
515 
516 // Check for a spacing table influence violation.
BOOST_AUTO_TEST_CASE(spacing_table_infl_vertical)517 BOOST_AUTO_TEST_CASE(spacing_table_infl_vertical)
518 {
519   // Setup
520   makeSpacingTableInfluenceConstraint(2, {10}, {{200, 100}});
521 
522   frNet* n1 = makeNet("n1");
523 
524   makePathseg(n1, 2, {50, 0}, {50, 200});
525   makePathseg(n1, 2, {0, 100}, {350, 100});
526   makePathseg(n1, 2, {0, 250}, {350, 250});
527 
528   runGC();
529 
530   // Test the results
531   auto& markers = worker.getMarkers();
532 
533   BOOST_TEST(markers.size() == 1);
534   testMarker(markers[0].get(),
535              2,
536              frConstraintTypeEnum::frcSpacingTableInfluenceConstraint,
537              frBox(100, 150, 300, 200));
538 }
539 // Check for a spacing table influence violation.
BOOST_AUTO_TEST_CASE(spacing_table_infl_horizontal)540 BOOST_AUTO_TEST_CASE(spacing_table_infl_horizontal)
541 {
542   // Setup
543   makeSpacingTableInfluenceConstraint(2, {10}, {{200, 150}});
544 
545   frNet* n1 = makeNet("n1");
546 
547   makePathseg(n1, 2, {0, 500}, {500, 500});
548   makePathseg(n1, 2, {100, 0}, {100, 500});
549   makePathseg(n1, 2, {300, 0}, {300, 500});
550   runGC();
551 
552   // Test the results
553   auto& markers = worker.getMarkers();
554 
555   BOOST_TEST(markers.size() == 1);
556   testMarker(markers[0].get(),
557              2,
558              frConstraintTypeEnum::frcSpacingTableInfluenceConstraint,
559              frBox(150, 250, 250, 450));
560 }
561 
562 // Check for a spacing table twowidths violation.
BOOST_AUTO_TEST_CASE(spacing_table_twowidth)563 BOOST_AUTO_TEST_CASE(spacing_table_twowidth)
564 {
565   // Setup
566   makeSpacingTableTwConstraint(2, {90, 190}, {-1, -1}, {{0, 50}, {50, 100}});
567 
568   frNet* n1 = makeNet("n1");
569 
570   makePathseg(n1, 2, {0, 50}, {500, 50}, 100);
571   makePathseg(n1, 2, {0, 240}, {500, 240}, 200);
572 
573   runGC();
574 
575   // Test the results
576   auto& markers = worker.getMarkers();
577 
578   BOOST_TEST(markers.size() == 1);
579   testMarker(markers[0].get(),
580              2,
581              frConstraintTypeEnum::frcSpacingTableTwConstraint,
582              frBox(0, 100, 500, 140));
583 }
584 
585 // Check for a basic end-of-line (EOL) spacing violation.
586 BOOST_DATA_TEST_CASE(eol_basic, (bdata::make({true, false})), lef58)
587 {
588   // Setup
589   if (lef58)
590     makeLef58SpacingEolConstraint(2);
591   else
592     makeSpacingEndOfLineConstraint(2);
593 
594   frNet* n1 = makeNet("n1");
595 
596   makePathseg(n1, 2, {500, 0}, {500, 500});
597   makePathseg(n1, 2, {0, 700}, {1000, 700});
598 
599   runGC();
600 
601   // Test the results
602   auto& markers = worker.getMarkers();
603   BOOST_TEST(markers.size() == 1);
604   testMarker(markers[0].get(),
605              2,
606              lef58 ? frConstraintTypeEnum::frcLef58SpacingEndOfLineConstraint
607                    : frConstraintTypeEnum::frcSpacingEndOfLineConstraint,
608              frBox(450, 500, 550, 650));
609 }
610 BOOST_DATA_TEST_CASE(eol_ext_basic,
611                      (bdata::make({30, 50})) ^ (bdata::make({true, false})),
612                      ext,
613                      legal)
614 {
615   // Setup
616   makeEolExtensionConstraint(2, 100, {51, 101}, {20, ext}, false);
617 
618   frNet* n1 = makeNet("n1");
619 
620   makePathseg(n1, 2, {0, 100}, {500, 100});
621   makePathseg(n1, 2, {690, 100}, {1000, 100});
622 
623   runGC();
624 
625   // Test the results
626   auto& markers = worker.getMarkers();
627   if (legal)
628     BOOST_TEST(markers.size() == 0);
629   else {
630     BOOST_TEST(markers.size() == 1);
631     if (markers.size() == 1)
632       testMarker(markers[0].get(),
633                  2,
634                  frConstraintTypeEnum::frcLef58EolExtensionConstraint,
635                  frBox(500, 50, 690, 150));
636   }
637 }
638 
639 BOOST_DATA_TEST_CASE(eol_ext_paronly, (bdata::make({true, false})), parOnly)
640 {
641   // Setup
642   makeEolExtensionConstraint(2, 100, {101}, {50}, parOnly);
643 
644   frNet* n1 = makeNet("n1");
645 
646   makePathseg(n1, 2, {0, 100}, {500, 100});
647   makePathseg(n1, 2, {520, 290}, {910, 290});
648   runGC();
649 
650   // Test the results
651   auto& markers = worker.getMarkers();
652   if (parOnly)
653     BOOST_TEST(markers.size() == 0);
654   else {
655     BOOST_TEST(markers.size() == 1);
656     testMarker(markers[0].get(),
657                2,
658                frConstraintTypeEnum::frcLef58EolExtensionConstraint,
659                frBox(500, 150, 520, 240));
660   }
661 }
662 // Check for eol keepout violation.
663 BOOST_DATA_TEST_CASE(eol_keepout, (bdata::make({true, false})), legal)
664 {
665   // Setup
666   makeLef58EolKeepOutConstraint(2);
667 
668   frNet* n1 = makeNet("n1");
669 
670   makePathseg(n1, 2, {500, 0}, {500, 500});
671   frCoord x_extra = 0;
672   if (legal)
673     x_extra = 200;
674   makePathseg(n1, 2, {400 + x_extra, 700}, {700 + x_extra, 700});
675 
676   runGC();
677 
678   // Test the results
679   auto& markers = worker.getMarkers();
680   if (legal)
681     BOOST_TEST(markers.size() == 0);
682   else {
683     BOOST_TEST(markers.size() == 1);
684     testMarker(markers[0].get(),
685                2,
686                frConstraintTypeEnum::frcLef58EolKeepOutConstraint,
687                frBox(450, 500, 550, 650));
688   }
689 }
690 
BOOST_AUTO_TEST_CASE(eol_keepout_except_within)691 BOOST_AUTO_TEST_CASE(eol_keepout_except_within)
692 {
693   // Setup
694   makeLef58EolKeepOutConstraint(2, false, true);
695 
696   frNet* n1 = makeNet("n1");
697 
698   makePathseg(n1, 2, {500, 0}, {500, 500});
699   makePathseg(n1, 2, {400, 700}, {700, 700});
700 
701   runGC();
702 
703   auto& markers = worker.getMarkers();
704   BOOST_TEST(markers.size() == 0);
705 }
706 
707 // Check for eol keepout violation CORNERONLY.
708 BOOST_DATA_TEST_CASE(eol_keepout_corner,
709                      (bdata::make({true, false}) * bdata::make({true, false})),
710                      concave,
711                      legal)
712 {
713   // Setup
714   makeLef58EolKeepOutConstraint(2, true);
715 
716   frNet* n1 = makeNet("n1");
717 
718   makePathseg(n1, 2, {500, 0}, {500, 500});
719   frCoord x_extra = 0;
720   if (concave && !legal)
721     makePathseg(n1, 2, {360, 400}, {360, 750});
722   if (!concave && !legal)
723     x_extra = 10;
724   makePathseg(n1, 2, {400 + x_extra, 700}, {600 + x_extra, 700});
725 
726   runGC();
727 
728   // Test the results
729   auto& markers = worker.getMarkers();
730   if (legal)
731     BOOST_TEST(markers.size() == 0);
732   else {
733     BOOST_TEST(markers.size() == 1);
734     testMarker(markers[0].get(),
735                2,
736                frConstraintTypeEnum::frcLef58EolKeepOutConstraint,
737                frBox(410, 500, 450, 650));
738   }
739 }
740 
741 // Check for an end-of-line (EOL) spacing violation involving one
742 // parallel edge
743 BOOST_DATA_TEST_CASE(eol_parallel_edge, (bdata::make({true, false})), lef58)
744 {
745   // Setup
746   if (lef58)
747     makeLef58SpacingEolParEdgeConstraint(
748         makeLef58SpacingEolConstraint(2), 200, 200);
749   else
750     makeSpacingEndOfLineConstraint(
751         2, /* par_space */ 200, /* par_within */ 200);
752 
753   frNet* n1 = makeNet("n1");
754 
755   makePathseg(n1, 2, {500, 0}, {500, 500});
756   makePathseg(n1, 2, {0, 700}, {1000, 700});
757   makePathseg(n1, 2, {300, 0}, {300, 450});
758 
759   runGC();
760 
761   // Test the results
762   auto& markers = worker.getMarkers();
763   BOOST_TEST(markers.size() == 1);
764   testMarker(markers[0].get(),
765              2,
766              lef58 ? frConstraintTypeEnum::frcLef58SpacingEndOfLineConstraint
767                    : frConstraintTypeEnum::frcSpacingEndOfLineConstraint,
768              frBox(450, 500, 550, 650));
769 }
770 
771 // Check for an end-of-line (EOL) spacing violation involving two
772 // parallel edges
773 BOOST_DATA_TEST_CASE(eol_parallel_two_edge, (bdata::make({true, false})), lef58)
774 {
775   // Setup
776   if (lef58)
777     makeLef58SpacingEolParEdgeConstraint(
778         makeLef58SpacingEolConstraint(2), 200, 200, true);
779   else
780     makeSpacingEndOfLineConstraint(2,
781                                    /* par_space */ 200,
782                                    /* par_within */ 200,
783                                    /* two_edges */ true);
784 
785   frNet* n1 = makeNet("n1");
786 
787   makePathseg(n1, 2, {500, 0}, {500, 500});
788   makePathseg(n1, 2, {0, 700}, {1000, 700});
789   makePathseg(n1, 2, {300, 0}, {300, 450});
790   makePathseg(n1, 2, {700, 0}, {700, 450});
791 
792   runGC();
793 
794   // Test the results
795   auto& markers = worker.getMarkers();
796   BOOST_TEST(markers.size() == 1);
797   testMarker(markers[0].get(),
798              2,
799              lef58 ? frConstraintTypeEnum::frcLef58SpacingEndOfLineConstraint
800                    : frConstraintTypeEnum::frcSpacingEndOfLineConstraint,
801              frBox(450, 500, 550, 650));
802 }
803 
804 BOOST_DATA_TEST_CASE(eol_min_max,
805                      (bdata::make({true, false}) * bdata::make({true, false})
806                       * bdata::make({true, false})),
807                      max,
808                      twoSides,
809                      legal)
810 {
811   makeLef58SpacingEolMinMaxLenConstraint(
812       makeLef58SpacingEolConstraint(2), 500, max, twoSides);
813   frNet* n1 = makeNet("n1");
814   frCoord y = 500;
815   if (twoSides)  // both sides need to meet minMax for eolSpacing to be
816                  // triggered and one of them need to violate minMax for
817                  // eolSpacing to be neglected
818   {
819     if (max && legal)
820       y += 10;  // right(510) > max(500) --> minMax violated --> legal
821     else if (!max && !legal)
822       y += 100;      // right(600) & left(500) >= min(500) --> minMax is met
823                      // --> illegal
824   } else if (legal)  // both sides need to violate minMax to have no
825                      // eolSpacing violations
826   {
827     if (max)
828       y += 110;  // right(610) & left(510) > max(500)
829     else
830       y -= 10;  // right(490) & left(390) < min(500)
831   }
832   makePathseg(n1, 2, {500, 0}, {500, y});
833   makePathseg(n1, 2, {0, 700}, {1000, 700});
834 
835   makePathseg(n1, 2, {0, 50}, {450, 50});
836   runGC();
837 
838   // Test the results
839   auto& markers = worker.getMarkers();
840   if (legal)
841     BOOST_TEST(markers.size() == 0);
842   else {
843     BOOST_TEST(markers.size() == 1);
844     if (markers.size() == 1)
845       testMarker(markers[0].get(),
846                  2,
847                  frConstraintTypeEnum::frcLef58SpacingEndOfLineConstraint,
848                  frBox(450, y, 550, 650));
849   }
850 }
851 BOOST_DATA_TEST_CASE(eol_enclose_cut,
852                      (bdata::make({0, 350})) ^ (bdata::make({true, false})),
853                      y,
854                      legal)
855 {
856   addLayer(design->getTech(), "v2", frLayerTypeEnum::CUT);
857   addLayer(design->getTech(), "m2", frLayerTypeEnum::ROUTING);
858   makeLef58SpacingEolCutEncloseConstraint(makeLef58SpacingEolConstraint(4));
859   frNet* n1 = makeNet("n1");
860   frViaDef* vd = makeViaDef("v", 3, {0, 0}, {100, 100});
861 
862   makePathseg(n1, 4, {500, 0}, {500, 500});
863   makePathseg(n1, 4, {0, 700}, {1000, 700});
864   auto v = makeVia(vd, n1, {400, y});
865   runGC();
866   auto& markers = worker.getMarkers();
867   if (legal)
868     BOOST_TEST(markers.size() == 0);
869   else {
870     BOOST_TEST(markers.size() == 1);
871     if (markers.size() == 1)
872       testMarker(markers[0].get(),
873                  4,
874                  frConstraintTypeEnum::frcLef58SpacingEndOfLineConstraint,
875                  frBox(450, 500, 550, 650));
876   }
877 }
878 BOOST_AUTO_TEST_SUITE_END();
879