1 /*
2     PADRING -- a padring generator for ASICs.
3 
4     Copyright (c) 2019, Niels Moseley <niels@symbioticeda.com>
5 
6     Permission to use, copy, modify, and/or distribute this software for any
7     purpose with or without fee is hereby granted, provided that the above
8     copyright notice and this permission notice appear in all copies.
9 
10     THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11     WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12     MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13     ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14     WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15     ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16     OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 
18     Changelog:
19 
20     0.02b - first release.
21     0.02c - exit when space can't be filled by filler cells.
22 
23 */
24 
25 #include <iostream>
26 #include <fstream>
27 
28 #define __PGMVERSION__ "0.02c"
29 
30 #include "logging.h"
31 
32 #include "cxxopts.h"
33 #include "prlefreader.h"
34 #include "configreader.h"
35 #include "layout.h"
36 #include "padringdb.h"
37 #include "svgwriter.h"
38 #include "defwriter.h"
39 #include "fillerhandler.h"
40 #include "debugutils.h"
41 #include "gds2/gds2writer.h"
42 
main(int argc,char * argv[])43 int main(int argc, char *argv[])
44 {
45     setLogLevel(LOG_INFO);
46 
47     cxxopts::Options options("padring","PADRING - Symbiotic EDA GmbH\ngenerates a GDS2 file containing a padring");
48 
49     options
50         .positional_help("config_file")
51         .show_positional_help();
52 
53     options.add_options()
54         ("h,help", "Print help")
55         ("L,lef", "LEF file", cxxopts::value<std::vector<std::string>>())
56         ("o,output", "GDS2 output file", cxxopts::value<std::string>())
57         ("svg", "SVG output file", cxxopts::value<std::string>())
58         ("def", "DEF output file", cxxopts::value<std::string>())
59         ("q,quiet", "produce no console output")
60         ("v,verbose", "produce verbose output")
61         ("filler", "set the filler cell prefix", cxxopts::value<std::vector<std::string>>())
62         ("positional",
63             "", cxxopts::value<std::vector<std::string>>());
64 
65     options.parse_positional({"configfile", "positional"});
66 
67     auto cmdresult = options.parse(argc, argv);
68 
69     if ((cmdresult.count("help")>0) ||
70         (cmdresult.count("positional")!=1))
71     {
72         std::cout << options.help({"", "Group"}) << std::endl;
73         exit(0);
74     }
75 
76     /** set log level according to command line options */
77     if (cmdresult.count("quiet") > 0)
78     {
79         setLogLevel(LOG_QUIET);
80     }
81     else if (cmdresult.count("verbose") > 0)
82     {
83         setLogLevel(LOG_VERBOSE);
84     }
85 
86     ////////////////////////////////////////////////////////////////////////////////
87     // Program banner
88     ////////////////////////////////////////////////////////////////////////////////
89     doLog(LOG_INFO,"PADRING version " __PGMVERSION__ " - compiled on " __DATE__ "\n");
90     doLog(LOG_INFO,"Symbiotic EDA GmbH\n\n");
91 
92     if (cmdresult.count("lef") < 1)
93     {
94         std::cout << "You must specify at least one LEF file containing the ASIC cells";
95         exit(0);
96     }
97 
98     PadringDB padring;
99 
100     double LEFDatabaseUnits = 0.0;
101 
102     // read the cells from the LEF files
103     // and save the most recent database units figure along the way..
104     auto &leffiles = cmdresult["lef"].as<std::vector<std::string> >();
105     for(auto leffile : leffiles)
106     {
107         std::ifstream lefstream(leffile, std::ifstream::in);
108         doLog(LOG_INFO, "Reading LEF %s\n", leffile.c_str());
109         padring.m_lefreader.parse(lefstream);
110         if (padring.m_lefreader.m_lefDatabaseUnits > 0.0)
111         {
112             LEFDatabaseUnits = padring.m_lefreader.m_lefDatabaseUnits;
113         }
114     }
115 
116     doLog(LOG_INFO,"%d cells read\n", padring.m_lefreader.m_cells.size());
117 
118     auto& v = cmdresult["positional"].as<std::vector<std::string> >();
119     std::string configFileName = v[0];
120 
121     std::ifstream configStream(configFileName, std::ifstream::in);
122     if (!padring.parse(configStream))
123     {
124         doLog(LOG_ERROR,"Cannot parse configuration file -- aborting\n");
125         exit(1);
126     }
127 
128     // if an explicit filler cell prefix was not given,
129     // search the cell database for filler cells
130     FillerHandler fillerHandler;
131     if (cmdresult.count("filler") == 0)
132     {
133         for(auto lefCell : padring.m_lefreader.m_cells)
134         {
135             if (lefCell.second->m_isFiller)
136             {
137                 fillerHandler.addFillerCell(lefCell.first, lefCell.second->m_sx);
138             }
139         }
140     }
141     else
142     {
143         // use the provided filler cell prefix to search for filler cells
144         for(auto lefCell : padring.m_lefreader.m_cells)
145         {
146             // match prefix
147             if (lefCell.first.rfind(padring.m_fillerPrefix, 0) == 0)
148             {
149                 fillerHandler.addFillerCell(lefCell.first, lefCell.second->m_sx);
150             }
151         }
152     }
153 
154     doLog(LOG_INFO, "Found %d filler cells\n", fillerHandler.getCellCount());
155 
156     if (fillerHandler.getCellCount() == 0)
157     {
158         doLog(LOG_ERROR, "Cannot proceed without filler cells. Please use the --filler option to explicitly specify a filler cell prefix\n");
159         exit(1);
160     }
161 
162     // check die size
163     if ((padring.m_dieWidth < 1.0e-6) || (padring.m_dieHeight < 1.0e-6))
164     {
165         doLog(LOG_ERROR, "Die area was not specified! - aborting.\n");
166         exit(1);
167     }
168 
169     // generate report
170     doLog(LOG_INFO,"Die area        : %f x %f microns\n", padring.m_dieWidth, padring.m_dieHeight);
171     doLog(LOG_INFO,"Grid            : %f microns\n", padring.m_grid);
172     doLog(LOG_INFO,"Padring cells   : %d\n", padring.getPadCellCount());
173     doLog(LOG_INFO,"Smallest filler : %f microns\n", fillerHandler.getSmallestWidth());
174 
175     padring.doLayout();
176 
177     // get corners
178     LayoutItem *topleft  = padring.m_north.getFirstCorner();
179     LayoutItem *topright = padring.m_north.getLastCorner();
180     LayoutItem *bottomleft  = padring.m_south.getFirstCorner();
181     LayoutItem *bottomright = padring.m_south.getLastCorner();
182 
183     // write the padring to an SVG file
184     std::ofstream svgos;
185     if (cmdresult.count("svg") != 0)
186     {
187         doLog(LOG_INFO,"Writing padring to SVG file: %s\n", cmdresult["svg"].as<std::string>().c_str());
188         svgos.open(cmdresult["svg"].as<std::string>(), std::ofstream::out);
189         if (!svgos.is_open())
190         {
191             doLog(LOG_ERROR, "Cannot open SVG file for writing!\n");
192             exit(1);
193         }
194     }
195 
196     // write the padring to an DEF file
197     std::ofstream defos;
198     if (cmdresult.count("def") != 0)
199     {
200         doLog(LOG_INFO,"Writing padring to DEF file: %s\n", cmdresult["def"].as<std::string>().c_str());
201         defos.open(cmdresult["def"].as<std::string>(), std::ofstream::out);
202         if (!defos.is_open())
203         {
204             doLog(LOG_ERROR, "Cannot open DEF file for writing!\n");
205             exit(1);
206         }
207     }
208 
209     SVGWriter svg(svgos, padring.m_dieWidth, padring.m_dieHeight);
210     DEFWriter def(defos, padring.m_dieWidth, padring.m_dieHeight);
211     def.setDatabaseUnits(LEFDatabaseUnits);
212     def.setDesignName(padring.m_designName);
213 
214     // emit GDS2 and SVG
215     GDS2Writer *writer = nullptr;
216 
217     if (cmdresult.count("output")> 0)
218     {
219         doLog(LOG_INFO,"Writing padring to GDS2 file: %s\n", cmdresult["output"].as<std::string>().c_str());
220         writer = GDS2Writer::open(cmdresult["output"].as<std::string>(),
221             padring.m_designName);
222     }
223 
224     if (writer != nullptr) writer->writeCell(topleft);
225     if (writer != nullptr) writer->writeCell(topright);
226     if (writer != nullptr) writer->writeCell(bottomleft);
227     if (writer != nullptr) writer->writeCell(bottomright);
228 
229     svg.writeCell(topleft);
230     svg.writeCell(topright);
231     svg.writeCell(bottomleft);
232     svg.writeCell(bottomright);
233 
234     def.writeCell(topleft);
235     def.writeCell(topright);
236     def.writeCell(bottomleft);
237     def.writeCell(bottomright);
238 
239     double north_y = padring.m_dieHeight;
240     for(auto item : padring.m_north)
241     {
242         if (item->m_ltype == LayoutItem::TYPE_CELL)
243         {
244             if (writer != nullptr) writer->writeCell(item);
245             svg.writeCell(item);
246             def.writeCell(item);
247         }
248         else if ((item->m_ltype == LayoutItem::TYPE_FIXEDSPACE) || (item->m_ltype == LayoutItem::TYPE_FLEXSPACE))
249         {
250             // do fillers
251             double space = item->m_size;
252             double pos = item->m_x;
253             while(space > 0)
254             {
255                 std::string cellName;
256                 double width = fillerHandler.getFillerCell(space, cellName);
257                 if (width > 0.0)
258                 {
259                     LayoutItem filler(LayoutItem::TYPE_FILLER);
260                     filler.m_cellname = cellName;
261                     filler.m_x = pos;
262                     filler.m_y = north_y;
263                     filler.m_size = width;
264                     filler.m_location = "N";
265                     filler.m_lefinfo = padring.m_lefreader.getCellByName(cellName);
266                     if (writer != nullptr) writer->writeCell(&filler);
267                     svg.writeCell(&filler);
268                     def.writeCell(&filler);
269                     space -= width;
270                     pos += width;
271                 }
272                 else
273                 {
274                     doLog(LOG_ERROR, "Cannot find filler cell that fits remaining width %f\n", space);
275                     exit(1);
276                 }
277             }
278         }
279     }
280 
281     double south_y = 0;
282     for(auto item : padring.m_south)
283     {
284         if (item->m_ltype == LayoutItem::TYPE_CELL)
285         {
286             if (writer != nullptr) writer->writeCell(item);
287             svg.writeCell(item);
288             def.writeCell(item);
289         }
290         else if ((item->m_ltype == LayoutItem::TYPE_FIXEDSPACE) || (item->m_ltype == LayoutItem::TYPE_FLEXSPACE))
291         {
292             // do fillers
293             double space = item->m_size;
294             double pos = item->m_x;
295             while(space > 0)
296             {
297                 std::string cellName;
298                 double width = fillerHandler.getFillerCell(space, cellName);
299                 if (width > 0.0)
300                 {
301                     LayoutItem filler(LayoutItem::TYPE_FILLER);
302                     filler.m_cellname = cellName;
303                     filler.m_x = pos;
304                     filler.m_y = south_y;
305                     filler.m_size = width;
306                     filler.m_location = "S";
307                     filler.m_lefinfo = padring.m_lefreader.getCellByName(cellName);
308                     if (writer != nullptr) writer->writeCell(&filler);
309                     svg.writeCell(&filler);
310                     def.writeCell(&filler);
311                     space -= width;
312                     pos += width;
313                 }
314                 else
315                 {
316                     doLog(LOG_ERROR, "Cannot find filler cell that fits remaining width %f\n", space);
317                     exit(1);
318                 }
319             }
320         }
321     }
322 
323     double west_x = 0;
324     for(auto item : padring.m_west)
325     {
326         if (item->m_ltype == LayoutItem::TYPE_CELL)
327         {
328             if (writer != nullptr) writer->writeCell(item);
329             svg.writeCell(item);
330             def.writeCell(item);
331         }
332         else if ((item->m_ltype == LayoutItem::TYPE_FIXEDSPACE) || (item->m_ltype == LayoutItem::TYPE_FLEXSPACE))
333         {
334             // do fillers
335             double space = item->m_size;
336             double pos = item->m_y;
337             while(space > 0)
338             {
339                 std::string cellName;
340                 double width = fillerHandler.getFillerCell(space, cellName);
341                 if (width > 0.0)
342                 {
343                     LayoutItem filler(LayoutItem::TYPE_FILLER);
344                     filler.m_cellname = cellName;
345                     filler.m_x = west_x;
346                     filler.m_y = pos;
347                     filler.m_size = width;
348                     filler.m_location = "W";
349                     filler.m_lefinfo = padring.m_lefreader.getCellByName(cellName);
350                     if (writer != nullptr) writer->writeCell(&filler);
351                     svg.writeCell(&filler);
352                     def.writeCell(&filler);
353                     space -= width;
354                     pos += width;
355                 }
356                 else
357                 {
358                     doLog(LOG_ERROR, "Cannot find filler cell that fits remaining width %f\n", space);
359                     exit(1);
360                 }
361             }
362         }
363     }
364 
365     double east_x = padring.m_dieWidth;
366     for(auto item : padring.m_east)
367     {
368         if (item->m_ltype == LayoutItem::TYPE_CELL)
369         {
370             if (writer != nullptr) writer->writeCell(item);
371             svg.writeCell(item);
372             def.writeCell(item);
373         }
374         else if ((item->m_ltype == LayoutItem::TYPE_FIXEDSPACE) || (item->m_ltype == LayoutItem::TYPE_FLEXSPACE))
375         {
376             // do fillers
377             double space = item->m_size;
378             double pos = item->m_y;
379             while(space > 0)
380             {
381                 std::string cellName;
382                 double width = fillerHandler.getFillerCell(space, cellName);
383                 if (width > 0.0)
384                 {
385                     LayoutItem filler(LayoutItem::TYPE_FILLER);
386                     filler.m_cellname = cellName;
387                     filler.m_x = east_x;
388                     filler.m_y = pos;
389                     filler.m_size = width;
390                     filler.m_location = "E";
391                     filler.m_lefinfo = padring.m_lefreader.getCellByName(cellName);
392                     if (writer != nullptr) writer->writeCell(&filler);
393                     svg.writeCell(&filler);
394                     def.writeCell(&filler);
395                     space -= width;
396                     pos += width;
397                 }
398                 else
399                 {
400                     doLog(LOG_ERROR, "Cannot find filler cell that fits remaining width %f\n", space);
401                     exit(1);
402                 }
403             }
404         }
405     }
406 
407     if (writer != nullptr) delete writer;
408 
409     for(auto cell : padring.m_lefreader.m_cells)
410     {
411         DebugUtils::dumpToConsole(cell.second);
412     }
413 
414     return 0;
415 }
416 
417