Skip to content

Commit 729faec

Browse files
upf: add supply-net commands, upf_version, and supply options (#5617)
Implements a conservative, tested subset of the missing UPF surface from issue #5617 that maps onto existing odb structures without a schema bump: New commands: - upf_version (validate known versions, store as block property) - create_supply_port (store as block string-property) - create_supply_net (store as block property, supports -domain/-reuse) - connect_supply_net (validate net/port refs, store connection) New options on existing commands: - create_power_domain -include_scope (maps to '.' element) and -supply (stored as a property on the domain) - set_isolation -isolation_supply / -source / -sink (stored as properties on the dbIsolation object) Supply-net/port constructs have no dedicated odb object, so they are persisted via the generic dbStringProperty mechanism, keeping them queryable and round-trippable through DB save/load without new schema. PST, create_supply_set, set_port_attributes, and add_port_state are deferred (they require new odb schema). See AGENT_REPORT.md. Adds src/upf/test/supply.tcl exercising all new commands/options and asserting the resulting odb state, registered in both CMake and Bazel. Signed-off-by: Saurav Singh <saurav.singh@fermions.co>
1 parent 4c26918 commit 729faec

9 files changed

Lines changed: 539 additions & 3 deletions

File tree

src/upf/include/upf/upf.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,49 @@ bool create_logic_port(utl::Logger* logger,
2929
const std::string& name,
3030
const std::string& direction);
3131

32+
// Accepts and validates the UPF version declared by the file.
33+
// OpenROAD does not implement version-specific behavior, so the version is
34+
// stored on the block (as a property) and otherwise treated as a no-op.
35+
bool set_upf_version(utl::Logger* logger,
36+
odb::dbBlock* block,
37+
const std::string& version);
38+
39+
// Declares a supply port. Supply ports/nets are tracked as block properties
40+
// (no dedicated odb object exists) so that connect_supply_net can validate
41+
// references and write_upf can round-trip them.
42+
bool create_supply_port(utl::Logger* logger,
43+
odb::dbBlock* block,
44+
const std::string& name,
45+
const std::string& direction);
46+
47+
// Declares a supply net (optionally belonging to a power domain).
48+
bool create_supply_net(utl::Logger* logger,
49+
odb::dbBlock* block,
50+
const std::string& name,
51+
const std::string& domain,
52+
bool reuse);
53+
54+
// Connects a previously declared supply net to a supply port.
55+
bool connect_supply_net(utl::Logger* logger,
56+
odb::dbBlock* block,
57+
const std::string& net,
58+
const std::string& port);
59+
60+
// Stores the -supply association of a power domain. No supply-set object
61+
// exists in odb, so the handle list is persisted as a property on the domain.
62+
bool set_power_domain_supply(utl::Logger* logger,
63+
odb::dbBlock* block,
64+
const std::string& domain,
65+
const std::string& supply);
66+
67+
// Stores a supply association (-isolation_supply / -source / -sink) of an
68+
// isolation strategy as a property, since odb has no field for it.
69+
bool set_isolation_supply(utl::Logger* logger,
70+
odb::dbBlock* block,
71+
const std::string& isolation,
72+
const std::string& key,
73+
const std::string& value);
74+
3275
bool create_power_switch(utl::Logger* logger,
3376
odb::dbBlock* block,
3477
const std::string& name,

src/upf/src/upf.cpp

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,193 @@ bool create_logic_port(utl::Logger* logger,
6666
return true;
6767
}
6868

69+
namespace {
70+
71+
// Property-name prefixes used to persist supply-net constructs on the block.
72+
// OpenROAD's odb schema has no dedicated supply-net/supply-port object, so we
73+
// piggy-back on the generic dbProperty mechanism. This keeps the information
74+
// queryable and round-trippable through DB save/load without a schema bump.
75+
constexpr char kUpfVersionProp[] = "__upf_version";
76+
constexpr char kSupplyPortPrefix[] = "__upf_supply_port:";
77+
constexpr char kSupplyNetPrefix[] = "__upf_supply_net:";
78+
constexpr char kSupplyNetConnPrefix[] = "__upf_supply_net_conn:";
79+
80+
void setStringProperty(odb::dbBlock* block,
81+
const std::string& name,
82+
const std::string& value)
83+
{
84+
odb::dbStringProperty* prop = odb::dbStringProperty::find(block, name.c_str());
85+
if (prop != nullptr) {
86+
prop->setValue(value.c_str());
87+
} else {
88+
odb::dbStringProperty::create(block, name.c_str(), value.c_str());
89+
}
90+
}
91+
92+
} // namespace
93+
94+
bool set_upf_version(utl::Logger* logger,
95+
odb::dbBlock* block,
96+
const std::string& version)
97+
{
98+
// Accept the documented UPF versions. Anything else is flagged but still
99+
// stored, so that strict UPF files do not silently misbehave.
100+
static const std::vector<std::string> kKnownVersions
101+
= {"1.0", "2.0", "2.1", "3.0", "3.1"};
102+
bool known = false;
103+
for (const auto& v : kKnownVersions) {
104+
if (v == version) {
105+
known = true;
106+
break;
107+
}
108+
}
109+
if (!known) {
110+
logger->warn(utl::UPF,
111+
80,
112+
"Unrecognized UPF version '{}'; accepting it as-is. OpenROAD "
113+
"does not implement version-specific behavior.",
114+
version);
115+
}
116+
setStringProperty(block, kUpfVersionProp, version);
117+
return true;
118+
}
119+
120+
bool create_supply_port(utl::Logger* logger,
121+
odb::dbBlock* block,
122+
const std::string& name,
123+
const std::string& direction)
124+
{
125+
if (!direction.empty() && direction != "in" && direction != "out") {
126+
logger->warn(utl::UPF,
127+
81,
128+
"Invalid -direction '{}' for supply port {} (expected in or "
129+
"out)",
130+
direction,
131+
name);
132+
return false;
133+
}
134+
135+
const std::string prop_name = kSupplyPortPrefix + name;
136+
if (odb::dbStringProperty::find(block, prop_name.c_str()) != nullptr) {
137+
logger->warn(utl::UPF, 82, "Supply port {} already exists", name);
138+
return false;
139+
}
140+
odb::dbStringProperty::create(block, prop_name.c_str(), direction.c_str());
141+
return true;
142+
}
143+
144+
bool create_supply_net(utl::Logger* logger,
145+
odb::dbBlock* block,
146+
const std::string& name,
147+
const std::string& domain,
148+
bool reuse)
149+
{
150+
if (!domain.empty() && block->findPowerDomain(domain.c_str()) == nullptr) {
151+
logger->warn(utl::UPF,
152+
83,
153+
"Couldn't retrieve power domain {} while creating supply net "
154+
"{}",
155+
domain,
156+
name);
157+
return false;
158+
}
159+
160+
const std::string prop_name = kSupplyNetPrefix + name;
161+
odb::dbStringProperty* existing
162+
= odb::dbStringProperty::find(block, prop_name.c_str());
163+
if (existing != nullptr) {
164+
if (!reuse) {
165+
logger->warn(utl::UPF, 84, "Supply net {} already exists", name);
166+
return false;
167+
}
168+
// -reuse: tolerate the redeclaration, refresh the domain association.
169+
existing->setValue(domain.c_str());
170+
return true;
171+
}
172+
odb::dbStringProperty::create(block, prop_name.c_str(), domain.c_str());
173+
return true;
174+
}
175+
176+
bool connect_supply_net(utl::Logger* logger,
177+
odb::dbBlock* block,
178+
const std::string& net,
179+
const std::string& port)
180+
{
181+
const std::string net_prop = kSupplyNetPrefix + net;
182+
if (odb::dbStringProperty::find(block, net_prop.c_str()) == nullptr) {
183+
logger->warn(utl::UPF,
184+
85,
185+
"Couldn't retrieve supply net {} while connecting it",
186+
net);
187+
return false;
188+
}
189+
190+
if (!port.empty()) {
191+
const std::string port_prop = kSupplyPortPrefix + port;
192+
if (odb::dbStringProperty::find(block, port_prop.c_str()) == nullptr) {
193+
logger->warn(utl::UPF,
194+
86,
195+
"Couldn't retrieve supply port {} while connecting supply "
196+
"net {}",
197+
port,
198+
net);
199+
return false;
200+
}
201+
}
202+
203+
setStringProperty(block, kSupplyNetConnPrefix + net, port);
204+
return true;
205+
}
206+
207+
bool set_power_domain_supply(utl::Logger* logger,
208+
odb::dbBlock* block,
209+
const std::string& domain,
210+
const std::string& supply)
211+
{
212+
odb::dbPowerDomain* pd = block->findPowerDomain(domain.c_str());
213+
if (pd == nullptr) {
214+
logger->warn(utl::UPF,
215+
87,
216+
"Couldn't retrieve power domain {} while setting -supply",
217+
domain);
218+
return false;
219+
}
220+
odb::dbStringProperty* prop
221+
= odb::dbStringProperty::find(pd, "__upf_supply");
222+
if (prop != nullptr) {
223+
prop->setValue(supply.c_str());
224+
} else {
225+
odb::dbStringProperty::create(pd, "__upf_supply", supply.c_str());
226+
}
227+
return true;
228+
}
229+
230+
bool set_isolation_supply(utl::Logger* logger,
231+
odb::dbBlock* block,
232+
const std::string& isolation,
233+
const std::string& key,
234+
const std::string& value)
235+
{
236+
odb::dbIsolation* iso = block->findIsolation(isolation.c_str());
237+
if (iso == nullptr) {
238+
logger->warn(utl::UPF,
239+
88,
240+
"Couldn't retrieve isolation {} while setting -{}",
241+
isolation,
242+
key);
243+
return false;
244+
}
245+
const std::string prop_name = "__upf_iso_" + key;
246+
odb::dbStringProperty* prop
247+
= odb::dbStringProperty::find(iso, prop_name.c_str());
248+
if (prop != nullptr) {
249+
prop->setValue(value.c_str());
250+
} else {
251+
odb::dbStringProperty::create(iso, prop_name.c_str(), value.c_str());
252+
}
253+
return true;
254+
}
255+
69256
bool create_power_switch(utl::Logger* logger,
70257
odb::dbBlock* block,
71258
const std::string& name,

src/upf/src/upf.i

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,48 @@
3737
getOpenRoad()->getLogger(), db->getChip()->getBlock(), name, direction);
3838
}
3939

40+
void set_upf_version_cmd(const char* version)
41+
{
42+
odb::dbDatabase* db = getOpenRoad()->getDb();
43+
upf::set_upf_version(
44+
getOpenRoad()->getLogger(), db->getChip()->getBlock(), version);
45+
}
46+
47+
void create_supply_port_cmd(const char* name, const char* direction)
48+
{
49+
odb::dbDatabase* db = getOpenRoad()->getDb();
50+
upf::create_supply_port(
51+
getOpenRoad()->getLogger(), db->getChip()->getBlock(), name, direction);
52+
}
53+
54+
void create_supply_net_cmd(const char* name, const char* domain, bool reuse)
55+
{
56+
odb::dbDatabase* db = getOpenRoad()->getDb();
57+
upf::create_supply_net(
58+
getOpenRoad()->getLogger(), db->getChip()->getBlock(), name, domain, reuse);
59+
}
60+
61+
void connect_supply_net_cmd(const char* net, const char* port)
62+
{
63+
odb::dbDatabase* db = getOpenRoad()->getDb();
64+
upf::connect_supply_net(
65+
getOpenRoad()->getLogger(), db->getChip()->getBlock(), net, port);
66+
}
67+
68+
void set_power_domain_supply_cmd(const char* domain, const char* supply)
69+
{
70+
odb::dbDatabase* db = getOpenRoad()->getDb();
71+
upf::set_power_domain_supply(
72+
getOpenRoad()->getLogger(), db->getChip()->getBlock(), domain, supply);
73+
}
74+
75+
void set_isolation_supply_cmd(const char* isolation, const char* key, const char* value)
76+
{
77+
odb::dbDatabase* db = getOpenRoad()->getDb();
78+
upf::set_isolation_supply(
79+
getOpenRoad()->getLogger(), db->getChip()->getBlock(), isolation, key, value);
80+
}
81+
4082
void create_power_switch_cmd(
4183
const char* name, const char* domain)
4284
{

0 commit comments

Comments
 (0)