

.sdc in the template has this at the end:
if { [info exists ::env(OPENLANE_SDC_IDEAL_CLOCKS)] && $::env(OPENLANE_SDC_IDEAL_CLOCKS) } {
unset_propagated_clock [all_clocks]
} else {
set_propagated_clock [all_clocks]
}
However I've instrumented this and that variable is never set in any stages, which is probably why I have multi-hundred-ns clock slew times in pre-CTS STA. @Leo Moser (mole99) do you know what's up with that? librelane sets it here: https://github.com/librelane/librelane/blob/774d6e6f221ccf21bd499e9b8ccba4afeffff4fd/librelane/steps/openroad.py#L725-L728
set clk_pins [get_pins -of_objects [get_net -of_objects [get_pins i_chip_core.clkroot_sys_u.magic_clkroot_anchor_u/Z]]]
set clk_loads [llength $clk_pins]
puts "clk_sys has $clk_loads directly connected pins"
if { [expr $clk_loads > 10] } {
puts "Setting all clocks to ideal!"
unset_propagated_clock [all_clocks]
} else {
puts "Setting all clocks to non-ideal"
set_propagated_clock [all_clocks]
}
This prints the right messages in the right phases (ideal until after CTS), but even with unset_propagated_clock [all_clocks] my early STAs still have enormous clock rise/fall times. Is this just a me problem or do other people's pre-CTS STAs also have huge setup violations due to clock slew? (edited)


0.150000 0.000000 0.000000 clock clk_PAD (rise edge)
0.000000 0.000000 clock network delay (ideal)
0.150000 0.000000 0.000000 ^ i_chip_core.tt.i_peripherals.i_simple_peri16.i_spi_ctrl.rstn_gf180mcu_fd_sc_mcu7t5v0__dffq_1_Q/CLK (gf180mcu_fd_sc_mcu7t5v0__dffq_1)
832 4.638381 110.209221 63.362564 63.362564 ^ i_chip_core.tt.i_peripherals.i_simple_peri16.i_spi_ctrl.rstn_gf180mcu_fd_sc_mcu7t5v0__dffq_1_Q/Q (gf180mcu_fd_sc_mcu7t5v0__dffq_1)
i_chip_core.tt.i_peripherals.i_simple_peri16.i_spi_ctrl.rstn (net)
110.209221 0.000000 63.362564 ^ i_chip_core.tt.i_peripherals.i_simple_peri19.ay8913.noise_generator.reset_gf180mcu_fd_sc_mcu7t5v0__clkinv_1_ZN/I (gf180mcu_fd_sc_mcu7t5v0__clkinv_1)
164 0.691456 3.280350 53.123268 116.485832 v i_chip_core.tt.i_peripherals.i_simple_peri19.ay8913.noise_generator.reset_gf180mcu_fd_sc_mcu7t5v0__clkinv_1_ZN/ZN (gf180mcu_fd_sc_mcu7t5v0__clkinv_1)
i_chip_core.tt.i_peripherals.i_simple_peri19.ay8913.noise_generator.reset (net)


set_ideal_network which you can use to do the same thing for resets but I don't think OpenSTA supports it

set main_clk_pin [get_pins i_chip_core.clkroot_sys_u.magic_clkroot_anchor_u/Z]
set rst_pins [get_pins i_chip_core.*sync*flop2/Q]
set clk_pins [get_pins -of_objects [get_net -of_objects ${main_clk_pin}]]
set clk_loads [llength ${clk_pins}]
puts "clk_sys has ${clk_loads} directly connected pins"
if { [expr $clk_loads > 10] } {
puts "Setting all clocks to ideal!"
unset_propagated_clock [all_clocks]
puts "Case analysing reset nets:"
foreach pin ${rst_pins} {puts [sta::get_full_name ${pin}]}
set_case_analysis 1 ${rst_pins}
} else {
puts "Setting all clocks to non-ideal"
set_propagated_clock [all_clocks]
unset_case_analysis ${rst_pins}
}
Seems to work for me. Might give better placement as the datapath delays are actually realistic. (edited)

AREA 3 seems to be working best/fastest for my design so far

