odb: insert_buffer on two ports that share a net leaves the driver net named after one port; write_verilog then shorts the buffer · Issue #11789 · The-OpenROAD-Project/OpenROAD · GitHub
You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
odb: insert_buffer on two ports that share a net leaves the driver net named after one port; write_verilog then shorts the buffer #11789
insert_buffer (dbInsertBuffer::createNewFlatNet) can leave the original net named after an output port it no longer connects to.
This happens when the moved loads are two output ports that share a net, which is what remove_buffers leaves when two output ports are driven by the same signal:
the new net takes the name of the first BTerm among the moved loads;
the original net is renamed only if its name equals that BTerm's name;
if the original net is named after the other port, it keeps that name, although both ports now sit on the new net.
The database is still consistent, but write_verilog then writes the driver's net under the port's name and adds an assign between the two ports. In Verilog a net named like a port is that port, so the written netlist shorts the buffer's input to its output. Re-reading it gives a net with two drivers and a combinational loop, and STA on the re-read netlist reports infinite slews there.
We hit this with ORFS placement repair (repair_design inserting a buffer in front of two such ports) on several open-source cores (opene902, opene906, veer, cva6). The in-database results were fine, but the written netlist and SPEF are not.
This is the multi-port case of #9675, which made createNewFlatNet name the new net after the BTerm and rename a colliding original net or modnet. The comment in createNewFlatNet describes exactly this case ("remove_buffers merges two port-named nets, leaving a BTerm on a net with a different port's name"). The check only compares the original net's name with the first BTerm found, though, so it depends on which port that is. The code is unchanged on master since 2026-07-21.
Expected Behavior
After insert_buffer, no net that isn't connected to a port has that port's name. Concretely, the original net should be renamed whenever its name equals the name of any BTerm moved onto the new net. Equivalently, write_verilog should never write a non-port net under a port's name.
The re-read netlist should be equivalent to the database: drv/ZN drives rbuf1/A and ld/A, and rbuf1/Z drives p and q.
Environment
OpenROAD 27288f5c90265ae5296b2f5a9fa6e8714fa191ad (2026-10-01), from the ORFS image openroad/orfs:26Q4-4-g214988303
Ubuntu 22.04.5 LTS
To Reproduce
Run from OpenROAD's test/ directory with openroad -exit port_alias.tcl, then openroad -exit reread.tcl.
port_alias.v: two output ports driven by one signal, each through its own buffer.
moduletop (a, b, p, q, y);
input a, b;
output p, q, y;
wire n;
NAND2_X1drv (.A1(a), .A2(b), .ZN(n));
BUF_X1ob1 (.A(n), .Z(q));
BUF_X1ob2 (.A(n), .Z(p));
INV_X1ld (.A(n), .ZN(y));
endmodule
port_alias.tcl:
read_lef Nangate45/Nangate45.lef
read_liberty Nangate45/Nangate45_typ.lib
read_verilog port_alias.v
link_design top
initialize_floorplan -die_area {0 0 40 40} -core_area {5 5 35 35} \
-site FreePDK45_38x28_10R_NP_162NW_34O
place_inst -name drv -location {10 10}
place_inst -name ob1 -location {12 10}
place_inst -name ob2 -location {14 10}
place_inst -name ld -location {16 10}
procshow_nets {tag} {
puts"== $tag"foreach net [[ord::get_db_block] getNets] {
set drv {}
set lds {}
foreach it [$net getITerms] {
set pin [[$it getInst] getName]/[[$it getMTerm] getName]
if { [$it isOutputSignal] } { lappend drv $pin } else { lappend lds $pin }
}
puts [format" net %-6s ports {%s} drivers {%s} loads {%s}" [$net getName] \
[lmap b [$net getBTerms] {$b getName}] $drv$lds]
}
}
# remove_buffers leaves p and q on one net, named after one of them (q)
remove_buffers
show_nets "after remove_buffers"# A buffer between the driver and both ports, as repair_design inserts one
insert_buffer -buffer_cell BUF_X1 -net [get_nets -of_objects [get_pins drv/ZN]] \
-load_pins [get_ports {p q}] -location {20 10} -buffer_name rbuf
show_nets "after insert_buffer"
write_verilog written.v
reread.tcl:
read_lef Nangate45/Nangate45.lef
read_liberty Nangate45/Nangate45_typ.lib
read_verilog written.v
link_design top
foreach net [[ord::get_db_block] getNets] {
set drivers [lmap it [$net getITerms] {
if { [$it isOutputSignal] } { string cat [[$it getInst] getName]/[[$it getMTerm] getName] } else continue
}]
if { [llength$drivers] > 1 } {
puts"net [$net getName] has [llength$drivers] drivers: $drivers"
}
}
Relevant log output
== after remove_buffers
net a ports {a} drivers {} loads {drv/A1}
net b ports {b} drivers {} loads {drv/A2}
net q ports {p q} drivers {drv/ZN} loads {ld/A}
net y ports {y} drivers {ld/ZN} loads {}
== after insert_buffer
net a ports {a} drivers {} loads {drv/A1}
net b ports {b} drivers {} loads {drv/A2}
net p ports {q p} drivers {rbuf1/Z} loads {}
net q ports {} drivers {drv/ZN} loads {rbuf1/A ld/A}
net y ports {y} drivers {ld/ZN} loads {}
written.v (the driver's net is written as the port q, and assign q = p closes the loop):
If the buffers are swapped (ob1 drives p), remove_buffers names the merged net p. The first BTerm then matches, the original net is renamed net1, and the written netlist is correct. So the outcome depends on which port the merged net was named after.
Additional Context
Our workaround. Before writing the netlist and SPEF, we rename every net whose name equals a port (or bus port base) name it isn't connected to. Afterwards we check the written Verilog for nets with more than one driver.
The equivalence check doesn't catch it. In a slightly larger version of this reproducer, kepler-formal reported the broken netlist as identical to the original, but compared only 7 of its 9 outputs.
A separate crash. In the same script, insert_buffer without -buffer_name segfaults (Signal 11 received) on this build. With -buffer_name rbuf it runs as shown above.
Activity
github-actions commentedon Oct 16, 2026
Welcome to OpenROAD! Thanks for opening your first issue.
To get started:
Please search existing issues before submitting to avoid duplicates.
A maintainer will get back to you soon!
odb: avoid moved port names on buffered driver nets
akashlevy commentedon Oct 23, 2026
Thank you @maliberty and @jinchuntan!