Module 5: Caravel User Project Wrapper Integration

Prerequisites

Before proceeding, ensure your aes_wb_wrapper has completed full physical signoff in Module 4 and that the classic_flow_eco run tag exists and is complete. The copy_views.sh script used later in this module will pull its inputs directly from that run directory.


Table of Contents

  1. What is the User Project Wrapper?

  2. Integration Strategies Overview

  3. Strategy 1 — Macro-First Hardening

  4. Strategy 2 — Top-Level Integration

  5. Strategy 3 — Full-Wrapper Flattening


1. What is the User Project Wrapper?

The User Project Wrapper (user_project_wrapper) is the physical boundary between your custom design and the Caravel SoC harness. Caravel provides the padframe, the management SoC, and a standardised set of GPIO and Wishbone signals — the user_project_wrapper is the slot where your design plugs in.

_images/user_project_wrapper.png

Fig. 32 the User Project Wrapper occupies the central user area and connects to the management SoC via the Wishbone bus and GPIO interface.

Because Caravel is a fixed physical chip, it imposes strict requirements on the wrapper that cannot be changed:

  • The die area must be exactly 2920 × 3520 µm.

  • All I/O pins must appear at exact coordinate locations with exact metal shapes.

  • The power rings must be generated with specific widths, spacings, and offsets to align with Caravel’s top-level PDN.

These constraints are encoded in the fixed_dont_change/ directory of the project template and must never be edited.


2. Integration Strategies Overview

There are three established approaches for placing your design inside the user_project_wrapper. The right choice depends on design size, timing requirements, and how much control you need over the top-level physical implementation.

Strategy 1 — Macro-First Hardening
_images/strategy_macro_first.webp

Harden the user IP as a standalone macro first, then drop it into the wrapper without adding any top-level standard cells. The wrapper acts as a pass-through. Ideal for small-to-medium designs — fastest runtime and simplest flow.

Strategy 2 — Top-Level Integration
_images/strategy_top_level.webp

Harden the macro first, then integrate it into the wrapper with top-level standard cells enabled. The tool may insert buffers or repair logic at the boundary. A hybrid approach useful when the macro has boundary violations that require top-level fixing.

Strategy 3 — Full-Wrapper Flattening
_images/strategy_flattening.webp

Merge all RTL — AES core, wrapper, and everything else — into one large flattened design that covers the full ≈ 3mm × 3.6mm area. Maximum performance, but requires the most PnR time and iteration. Best for designs that need the full wrapper area.


3. Strategy 1 — Macro-First Hardening

In this strategy, aes_wb_wrapper is treated as a pre-hardened black box. The wrapper only connects its pins to the Caravel interface — no new logic is synthesised or optimised at the top level, so most flow steps are disabled. This is the approach we use in this workshop.


3.1 RTL — Instantiating the AES Macro

The RTL wrapper connects the Caravel Wishbone and GPIO signals to the aes_wb_wrapper macro, instantiated as mprj. Open the file for editing:

$ gedit ~/Silicon-Sprint-AUC/verilog/rtl/user_project_wrapper.v
user_project_wrapper.v
// SPDX-FileCopyrightText: 2020 Efabless Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0

`default_nettype none
/*
 *-------------------------------------------------------------
 *
 * user_project_wrapper
 *
 * This wrapper enumerates all of the pins available to the
 * user for the user project.
 *
 * An example user project is provided in this wrapper.  The
 * example should be removed and replaced with the actual
 * user project.
 *
 *-------------------------------------------------------------
 */

module user_project_wrapper #(
    parameter BITS = 32
) (
`ifdef USE_POWER_PINS
    inout vdda1,	// User area 1 3.3V supply
    inout vdda2,	// User area 2 3.3V supply
    inout vssa1,	// User area 1 analog ground
    inout vssa2,	// User area 2 analog ground
    inout vccd1,	// User area 1 1.8V supply
    inout vccd2,	// User area 2 1.8v supply
    inout vssd1,	// User area 1 digital ground
    inout vssd2,	// User area 2 digital ground