librelane-explore: ## Run synthesis exploration
librelane librelane/config.yaml --pdk ${PDK} --pdk-root ${PDK_ROOT} --manual-pdk --last-run --flow SynthesisExploration
.PHONY: librelane-explore
And then run make librelane-explore it will try all 9 strategies and tell you which is best (though it's just synthesis timing)




RUN_POST_GRT_RESIZER_TIMING: True

1



librelane with a --last-run --from (some pass) argument to make it restart a run from part way. Since I'm pressed for time I'd probably just edit the netlist with a text editor and re-run from there

max.rpt from the final STA





RUN_POST_GRT_RESIZER_TIMING: True 



yaml before config.yaml broke 9t cell support! (edited)STD_CELL_LIBRARY: gf180mcu_fd_sc_mcu9t5v0 in slot_*.yaml file instead of config.yamlyaml file passed into the librelane 

yaml before config.yaml broke 9t cell support! (edited)STD_CELL_LIBRARY to slot_*.yaml instead is a workaround.
I'm suspect that only STD_CELL_LIBRARY is affected because it is a special variable that can be set by passing --scl to LibreLane. If you see other variables being overwritten, please let me know. However, I think that's unlikely.

STD_CELL_LIBRARY to slot_*.yaml instead is a workaround.
I'm suspect that only STD_CELL_LIBRARY is affected because it is a special variable that can be set by passing --scl to LibreLane. If you see other variables being overwritten, please let me know. However, I think that's unlikely. 



SYNTH_AUTONAME: True, however that results in very long names so you'll likely have to go up to Magic 8.3.578 or later to avoid LVS errors

RUN_POST_GRT_RESIZER_TIMING: True 





config.json from the CTS pass:
"CTS_CLK_MAX_WIRE_LENGTH": 0,
Would limiting the max wire length help with the pre/post extraction differences?


DESIGN_REPAIR_MAX_WIRE_LENGTH in the config, not sure if that ends up being the same as the CTS setting














config.json from the CTS pass:
"CTS_CLK_MAX_WIRE_LENGTH": 0,
Would limiting the max wire length help with the pre/post extraction differences? CTS_CLK_MAX_WIRE_LENGTH only constraints the wire from the clock input pin to the clock root buffer. So it would only shift the whole clock tree, as I understand it.



CTS_CLK_MAX_WIRE_LENGTH only constraints the wire from the clock input pin to the clock root buffer. So it would only shift the whole clock tree, as I understand it. CTS_DISTANCE_BETWEEN_BUFFERS seems to do nothing at all

CTS_TOLERANCE from OpenLANE was removed 



1
1
1




Chip' - Stage 68 - Design Rule Check (KLayout) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸━━━━━ 67/77 7:51:20
I am having a time of it












Chip' - Stage 68 - Design Rule Check (KLayout) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸━━━━━ 67/77 7:51:20
I am having a time of it Chip' - Stage 68 - Design Rule Check (KLayout) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸━━━━━ 67/77 11:45:25
um. what do I do when klayout gets stuck on my design.

Chip' - Stage 68 - Design Rule Check (KLayout) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸━━━━━ 67/77 11:45:25
um. what do I do when klayout gets stuck on my design. 





Poly overlap of contact < 0.065um (CO.3) in 9-track std cells

Poly overlap of contact < 0.065um (CO.3) in 9-track std cells gf180mcuD pdk,
co3_l.output('CO.3', 'CO.3 : Poly2 overlap of contact. : 0.07µm')
Does wafer.space have it's own version of the pdk? (edited)
clone-pdk: ## Clone the GF180MCU PDK repository
rm -rf $(MAKEFILE_DIR)/gf180mcu
git clone https://github.com/wafer-space/gf180mcu.git $(MAKEFILE_DIR)/gf180mcu --depth 1
.PHONY: clone-pdk

.lyrdb

checker-klayoutdrc)


librelane Makefile target and add --last-run --from KLayout.DRC or some such. I never got the re-runs to work though
--with-initial-state should point to the input state of e.g. Klayout DRC.


select top cell
property FIXED_BBOX [box values]
lef write <lef_file> -hide
Before that, you could load the lef in klayout to see if the coordinates match the gds. If they don't, then you might try the above solution.




DF.6_MV : Min. COMP extend beyond gate (it also means source/drain overhang). : 0.4µm.
COMP on top belongs to a sdffrnq_4 and poly below belongs to a nand3_4 (edited)



DF.6_MV : Min. COMP extend beyond gate (it also means source/drain overhang). : 0.4µm.
COMP on top belongs to a sdffrnq_4 and poly below belongs to a nand3_4 (edited)



sdffrnq_4 in the future 

sdffrnq_4 in the future 




























reg [WIDTH-1:0] mem [0:DEPTH-1];
reg [WIDTH-1:0] rdata_q;
assign rdata = rdata_q;
always @ (posedge clk) begin: update
integer i;
if (!cs_n && we_n) begin
rdata_q <= mem[addr];
end
if (!cs_n && !we_n) begin
for (i = 0; i < WIDTH / 8; i = i + 1) begin
if (!be_n[i]) begin
mem[addr][i * 8 +: 8] <= wdata[i * 8 +: 8];
end
end
end
end










