Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 38 additions & 27 deletions src/odb/src/db/dbInsertBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -447,46 +447,57 @@ dbNet* dbInsertBuffer::createNewFlatNet(
// Algorithm:
// - The new net will drive the connected_terms, and the original net will be
// connected to the buffer terminal.
// - If the original net name matches a connected BTerm, rename the original
// net to a unique name and assign the BTerm's name to the new net.
// This ensures that the net name matches the port name for Verilog
// compatibility.
// - If connected_terms contains a BTerm, the new net is always named after
// the BTerm to ensure Verilog compatibility. If the original net or its
// modnet has the same name, they are renamed first to avoid collision.
// This handles the case where remove_buffers merges two port-named nets,
// leaving a BTerm on a net with a different port's name.

std::string new_net_name = (new_net_base_name_) ? new_net_base_name_ : "net";
dbNameUniquifyType new_net_uniquify = uniquify_;

// Check if the net name conflicts with any port name
// Find the first BTerm in connected_terms (if any) to use its port name
// as the new net name for Verilog compatibility.
dbBTerm* bterm = nullptr;
for (dbObject* obj : connected_terms) {
if (obj->getObjectType() != dbBTermObj) {
continue;
if (obj->getObjectType() == dbBTermObj) {
bterm = static_cast<dbBTerm*>(obj);
break;
}
}

dbBTerm* bterm = static_cast<dbBTerm*>(obj);
if (bterm) {
std::string_view bterm_name{bterm->getConstName()};
if (bterm_name == block_->getBaseName(net_->getConstName())) {
// The original net names uses the BTerm name.
// Rename this net if its name is the same as a port name in loads_pins
std::string new_orig_net_name = block_->makeNewNetName(

// Helper: generate a unique name to avoid collision with the port name.
auto make_avoidance_name = [&]() {
return block_->makeNewNetName(
target_module_ ? target_module_->getModInst() : nullptr,
new_net_name.c_str(),
dbNameUniquifyType::ALWAYS);
net_->rename(new_orig_net_name.c_str());

// Rename the mod net name connected to the load pin if it is the
// same as the port name
dbModNet* load_mnet = bterm->getModNet();
if (load_mnet) {
std::string_view mnet_name{load_mnet->getConstName()};
if (mnet_name == bterm_name) {
load_mnet->rename(new_orig_net_name.c_str());
}
};

// Rename the original flat net and/or modnet if they have the same name as
// the port.
const bool flat_net_collides
= (bterm_name == block_->getBaseName(net_->getConstName()));
dbModNet* load_mnet = bterm->getModNet();
const bool mod_net_collides
= (load_mnet
&& std::string_view{load_mnet->getConstName()} == bterm_name);

if (flat_net_collides || mod_net_collides) {
const std::string avoidance_name = make_avoidance_name();
if (flat_net_collides) {
net_->rename(avoidance_name.c_str());
}
if (mod_net_collides) {
load_mnet->rename(avoidance_name.c_str());
}

// New net name should be the port name
new_net_name = bterm_name;
new_net_uniquify = dbNameUniquifyType::IF_NEEDED;
break;
}

new_net_name = bterm_name;
new_net_uniquify = dbNameUniquifyType::IF_NEEDED;
}

// Create a new net
Expand Down
4 changes: 2 additions & 2 deletions src/rsz/test/buffer_ports10.defok
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ PINS 4 ;
+ PORT
+ LAYER metal2 ( -70 -70 ) ( 70 70 )
+ PLACED ( 100510 199930 ) N ;
- out2 + NET net4 + DIRECTION OUTPUT + USE SIGNAL
- out2 + NET out2 + DIRECTION OUTPUT + USE SIGNAL
+ PORT
+ LAYER metal2 ( -70 -70 ) ( 70 70 )
+ PLACED ( 98990 199930 ) N ;
Expand All @@ -111,7 +111,7 @@ NETS 7 ;
- net1 ( input1 Z ) ( r1 CK ) + USE SIGNAL ;
- net2 ( input2 Z ) ( r1 D ) + USE SIGNAL ;
- net3 ( output4 A ) ( output3 A ) ( r1 Q ) + USE SIGNAL ;
- net4 ( PIN out2 ) ( output4 Z ) + USE SIGNAL ;
- out1 ( PIN out1 ) ( output3 Z ) + USE SIGNAL ;
- out2 ( PIN out2 ) ( output4 Z ) + USE SIGNAL ;
END NETS
END DESIGN
4 changes: 2 additions & 2 deletions src/rsz/test/buffer_ports8.defok
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ PINS 4 ;
+ PORT
+ LAYER metal2 ( -70 -70 ) ( 70 70 )
+ PLACED ( 100510 199930 ) N ;
- out2 + NET net4 + DIRECTION OUTPUT + USE SIGNAL
- out2 + NET out2 + DIRECTION OUTPUT + USE SIGNAL
+ PORT
+ LAYER metal2 ( -70 -70 ) ( 70 70 )
+ PLACED ( 98990 199930 ) N ;
Expand All @@ -111,7 +111,7 @@ NETS 7 ;
- net1 ( input1 Z ) ( r1 CK ) + USE SIGNAL ;
- net2 ( input2 Z ) ( r1 D ) + USE SIGNAL ;
- net3 ( output4 A ) ( output3 A ) ( r1 Q ) + USE SIGNAL ;
- net4 ( PIN out2 ) ( output4 Z ) + USE SIGNAL ;
- out1 ( PIN out1 ) ( output3 Z ) + USE SIGNAL ;
- out2 ( PIN out2 ) ( output4 Z ) + USE SIGNAL ;
END NETS
END DESIGN
4 changes: 2 additions & 2 deletions src/rsz/test/buffer_ports9.defok
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ PINS 4 ;
+ PORT
+ LAYER metal2 ( -70 -70 ) ( 70 70 )
+ PLACED ( 100510 199930 ) N ;
- out2 + NET net4 + DIRECTION OUTPUT + USE SIGNAL
- out2 + NET out2 + DIRECTION OUTPUT + USE SIGNAL
+ PORT
+ LAYER metal2 ( -70 -70 ) ( 70 70 )
+ PLACED ( 98990 199930 ) N ;
Expand All @@ -111,7 +111,7 @@ NETS 7 ;
- net1 ( input1 Z ) ( r1 CK ) + USE SIGNAL ;
- net2 ( input2 Z ) ( r1 D ) + USE SIGNAL ;
- net3 ( output4 A ) ( output3 A ) ( r1 Q ) + USE SIGNAL ;
- net4 ( PIN out2 ) ( output4 Z ) + USE SIGNAL ;
- out1 ( PIN out1 ) ( output3 Z ) + USE SIGNAL ;
- out2 ( PIN out2 ) ( output4 Z ) + USE SIGNAL ;
END NETS
END DESIGN
70 changes: 70 additions & 0 deletions src/rsz/test/cpp/TestInsertBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3199,4 +3199,74 @@ TEST_F(TestInsertBuffer, BeforeLoads_Case32)
writeAndCompareVerilogOutputFile(test_name, test_name + "_post.v");
}

// Reproduce the real bug: remove_buffers + insertBufferBeforeLoad
// on a hierarchical design.
//
// Synthesis creates a submodule (MOD0) with two output ports:
// MOD0: LOGIC0_X1 drvr -> Z_a, BUF_X1 cross_buf: Z_a -> Z_b
// At the top level:
// MOD0 mod0 (.Z_a(port_a), .Z_b(port_b))
//
// The cross_buf connects two port-named nets (port_a and port_b).
// remove_buffers removes it, merging port_b into port_a.
// BTerm port_b ends up on net port_a — name mismatch.
//
// Then insertBufferBeforeLoad inserts a buffer before port_b.
// Without the fix, createNewFlatNet gives the new net a generic name,
// and write_verilog produces:
// MOD0 mod0 (.Z_a(port_a), .Z_b(port_b)); -- submodule drives port_b
// assign port_b = net1; -- assign also drives port_b
// This is a multi-driver on port_b, rejected by Kepler LEC.
TEST_F(TestInsertBuffer, BeforeLoads_Case33)
{
const auto* test_info = testing::UnitTest::GetInstance()->current_test_info();
const std::string test_name
= std::string(test_info->test_suite_name()) + "_" + test_info->name();

readVerilogAndSetup(test_name + "_pre.v");

dbMaster* buf_x4_master = db_->findMaster("BUF_X4");
ASSERT_NE(buf_x4_master, nullptr);

// Verify initial state: BTerm port_b matches its net name
dbBTerm* bterm_b = block_->findBTerm("port_b");
ASSERT_NE(bterm_b, nullptr);
EXPECT_STREQ(bterm_b->getConstName(), bterm_b->getNet()->getConstName());

// --- Step 1: remove_buffers (reproduces the real flow) ---
// Removes cross_buf that connects port_a -> port_b.
// Both nets have BTerms, so survivor = input net (port_a).
// BTerm port_b moves to net port_a — name mismatch.
resizer_.removeBuffers({});

EXPECT_EQ(block_->findInst("cross_buf"), nullptr)
<< "cross_buf should have been removed by remove_buffers";
EXPECT_STREQ(bterm_b->getNet()->getConstName(), "port_a")
<< "After remove_buffers, BTerm port_b should be on net port_a "
"(name mismatch)";

// --- Step 2: insertBufferBeforeLoad on port_b (reproduces buffer_ports) ---
dbNet* target_net = bterm_b->getNet();
dbInst* new_buf = target_net->insertBufferBeforeLoad(
bterm_b, buf_x4_master, nullptr, "output");
ASSERT_NE(new_buf, nullptr);

// The buffer output net must be named "port_b" — not a generic name.
// Without the fix, createNewFlatNet produces "net1" because
// bterm_name ("port_b") != net_name ("port_a"), skipping the rename.
// This causes write_verilog to emit a spurious assign statement,
// and in hierarchical designs, creates a multi-driver because the
// submodule port map still references port_b.
dbNet* buf_out_net = new_buf->findITerm("Z")->getNet();
ASSERT_NE(buf_out_net, nullptr);
EXPECT_EQ(bterm_b->getNet(), buf_out_net);
EXPECT_STREQ(buf_out_net->getConstName(), "port_b")
<< "Buffer output net must be named after the BTerm (port_b), "
"not a generic name. Without the fix, this would be 'net1' "
"and write_verilog would emit 'assign port_b = net1;'";

// Write verilog and compare
writeAndCompareVerilogOutputFile(test_name, test_name + "_post.v");
}

} // namespace odb
4 changes: 1 addition & 3 deletions src/rsz/test/cpp/TestInsertBuffer_BeforeLoad_Case1_post.v
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,17 @@ module top (load_output);
wire net;
wire net1;
wire net3;
wire net4;

BUF_X4 buf1 (.A(net),
.Z(net1));
BUF_X4 buf3 (.A(net),
.Z(net3));
BUF_X4 buf4 (.A(net),
.Z(net4));
.Z(load_output));
LOGIC0_X1 drvr_inst (.Z(net));
BUF_X1 load0_inst (.A(net1));
BUF_X1 load2_inst (.A(net3));
MOD0 mi0 (.A(net));
assign load_output = net4;
endmodule
module MOD0 (A);
input A;
Expand Down
4 changes: 1 addition & 3 deletions src/rsz/test/cpp/TestInsertBuffer_BeforeLoads_Case10_post.v
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ module top (in,
output out;

wire n1;
wire net1;

BUF_X4 new_buf1 (.A(n1),
.Z(net1));
assign out = net1;
.Z(out));
endmodule
6 changes: 2 additions & 4 deletions src/rsz/test/cpp/TestInsertBuffer_BeforeLoads_Case12_post.v
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ module top (out);
output out;

wire n1;
wire net1;

BUF_X1 drvr (.Z(n1));
BUF_X1 load1 (.A(net1));
BUF_X1 load1 (.A(out));
BUF_X4 new_buf1 (.A(n1),
.Z(net1));
assign out = net1;
.Z(out));
endmodule
8 changes: 3 additions & 5 deletions src/rsz/test/cpp/TestInsertBuffer_BeforeLoads_Case13_post.v
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
module top (out);
output out;

wire net1;
wire n1;

BUF_X1 drvr (.Z(n1));
BUF_X1 load1 (.A(net1));
BUF_X1 load1 (.A(out));
BUF_X4 new_buf1 (.A(n1),
.Z(net1));
MOD1 u1 (.A(net1));
assign out = net1;
.Z(out));
MOD1 u1 (.A(out));
endmodule
module MOD1 (A);
input A;
Expand Down
21 changes: 21 additions & 0 deletions src/rsz/test/cpp/TestInsertBuffer_BeforeLoads_Case33_post.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module top (port_a,
port_b);
output port_a;
output port_b;

