Skip to content
Merged
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
3 changes: 3 additions & 0 deletions chipcompiler/data/pdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class PDK:
dont_use: list[str] = field(default_factory=list[str]) # don't use cell list
abc_driver_cell: str = "" # ABC driving cell
abc_load: float = 0.015 # ABC output load
sdc_load: float = 0.0 # output load (pF) for generated SDC; 0 omits set_load

def __post_init__(self) -> None:
self.root = optional_path(self.root)
Expand Down Expand Up @@ -222,6 +223,7 @@ def _pdk_from_external_config(data: dict, pdk_name: str = "") -> PDK:
dont_use=data.get("dont_use", []),
abc_driver_cell=str(data.get("abc_driver_cell", "")),
abc_load=float(data.get("abc_load", 0.015)),
sdc_load=float(data.get("sdc_load", 0.0)),
)


Expand Down Expand Up @@ -360,6 +362,7 @@ def PDK_ICS55(pdk_root: str | Path = "") -> PDK:
tie_low_port="Z",
abc_driver_cell="BUFX0P5H7R",
abc_load=0.015,
sdc_load=0.001,
dont_use=[
"DFFSRQX*",
"DFFSRX*",
Expand Down
102 changes: 89 additions & 13 deletions chipcompiler/data/workspace/sdc.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,102 @@
if TYPE_CHECKING:
from chipcompiler.data import Workspace

_SDC_HEAD_CLOCK = """\
# Auto-generated SDC file

set clk_name {clock}
set clk_port_name {clock}
set clk_freq_mhz {freq_mhz}
set clk_period [expr 1000.0 / $clk_freq_mhz]

# -------------------------------------------------
# Clock definition
# -------------------------------------------------
set clk_port [get_ports $clk_port_name]
create_clock -name $clk_name -period $clk_period $clk_port

# -------------------------------------------------
# IO Delay
# -------------------------------------------------
set clk_input [get_ports $clk_port_name]
set all_inputs_wo_clk [remove_from_collection [all_inputs] $clk_input]

set_input_delay 0 -clock [get_clocks $clk_name] $all_inputs_wo_clk
set_output_delay 0 -clock [get_clocks $clk_name] [all_outputs]
"""

_SDC_HEAD_VIRTUAL_CLOCK = """\
# Auto-generated SDC file

set clk_name __VIRTUAL_CLK__
set clk_freq_mhz {freq_mhz}
set clk_period [expr 1000.0 / $clk_freq_mhz]

# -------------------------------------------------
# Clock definition
# -------------------------------------------------
create_clock -name $clk_name -period $clk_period

# -------------------------------------------------
# IO Delay
# -------------------------------------------------
set all_inputs_wo_clk [all_inputs]

set_input_delay 0 -clock [get_clocks $clk_name] $all_inputs_wo_clk
set_output_delay 0 -clock [get_clocks $clk_name] [all_outputs]
"""

_SDC_OUTPUT_LOAD = """
# -------------------------------------------------
# Output load (pF) - {pdk_name} pdk
# -------------------------------------------------
set_load {sdc_load} [all_outputs]
"""

_SDC_TAIL = """
# -------------------------------------------------
# Clock uncertainty & transition
# -------------------------------------------------
set clk_uncertainty [expr $clk_period * 0.05] ;# 5% of period
set clk_transition [expr min(0.15, $clk_period * 0.03)] ;# 3%, cap 0.15ns
set input_transition [expr min(0.20, $clk_period * 0.05)] ;# 5%, cap 0.20ns

set_clock_uncertainty $clk_uncertainty [get_clocks $clk_name]
set_clock_transition $clk_transition [get_clocks $clk_name]
set_input_transition $input_transition $all_inputs_wo_clk

# -------------------------------------------------
# Design-level constraints
# -------------------------------------------------
set_max_fanout {max_fanout} [current_design]
"""


def create_default_sdc(workspace: "Workspace") -> None:
"""
Create SDC file based on PDK and workspace parameters.

A design without a clock port gets a virtual clock instead, so
downstream tools still have a clock object as timing reference.
"""
sdc_content = []
sdc_content.append("# Auto-generated SDC file\n")
sdc_content.append("\n")
sdc_content.append("set clk_name {} \n".format(workspace.parameters.data.get("clock", "")))
sdc_content.append("set clk_port_name {}\n".format(workspace.parameters.data.get("clock", "")))
sdc_content.append(
"set clk_freq_mhz {}\n".format(workspace.parameters.data.get("frequency_max", 100))
)
sdc_content.append("set clk_period [expr 1000.0 / $clk_freq_mhz]\n")
sdc_content.append("set clk_io_pct 0.2\n")
sdc_content.append("set clk_port [get_ports $clk_port_name]\n")
sdc_content.append("create_clock -name $clk_name -period $clk_period $clk_port\n")
parameters = workspace.parameters.data
freq_mhz = parameters.get("frequency_max", 100)

clock = parameters.get("clock", "")
if clock:
sdc_content = _SDC_HEAD_CLOCK.format(clock=clock, freq_mhz=freq_mhz)
else:
sdc_content = _SDC_HEAD_VIRTUAL_CLOCK.format(freq_mhz=freq_mhz)

if workspace.pdk.sdc_load > 0:
sdc_content += _SDC_OUTPUT_LOAD.format(
pdk_name=workspace.pdk.name, sdc_load=workspace.pdk.sdc_load
)

sdc_content += _SDC_TAIL.format(max_fanout=parameters.get("max_fanout", 20))

with open(workspace.pdk.sdc, "w") as file:
file.writelines(sdc_content)
file.write(sdc_content)


def refresh_generated_sdc(workspace: "Workspace") -> None:
Expand Down
2 changes: 1 addition & 1 deletion chipcompiler/thirdparty/ecc-tools
Submodule ecc-tools updated 44 files
+28 −0 src/database/manager/parser/liberty/Lib.cc
+7 −1 src/database/manager/parser/liberty/Lib.hh
+47 −86 src/database/manager/parser/liberty/LibParserCpp.cc
+3 −0 src/database/manager/parser/liberty/LibParserCpp.hh
+1 −12 src/operation/iFP/interface/FPInterface.cpp
+2 −2 src/operation/iFP/source/data_manager/DataManager.cpp
+1 −2 src/operation/iFP/source/data_manager/advance/Config.hpp
+26 −0 src/operation/iSTA/interface/STAInterface.cpp
+5 −0 src/operation/iSTA/source/data_manager/DataManager.cpp
+3 −0 src/operation/iSTA/source/data_manager/advance/Database.hpp
+6 −0 src/operation/iSTA/source/data_manager/advance/TimingCellPort.hpp
+11 −0 src/operation/iSTA/source/data_manager/advance/TimingClock.hpp
+9 −0 src/operation/iSTA/source/data_manager/advance/TimingConstraint.hpp
+65 −0 src/operation/iSTA/source/data_manager/advance/TimingException.hpp
+48 −0 src/operation/iSTA/source/data_manager/advance/TimingFanoutCheck.hpp
+6 −0 src/operation/iSTA/source/data_manager/advance/TimingPath.hpp
+28 −1 src/operation/iSTA/source/module/clock_propagator/ClockPropagator.cpp
+9 −0 src/operation/iSTA/source/module/sdc_command/CMakeLists.txt
+6 −0 src/operation/iSTA/source/module/sdc_command/SdcCommand.cpp
+64 −0 src/operation/iSTA/source/module/sdc_command/sdc_commands/AllPorts.cpp
+186 −0 src/operation/iSTA/source/module/sdc_command/sdc_commands/CreateGeneratedClock.cpp
+40 −0 src/operation/iSTA/source/module/sdc_command/sdc_commands/CurrentDesign.cpp
+11 −23 src/operation/iSTA/source/module/sdc_command/sdc_commands/GetClocks.cpp
+45 −0 src/operation/iSTA/source/module/sdc_command/sdc_commands/GetPins.cpp
+11 −24 src/operation/iSTA/source/module/sdc_command/sdc_commands/GetPorts.cpp
+73 −0 src/operation/iSTA/source/module/sdc_command/sdc_commands/RemoveFromCollection.cpp
+98 −0 src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcCommandUtils.cpp
+12 −0 src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcCommandUtils.hpp
+80 −0 src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcCommands.hpp
+17 −2 src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcTclCmd.cpp
+8 −2 src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcTclCmd.hpp
+76 −0 src/operation/iSTA/source/module/sdc_command/sdc_commands/SetClockGroups.cpp
+69 −0 src/operation/iSTA/source/module/sdc_command/sdc_commands/SetClockTransition.cpp
+11 −18 src/operation/iSTA/source/module/sdc_command/sdc_commands/SetClockUncertainty.cpp
+50 −0 src/operation/iSTA/source/module/sdc_command/sdc_commands/SetFalsePath.cpp
+79 −0 src/operation/iSTA/source/module/sdc_command/sdc_commands/SetMaxFanout.cpp
+114 −19 src/operation/iSTA/source/module/timing_analyzer/TimingAnalyzer.cpp
+3 −0 src/operation/iSTA/source/module/timing_analyzer/TimingAnalyzer.hpp
+14 −9 src/operation/iSTA/source/module/timing_propagator/TimingPropagator.cpp
+106 −36 src/operation/iSTA/source/module/timing_reporter/TimingReporter.cpp
+4 −1 src/operation/iSTA/source/module/timing_reporter/TimingReporter.hpp
+121 −0 src/operation/iSTA/source/toolkit/utility/Utility.cpp
+12 −0 src/operation/iSTA/source/toolkit/utility/Utility.hpp
+0 −1 test/fixtures/gcd/config/floorplan_ecc.json
133 changes: 132 additions & 1 deletion test/data/test_workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,137 @@ def test_create_workspace_rejects_existing_non_empty_directory(tmp_path):
assert workspace is None


EXPECTED_ICS55_DEFAULT_SDC = """\
# Auto-generated SDC file

set clk_name clk
set clk_port_name clk
set clk_freq_mhz 100
set clk_period [expr 1000.0 / $clk_freq_mhz]

# -------------------------------------------------
# Clock definition
# -------------------------------------------------
set clk_port [get_ports $clk_port_name]
create_clock -name $clk_name -period $clk_period $clk_port

# -------------------------------------------------
# IO Delay
# -------------------------------------------------
set clk_input [get_ports $clk_port_name]
set all_inputs_wo_clk [remove_from_collection [all_inputs] $clk_input]

set_input_delay 0 -clock [get_clocks $clk_name] $all_inputs_wo_clk
set_output_delay 0 -clock [get_clocks $clk_name] [all_outputs]

# -------------------------------------------------
# Output load (pF) - ics55 pdk
# -------------------------------------------------
set_load 0.001 [all_outputs]

# -------------------------------------------------
# Clock uncertainty & transition
# -------------------------------------------------
set clk_uncertainty [expr $clk_period * 0.05] ;# 5% of period
set clk_transition [expr min(0.15, $clk_period * 0.03)] ;# 3%, cap 0.15ns
set input_transition [expr min(0.20, $clk_period * 0.05)] ;# 5%, cap 0.20ns

set_clock_uncertainty $clk_uncertainty [get_clocks $clk_name]
set_clock_transition $clk_transition [get_clocks $clk_name]
set_input_transition $input_transition $all_inputs_wo_clk

# -------------------------------------------------
# Design-level constraints
# -------------------------------------------------
set_max_fanout 32 [current_design]
"""


def test_create_workspace_generates_default_sdc_from_parameters(
tmp_path, minimal_ics55_pdk_factory, default_ics55_parameters
):
pdk_root = minimal_ics55_pdk_factory(tmp_path / "ics55")
rtl_path = tmp_path / "gcd.v"
rtl_path.write_text("module gcd(input clk, output y); assign y = clk; endmodule\n")

workspace_dir = tmp_path / "workspace"
create_workspace(
directory=str(workspace_dir),
origin_def="",
origin_verilog=str(rtl_path),
pdk="ics55",
parameters={**default_ics55_parameters, "max_fanout": 32},
pdk_root=str(pdk_root),
)

sdc_path = workspace_dir / "origin" / "gcd.sdc"
assert sdc_path.read_text() == EXPECTED_ICS55_DEFAULT_SDC


EXPECTED_ICS55_VIRTUAL_CLOCK_SDC = """\
# Auto-generated SDC file

set clk_name __VIRTUAL_CLK__
set clk_freq_mhz 100
set clk_period [expr 1000.0 / $clk_freq_mhz]

# -------------------------------------------------
# Clock definition
# -------------------------------------------------
create_clock -name $clk_name -period $clk_period

# -------------------------------------------------
# IO Delay
# -------------------------------------------------
set all_inputs_wo_clk [all_inputs]

set_input_delay 0 -clock [get_clocks $clk_name] $all_inputs_wo_clk
set_output_delay 0 -clock [get_clocks $clk_name] [all_outputs]

# -------------------------------------------------
# Output load (pF) - ics55 pdk
# -------------------------------------------------
set_load 0.001 [all_outputs]

# -------------------------------------------------
# Clock uncertainty & transition
# -------------------------------------------------
set clk_uncertainty [expr $clk_period * 0.05] ;# 5% of period
set clk_transition [expr min(0.15, $clk_period * 0.03)] ;# 3%, cap 0.15ns
set input_transition [expr min(0.20, $clk_period * 0.05)] ;# 5%, cap 0.20ns

set_clock_uncertainty $clk_uncertainty [get_clocks $clk_name]
set_clock_transition $clk_transition [get_clocks $clk_name]
set_input_transition $input_transition $all_inputs_wo_clk

# -------------------------------------------------
# Design-level constraints
# -------------------------------------------------
set_max_fanout 20 [current_design]
"""


def test_create_workspace_generates_virtual_clock_sdc_for_clockless_design(
tmp_path, minimal_ics55_pdk_factory, default_ics55_parameters
):
pdk_root = minimal_ics55_pdk_factory(tmp_path / "ics55")
rtl_path = tmp_path / "gcd.v"
rtl_path.write_text("module gcd(input clk, output y); assign y = clk; endmodule\n")

workspace_dir = tmp_path / "workspace"
create_workspace(
directory=str(workspace_dir),
origin_def="",
origin_verilog=str(rtl_path),
pdk="ics55",
parameters={**default_ics55_parameters, "clock": ""},
pdk_root=str(pdk_root),
)

sdc_path = workspace_dir / "origin" / "gcd.sdc"
assert sdc_path.read_text() == EXPECTED_ICS55_VIRTUAL_CLOCK_SDC


def test_create_workspace_persists_dynamic_flow_steps(
tmp_path, minimal_ics55_pdk_factory, default_ics55_parameters
):
Expand Down Expand Up @@ -1046,7 +1177,7 @@ def test_refresh_workspace_config_updates_generated_sdc_frequency(

refresh_workspace_config(workspace)

assert "set clk_freq_mhz 250.0" in workspace.pdk.sdc.read_text(encoding="utf-8")
assert "set clk_freq_mhz 250.0" in workspace.pdk.sdc.read_text(encoding="utf-8")


def test_refresh_workspace_config_preserves_external_sdc(
Expand Down
Loading