`endif

    // Wishbone Slave ports (WB MI A)
    input wb_clk_i,
    input wb_rst_i,
    input wbs_stb_i,
    input wbs_cyc_i,
    input wbs_we_i,
    input [3:0] wbs_sel_i,
    input [31:0] wbs_dat_i,
    input [31:0] wbs_adr_i,
    output wbs_ack_o,
    output [31:0] wbs_dat_o,

    // Logic Analyzer Signals
    input  [127:0] la_data_in,
    output [127:0] la_data_out,
    input  [127:0] la_oenb,

    // IOs
    input  [`MPRJ_IO_PADS-1:0] io_in,
    output [`MPRJ_IO_PADS-1:0] io_out,
    output [`MPRJ_IO_PADS-1:0] io_oeb,

    // Analog (direct connection to GPIO pad---use with caution)
    // Note that analog I/O is not available on the 7 lowest-numbered
    // GPIO pads, and so the analog_io indexing is offset from the
    // GPIO indexing by 7 (also upper 2 GPIOs do not have analog_io).
    inout [`MPRJ_IO_PADS-10:0] analog_io,

    // Independent clock (on independent integer divider)
    input   user_clock2,

    // User maskable interrupt signals
    output [2:0] user_irq
);

/*--------------------------------------*/
/* User project is instantiated  here   */
/*--------------------------------------*/

aes_wb_wrapper mprj (
`ifdef USE_POWER_PINS
	.VPWR(vccd2),
	.VGND(vssd2),
`endif

    .wb_clk_i(wb_clk_i),
    .wb_rst_i(wb_rst_i),

    // MGMT SoC Wishbone Slave

    .wbs_cyc_i(wbs_cyc_i),
    .wbs_stb_i(wbs_stb_i),
    .wbs_we_i(wbs_we_i),
    .wbs_sel_i(wbs_sel_i),
    .wbs_adr_i(wbs_adr_i),
    .wbs_dat_i(wbs_dat_i),
    .wbs_ack_o(wbs_ack_o),
    .wbs_dat_o(wbs_dat_o)
);

endmodule	// user_project_wrapper

`default_nettype wire

The instance name mprj must match exactly in both the Verilog instantiation and the MACROS configuration dictionary.

Create the wrapper configuration file:

$ gedit ~/Silicon-Sprint-AUC/openlane/user_project_wrapper/config.json

Paste the following complete configuration:

config.json
{
    "VERILOG_FILES": [
        "dir::../../verilog/rtl/defines.v",
        "dir::../../verilog/rtl/user_project_wrapper.v"
    ],

    "SYNTH_ELABORATE_ONLY": true,
    "RUN_POST_GPL_DESIGN_REPAIR": false,
    "RUN_POST_CTS_RESIZER_TIMING": false,
    "DESIGN_REPAIR_BUFFER_INPUT_PORTS": false,
    "PDN_ENABLE_RAILS": false,
    "RUN_ANTENNA_REPAIR": false,
    "RUN_FILL_INSERTION": false,
    "RUN_TAP_ENDCAP_INSERTION": false,
    "RUN_CTS": false,
    "RUN_IRDROP_REPORT": false,

    "MACROS": {
        "aes_wb_wrapper": {
            "gds":  ["dir::../../gds/aes_wb_wrapper.gds"],
            "lef":  ["dir::../../lef/aes_wb_wrapper.lef"],
            "instances": {
                "mprj": {
                    "location": [10, 20],
                    "orientation": "N"
                }
            },
            "nl":   ["dir::../../verilog/gl/aes_wb_wrapper.v"],
            "spef": {
                "min_*": ["dir::../../spef/multicorner/aes_wb_wrapper.min.spef"],
                "nom_*": ["dir::../../spef/multicorner/aes_wb_wrapper.nom.spef"],
                "max_*": ["dir::../../spef/multicorner/aes_wb_wrapper.max.spef"]
            },
            "lib":  {"*": "dir::../../lib/aes_wb_wrapper.lib"}
        }
    },
    "PDN_MACRO_CONNECTIONS": ["mprj vccd2 vssd2 VPWR VGND"],

    "PDN_VOFFSET": 5,
    "PDN_HOFFSET": 5,
    "PDN_VWIDTH": 3.1,
    "PDN_HWIDTH": 3.1,
    "PDN_VSPACING": 15.5,
    "PDN_HSPACING": 15.5,
    "PDN_VPITCH": 180,
    "PDN_HPITCH": 180,
    "QUIT_ON_PDN_VIOLATIONS": false,
    "MAGIC_DRC_USE_GDS": true,
    "MAX_TRANSITION_CONSTRAINT": 1.5,

    "//": "Fixed configurations for Caravel — do NOT edit below this line",
    "DESIGN_NAME": "user_project_wrapper",
    "FP_SIZING": "absolute",
    "DIE_AREA": [0, 0, 2920, 3520],
    "FP_DEF_TEMPLATE": "dir::fixed_dont_change/user_project_wrapper.def",
    "VDD_NETS": ["vccd1", "vccd2", "vdda1", "vdda2"],
    "GND_NETS": ["vssd1", "vssd2", "vssa1", "vssa2"],
    "PDN_CORE_RING": 1,
    "PDN_CORE_RING_VWIDTH": 3.1,
    "PDN_CORE_RING_HWIDTH": 3.1,
    "PDN_CORE_RING_VOFFSET": 12.45,
    "PDN_CORE_RING_HOFFSET": 12.45,
    "PDN_CORE_RING_VSPACING": 1.7,
    "PDN_CORE_RING_HSPACING": 1.7,
    "CLOCK_PORT": "wb_clk_i",
    "PNR_SDC_FILE": "dir::signoff.sdc",
    "SIGNOFF_SDC_FILE": "dir::signoff.sdc",
    "MAGIC_DEF_LABELS": 0,
    "CLOCK_PERIOD": 25,
    "MAGIC_ZEROIZE_ORIGIN": 0
}

3.2 The DEF Template

"FP_DEF_TEMPLATE": "dir::fixed_dont_change/user_project_wrapper.def"

The Odb.ApplyDEFTemplate step imports a pre-built template into the OpenROAD database. It copies the die area, core area, and I/O pin data (names/locations) to ensure a perfect physical match with the top-level harness.

Key Requirements

  • Pin Matching: By default, the design’s pin set must identically match the template. Any mismatch will cause the flow to fail.

  • Immutable Source: Templates are stored in fixed_dont_change/ and must not be edited, as they represent fixed physical hardware.

Why It Is Required for Caravel

The template (FP_DEF_TEMPLATE) encodes three attributes that config.json cannot define:

  1. Exact Pin Geometry: Assigns the precise shape, layer, and coordinates required for connection to the management SoC and padframe.

  2. Fixed Power Rings: Includes pre-drawn VDD/GND rings designed to align perfectly with the top-level Power Distribution Network (PDN).

  3. Fixed Core Area: Enforces the exact 2,920 × 3,520 µm boundary available in the fabricated silicon.

_images/DEF_Templet.png

Fig. 33 User Project Wrapper DEF template in KLayout — pre-placed I/O pins, power ring, and die boundary. The open core area is where your macro is placed.

Warning

This file must never be modified. Moving even a single pin by one manufacturing grid step will cause fatal DRC violations during top-level chip assembly.


3.3 Macro Physical Views

LibreLane requires several physical view files to place, route, analyse, and verify the macro inside the wrapper. The two essential views are:

LEF (.lef) — Essential for PnR

The physical interface of the macro — boundary dimensions, pin locations on metal layers, and routing obstructions. LibreLane uses the LEF to keep routing out of the macro boundary and prevent shorts against its pins.

GDSII (.gds) — Essential for Tape-Out

The complete physical layout including silicon implant and diffusion layers. Used during GDS stream-out, DRC, and LVS.

The remaining views are optional but improve analysis quality:

View

Extension

Role

Gate-Level Netlist

.nl.v

Used during STA and as a fallback for linting.

Powered Netlist

.pnl.v

Same as above with power pins — preferred for LVS.

Liberty Model

.lib

Cell timing models for OpenSTA path analysis through the macro.

Parasitics

.spef

Per-corner RC extraction — enables accurate post-layout STA.


3.4 Macro Registration and Placement

Macros are registered in the MACROS dictionary. The instances sub-key maps the RTL instance name to a physical placement:

"instances": {
    "mprj": {
        "location": [10, 20],
        "orientation": "N"
    }
}
  • Instance name — must match exactly as instantiated in user_project_wrapper.v.

  • Location[10, 20] places the macro near the bottom-left corner of the core area, minimising wire length to the Wishbone signals that enter the wrapper from that edge.

  • OrientationN (North, no rotation) keeps the macro’s Wishbone pins aligned with the correct wrapper edge.


3.5 Power Connections

"PDN_MACRO_CONNECTIONS": ["mprj vccd2 vssd2 VPWR VGND"]

Field

Value

Meaning

Instance name

mprj

Matches the RTL instance name. Accepts regex for multi-instance designs.

VDD net

vccd2

The secondary digital power domain in Caravel’s multi-supply architecture.

GND net

vssd2

The secondary digital ground.

VDD pin

VPWR

Physical power pin name on the macro (from its LEF).

GND pin

VGND

Physical ground pin name on the macro.


3.6 Disabled Flow Steps

Because aes_wb_wrapper is pre-hardened and timing-closed, most optimisation steps are disabled at the wrapper level to avoid modifying a physically finalised design.

Parameter

Why Disabled

SYNTH_ELABORATE_ONLY: true

The wrapper contains only a macro instantiation — elaboration-only mode preserves hierarchy without technology mapping.

RUN_CTS: false

The macro has its own internal clock tree. No new CTS is needed at the wrapper level.

RUN_POST_GPL_DESIGN_REPAIR: false

Prevents the resizer from attempting to fix violations on an already finalised design.

RUN_POST_CTS_RESIZER_TIMING: false

No CTS is run, so post-CTS timing optimisation has nothing to act on.

DESIGN_REPAIR_BUFFER_INPUT_PORTS: false

Prevents buffer insertion at wrapper input ports, preserving direct connections to macro pins.

PDN_ENABLE_RAILS: false

Metal 1 standard cell rails are only needed where standard cells exist — the wrapper core is occupied by the macro.

RUN_ANTENNA_REPAIR: false

Antenna repair was already performed during Module 4 hardening.

RUN_TAP_ENDCAP_INSERTION: false

Tap cells support standard cell rows — not needed when the wrapper contains only a hardened macro.

RUN_FILL_INSERTION: false

Filler cells close gaps between standard cells. No standard cells means no gaps.

RUN_IRDROP_REPORT: false

IR drop analysis is performed at the full chip level; skipping it reduces runtime.


3.7 Full Configuration Reference

Variable

Type

Description

Default

SYNTH_ELABORATE_ONLY

bool

Elaborates hierarchy without technology mapping.

False

MACROS

Dict[str, Macro]?

Dictionary of macro physical views, instances, and timing models.

None

PDN_MACRO_CONNECTIONS

List[str]?

Explicit power connections. Format: <instance> <vdd_net> <gnd_net> <vdd_pin> <gnd_pin>.

None

DESIGN_NAME

str

Top-level module name. Must be user_project_wrapper.

None

FP_SIZING

'absolute' | 'relative'

absolute uses exact die coordinates.

relative

DIE_AREA

Tuple?

Fixed die boundary "x0 y0 x1 y1" µm. Must be [0, 0, 2920, 3520] for Caravel.

None

FP_DEF_TEMPLATE

Path?

Pre-built DEF with fixed I/O pins, power ring, and die boundary.

None

VDD_NETS

List[str]?

Power net names for PDN generation.

None

GND_NETS

List[str]?

Ground net names.

None

PDN_CORE_RING

bool

Generates a power ring around the core perimeter.

False

PDN_CORE_RING_VWIDTH

Decimal

Width of vertical ring segments (µm).

None

PDN_CORE_RING_HWIDTH

Decimal

Width of horizontal ring segments (µm).

None

PDN_CORE_RING_VSPACING

Decimal

Spacing between VDD/GND vertical ring straps (µm).

None

PDN_CORE_RING_HSPACING

Decimal

Spacing between VDD/GND horizontal ring straps (µm).

None

PDN_CORE_RING_VOFFSET

Decimal

Offset of vertical ring from core boundary (µm).

None

PDN_CORE_RING_HOFFSET

Decimal

Offset of horizontal ring from core boundary (µm).

None

CLOCK_PORT

(str | List[str])?

Primary clock port. wb_clk_i is the Wishbone clock from the management SoC.

None

MAGIC_ZEROIZE_ORIGIN

bool

Moves layout origin to (0, 0) in Magic’s LEF. Must be 0 for Caravel.

False


3.8 Copying Macro Views

Before running the wrapper flow, copy all physical view files from the aes_wb_wrapper hardening run into the shared project directories:

$ bash ~/Silicon-Sprint-AUC/openlane/copy_views.sh \
    ~/Silicon-Sprint-AUC \
    aes_wb_wrapper \
    classic_flow_eco

The three arguments are: project root directory, macro name, and run tag. The script copies final/gds/gds/, final/lef/lef/, final/nl/verilog/gl/, final/spef/spef/multicorner/, and final/lib/lib/.

Note

Verify that all five destination directories are populated before proceeding. A missing file will cause a file-not-found error at the step that first needs that view.


3.9 Flow Execution

Enter the Nix shell:

$ nix-shell ~/librelane/shell.nix

Run the wrapper hardening:

[nix-shell:~]$ librelane \
    ~/Silicon-Sprint-AUC/openlane/user_project_wrapper/config.json \
    --run-tag project_wrapper

When the flow completes successfully:

 Antenna
Passed ✅
 LVS
Passed ✅
 DRC
Passed ✅

3.10 Viewing the Layout

[nix-shell:~]$ librelane \
    --last-run \
    --flow openinklayout \
    ~/Silicon-Sprint-AUC/openlane/user_project_wrapper/config.json
_images/user_project_layout.webp

Fig. 34 User Project Wrapper layout — the aes_wb_wrapper macro at the bottom-left corner, close to the Wishbone signal pins entering from that edge.

Toggle layer visibility to prBoundary.boundary, met1.drawing, met2.drawing, and met3.drawing to inspect the internal macro routing.

_images/mprj-gds.webp

Fig. 35 AES macro internal view — the absence of long routes confirms that all internal routing was completed cleanly during Module 4.


3.11 Checking the Reports

Antenna Check (OpenROAD.CheckAntennas)

Location: runs/project_wrapper/XX-openroad-checkantennas/

┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━┳━━━━━┳━━━━━┳━━━━━━━┓
┃ Partial/Required ┃ Required ┃ Partial ┃ Net ┃ Pin ┃ Layer ┃
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━╇━━━━━╇━━━━━╇━━━━━━━┩
└──────────────────┴──────────┴─────────┴─────┴─────┴───────┘

Post-PnR STA (OpenROAD.STAPostPNR)

Location: runs/project_wrapper/XX-openroad-stapostpnr/summary.rpt

┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┓
┃                      ┃ Hold     ┃ Reg to   ┃          ┃          ┃ of which  ┃ Setup    ┃           ┃          ┃           ┃ of which ┃           ┃          ┃
┃                      ┃ Worst    ┃ Reg      ┃          ┃ Hold Vio ┃ reg to    ┃ Worst    ┃ Reg to    ┃ Setup    ┃ Setup Vio ┃ reg to   ┃ Max Cap   ┃ Max Slew ┃
┃ Corner/Group         ┃ Slack    ┃ Paths    ┃ Hold TNS ┃ Count    ┃ reg       ┃ Slack    ┃ Reg Paths ┃ TNS      ┃ Count     ┃ reg      ┃ Violatio… ┃ Violati… ┃
┡━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━┩
│ Overall              │ 0.0047   │ 0.0047   │ 0.0000   │ 0        │ 0         │ 2.1041   │ 2.1041    │ 0.0000   │ 0         │ 0        │ 1         │ 2        │
│ nom_tt_025C_1v80     │ 0.1483   │ 0.1483   │ 0.0000   │ 0        │ 0         │ 7.9981   │ 13.3840   │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
│ nom_ss_100C_1v60     │ 0.5562   │ 0.5562   │ 0.0000   │ 0        │ 0         │ 2.5763   │ 2.5763    │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
│ nom_ff_n40C_1v95     │ 0.0053   │ 0.0053   │ 0.0000   │ 0        │ 0         │ 9.1132   │ 17.4716   │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
│ min_tt_025C_1v80     │ 0.1474   │ 0.1474   │ 0.0000   │ 0        │ 0         │ 8.1437   │ 13.6547   │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
│ min_ss_100C_1v60     │ 0.5545   │ 0.5545   │ 0.0000   │ 0        │ 0         │ 3.0678   │ 3.0678    │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
│ min_ff_n40C_1v95     │ 0.0047   │ 0.0047   │ 0.0000   │ 0        │ 0         │ 9.2076   │ 17.7111   │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
│ max_tt_025C_1v80     │ 0.1491   │ 0.1491   │ 0.0000   │ 0        │ 0         │ 7.8819   │ 13.1149   │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
│ max_ss_100C_1v60     │ 0.5582   │ 0.5582   │ 0.0000   │ 0        │ 0         │ 2.1041   │ 2.1041    │ 0.0000   │ 0         │ 0        │ 1         │ 2        │
│ max_ff_n40C_1v95     │ 0.0059   │ 0.0059   │ 0.0000   │ 0        │ 0         │ 9.0334   │ 17.2358   │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
└──────────────────────┴──────────┴──────────┴──────────┴──────────┴───────────┴──────────┴───────────┴──────────┴───────────┴──────────┴───────────┴──────────┘

Interpreting the Results

Hold and Setup: zero violations across all 9 corners. The macro’s pre-hardened timing is correctly preserved at the wrapper level.

Max Slew (2) and Max Cap (1) in max_ss_100C_1v60: inherited from the aes_wb_wrapper interface pins — not resolvable at the wrapper level in this strategy. They do not affect functional correctness.

DRC and LVS

Magic DRCruns/project_wrapper/XX-magic-drc/reports/drc.rpt:

aes_wb_wrapper
----------------------------------------
[INFO] COUNT: 0
[INFO] Should be divided by 3 or 4

KLayout DRCruns/project_wrapper/XX-klayout-drc/violations.json:

{ "total": 0 }

LVSruns/project_wrapper/XX-netgen-lvs/reports/lvs.rpt:

Cell pin lists are equivalent.
Device classes user_project_wrapper and user_project_wrapper are equivalent.
Final result: Circuits match uniquely.

3.12 Saving the Views

$ bash ~/Silicon-Sprint-AUC/openlane/copy_views.sh \
    ~/Silicon-Sprint-AUC \
    user_project_wrapper \
    project_wrapper

Congratulations — Strategy 1 Complete!

The user_project_wrapper.gds produced by this run is ready for integration into the Caravel harness.


4. Strategy 2 — Top-Level Integration

In the top-level integration methodology, aes_wb_wrapper is still hardened as a separate macro, but the wrapper is now allowed to place additional standard cells at the top level alongside it. The tool performs full CTS, placement repair, and buffering on the wrapper-level logic, closing any boundary timing violations that the macro-first strategy cannot address.

The key differences from Strategy 1 are that all optimisation steps are enabled, and the macro is placed away from the corner ([100, 100] instead of [10, 20]) to leave room for the tool to insert routing, well taps, and buffers at the boundary without creating congestion.

Warning

Do not use "location": [10, 20] with this strategy. Placing the macro at the extreme corner leaves no routing headroom for the top-level standard cells, tap cells, and input port buffers the tool inserts, causing routing overflow.


4.1 Timing Constraints (Top-Level)

Both SDC files are used with Strategy 2 — the same files used in Strategy 1. The PnR file over-constrains during implementation; the signoff file applies realistic values for the final timing check.

signoff.sdc
# generated by get_cup_sdc.py
# Date: 2023/06/20

### Note:
# - input clock transition and latency are set for wb_clk_i port.
#   If your design is using the user_clock2, update the clock constraints to reflect that and use usr_* variables.
# - IO ports are assumed to be asynchronous. If they're synchronous to the clock, update the variable IO_SYNC to 1.
#   As well, update in_ext_delay and out_ext_delay with the required I/O external delays.

#------------------------------------------#
# Pre-defined Constraints
#------------------------------------------#

set ::env(IO_SYNC) 0
# Clock network
if {[info exists ::env(CLOCK_PORT)] && $::env(CLOCK_PORT) != ""} {
	set clk_input $::env(CLOCK_PORT)
	create_clock [get_ports $clk_input] -name clk -period $::env(CLOCK_PERIOD)
	puts "\[INFO\]: Creating clock {clk} for port $clk_input with period: $::env(CLOCK_PERIOD)"
} else {
	set clk_input __VIRTUAL_CLK__
	create_clock -name clk -period $::env(CLOCK_PERIOD)
	puts "\[INFO\]: Creating virtual clock with period: $::env(CLOCK_PERIOD)"
}
if { ![info exists ::env(SYNTH_CLK_DRIVING_CELL)] } {
	set ::env(SYNTH_CLK_DRIVING_CELL) $::env(SYNTH_DRIVING_CELL)
}
if { ![info exists ::env(SYNTH_CLK_DRIVING_CELL_PIN)] } {
	set ::env(SYNTH_CLK_DRIVING_CELL_PIN) $::env(SYNTH_DRIVING_CELL_PIN)
}

# Clock non-idealities
set_propagated_clock [all_clocks]
set_clock_uncertainty $::env(SYNTH_CLOCK_UNCERTAINTY) [get_clocks {clk}]
puts "\[INFO\]: Setting clock uncertainity to: $::env(SYNTH_CLOCK_UNCERTAINTY)"
set_clock_transition $::env(SYNTH_CLOCK_TRANSITION) [get_clocks {clk}]
puts "\[INFO\]: Setting clock transition to: $::env(SYNTH_CLOCK_TRANSITION)"

# Maximum transition time for the design nets
set_max_transition 1.5 [current_design]
puts "\[INFO\]: Setting maximum transition to: 1.5"

# Maximum fanout
set_max_fanout $::env(MAX_FANOUT_CONSTRAINT) [current_design]
puts "\[INFO\]: Setting maximum fanout to: $::env(MAX_FANOUT_CONSTRAINT)"

# Timing paths delays derate
set_timing_derate -early [expr {1-$::env(SYNTH_TIMING_DERATE)}]
set_timing_derate -late [expr {1+$::env(SYNTH_TIMING_DERATE)}]
puts "\[INFO\]: Setting timing derate to: [expr {$::env(SYNTH_TIMING_DERATE) * 100}] %"

# Reset input delay
set_input_delay [expr $::env(CLOCK_PERIOD) * 0.5] -clock [get_clocks {clk}] [get_ports {wb_rst_i}]

# Multicycle paths
set_multicycle_path -setup 2 -through [get_ports {wbs_ack_o}]
set_multicycle_path -hold 1  -through [get_ports {wbs_ack_o}]
set_multicycle_path -setup 2 -through [get_ports {wbs_cyc_i}]
set_multicycle_path -hold 1  -through [get_ports {wbs_cyc_i}]
set_multicycle_path -setup 2 -through [get_ports {wbs_stb_i}]
set_multicycle_path -hold 1  -through [get_ports {wbs_stb_i}]

#------------------------------------------#
# Retrieved Constraints
#------------------------------------------#

# Clock source latency
set usr_clk_max_latency 4.57
set usr_clk_min_latency 4.11
set clk_max_latency 5.57
set clk_min_latency 4.65
set_clock_latency -source -max $clk_max_latency [get_clocks {clk}]
set_clock_latency -source -min $clk_min_latency [get_clocks {clk}]
puts "\[INFO\]: Setting clock latency range: $clk_min_latency : $clk_max_latency"

# Clock input Transition
set usr_clk_tran 0.13
set clk_tran 0.61
set_input_transition $clk_tran [get_ports $clk_input]
puts "\[INFO\]: Setting clock transition: $clk_tran"

# Input delays
set_input_delay -max 1.87 -clock [get_clocks {clk}] [get_ports {la_data_in[*]}]
set_input_delay -max 1.89 -clock [get_clocks {clk}] [get_ports {la_oenb[*]}]
set_input_delay -max 3.17 -clock [get_clocks {clk}] [get_ports {wbs_sel_i[*]}]
set_input_delay -max 3.74 -clock [get_clocks {clk}] [get_ports {wbs_we_i}]
set_input_delay -max 3.89 -clock [get_clocks {clk}] [get_ports {wbs_adr_i[*]}]
set_input_delay -max 4.13 -clock [get_clocks {clk}] [get_ports {wbs_stb_i}]
set_input_delay -max 4.61 -clock [get_clocks {clk}] [get_ports {wbs_dat_i[*]}]
set_input_delay -max 4.74 -clock [get_clocks {clk}] [get_ports {wbs_cyc_i}]
set_input_delay -min 0.18 -clock [get_clocks {clk}] [get_ports {la_data_in[*]}]
set_input_delay -min 0.3  -clock [get_clocks {clk}] [get_ports {la_oenb[*]}]
set_input_delay -min 0.79 -clock [get_clocks {clk}] [get_ports {wbs_adr_i[*]}]
set_input_delay -min 1.04 -clock [get_clocks {clk}] [get_ports {wbs_dat_i[*]}]
set_input_delay -min 1.19 -clock [get_clocks {clk}] [get_ports {wbs_sel_i[*]}]
set_input_delay -min 1.65 -clock [get_clocks {clk}] [get_ports {wbs_we_i}]
set_input_delay -min 1.69 -clock [get_clocks {clk}] [get_ports {wbs_cyc_i}]
set_input_delay -min 1.86 -clock [get_clocks {clk}] [get_ports {wbs_stb_i}]
if { $::env(IO_SYNC) } {
	set in_ext_delay 4
	puts "\[INFO\]: Setting input ports external delay to: $in_ext_delay"
	set_input_delay -max [expr $in_ext_delay + 4.55] -clock [get_clocks {clk}] [get_ports {io_in[*]}]
	set_input_delay -min [expr $in_ext_delay + 1.26] -clock [get_clocks {clk}] [get_ports {io_in[*]}]
}

# Input Transition
set_input_transition -max 0.14  [get_ports {wbs_we_i}]
set_input_transition -max 0.15  [get_ports {wbs_stb_i}]
set_input_transition -max 0.17  [get_ports {wbs_cyc_i}]
set_input_transition -max 0.18  [get_ports {wbs_sel_i[*]}]
set_input_transition -max 0.38  [get_ports {io_in[*]}]
set_input_transition -max 0.84  [get_ports {wbs_dat_i[*]}]
set_input_transition -max 0.86  [get_ports {la_data_in[*]}]
set_input_transition -max 0.92  [get_ports {wbs_adr_i[*]}]
set_input_transition -max 0.97  [get_ports {la_oenb[*]}]
set_input_transition -min 0.05  [get_ports {io_in[*]}]
set_input_transition -min 0.06  [get_ports {la_oenb[*]}]
set_input_transition -min 0.07  [get_ports {la_data_in[*]}]
set_input_transition -min 0.07  [get_ports {wbs_adr_i[*]}]
set_input_transition -min 0.07  [get_ports {wbs_dat_i[*]}]
set_input_transition -min 0.09  [get_ports {wbs_cyc_i}]
set_input_transition -min 0.09  [get_ports {wbs_sel_i[*]}]
set_input_transition -min 0.09  [get_ports {wbs_we_i}]
set_input_transition -min 0.15  [get_ports {wbs_stb_i}]

# Output delays
set_output_delay -max 0.7  -clock [get_clocks {clk}] [get_ports {user_irq[*]}]
set_output_delay -max 1.0  -clock [get_clocks {clk}] [get_ports {la_data_out[*]}]
set_output_delay -max 3.62 -clock [get_clocks {clk}] [get_ports {wbs_dat_o[*]}]
set_output_delay -max 8.41 -clock [get_clocks {clk}] [get_ports {wbs_ack_o}]
set_output_delay -min 0    -clock [get_clocks {clk}] [get_ports {la_data_out[*]}]
set_output_delay -min 0    -clock [get_clocks {clk}] [get_ports {user_irq[*]}]
set_output_delay -min 1.13 -clock [get_clocks {clk}] [get_ports {wbs_dat_o[*]}]
set_output_delay -min 1.37 -clock [get_clocks {clk}] [get_ports {wbs_ack_o}]
if { $::env(IO_SYNC) } {
	set out_ext_delay 4
	puts "\[INFO\]: Setting output ports external delay to: $out_ext_delay"
	set_output_delay -max [expr $out_ext_delay + 9.12] -clock [get_clocks {clk}] [get_ports {io_out[*]}]
	set_output_delay -max [expr $out_ext_delay + 9.32] -clock [get_clocks {clk}] [get_ports {io_oeb[*]}]
	set_output_delay -min [expr $out_ext_delay + 2.34] -clock [get_clocks {clk}] [get_ports {io_oeb[*]}]
	set_output_delay -min [expr $out_ext_delay + 3.9]  -clock [get_clocks {clk}] [get_ports {io_out[*]}]
}

# Output loads
set_load 0.19 [all_outputs]
pnr.sdc
# Copied from signoff.sdc then edited

## Note:
# - input clock transition and latency are set for wb_clk_i port.
#   If your design is using the user_clock2, update the clock constraints to reflect that and use usr_* variables.
# - IO ports are assumed to be asynchronous. If they're synchronous to the clock, update the variable IO_SYNC to 1.
#   As well, update in_ext_delay and out_ext_delay with the required I/O external delays.

#------------------------------------------#
# Pre-defined Constraints
#------------------------------------------#

set ::env(IO_SYNC) 0
# Clock network
if {[info exists ::env(CLOCK_PORT)] && $::env(CLOCK_PORT) != ""} {
    set clk_input $::env(CLOCK_PORT)
    create_clock [get_ports $clk_input] -name clk -period $::env(CLOCK_PERIOD)
    puts "\[INFO\]: Creating clock {clk} for port $clk_input with period: $::env(CLOCK_PERIOD)"
} else {
    set clk_input __VIRTUAL_CLK__
    create_clock -name clk -period $::env(CLOCK_PERIOD)
    puts "\[INFO\]: Creating virtual clock with period: $::env(CLOCK_PERIOD)"
}
if { ![info exists ::env(SYNTH_CLK_DRIVING_CELL)] } {
    set ::env(SYNTH_CLK_DRIVING_CELL) $::env(SYNTH_DRIVING_CELL)
}
if { ![info exists ::env(SYNTH_CLK_DRIVING_CELL_PIN)] } {
    set ::env(SYNTH_CLK_DRIVING_CELL_PIN) $::env(SYNTH_DRIVING_CELL_PIN)
}

# Clock non-idealities
set_propagated_clock [all_clocks]
set_clock_uncertainty 0.15 [get_clocks {clk}]
puts "\[INFO\]: Setting clock uncertainty to: 0.15"
set_clock_transition $::env(SYNTH_CLOCK_TRANSITION) [get_clocks {clk}]
puts "\[INFO\]: Setting clock transition to: $::env(SYNTH_CLOCK_TRANSITION)"

# Maximum transition time for the design nets
set_max_transition 0.75 [current_design]
puts "\[INFO\]: Setting maximum transition to: 0.75"

# Maximum fanout
set_max_fanout 16 [current_design]
puts "\[INFO\]: Setting maximum fanout to: 16"

# Timing paths delays derate
set_timing_derate -early [expr {1-0.07}]
set_timing_derate -late [expr {1+0.07}]
puts "\[INFO\]: Setting timing derate to: [expr {0.07 * 100}] %"

# Reset input delay
set_input_delay [expr $::env(CLOCK_PERIOD) * 0.5] -clock [get_clocks {clk}] [get_ports {wb_rst_i}]

# Multicycle paths
set_multicycle_path -setup 2 -through [get_ports {wbs_ack_o}]
set_multicycle_path -hold 1  -through [get_ports {wbs_ack_o}]
set_multicycle_path -setup 2 -through [get_ports {wbs_cyc_i}]
set_multicycle_path -hold 1  -through [get_ports {wbs_cyc_i}]
set_multicycle_path -setup 2 -through [get_ports {wbs_stb_i}]
set_multicycle_path -hold 1  -through [get_ports {wbs_stb_i}]

#------------------------------------------#
# Retrieved Constraints
#------------------------------------------#

# Clock source latency
set usr_clk_max_latency 4.57
set usr_clk_min_latency 4.11
set clk_max_latency 5.57
set clk_min_latency 4.65
set_clock_latency -source -max $clk_max_latency [get_clocks {clk}]
set_clock_latency -source -min $clk_min_latency [get_clocks {clk}]
puts "\[INFO\]: Setting clock latency range: $clk_min_latency : $clk_max_latency"

# Clock input Transition
set usr_clk_tran 0.13
set clk_tran 0.61
set_input_transition $clk_tran [get_ports $clk_input]
puts "\[INFO\]: Setting clock transition: $clk_tran"

# Input delays
set_input_delay -max 1.87 -clock [get_clocks {clk}] [get_ports {la_data_in[*]}]
set_input_delay -max 1.89 -clock [get_clocks {clk}] [get_ports {la_oenb[*]}]
set_input_delay -max 3.17 -clock [get_clocks {clk}] [get_ports {wbs_sel_i[*]}]
set_input_delay -max 3.74 -clock [get_clocks {clk}] [get_ports {wbs_we_i}]
set_input_delay -max 3.89 -clock [get_clocks {clk}] [get_ports {wbs_adr_i[*]}]
set_input_delay -max 4.13 -clock [get_clocks {clk}] [get_ports {wbs_stb_i}]
set_input_delay -max 4.61 -clock [get_clocks {clk}] [get_ports {wbs_dat_i[*]}]
set_input_delay -max 4.74 -clock [get_clocks {clk}] [get_ports {wbs_cyc_i}]
set_input_delay -min 0.18 -clock [get_clocks {clk}] [get_ports {la_data_in[*]}]
set_input_delay -min 0.3  -clock [get_clocks {clk}] [get_ports {la_oenb[*]}]
set_input_delay -min 0.79 -clock [get_clocks {clk}] [get_ports {wbs_adr_i[*]}]
# wbs_dat_i minimum input delay was decreased here to fix hold violations
set_input_delay -min 0.80 -clock [get_clocks {clk}] [get_ports {wbs_dat_i[*]}]
set_input_delay -min 1.19 -clock [get_clocks {clk}] [get_ports {wbs_sel_i[*]}]
set_input_delay -min 1.65 -clock [get_clocks {clk}] [get_ports {wbs_we_i}]
set_input_delay -min 1.69 -clock [get_clocks {clk}] [get_ports {wbs_cyc_i}]
set_input_delay -min 1.86 -clock [get_clocks {clk}] [get_ports {wbs_stb_i}]
if { $::env(IO_SYNC) } {
    set in_ext_delay 4
    puts "\[INFO\]: Setting input ports external delay to: $in_ext_delay"
    set_input_delay -max [expr $in_ext_delay + 4.55] -clock [get_clocks {clk}] [get_ports {io_in[*]}]
    set_input_delay -min [expr $in_ext_delay + 1.26] -clock [get_clocks {clk}] [get_ports {io_in[*]}]
}

# Input Transition
set_input_transition -max 0.14  [get_ports {wbs_we_i}]
set_input_transition -max 0.15  [get_ports {wbs_stb_i}]
set_input_transition -max 0.17  [get_ports {wbs_cyc_i}]
set_input_transition -max 0.18  [get_ports {wbs_sel_i[*]}]
set_input_transition -max 0.38  [get_ports {io_in[*]}]
set_input_transition -max 0.84  [get_ports {wbs_dat_i[*]}]
set_input_transition -max 0.86  [get_ports {la_data_in[*]}]
set_input_transition -max 0.92  [get_ports {wbs_adr_i[*]}]
set_input_transition -max 0.97  [get_ports {la_oenb[*]}]
set_input_transition -min 0.05  [get_ports {io_in[*]}]
set_input_transition -min 0.06  [get_ports {la_oenb[*]}]
set_input_transition -min 0.07  [get_ports {la_data_in[*]}]
set_input_transition -min 0.07  [get_ports {wbs_adr_i[*]}]
set_input_transition -min 0.07  [get_ports {wbs_dat_i[*]}]
set_input_transition -min 0.09  [get_ports {wbs_cyc_i}]
set_input_transition -min 0.09  [get_ports {wbs_sel_i[*]}]
set_input_transition -min 0.09  [get_ports {wbs_we_i}]
set_input_transition -min 0.15  [get_ports {wbs_stb_i}]

# Output delays
set_output_delay -max 0.7  -clock [get_clocks {clk}] [get_ports {user_irq[*]}]
set_output_delay -max 1.0  -clock [get_clocks {clk}] [get_ports {la_data_out[*]}]
set_output_delay -max 3.62 -clock [get_clocks {clk}] [get_ports {wbs_dat_o[*]}]
set_output_delay -max 8.41 -clock [get_clocks {clk}] [get_ports {wbs_ack_o}]
set_output_delay -min 0    -clock [get_clocks {clk}] [get_ports {la_data_out[*]}]
set_output_delay -min 0    -clock [get_clocks {clk}] [get_ports {user_irq[*]}]
set_output_delay -min 1.13 -clock [get_clocks {clk}] [get_ports {wbs_dat_o[*]}]
set_output_delay -min 1.37 -clock [get_clocks {clk}] [get_ports {wbs_ack_o}]
if { $::env(IO_SYNC) } {
    set out_ext_delay 4
    puts "\[INFO\]: Setting output ports external delay to: $out_ext_delay"
    set_output_delay -max [expr $out_ext_delay + 9.12] -clock [get_clocks {clk}] [get_ports {io_out[*]}]
    set_output_delay -max [expr $out_ext_delay + 9.32] -clock [get_clocks {clk}] [get_ports {io_oeb[*]}]
    set_output_delay -min [expr $out_ext_delay + 2.34] -clock [get_clocks {clk}] [get_ports {io_oeb[*]}]
    set_output_delay -min [expr $out_ext_delay + 3.9]  -clock [get_clocks {clk}] [get_ports {io_out[*]}]
}

# Output loads
set_load 0.19 [all_outputs]

4.2 Configuration (Top-Level)

The key change from Strategy 1 is that all disabled flags are now set to true, and SYNTH_ELABORATE_ONLY is set to false:

config.json
{
    "QUIT_ON_SYNTH_CHECKS": false,

    "//": "Design files",
    "VERILOG_FILES": [
        "dir::../../verilog/rtl/defines.v",
        "dir::../../verilog/rtl/user_project_wrapper.v"
    ],
    
    "SYNTH_ELABORATE_ONLY": false,
    "RUN_POST_GPL_DESIGN_REPAIR": true,
    "RUN_POST_CTS_RESIZER_TIMING": true,
    "DESIGN_REPAIR_BUFFER_INPUT_PORTS": true,
    "PDN_ENABLE_RAILS": true,
    "RUN_ANTENNA_REPAIR": true,
    "RUN_FILL_INSERTION": true,
    "RUN_TAP_ENDCAP_INSERTION": true,
    "RUN_CTS": true,
    "RUN_IRDROP_REPORT": false,

    "//": "Macros configurations",
    "MACROS": {
        "aes_wb_wrapper": {
            "gds": [
                "dir::../../gds/aes_wb_wrapper.gds"
            ],
            "lef": [
                "dir::../../lef/aes_wb_wrapper.lef"
            ],
            "instances": {
                "mprj": {
                    "location": [100, 100],
                    "orientation": "N"
                }
            },
            "nl": [
                "dir::../../verilog/gl/aes_wb_wrapper.v"
            ],
            "spef": {
                "min_*": [
                    "dir::../../spef/multicorner/aes_wb_wrapper.min.spef"
                ],
                "nom_*": [
                    "dir::../../spef/multicorner/aes_wb_wrapper.nom.spef"
                ],
                "max_*": [
                    "dir::../../spef/multicorner/aes_wb_wrapper.max.spef"
                ]
            },
            "lib": {
                "*": "dir::../../lib/aes_wb_wrapper.lib"
            }
        }
    },
    "PDN_MACRO_CONNECTIONS": ["mprj vccd2 vssd2 VPWR VGND"],

    "//": "PDN configurations",
    "PDN_VOFFSET": 5,
    "PDN_HOFFSET": 5,
    "PDN_VWIDTH": 3.1,
    "PDN_HWIDTH": 3.1,
    "PDN_VSPACING": 15.5,
    "PDN_HSPACING": 15.5,
    "PDN_VPITCH": 180,
    "PDN_HPITCH": 180,
    "QUIT_ON_PDN_VIOLATIONS": false,

    "//": "Magic variables",
    "MAGIC_DRC_USE_GDS": true,
    
    "MAX_TRANSITION_CONSTRAINT": 1.5,

    "//": "Fixed configurations for caravel. You should NOT edit this section",
    "DESIGN_NAME": "user_project_wrapper",
    "FP_SIZING": "absolute",
    "DIE_AREA": [0, 0, 2920, 3520],
    "FP_DEF_TEMPLATE": "dir::fixed_dont_change/user_project_wrapper.def",
    "VDD_NETS": [
        "vccd1",
        "vccd2",
        "vdda1",
        "vdda2"
    ],
    "GND_NETS": [
        "vssd1",
        "vssd2",
        "vssa1",
        "vssa2"
    ],
    "PDN_CORE_RING": 1,
    "PDN_CORE_RING_VWIDTH": 3.1,
    "PDN_CORE_RING_HWIDTH": 3.1,
    "PDN_CORE_RING_VOFFSET": 12.45,
    "PDN_CORE_RING_HOFFSET": 12.45,
    "PDN_CORE_RING_VSPACING": 1.7,
    "PDN_CORE_RING_HSPACING": 1.7,
    "CLOCK_PORT": "wb_clk_i",
    "PNR_SDC_FILE": "dir::pnr.sdc",
    "SIGNOFF_SDC_FILE": "dir::signoff.sdc",
    "MAGIC_DEF_LABELS": 0,
    "CLOCK_PERIOD": 25,
    "MAGIC_ZEROIZE_ORIGIN": 0
}

The table below summarises the key behavioural difference between the two strategies:

Parameter

Strategy 1 (Macro-First)

Strategy 2 (Top-Level)

Reason

SYNTH_ELABORATE_ONLY

true

false

Strategy 2 synthesises top-level glue logic.

RUN_CTS

false

true

A new clock tree is built for the top-level standard cells.

RUN_POST_GPL_DESIGN_REPAIR

false

true

The resizer fixes violations on the new top-level cells.

PDN_ENABLE_RAILS

false

true

Metal 1 rails are needed for the top-level standard cell rows.

RUN_TAP_ENDCAP_INSERTION

false

true

Tap cells bias the N-well of the new standard cell rows.

RUN_FILL_INSERTION

false

true

Filler cells close gaps in the standard cell rows.

Macro location

[10, 20]

[100, 100]

Margin required for top-level cells and routing.


4.3 Running the Flow (Top-Level)

[nix-shell:~]$ librelane \
    ~/Silicon-Sprint-AUC/openlane/user_project_wrapper/config.json \
    --run-tag top_level

Note

If the flow crashes during the parasitic extraction (OpenROAD.RCX) step, add the following line to config.json and re-run. This forces single-threaded STA — slower but reliable when multi-thread STA causes memory or scheduling failures:

“STA_THREADS”: 1


4.4 Post-PnR STA Results (Top-Level)

Location: runs/top_level/XX-openroad-stapostpnr/summary.rpt

┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┓
┃                      ┃ Hold     ┃ Reg to   ┃          ┃          ┃ of which  ┃ Setup    ┃           ┃          ┃           ┃ of which ┃           ┃          ┃
┃                      ┃ Worst    ┃ Reg      ┃          ┃ Hold Vio ┃ reg to    ┃ Worst    ┃ Reg to    ┃ Setup    ┃ Setup Vio ┃ reg to   ┃ Max Cap   ┃ Max Slew ┃
┃ Corner/Group         ┃ Slack    ┃ Paths    ┃ Hold TNS ┃ Count    ┃ reg       ┃ Slack    ┃ Reg Paths ┃ TNS      ┃ Count     ┃ reg      ┃ Violatio… ┃ Violati… ┃
┡━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━┩
│ Overall              │ 0.0047   │ 0.0047   │ 0.0000   │ 0        │ 0         │ 2.1041   │ 2.1041    │ 0.0000   │ 0         │ 0        │ 1         │ 2        │
│ nom_tt_025C_1v80     │ 0.1483   │ 0.1483   │ 0.0000   │ 0        │ 0         │ 7.9981   │ 13.3840   │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
│ nom_ss_100C_1v60     │ 0.5562   │ 0.5562   │ 0.0000   │ 0        │ 0         │ 2.5763   │ 2.5763    │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
│ nom_ff_n40C_1v95     │ 0.0053   │ 0.0053   │ 0.0000   │ 0        │ 0         │ 9.1132   │ 17.4716   │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
│ min_tt_025C_1v80     │ 0.1474   │ 0.1474   │ 0.0000   │ 0        │ 0         │ 8.1437   │ 13.6547   │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
│ min_ss_100C_1v60     │ 0.5545   │ 0.5545   │ 0.0000   │ 0        │ 0         │ 3.0678   │ 3.0678    │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
│ min_ff_n40C_1v95     │ 0.0047   │ 0.0047   │ 0.0000   │ 0        │ 0         │ 9.2076   │ 17.7111   │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
│ max_tt_025C_1v80     │ 0.1491   │ 0.1491   │ 0.0000   │ 0        │ 0         │ 7.8819   │ 13.1149   │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
│ max_ss_100C_1v60     │ 0.5582   │ 0.5582   │ 0.0000   │ 0        │ 0         │ 2.1041   │ 2.1041    │ 0.0000   │ 0         │ 0        │ 1         │ 2        │
│ max_ff_n40C_1v95     │ 0.0059   │ 0.0059   │ 0.0000   │ 0        │ 0         │ 9.0334   │ 17.2358   │ 0.0000   │ 0         │ 0        │ 0         │ 0        │
└──────────────────────┴──────────┴──────────┴──────────┴──────────┴───────────┴──────────┴───────────┴──────────┴───────────┴──────────┴───────────┴──────────┘

To open the final layout:

[nix-shell:~]$ librelane \
    --last-run \
    --flow openinklayout \
    ~/Silicon-Sprint-AUC/openlane/user_project_wrapper/config.json

5. Strategy 3 — Full-Wrapper Flattening

In this strategy, the user_project_wrapper is hardened with all AES RTL included directly — no pre-hardened macro. Yosys synthesises the entire design as one flat netlist and the router covers the full ≈ 3 mm × 3.6 mm area. This produces the best possible timing but requires the most PnR time and iteration.


5.1 RTL Modification

Because this strategy performs full synthesis, Yosys flags undriven top-level output ports as errors. To prevent approximately 207 synthesis errors, add the following tie-off assignments to user_project_wrapper.v:

// Required for Full-Wrapper Flattening — tie off unused top-level outputs
assign io_out      = {`MPRJ_IO_PADS{1'b0}};
assign io_oeb      = {`MPRJ_IO_PADS{1'b0}};
assign la_data_out = 128'b0;
assign user_irq    = 3'b0;

Warning

While these tie-offs are mandatory for Full-Wrapper Flattening prevent Yosys synthesis errors ,they must be removed if you switch back to the Macro-First Hardening strategy.

In Macro-First mode, the tool expects the wrapper to be a pure elaboration frame for the pre-hardened macro. If these assign statements are present, Yosys preserves them in the synthesized gate-level netlist (.nl.v). This triggers the Netlist Assign Statement Checker (Stage 9), which is designed to raise a StepError and terminate the flow because assign statements are known to cause bugs in physical design (PnR) tools


5.2 Configuration (Flattening)

The MACROS section is removed entirely. All AES RTL source files are added directly to VERILOG_FILES. All optimisation flags are true (which is the LibreLane Classic flow default — they do not need to be explicitly written in config.json).

{
    "VERILOG_FILES": [
        "dir::../../../secworks_aes/src/rtl/*.v",
        "dir::../../verilog/rtl/aes_wb_wrapper.v",
        "dir::../../verilog/rtl/defines.v",
        "dir::../../verilog/rtl/user_project_wrapper.v"
    ],

     "SYNTH_STRATEGY": "DELAY 4",
    "DEFAULT_CORNER": "max_tt_025C_1v80",
    "DESIGN_REPAIR_MAX_SLEW_PCT": 30,
    "DESIGN_REPAIR_MAX_CAP_PCT": 30,

    "GRT_ANTENNA_REPAIR_ITERS": 10,
    "GRT_ANTENNA_REPAIR_MARGIN": 15,
    
    "VSRC_LOC_FILES": {
        "vccd1": "dir::vsrc/upw_vccd1_vsrc.loc",
        "vssd1": "dir::vsrc/upw_vssd1_vsrc.loc"
    },

    "PDN_VOFFSET": 5,
    "PDN_HOFFSET": 5,
    "PDN_VWIDTH": 3.1,
    "PDN_HWIDTH": 3.1,
    "PDN_VSPACING": 15.5,
    "PDN_HSPACING": 15.5,
    "PDN_VPITCH": 180,
    "PDN_HPITCH": 180,
    "ERROR_ON_PDN_VIOLATIONS": false,



    "//": "Fixed configurations for Caravel — do NOT edit below this line",
    "DESIGN_NAME": "user_project_wrapper",
    "FP_SIZING": "absolute",
    "DIE_AREA": [0, 0, 2920, 3520],
    "FP_DEF_TEMPLATE": "dir::fixed_dont_change/user_project_wrapper.def",
    "VDD_NETS": ["vccd1", "vccd2", "vdda1", "vdda2"],
    "GND_NETS": ["vssd1", "vssd2", "vssa1", "vssa2"],
    "PDN_CORE_RING": 1,
    "PDN_CORE_RING_VWIDTH": 3.1,
    "PDN_CORE_RING_HWIDTH": 3.1,
    "PDN_CORE_RING_VOFFSET": 12.45,
    "PDN_CORE_RING_HOFFSET": 12.45,
    "PDN_CORE_RING_VSPACING": 1.7,
    "PDN_CORE_RING_HSPACING": 1.7,
    "CLOCK_PORT": "wb_clk_i",
    "PNR_SDC_FILE": "dir::pnr.sdc",
    "SIGNOFF_SDC_FILE": "dir::signoff.sdc",
    "MAGIC_DEF_LABELS": 0,
    "CLOCK_PERIOD": 25,
    "MAGIC_ZEROIZE_ORIGIN": 0
}

Note

The VSRC_LOC_FILES parameter specifies voltage source locations for IR drop analysis. This is especially important in the flattening strategy because the full design spans the entire wrapper area and IR drop hotspots are more likely to occur at points far from the power ring. The .loc files define where the PDN voltage sources are injected for the OpenROAD.IRDropReport simulation.

4.3 Running the Flow (Top-Level)

[nix-shell:~]$ librelane \
    ~/Silicon-Sprint-AUC/openlane/user_project_wrapper/config.json \
    --run-tag flatten

Post-PnR STA (OpenROAD.STAPostPNR)

Location: runs/project_wrapper/XX-openroad-stapostpnr/summary.rpt

┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━┓
┃                      ┃            ┃            ┃          ┃            ┃            ┃ Setup      ┃             ┃           ┃            ┃             ┃            ┃             ┃
┃                      ┃ Hold Worst ┃ Reg to Reg ┃          ┃ Hold Vio   ┃ of which   ┃ Worst      ┃ Reg to Reg  ┃           ┃ Setup Vio  ┃ of which    ┃ Max Cap    ┃ Max Slew    ┃
┃ Corner/Group         ┃ Slack      ┃ Paths      ┃ Hold TNS ┃ Count      ┃ reg to reg ┃ Slack      ┃ Paths       ┃ Setup TNS ┃ Count      ┃ reg to reg  ┃ Violations ┃ Violations  ┃
┡━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━┩
│ Overall              │ 0.0332     │ 0.0332     │ 0.0000   │ 0          │ 0          │ 2.5400     │ 2.5400      │ 0.0000    │ 0          │ 0           │ 0          │ 0           │
│ nom_tt_025C_1v80     │ 0.1949     │ 0.1949     │ 0.0000   │ 0          │ 0          │ 8.6580     │ 13.2886     │ 0.0000    │ 0          │ 0           │ 0          │ 0           │
│ nom_ss_100C_1v60     │ 0.6139     │ 0.7559     │ 0.0000   │ 0          │ 0          │ 3.0788     │ 3.0788      │ 0.0000    │ 0          │ 0           │ 0          │ 0           │
│ nom_ff_n40C_1v95     │ 0.0351     │ 0.0351     │ 0.0000   │ 0          │ 0          │ 9.5322     │ 17.1408     │ 0.0000    │ 0          │ 0           │ 0          │ 0           │
│ min_tt_025C_1v80     │ 0.1925     │ 0.1925     │ 0.0000   │ 0          │ 0          │ 8.7362     │ 13.8739     │ 0.0000    │ 0          │ 0           │ 0          │ 0           │
│ min_ss_100C_1v60     │ 0.6547     │ 0.6547     │ 0.0000   │ 0          │ 0          │ 3.6504     │ 3.6504      │ 0.0000    │ 0          │ 0           │ 0          │ 0           │
│ min_ff_n40C_1v95     │ 0.0332     │ 0.0332     │ 0.0000   │ 0          │ 0          │ 9.5891     │ 17.5575     │ 0.0000    │ 0          │ 0           │ 0          │ 0           │
│ max_tt_025C_1v80     │ 0.1975     │ 0.1975     │ 0.0000   │ 0          │ 0          │ 8.5800     │ 12.6022     │ 0.0000    │ 0          │ 0           │ 0          │ 0           │
│ max_ss_100C_1v60     │ 0.4939     │ 0.7547     │ 0.0000   │ 0          │ 0          │ 2.5400     │ 2.5400      │ 0.0000    │ 0          │ 0           │ 0          │ 0           │
│ max_ff_n40C_1v95     │ 0.0371     │ 0.0371     │ 0.0000   │ 0          │ 0          │ 9.4746     │ 16.6085     │ 0.0000    │ 0          │ 0           │ 0          │ 0           │
└──────────────────────┴────────────┴────────────┴──────────┴────────────┴────────────┴────────────┴─────────────┴───────────┴────────────┴─────────────┴────────────┴─────────────┘

PDN

Power Distribution Network. The network of metal conductors delivering VDD and GND to every cell in the chip.

DRC

Design Rule Check. Verification that the layout conforms to the foundry’s manufacturing constraints.

LVS

Layout vs. Schematic. Verification that the physical layout is electrically equivalent to the design netlist.

LEF

Library Exchange Format. A file describing the physical interface of a macro — boundary, pin locations, and blockages.

SPEF

Standard Parasitic Exchange Format. A text-based map of the resistance and capacitance of every net.

STA

Static Timing Analysis. Exhaustive path-by-path timing verification against declared constraints.

GDSII

Graphic Database System II. The standard binary layout format delivered to the foundry for fabrication.

SDC

Synopsys Design Constraints. Tcl-based timing and clocking constraints.

WNS

Worst Negative Slack. The largest magnitude of negative slack across all failing timing paths.