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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ build
build_linux
build_mac
test/results
/sandbox
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this needed?


docs/main
README2.md
Expand Down
2 changes: 1 addition & 1 deletion src/OpenRoad.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ proc global_connect { args } {
keys {} \
flags {-force -verbose}

sta::check_argc_eq0 "add_global_connection" $args
sta::check_argc_eq0 "global_connect" $args
[ord::get_db_block] globalConnect [info exists flags(-force)] [info exists flags(-verbose)]
}

Expand Down
3 changes: 3 additions & 0 deletions src/dft/include/dft/Dft.hh
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ class Dft
// Resets the internal state
void reset();

void writeToOdb();

// Common function to perform scan replace and scan architect. Shared between
// report_dft_plan and execute_dft_plan
std::vector<std::unique_ptr<ScanChain>> scanArchitect();
Expand All @@ -109,6 +111,7 @@ class Dft
// Internal state
std::unique_ptr<ScanReplace> scan_replace_;
std::unique_ptr<DftConfig> dft_config_;
std::vector<std::unique_ptr<ScanChain>> scan_chains_;
};

} // namespace dft
133 changes: 122 additions & 11 deletions src/dft/src/Dft.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "ClockDomain.hh"
#include "DftConfig.hh"
#include "Opt.hh"
#include "ScanArchitect.hh"
#include "ScanArchitectConfig.hh"
#include "ScanCell.hh"
Expand Down Expand Up @@ -84,21 +85,15 @@ void Dft::scanReplace()
scan_replace_->scanReplace();
}

void Dft::executeDftPlan()
void Dft::writeToOdb()
{
if (need_to_run_pre_dft_) {
pre_dft();
}
std::vector<std::unique_ptr<ScanChain>> scan_chains = scanArchitect();

ScanStitch stitch(db_, logger_, dft_config_->getScanStitchConfig());
stitch.Stitch(scan_chains);

// Write scan chains to odb
odb::dbBlock* db_block = db_->getChip()->getBlock();
odb::dbDft* db_dft = db_block->getDft();

for (const auto& chain : scan_chains) {
db_dft->reset();

for (const auto& chain : scan_chains_) {
odb::dbScanChain* db_sc = odb::dbScanChain::create(db_dft);
db_sc->setName(chain->getName());
odb::dbScanPartition* db_part = odb::dbScanPartition::create(db_sc);
Expand Down Expand Up @@ -148,6 +143,22 @@ void Dft::executeDftPlan()
sc_out_load.value().getValue());
}
}

db_dft->setScanInserted(true);
}

void Dft::executeDftPlan()
{
if (need_to_run_pre_dft_) {
pre_dft();
}

scan_chains_ = scanArchitect();

ScanStitch stitch(db_, logger_, dft_config_->getScanStitchConfig());
stitch.Stitch(scan_chains_);

writeToOdb();
}

DftConfig* Dft::getMutableDftConfig()
Expand Down Expand Up @@ -189,7 +200,107 @@ std::vector<std::unique_ptr<ScanChain>> Dft::scanArchitect()