* Project: 018 5VGREEN SRAM
* Author: GlobalFoundries PDK Authors
* Data Created: 05-06-2014
* Revision: 0.0


!CEN && GWEN is a read enable which is aligned with the address. If that function is true on cycle n then new read data is valid on Q on cycle n + 1 (edited)



div or rem?





librelane-yolo: ## Run full flow, including DRC, but don't quit after DRC errors
librelane librelane/config.yaml --pdk ${PDK} --pdk-root ${PDK_ROOT} --manual-pdk --skip Checker.MagicDRC --skip Checker.KLayoutDRC
.PHONY: librelane-yolo
QUIT_ON_MAGIC_DRC: False
QUIT_ON_KLAYOUT_DRC: False









--noextract option which will use the existing extracted data in the WORK_ROOT directory. (edited)









/*******************************************************************************
* SRAM_CHICKEN *
*******************************************************************************/
// The foundry SRAM models return incorrect data if chip select transitions on a
// clock edge. 99% sure this is just an issue with the model, but just in case,
// this register forces all SRAMs to be permanently enabled. You should clear
// this bit to avoid wasting power.
// Field: SRAM_CHICKEN Access: RW
// Reset: 0x1
#define SYSCFG_SRAM_CHICKEN_LSB 0
#define SYSCFG_SRAM_CHICKEN_BITS 1
#define SYSCFG_SRAM_CHICKEN_MASK 0x1
I can simulate with the unmodified foundry models now. When I get chips back I'll find out whether that was necessary.

















klayout -e is the magic flag required to actually let you edit anything)






1
















STD_CELL_LIBRARY: gf180mcu_fd_sc_mcu9t5v0 in the project template and it's not applied (still the 7 track cell). Is this overriden somewhere I'm missing?
export STD_CELL_LIBRARY=gf180mcu_fd_sc_mcu9t5v0 in your shell (edited)

STD_CELL_LIBRARY: gf180mcu_fd_sc_mcu9t5v0 in the project template and it's not applied (still the 7 track cell). Is this overriden somewhere I'm missing? 
















sdffq_4 which are also flagged by Magic. (edited)






ANTENNAGATEPLUSDIFF in the techlef for thick oxide devices. (thanks @egor!)

ANTENNAGATEPLUSDIFF in the techlef for thick oxide devices. (thanks @egor!)





6















config.yaml setting?




STD_CELL_LIBRARY from 7-track to 9-track, you need to go through Yosys again




























ANTENNADIFFSIDEAREARATIO.







Metal2_active or accidentally left in COMP_fill_cell / Poly2_fill_cell... but no, that merged cleanly

2







-rd Metaln_active to the script, where n is the number of the metal layer. This moves the fill shapes to the active metal layer and thus ignores some of the dummy fill rules.







filler to drawing and then run the full precheck on that.



Metal2.drawing between the bond pads.
start_changes() and end_changes() I can add the top-level filler cells before generating the actual fill. This allows each fill layer to consider the previous one. However, it does not allow to consider the subsequent layer, i.e. if you generate dummy metal on Metal2, you would need to consider this when placing active Metal3. Therefore, fill would need to be done in two passes: first generate the active metal fill for some layers, then generate the dummy metal fill for the other layers. But yeah, it would prevent fill on the layers above and below the active fill, so not a solution anyways...
Metaln_ignore_active.












Metaln_ignore_active.





















Required PDK variable 'DPL_CELL_PADDING' did not get a specified value. This PDK may be incompatible with your flow while trying to run the make librelane-openroad (launching openroad). I'm not sure if it's something I did, and I'm looking around, but I did just update the PDK. Does anyone else have this issue on the latest pdk update?

1



















-rd Metal2_ignore_active

-rd Metal2_ignore_active 
















u_sram in my pdn.tclu_chip_core.gen_proc_wrapper[<instance_number>].u_proc_wrapper.u_sram blocks[ ], but escaping them stopped them matching anything 




















copy-reports, and copy & ZIP the GDS for submission copy-gds. There is also a Makefile target build-all, which enables everyone to simply clone the repo, install the nix-shell, execute make build-all in it, and the whole chip will be built submission-ready. Maybe this is useful for someone for future runs. Feel free to use it. 



4





















