Skip to content

Commit 2958413

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 2958413

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
85+
= odb::dbStringProperty::find(block, name.c_str());
86+
if (prop != nullptr) {
87+
prop->setValue(value.c_str());
88+
} else {
89+
odb::dbStringProperty::create(block, name.c_str(), value.c_str());
90+
}
91+
}
92+
93+
} // namespace
94+
95+
bool set_upf_version(utl::Logger* logger,
96+
odb::dbBlock* block,
97+
const std::string& version)
98+
{
99+
// Accept the documented UPF versions. Anything else is flagged but still
100+
// stored, so that strict UPF files do not silently misbehave.
101+
static const std::vector<std::string> kKnownVersions
102+
= {"1.0", "2.0", "2.1", "3.0", "3.1"};
103+
bool known = false;
104+
for (const auto& v : kKnownVersions) {
105+
if (v == version) {
106+
known = true;
107+
break;
108+
}
109+
}
110+
if (!known) {
111+
logger->warn(utl::UPF,
112+
80,
113+
"Unrecognized UPF version '{}'; accepting it as-is. OpenROAD "
114+
"does not implement version-specific behavior.",
115+
version);
116+
}
117+
setStringProperty(block, kUpfVersionProp, version);
118+
return true;
119+
}
120+
121+
bool create_supply_port(utl::Logger* logger,
122+
odb::dbBlock* block,
123+
const std::string& name,
124+
const std::string& direction)
125+
{
126+
if (!direction.empty() && direction != "in" && direction != "out") {
127+
logger->warn(utl::UPF,
128+
81,
129+
"Invalid -direction '{}' for supply port {} (expected in or "
130+
"out)",
131+
direction,
132+
name);
133+
return false;
134+
}
135+
136+
const std::string prop_name = kSupplyPortPrefix + name;
137+
if (odb::dbStringProperty::find(block, prop_name.c_str()) != nullptr) {
138+
logger->warn(utl::UPF, 82, "Supply port {} already exists", name);
139+
return false;
140+
}
141+
odb::dbStringProperty::create(block, prop_name.c_str(), direction.c_str());
142+
return true;
143+
}
144+
145+
bool create_supply_net(utl::Logger* logger,
146+
odb::dbBlock* block,
147+
const std::string& name,
148+
const std::string& domain,
149+
bool reuse)
150+
{
151+
if (!domain.empty() && block->findPowerDomain(domain.c_str()) == nullptr) {
152+
logger->warn(utl::UPF,
153+
83,
154+
"Couldn't retrieve power domain {} while creating supply net "
155+
"{}",
156+
domain,
157+
name);
158+
return false;
159+
}
160+
161+
const std::string prop_name = kSupplyNetPrefix + name;
162+
odb::dbStringProperty* existing
163+
= odb::dbStringProperty::find(block, prop_name.c_str());
164+
if (existing != nullptr) {
165+
if (!reuse) {
166+
logger->warn(utl::UPF, 84, "Supply net {} already exists", name);
167+
return false;
168+
}
169+
// -reuse: tolerate the redeclaration, refresh the domain association.
170+
existing->setValue(domain.c_str());
171+
return true;
172+
}
173+
odb::dbStringProperty::create(block, prop_name.c_str(), domain.c_str());
174+
return true;
175+
}
176+
177+
bool connect_supply_net(utl::Logger* logger,
178+
odb::dbBlock* block,
179+
const std::string& net,
180+
const std::string& port)
181+
{
182+
const std::string net_prop = kSupplyNetPrefix + net;
183+
if (odb::dbStringProperty::find(block, net_prop.c_str()) == nullptr) {
184+
logger->warn(utl::UPF,
185+
85,
186+
"Couldn't retrieve supply net {} while connecting it",
187+
net);
188+
return false;
189+
}
190+
191+
if (!port.empty()) {
192+
const std::string port_prop = kSupplyPortPrefix + port;
193+
if (odb::dbStringProperty::find(block, port_prop.c_str()) == nullptr) {
194+
logger->warn(utl::UPF,
195+
86,
196+
"Couldn't retrieve supply port {} while connecting supply "
197+
"net {}",
198+
port,
199+
net);
200+
return false;
201+
}
202+
}
203+
204+
setStringProperty(block, kSupplyNetConnPrefix + net, port);
205+
return true;
206+
}
207+
208+
bool set_power_domain_supply(utl::Logger* logger,
209+
odb::dbBlock* block,
210+
const std::string& domain,
211+
const std::string& supply)
212+
{
213+
odb::dbPowerDomain* pd = block->findPowerDomain(domain.c_str());
214+
if (pd == nullptr) {
215+
logger->warn(utl::UPF,
216+
87,
217+
"Couldn't retrieve power domain {} while setting -supply",
218+
domain);
219+
return false;
220+
}
221+
odb::dbStringProperty* prop = 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)