wire net1;

MOD0 mod0 (.Z_a(port_a),
.Z_b(net1));
BUF_X4 output1 (.A(net1),
.Z(port_b));
endmodule
module MOD0 (Z_a,
Z_b);
output Z_a;
output Z_b;


LOGIC0_X1 drvr (.Z(Z_a));
assign Z_b = Z_a;
endmodule
26 changes: 26 additions & 0 deletions src/rsz/test/cpp/TestInsertBuffer_BeforeLoads_Case33_pre.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Reproduce the real bug: remove_buffers + insertBufferBeforeLoad.
// Hierarchical design where a submodule drives two output ports,
// with a buffer connecting port_a to port_b inside the submodule.
//
// After remove_buffers + insertBufferBeforeLoad (buggy):
// VerilogWriter emits both submodule .Z_b(port_b) AND assign port_b = net1
// → multi-driver on port_b
module top (port_a,
port_b);
output port_a;
output port_b;


MOD0 mod0 (.Z_a(port_a),
.Z_b(port_b));
endmodule
module MOD0 (Z_a,
Z_b);
output Z_a;
output Z_b;


LOGIC0_X1 drvr (.Z(Z_a));
BUF_X1 cross_buf (.A(Z_a),
.Z(Z_b));
endmodule
4 changes: 1 addition & 3 deletions src/rsz/test/cpp/TestInsertBuffer_BeforeLoads_Case3_post.v
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ module top (in,

wire n2;
wire n1;
wire net2;

MOD0 mod0 (.out(n2),
.in(n1));
BUF_X4 new_buf22 (.A(n2),
.Z(net2));
assign out = net2;
.Z(out));
endmodule
module MOD0 (out,
in);
Expand Down
4 changes: 1 addition & 3 deletions src/rsz/test/cpp/TestInsertBuffer_BeforeLoads_Case9_post.v
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ module top (out);
output out;

wire n1;
wire net1;

BUF_X1 drvr (.Z(n1));
BUF_X4 new_buf1 (.A(n1),
.Z(net1));
assign out = net1;
.Z(out));
endmodule
Loading