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
67 changes: 67 additions & 0 deletions src/OpenRoad.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,73 @@ proc write_db { args } {
ord::write_db_cmd $filename
}

# ECO (Engineering Change Order) capture / replay.
#
# The odb journaling layer records every netlist edit (instance create/destroy,
# connect/disconnect, master swap, placement changes, ...) made while an ECO is
# active. write_eco serializes that journal to a file; read_eco replays it onto
# a pristine copy of the original design. This lets a designer snapshot a
# placed/routed netlist, run targeted timing fixes (e.g. repair_timing), and
# capture just the delta as a portable ECO that can be replayed later -- the
# incremental closure loop instead of re-running the whole flow.
#
# Typical usage:
# read_def placed.def
# begin_eco ;# start recording netlist edits
# repair_timing -setup
# write_eco fix.eco ;# persist the delta
# and later, on the original (unmodified) netlist:
# read_def placed.def
# read_eco fix.eco ;# replay the delta -> matches the modified design

proc ord_eco_block { cmd } {
set db [ord::get_db]
if { $db eq "NULL" } {
utl::error ORD 1060 "no database is loaded; $cmd requires a design."
}
set chip [$db getChip]
if { $chip eq "NULL" } {
utl::error ORD 1061 "no chip is loaded; $cmd requires a design."
}
set block [$chip getBlock]
if { $block eq "NULL" } {
utl::error ORD 1062 "no block is loaded; $cmd requires a design."
}
return $block
}

sta::define_cmd_args "begin_eco" {}

proc begin_eco { args } {
sta::check_argc_eq0 "begin_eco" $args
set block [ord_eco_block "begin_eco"]
odb::dbDatabase_beginEco $block
}

sta::define_cmd_args "write_eco" {filename}

proc write_eco { args } {
sta::check_argc_eq1 "write_eco" $args
set block [ord_eco_block "write_eco"]
set filename [file nativename [lindex $args 0]]
odb::dbDatabase_writeEco $block $filename
}

sta::define_cmd_args "read_eco" {filename}

proc read_eco { args } {
sta::check_argc_eq1 "read_eco" $args
set block [ord_eco_block "read_eco"]
set filename [file nativename [lindex $args 0]]
if { ![file exists $filename] } {
utl::error ORD 1063 "$filename does not exist."
}
if { ![file readable $filename] } {
utl::error ORD 1064 "$filename is not readable."
}
odb::dbDatabase_readEco $block $filename
}

sta::define_cmd_args "assign_ndr" { -ndr name (-net name | -all_clocks) }

proc assign_ndr { args } {
Expand Down
53 changes: 53 additions & 0 deletions src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,59 @@ technology exists in the database.
design.writedb("reg1.db")
````

## ECO (Engineering Change Order) capture and replay

OpenROAD's database records netlist edits in a journal so that a set of
targeted changes can be captured and replayed as an ECO, instead of re-running
the whole flow for design closure. The commands below expose this journal:

````{eval-rst}
.. tabs::

.. code-tab:: tcl

begin_eco # start recording netlist edits on the current block
write_eco filename # write the recorded edits to an ECO file
read_eco filename # replay an ECO file onto the current block
````

A typical incremental timing-closure loop is to snapshot a placed (and ideally
routed) design, apply targeted fixes, and persist just the delta:

````{eval-rst}
.. tabs::

.. code-tab:: tcl

read_lef Nangate45/Nangate45.lef
read_def placed.def
create_clock -period 0.3 clk
set_wire_rc -layer metal3
estimate_parasitics -placement

begin_eco # start recording
repair_timing -setup # targeted fix (records cell resizes, buffer edits, ...)
write_eco fix.eco # persist the delta
````

The persisted ECO can later be replayed on the original, unmodified netlist to
reproduce the fixed design without re-optimizing:

````{eval-rst}
.. tabs::

.. code-tab:: tcl

read_lef Nangate45/Nangate45.lef
read_def placed.def # the original, pre-fix netlist
read_eco fix.eco # replay -> matches the fixed design
````

`read_eco` requires a database that is an exact copy of the one in which the
ECO was recorded, prior to the recorded edits. `write_eco` captures the journal
that is currently open (started by `begin_eco`); issue it before closing the
ECO.

The `read_verilog` command is used to build an OpenDB database as shown
below. Multiple Verilog files for a hierarchical design can be read.
The `link_design` command is used to flatten the design and make a database.
Expand Down
22 changes: 22 additions & 0 deletions src/dpl/include/dpl/Opendp.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,21 @@ class Opendp
double drc_penalty = -1.0);
void reportLegalizationStats() const;

// OpenROAD-fork: eco-legalize
// Incrementally legalize only the cells touched by an ECO (resized,
// inserted, moved) plus any cells that physically overlap them, leaving every
// other placed cell pinned at its exact coordinates. This is the placement
// companion to repair_timing_eco: it snaps the ECO cells to site and resolves
// local overlaps with the existing diamond-search legalizer using a bounded
// window so untouched regions stay put. Returns the number of cells that
// were (re-)legalized. max_displacement_* are in sites; 0 selects a small
// ECO-appropriate default. This entry point is additive and does not change
// detailedPlacement() behavior.
int ecoLegalizePlacement(const std::vector<odb::dbInst*>& eco_insts,
int max_displacement_x,
int max_displacement_y,
bool verbose);

void setPaddingGlobal(int left, int right);
void setPadding(odb::dbMaster* master, int left, int right);
void setPadding(odb::dbInst* inst, int left, int right);
Expand Down Expand Up @@ -329,6 +344,13 @@ class Opendp
dbMasterSeq filterFillerMasters(const dbMasterSeq& filler_masters) const;
MasterByImplant splitByImplant(const dbMasterSeq& filler_masters);
void setInitialGridCells();
// OpenROAD-fork: eco-legalize
// Paint the grid for an ECO-scoped incremental legalization: every legal
// placed cell that is NOT in the unplace set keeps its pixels (pinned), while
// cells in the unplace set are unplaced so place() will re-legalize only
// them. Returns the set of cells left unplaced (to be legalized).
std::unordered_set<Node*> setEcoGridCells(
const std::unordered_set<Node*>& eco_cells);
void setGridCells();
dbMasterSeq& gapFillers(odb::dbTechLayer* implant,
GridX gap,
Expand Down
Loading
Loading