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