1 /////////////////////////////////////////////////////////////////////////////
2 //
3 // BSD 3-Clause License
4 //
5 // Copyright (c) 2019, The Regents of the University of California
6 // All rights reserved.
7 //
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions are met:
10 //
11 // * Redistributions of source code must retain the above copyright notice, this
12 //   list of conditions and the following disclaimer.
13 //
14 // * Redistributions in binary form must reproduce the above copyright notice,
15 //   this list of conditions and the following disclaimer in the documentation
16 //   and/or other materials provided with the distribution.
17 //
18 // * Neither the name of the copyright holder nor the names of its
19 //   contributors may be used to endorse or promote products derived from
20 //   this software without specific prior written permission.
21 //
22 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 // POSSIBILITY OF SUCH DAMAGE.
33 //
34 ///////////////////////////////////////////////////////////////////////////////
35 
36 #include "par/PartitionMgr.h"
37 
38 #include "opendb/db.h"
39 #include "utl/Logger.h"
40 #include "ord/OpenRoad.hh"
41 #include "db_sta/dbNetwork.hh"
42 #include "db_sta/dbSta.hh"
43 
44 #include "sta/MakeConcreteNetwork.hh"
45 #include "sta/Liberty.hh"
46 #include "sta/VerilogWriter.hh"
47 #include "sta/PortDirection.hh"
48 #include "sta/ParseBus.hh"
49 
50 #include <map>
51 #include <set>
52 #include <algorithm>
53 
54 namespace par {
55 
56 using utl::PAR;
57 
58 using sta::Network;
59 using sta::ConcreteNetwork;
60 using sta::Library;
61 using sta::Cell;
62 using sta::Instance;
63 using sta::Net;
64 using sta::Port;
65 using sta::PortDirection;
66 using sta::writeVerilog;
67 using sta::NetTermIterator;
68 using sta::NetPinIterator;
69 using sta::InstancePinIterator;
70 using sta::Term;
71 using sta::Pin;
72 using sta::NetworkReader;
73 
74 using sta::isBusName;
75 using sta::parseBusName;
76 
77 using sta::dbNetwork;
78 using sta::dbSta;
79 
80 using odb::dbBlock;
81 using odb::dbInst;
82 using odb::dbIntProperty;
83 
84 // determine the required direction of a port.
85 PortDirection*
determinePortDirection(Net * net,std::set<Instance * > * insts,dbNetwork * db_network)86 determinePortDirection(Net* net, std::set<Instance*>* insts, dbNetwork* db_network) {
87   bool local_only = true;
88   bool locally_driven = false;
89   bool externally_driven = false;
90 
91   NetTermIterator* term_iter = db_network->termIterator(net);
92   while (term_iter->hasNext()) {
93     Term* term = term_iter->next();
94     PortDirection* dir = db_network->direction(db_network->pin(term));
95     if (dir == PortDirection::bidirect() ||
96         dir == PortDirection::input()) {
97       externally_driven = true;
98       local_only = false;
99     }
100   }
101   delete term_iter;
102 
103   if (insts != nullptr) {
104     NetPinIterator* pin_iter = db_network->pinIterator(net);
105     while (pin_iter->hasNext()) {
106       Pin* pin = pin_iter->next();
107       PortDirection* dir = db_network->direction(pin);
108 
109       Instance* inst = db_network->instance(pin);
110 
111       // check if instance is not present in the current partition, port will be needed
112       if (insts->find(inst) == insts->end()) {
113         local_only = false;
114         if (dir == PortDirection::output() ||
115             dir == PortDirection::bidirect()) {
116           externally_driven = true;
117         }
118         else {
119           if (dir == PortDirection::output() ||
120               dir == PortDirection::bidirect()) {
121             locally_driven = true;
122           }
123         }
124       }
125     }
126     delete pin_iter;
127   }
128 
129   // no port is needed
130   if (local_only)
131     return nullptr;
132 
133   if (locally_driven && externally_driven) {
134     return PortDirection::bidirect();
135   } else if (externally_driven) {
136     return PortDirection::input();
137   } else { // internally driven
138     return PortDirection::output();
139   }
140 }
141 
142 // find the correct brackets used in the liberty libraries.
143 void
determineLibraryBrackets(dbNetwork * db_network,char * left,char * right)144 determineLibraryBrackets(dbNetwork* db_network, char* left, char* right) {
145   *left = '[';
146   *right = ']';
147 
148   sta::LibertyLibraryIterator* lib_iter = db_network->libertyLibraryIterator();
149   while (lib_iter->hasNext()) {
150     sta::LibertyLibrary* lib = lib_iter->next();
151     *left = lib->busBrktLeft();
152     *right = lib->busBrktRight();
153   }
154   delete lib_iter;
155 }
156 
157 Instance*
buildPartitionedInstance(const char * name,const char * port_prefix,sta::Library * library,sta::NetworkReader * network,sta::Instance * parent,std::set<Instance * > * insts,std::map<Net *,Port * > * port_map)158 PartitionMgr::buildPartitionedInstance(
159     const char* name,
160     const char* port_prefix,
161     sta::Library* library,
162     sta::NetworkReader* network,
163     sta::Instance* parent,
164     std::set<Instance*>* insts,
165     std::map<Net*, Port*>* port_map) {
166 
167   // setup access
168   dbNetwork* db_network = ord::OpenRoad::openRoad()->getSta()->getDbNetwork();
169 
170   // build cell
171   Cell* cell = network->makeCell(library, name, false, nullptr);
172 
173   // add global ports
174   InstancePinIterator* pin_iter = db_network->pinIterator(db_network->topInstance());
175   while (pin_iter->hasNext()) {
176     Pin* pin = pin_iter->next();
177 
178     bool add_port = parent == nullptr; // add port if parent
179     Net* net = db_network->net(db_network->term(pin));
180     if (net != nullptr && insts != nullptr) {
181       NetPinIterator* net_pin_iter = db_network->pinIterator(net);
182       while (net_pin_iter->hasNext()) {
183         if (add_port)
184           break;
185 
186         // check if port is connected to instance in this partition
187         if (insts->find(db_network->instance(net_pin_iter->next()))  != insts->end()) {
188           add_port = true;
189           break;
190         }
191       }
192       delete net_pin_iter;
193     }
194 
195     if (add_port) {
196       const char* portname = db_network->name(pin);
197 
198       Port* port = network->makePort(cell, portname);
199       // copy exactly the parent port direction
200       network->setDirection(port, db_network->direction(pin));
201       if (parent != nullptr) {
202         PortDirection* sub_module_dir = determinePortDirection(net, insts, db_network);
203         if (sub_module_dir != nullptr)
204           network->setDirection(port, sub_module_dir);
205       }
206 
207       if (port_map != nullptr)
208         port_map->insert({net, port});
209     }
210   }
211   delete pin_iter;
212 
213   // make internal ports for partitions and if port is not needed.
214   if (insts != nullptr) {
215     for (Instance* inst : *insts) {
216       InstancePinIterator* pin_iter = db_network->pinIterator(inst);
217       while (pin_iter->hasNext()) {
218         Net* net = db_network->net(pin_iter->next());
219         if (net != nullptr && // connected
220             port_map->find(net) == port_map->end()) {// port not present
221           // check if connected to anything in a different partition
222           bool added_internal_port = false;
223 
224           NetPinIterator* net_pin_iter = db_network->pinIterator(net);
225           while (net_pin_iter->hasNext()) {
226             if (added_internal_port)
227               break;
228 
229             Net* net = db_network->net(net_pin_iter->next());
230             PortDirection* port_dir = determinePortDirection(net, insts, db_network);
231             if (port_dir != nullptr) {
232               std::string port_name = port_prefix;
233               port_name += db_network->name(net);
234 
235               Port* port = network->makePort(cell, port_name.c_str());
236               network->setDirection(port, port_dir);
237 
238               port_map->insert({net, port});
239 
240               added_internal_port = true;
241               break;
242             }
243           }
244           delete net_pin_iter;
245         }
246       }
247       delete pin_iter;
248     }
249   }
250 
251   if (parent != nullptr) {
252     // loop over buses and to ensure all bit ports are created, only needed for partitioned modules
253     char path_escape = db_network->pathEscape();
254     char left_bracket;
255     char right_bracket;
256     determineLibraryBrackets(db_network, &left_bracket, &right_bracket);
257     std::map<std::string, std::vector<Port*>> port_buses;
258     for (auto& [net, port] : *port_map) {
259       std::string portname = network->name(port);
260 
261       // check if bus and get name
262       if (isBusName(portname.c_str(), left_bracket, right_bracket, path_escape)) {
263         char* bus_name;
264         int idx;
265         parseBusName(portname.c_str(), left_bracket, right_bracket, path_escape, bus_name, idx);
266         portname = bus_name;
267         delete bus_name;
268 
269         if (port_buses.find(portname) == port_buses.end()) {
270           port_buses[portname] = std::vector<Port*>();
271         }
272         port_buses[portname].push_back(port);
273       }
274     }
275     for (auto& [bus, ports] : port_buses) {
276       std::set<int> port_idx;
277       std::set<PortDirection*> port_dirs;
278       for (Port* port : ports) {
279         char* bus_name;
280         int idx;
281         parseBusName(network->name(port), left_bracket, right_bracket, path_escape, bus_name, idx);
282         delete bus_name;
283 
284         port_idx.insert(idx);
285 
286         port_dirs.insert(network->direction(port));
287       }
288 
289       // determine real direction of port
290       PortDirection* overall_direction = nullptr;
291       if (port_dirs.size() == 1) // only one direction is used.
292         overall_direction = *port_dirs.begin();
293       else
294         overall_direction = PortDirection::bidirect();
295 
296       // set port direction to match
297       for (Port* port : ports)
298         network->setDirection(port, overall_direction);
299 
300       // fill in missing ports in bus
301       const auto [min_idx, max_idx] = std::minmax_element(port_idx.begin(), port_idx.end());
302       for (int idx = *min_idx; idx <= *max_idx; idx++) {
303         if (port_idx.find(idx) == port_idx.end()) {
304           // build missing port
305           std::string portname = bus;
306           portname += left_bracket + std::to_string(idx) + right_bracket;
307           Port* port = network->makePort(cell, portname.c_str());
308           network->setDirection(port, overall_direction);
309         }
310       }
311     }
312   }
313   network->groupBusPorts(cell);
314 
315   // build instance
316   std::string instname = name;
317   instname += "_inst";
318   Instance* inst = network->makeInstance(cell, instname.c_str(), parent);
319 
320   if (port_map != nullptr) {
321     // create nets for ports in cell
322     for (auto& [db_net, port] : *port_map)
323       network->makeNet(network->name(port), inst);
324   }
325 
326   if (insts != nullptr) {
327     // create and connect instances
328     for (Instance* instance : *insts) {
329       Instance* leaf_inst = network->makeInstance(db_network->cell(instance),
330                                                   db_network->name(instance),
331                                                   inst);
332 
333       InstancePinIterator* pin_iter = db_network->pinIterator(instance);
334       while (pin_iter->hasNext()) {
335         Pin* pin = pin_iter->next();
336         Net* net = db_network->net(pin);
337         if (net != nullptr) { // connected
338           Port* port = db_network->port(pin);
339 
340           // check if connected to a port
341           auto port_find = port_map->find(net);
342           if (port_find != port_map->end()) {
343             Net* new_net = network->findNet(inst, network->name(port_find->second));
344             network->connect(leaf_inst, port, new_net);
345           }
346           else {
347             Net* new_net = network->findNet(inst, db_network->name(net));
348             if (new_net == nullptr)
349               new_net = network->makeNet(db_network->name(net), inst);
350             network->connect(leaf_inst, port, new_net);
351           }
352         }
353       }
354       delete pin_iter;
355     }
356   }
357 
358   return inst;
359 }
360 
writePartitionVerilog(const char * path,const char * port_prefix,const char * module_suffix)361 void PartitionMgr::writePartitionVerilog(const char* path,
362                                          const char* port_prefix,
363                                          const char* module_suffix) {
364   dbBlock* block = getDbBlock();
365   if (block == nullptr)
366     return;
367 
368   _logger->report("Writing partition to verilog.");
369   // get top module name
370   dbNetwork* db_network = ord::OpenRoad::openRoad()->getSta()->getDbNetwork();
371   std::string top_name = db_network->name(db_network->topInstance());
372 
373   // build partition instance map
374   std::map<long, std::set<Instance*>> instance_map;
375   for (dbInst* inst : block->getInsts()) {
376     dbIntProperty* prop_id = dbIntProperty::find(inst, "partition_id");
377     if (!prop_id) {
378       _logger->warn(PAR, 15, "Property not found for inst {}", inst->getName());
379     }
380     else {
381       long partition = prop_id->getValue();
382       if (instance_map.find(partition) == instance_map.end())
383         instance_map.emplace(partition, std::set<Instance*>());
384 
385       instance_map[partition].insert(db_network->dbToSta(inst));
386     }
387   }
388 
389   // create new network and library
390   NetworkReader* network = sta::makeConcreteNetwork();
391   Library* library = network->makeLibrary("Partitions", nullptr);
392 
393   // new top module
394   Instance* top_inst = buildPartitionedInstance(top_name.c_str(),
395                                                 "", // no changes to port
396                                                 library,
397                                                 network,
398                                                 nullptr, // no parent
399                                                 nullptr,
400                                                 nullptr);
401 
402   // build submodule partitions
403   std::map<long, Instance*> sta_instance_map;
404   std::map<long, std::map<Net*, Port*>> sta_port_map;
405   for (auto& [partition, instances] : instance_map) {
406     std::string cell_name = top_name + module_suffix + std::to_string(partition);
407     sta_port_map[partition] = std::map<Net*, Port*>();
408     sta_instance_map[partition] = buildPartitionedInstance(cell_name.c_str(),
409                                                            port_prefix,
410                                                            library,
411                                                            network,
412                                                            top_inst,
413                                                            &instances,
414                                                            &sta_port_map[partition]);
415   }
416 
417   // connect submodule partitions in new top module
418   for (auto& [partition, instance] : sta_instance_map) {
419     for (auto& [portnet, port] : sta_port_map[partition]) {
420       const char* net_name = network->name(port);
421 
422       Net* net = network->findNet(top_inst, net_name);
423       if (net == nullptr)
424         net = network->makeNet(net_name, top_inst);
425 
426       network->connect(instance, port, net);
427     }
428   }
429 
430   reinterpret_cast<ConcreteNetwork*>(network)->setTopInstance(top_inst);
431 
432   writeVerilog(path, false, false, {}, network);
433 
434   delete network;
435 }
436 
437 }  // namespace par
438