default_nettype none at the start of all your files and default_nettype wire` at the end. (edited)











gf180mcu/flows/chip.py I see a list of steps that are executed for the whole flow. Then in gf180mcu/steps/* all the actual step processing logic and config/parameters lives, correct?
So when you add a OpenROAD.Floorplan step to chip.py, the corresponding logic is defined in the Floorplan(OpenROADStep) class residing in steps/openroad.py Is that the gist of how it works?
context: was investigating adding an optional rtl_macro_placer step to the flow (ala https://github.com/librelane/librelane/issues/537). Placing macros by hand was super tedious for me, and probably terrible layout performance 

gf180mcu/flows/chip.py I see a list of steps that are executed for the whole flow. Then in gf180mcu/steps/* all the actual step processing logic and config/parameters lives, correct?
So when you add a OpenROAD.Floorplan step to chip.py, the corresponding logic is defined in the Floorplan(OpenROADStep) class residing in steps/openroad.py Is that the gist of how it works?
context: was investigating adding an optional rtl_macro_placer step to the flow (ala https://github.com/librelane/librelane/issues/537). Placing macros by hand was super tedious for me, and probably terrible layout performance rmp stepfrom decimal import Decimal
import os
from typing import Literal, Optional, Tuple
from librelane.steps.openroad import Step, TclStep, OpenROADStep, CompositeStep, State, Variable, ViewsUpdate, MetricsUpdate, dpl_variables
from librelane.common import get_script_dir, process_list_file
@Step.factory.register()
class RMP(OpenROADStep):
id = "OpenROAD.RMP"
name = "Restructure RMP"
version = "0.1.0"
config_vars = OpenROADStep.config_vars + dpl_variables + [
Variable(
"RMP_CORNER",
Optional[str],
description="IPVT corner to use during restructure. If unspecified, the value for `DEFAULT_CORNER` from the PDK will be used.",
default="max_ss_125C_4v50"
),
Variable(
"RMP_TARGET",
Literal["timing", "area"],
description="In area mode, the focus is area reduction, and timing may degrade. In delay mode, delay is likely reduced, but the area may increase",
default="area",
),
Variable(
"RMP_SLACK_THRESHOLD",
Optional[Decimal],
description="Specifies a (setup) timing slack value below which timing paths need to be analyzed for restructuring",
default=Decimal()
),
Variable(
"RMP_DEPTH_THRESHOLD",
Optional[int],
description="Specifies the path depth above which a timing path would be considered for restructuring",
),
]
def get_script_path(self):
return "~/gf180mcu-chess/restructure.tcl"
def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
self.config = self.config.copy(DEFAULT_CORNER=self.config["RMP_CORNER"])
kwargs, env = self.extract_env(kwargs)
lib_list = self.toolbox.filter_views(
self.config, self.config["LIB"], timing_corner=self.config.get("RMP_CORNER")
)
excluded_cells: Set[str] = set(self.config["EXTRA_EXCLUDED_CELLS"] or [])
excluded_cells.update(
process_list_file(self.config["SYNTH_EXCLUDED_CELL_FILE"])
)
excluded_cells.update(process_list_file(self.config["PNR_EXCLUDED_CELL_FILE"]))
trimmed_lib = self.toolbox.remove_cells_from_lib(
frozenset([str(lib) for lib in lib_list]),
excluded_cells=frozenset(excluded_cells),
)
env["_RMP_LIB"] = TclStep.value_to_tcl(trimmed_lib)
env["_RMP_ABC_LOG"] = TclStep.value_to_tcl(
os.path.join(self.step_dir, "abc.log")
)
env["RMP_CORNER"] = TclStep.value_to_tcl(self.config.get("RMP_CORNER"))
return super().run(state_in, env=env, **kwargs)
@Step.factory.register()
class RMPPlus(CompositeStep):
id = "OpenROAD.RMPPlus"
Steps = [
RMP,
Step.factory.get("OpenROAD.GlobalPlacement"),
Step.factory.get("OpenROAD.DetailedPlacement"),
Step.factory.get("OpenROAD.GlobalRouting"),
]


PYTHONPATH, and which has a filename which begins with librelane_plugin_librelane_plugin_rmp.py) (edited)librelane/config.yaml under substituting_steps.
substituting_steps section was for (makes sense in retrospect). awesome, thank you!rmp you would have nulled out the GlobalPlacement, DetailedPlacement and GlobalRouting , then inserted your RMP to run (and which includes the others)?

rmp you would have nulled out the GlobalPlacement, DetailedPlacement and GlobalRouting , then inserted your RMP to run (and which includes the others)? OpenROAD.RMPPlus afterwards to try to use the information
















dffrnq (flop with async reset) are all drive=1. (edited)