void Dft::scanOpt()
{
logger_->warn(utl::DFT, 14, "Scan Opt is not currently implemented");
for (const auto& chain : scan_chains_) {
auto src_pin = chain->getScanIn();
auto sink_pin = chain->getScanOut();
int src_x, src_y, sink_x, sink_y;
if (!src_pin.has_value() || !src_pin->getLocation(src_x, src_y)
|| !sink_pin.has_value() || !sink_pin->getLocation(sink_x, sink_y)) {
logger_->warn(utl::DFT,
14,
"Cannot optimize: source/sink pins don't exist or have no "
"placement.");
return;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The return statement here will cause the entire scanOpt function to exit if a single scan chain cannot be optimized (e.g., due to missing source/sink pins or placement). This prevents other valid scan chains from being optimized. It should be replaced with continue to skip only the current chain.

Suggested change
return;
continue;

}

odb::Point src = odb::Point(src_x, src_y);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "odb::Point" is directly included [misc-include-cleaner]

src/dft/src/Dft.cpp:25:

- #include "utl/Logger.h"
+ #include "odb/geom.h"
+ #include "utl/Logger.h"

odb::Point sink = odb::Point(sink_x, sink_y);

int64_t twl_internal = chain->estimateInternalTWL();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "int64_t" is directly included [misc-include-cleaner]

src/dft/src/Dft.cpp:5:

- #include <memory>
+ #include <cstdint>
+ #include <memory>

int64_t twl = twl_internal;
const auto& scan_cells = chain->getScanCells();
if (scan_cells.size() > 0) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]

Suggested change
if (scan_cells.size() > 0) {
if (!scan_cells.empty()) {
Additional context

/usr/include/c++/13/bits/stl_vector.h:1087: method 'vector'::empty() defined here

      empty() const _GLIBCXX_NOEXCEPT
      ^

twl += odb::Point::manhattanDistance(src, scan_cells[0]->getOrigin());
twl += odb::Point::manhattanDistance(
scan_cells[scan_cells.size() - 1]->getOrigin(), sink);
}
logger_->info(utl::DFT,
15,
"Starting 2-Opt with initial total chain wire length {}",
twl);

logger_->metric(fmt::format("dft__chain_twl_internal__init__chain:{}",
chain->getName()),
twl_internal);
logger_->metric(
fmt::format("dft__chain_twl__init__chain:{}", chain->getName()), twl);

auto twl_opt = chain->sortScanCells(
[&](std::vector<std::unique_ptr<ScanCell>>& falling,
std::vector<std::unique_ptr<ScanCell>>& rising,
std::vector<std::unique_ptr<ScanCell>>& sorted) {
sorted.reserve(falling.size() + rising.size());
// Sort to reduce wire length
odb::Point f_src(src_x, src_y);
odb::Point f_sink(sink_x, sink_y);
if (rising.size() > 0) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]

Suggested change
if (rising.size() > 0) {
if (!rising.empty()) {
Additional context

/usr/include/c++/13/bits/stl_vector.h:1087: method 'vector'::empty() defined here

      empty() const _GLIBCXX_NOEXCEPT
      ^

f_sink = rising[0]->getOrigin();
}
auto falling_wire_length
= OptimizeScanWirelength2Opt(f_src, f_sink, falling, logger_);

odb::Point r_src(src_x, src_y);
if (falling.size() > 0) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]

Suggested change
if (falling.size() > 0) {
if (!falling.empty()) {
Additional context

/usr/include/c++/13/bits/stl_vector.h:1087: method 'vector'::empty() defined here

      empty() const _GLIBCXX_NOEXCEPT
      ^

r_src = falling[falling.size() - 1]->getOrigin();
}
odb::Point r_sink(sink_x, sink_y);
auto rising_wire_length
= OptimizeScanWirelength2Opt(r_src, r_sink, rising, logger_);

int64_t distance_between_falling_and_rising = 0;
if (rising.size() > 0 && falling.size() > 0) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]

Suggested change
if (rising.size() > 0 && falling.size() > 0) {
if (!rising.empty() && falling.size() > 0) {
Additional context

/usr/include/c++/13/bits/stl_vector.h:1087: method 'vector'::empty() defined here

      empty() const _GLIBCXX_NOEXCEPT
      ^

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]

Suggested change
if (rising.size() > 0 && falling.size() > 0) {
if (rising.size() > 0 && !falling.empty()) {
Additional context

/usr/include/c++/13/bits/stl_vector.h:1087: method 'vector'::empty() defined here

      empty() const _GLIBCXX_NOEXCEPT
      ^

distance_between_falling_and_rising = odb::Point::manhattanDistance(
falling[falling.size() - 1]->getOrigin(),
rising[0]->getOrigin());
}

std::ranges::move(falling, std::back_inserter(sorted));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "std::back_inserter" is directly included [misc-include-cleaner]

src/dft/src/Dft.cpp:5:

- #include <memory>
+ #include <iterator>
+ #include <memory>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "std::ranges::move" is directly included [misc-include-cleaner]

src/dft/src/Dft.cpp:5:

- #include <memory>
+ #include <algorithm>
+ #include <memory>

std::ranges::move(rising, std::back_inserter(sorted));

// distance between falling and rising is double-counted by both
// optimization algorithms
return falling_wire_length + rising_wire_length
- distance_between_falling_and_rising;
});

logger_->info(utl::DFT,
16,
"Concluded 2-Opt with total chain wire length {}.",
twl_opt);

int64_t twl_internal_opt = chain->estimateInternalTWL();
logger_->metric(fmt::format("dft__chain_twl_internal__post_opt__chain:{}",
chain->getName()),
twl_internal_opt);

const auto& scan_cells_opt = chain->getScanCells();
int64_t twl_opt_confirm = twl_internal_opt;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'twl_opt_confirm' set but not used [clang-diagnostic-unused-but-set-variable]

    int64_t twl_opt_confirm = twl_internal_opt;
            ^

if (scan_cells_opt.size() > 0) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]

Suggested change
if (scan_cells_opt.size() > 0) {
if (!scan_cells_opt.empty()) {
Additional context

/usr/include/c++/13/bits/stl_vector.h:1087: method 'vector'::empty() defined here

      empty() const _GLIBCXX_NOEXCEPT
      ^

twl_opt_confirm
+= odb::Point::manhattanDistance(src, scan_cells_opt[0]->getOrigin());
twl_opt_confirm += odb::Point::manhattanDistance(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: Value stored to 'twl_opt_confirm' is never read [clang-analyzer-deadcode.DeadStores]

      twl_opt_confirm += odb::Point::manhattanDistance(
      ^
Additional context

src/dft/src/Dft.cpp:290: Value stored to 'twl_opt_confirm' is never read

      twl_opt_confirm += odb::Point::manhattanDistance(
      ^

scan_cells_opt[scan_cells_opt.size() - 1]->getOrigin(), sink);
}
assert(twl_opt == twl_opt_confirm);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "assert" is directly included [misc-include-cleaner]

src/dft/src/Dft.cpp:5:

- #include <memory>
+ #include <cassert>
+ #include <memory>


logger_->metric(
fmt::format("dft__chain_twl__post_opt__chain:{}", chain->getName()),
twl_opt);
}

ScanStitch stitch(db_, logger_, dft_config_->getScanStitchConfig());
stitch.Stitch(scan_chains_);
writeToOdb();
}

} // namespace dft
113 changes: 107 additions & 6 deletions src/dft/src/architect/Opt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "Opt.hh"

#include <cinttypes>
#include <cstddef>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: included header cinttypes is not used directly [misc-include-cleaner]

Suggested change
#include <cstddef>
#include <cstddef>

#include <cstdint>
#include <limits>
Expand All @@ -14,8 +15,7 @@
#include "ScanCell.hh"
#include "boost/geometry/geometries/register/point.hpp"
#include "boost/geometry/geometry.hpp"
#include "boost/geometry/index/rtree.hpp"
#include "utl/Logger.h"
#include "odb/geom.h"

namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;
Expand All @@ -28,17 +28,17 @@ namespace {
constexpr int kMaxCellsToSearch = 10;
} // namespace

void OptimizeScanWirelength(std::vector<std::unique_ptr<ScanCell>>& cells,
utl::Logger* logger)
int64_t OptimizeScanWirelengthNN(std::vector<std::unique_ptr<ScanCell>>& cells,
utl::Logger* logger)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "utl::Logger" is directly included [misc-include-cleaner]

src/dft/src/architect/Opt.cpp:18:

+ #include "utl/Logger.h"

{
// Nothing to order
if (cells.empty()) {
return;
return 0;
}
// No point running this if the cells aren't placed yet
for (const auto& cell : cells) {
if (!cell->isPlaced()) {
return;
return 0;
}
}
// Define the starting node as the lower leftmost, so we don't accidentally
Expand Down Expand Up @@ -68,6 +68,7 @@ void OptimizeScanWirelength(std::vector<std::unique_ptr<ScanCell>>& cells,
// Search nearest neighbours
std::vector<std::unique_ptr<ScanCell>> result;
result.emplace_back(std::move(cells[cursor.second]));
int64_t twl = 0;
while (result.size() < cells.size()) {
// Search for next nearest
auto next = cursor;
Expand All @@ -85,13 +86,113 @@ void OptimizeScanWirelength(std::vector<std::unique_ptr<ScanCell>>& cells,
10,
"Couldn't find nearest neighbor, too many overlapping cells");
}
twl += std::llabs(boost::geometry::get<0>(cursor.first)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "std::llabs" is directly included [misc-include-cleaner]

src/dft/src/architect/Opt.cpp:8:

- #include <limits>
+ #include <cstdlib>
+ #include <limits>

- boost::geometry::get<0>(next.first))
+ std::llabs(boost::geometry::get<1>(cursor.first)
- boost::geometry::get<1>(next.first));
// Make sure we only visit things once
rtree.remove(cursor);
cursor = std::move(next);
result.emplace_back(std::move(cells[cursor.second]));
}
// Replace with sorted vector
std::swap(cells, result);

return twl;
}

int64_t OptimizeScanWirelength2Opt(
const odb::Point source,
const odb::Point sink,
std::vector<std::unique_ptr<ScanCell>>& cells,
utl::Logger* logger,
size_t max_iters)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const

{
// Nothing to order
if (cells.empty()) {
return 0;
}

size_t N = cells.size() + 2;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use snake_case for variables, a more meaningful name, and make it const


std::vector<odb::Point> points(N);
size_t i = -1;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In this repository, static_cast<size_t>(-1) is the standard sentinel value for an invalid size_t index because 0 is often a valid index. However, using -1 (which wraps to the maximum value of size_t) as an active index for points[i] will cause an out-of-bounds access. If i is intended to be a valid starting index, it should be initialized to 0.

Suggested change
size_t i = -1;
size_t i = 0;
points[i] = source;
References
  1. When checking for failure or sentinel values in size_t, use static_cast<size_t>(-1) because 0 is a valid index.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing this instead seems a bit clearer and achieves the same thing.

size_t i = 0;
points[i++] = source;

points[++i] = source;

int64_t score = 0;
odb::Point last = points[i];
for (const auto& cell : cells) {
if (!cell->isPlaced()) {
// No point running this if the cells aren't placed yet
return 0;
}

points[++i] = cell->getOrigin();
score += odb::Point::manhattanDistance(last, points[i]);
last = points[i];
}
points[++i] = sink;
score += odb::Point::manhattanDistance(last, sink);
assert(++i == N);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The assert contains a side effect (++i). In release builds where NDEBUG is defined, the assertion and its expression are removed, meaning i will not be incremented. This will lead to incorrect behavior in release builds. Side effects should be moved outside of the assert.

Suggested change
assert(++i == N);
++i;
assert(i == N);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well i is not used after this point but fair enough

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "assert" is directly included [misc-include-cleaner]

src/dft/src/architect/Opt.cpp:5:

- #include <cinttypes>
+ #include <cassert>
+ #include <cinttypes>


int64_t best_score = score;

// Already "optimal" (unplaced)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't be unplaced as we already returned on unplaced above.

if (best_score == 0) {
return 0;
}

size_t iters = 0;
bool improved_last_iter = true;
while (improved_last_iter && iters < max_iters) {
iters += 1;
debugPrint(logger,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "debugPrint" is directly included [misc-include-cleaner]

    debugPrint(logger,
    ^

utl::DFT,
"2opt",
1,
"Starting iteration {} (TWL: {})...",
iters,
best_score);
improved_last_iter = false;
for (size_t i = 0; i < N - 2; i += 1) {
for (size_t j = i + 2; j < N - 1; j += 1) {
auto&& x = points[i];
auto&& y = points[j];
auto&& u = points[i + 1];
auto&& v = points[j + 1];
auto score_d = -odb::Point::manhattanDistance(x, u)
- odb::Point::manhattanDistance(y, v)
+ odb::Point::manhattanDistance(x, y)
+ odb::Point::manhattanDistance(u, v);
if (score_d < 0) {
improved_last_iter = true;
best_score += score_d;
for (size_t k = 0; k < (j - i) / 2; k += 1) {
std::swap(points[i + 1 + k], points[j - k]);
std::swap(cells[i + k], cells[j - k - 1]);
}
}
}
}
}

if (improved_last_iter) {
debugPrint(logger,
utl::DFT,
"2opt",
1,
"Stopping because the max iteration count ({}) was reached.",
iters);
} else {
debugPrint(logger,
utl::DFT,
"2opt",
1,
"Stopping after {} iterations because of a lack of improvement.",
iters);
}

return best_score;
}

} // namespace dft
11 changes: 9 additions & 2 deletions src/dft/src/architect/Opt.hh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@
namespace dft {

// Order scan cells to reduce wirelength
void OptimizeScanWirelength(std::vector<std::unique_ptr<ScanCell>>& cells,
utl::Logger* logger);
int64_t OptimizeScanWirelengthNN(std::vector<std::unique_ptr<ScanCell>>& cells,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "int64_t" is directly included [misc-include-cleaner]

src/dft/src/architect/Opt.hh:5:

- #include <memory>
+ #include <cstdint>
+ #include <memory>

utl::Logger* logger);

int64_t OptimizeScanWirelength2Opt(
const odb::Point source,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "odb::Point" is directly included [misc-include-cleaner]

src/dft/src/architect/Opt.hh:10:

- #include "utl/Logger.h"
+ #include "odb/geom.h"
+ #include "utl/Logger.h"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: parameter 'source' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls]

Suggested change
const odb::Point source,
odb::Point source,

const odb::Point sink,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: parameter 'sink' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls]

Suggested change
const odb::Point sink,
odb::Point sink,

std::vector<std::unique_ptr<ScanCell>>& cells,
utl::Logger* logger,
size_t max_iters = 30);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "size_t" is directly included [misc-include-cleaner]

src/dft/src/architect/Opt.hh:5:

- #include <memory>
+ #include <cstddef>
+ #include <memory>


} // namespace dft
Loading
Loading