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