Implementing an IO pad ring#
Every build in the other tutorials stops at a rectangle of standard cells. A part you can put on a board needs more than that: it needs pads to bond to, supplies that reach them, and a ring of cells around the edge tying the two together.
This tutorial builds one. The design is examples/padring – a RISC-V
processor with an SRAM inside a complete sky130 pad ring – and it runs
to GDSII:
cd examples/padring
./padring.py
Note
This needs a local toolchain (Yosys, OpenROAD, OpenSTA and KLayout) and takes
around ten minutes. The pad ring is generated by
lambdalib, which installs
alongside lambdapdk, so there is nothing extra to fetch.
What a ring is made of#
Four things have to line up, and they live in different places:
Piece |
Where it lives |
|---|---|
The pad cells |
RTL. A dependency on lambdalib’s generator, plus a |
The interface |
RTL. A top level whose ports are the pads. |
Placement |
A TCL script, attached to the floorplanning task. |
Power |
Two more TCL scripts: the grid, and the global connections that tie the core’s supplies to the ring’s. |
The interface#
The top level is the module that becomes the die, and its ports are the pads:
module picorv32_top (
// Supplies. Core and IO are separate domains.
inout vdd,
inout vss,
inout vddio,
inout vssio,
// Clock and reset in.
input clk,
input resetn,
// Processor status out.
output trap,
// Memory bus, brought out so the processor can be watched on a bench.
output mem_valid,
output mem_la_read,
output mem_la_write,
output [ 3:0] mem_la_wstrb,
output [29:0] mem_addr
);
Two conventions are worth copying. The ports are named for what they carry, not
for where they sit in the ring – nobody debugging a board wants to work out
which bit of no_pad is the clock. And each one declares the direction it is
really used in: the cells in the ring are all bidirectional, but a pin on the
finished part is not, and only the supplies are genuinely inout.
Underneath, the logic never touches a pad cell. It drives four buses per side –
dout out, din back in, oen and ie for direction, and a
technology-specific configuration bus – which is what lets the same processor be
wrapped in another technology’s pads without editing it.
Describing the ring#
CELLMAP lists one side, cell by cell, in the order they abut. Each entry is
80 bits packing the cell type, the pin it carries, and its power section:
localparam [80*NCELLS-1:0] CELLMAP = {
{16'h0000, 16'h0000, LA_VSS, 16'h0000, PIN_NONE},
{16'h0000, 16'h0000, LA_VDD, 16'h0000, PIN_NONE},
{16'h0000, 16'h0000, LA_VDDIO, 16'h0000, PIN_NONE},
{16'h0000, 16'h0000, LA_VSSIO, 16'h0000, PIN_NONE},
{16'h0000, 16'h0000, LA_BIDIR, 16'h0000, 16'd0},
{16'h0000, 16'h0000, LA_BIDIR, 16'h0000, 16'd1},
{16'h0000, 16'h0000, LA_BIDIR, 16'h0000, 16'd2},
{16'h0000, 16'h0000, LA_BIDIR, 16'h0000, 16'd3},
{16'h0000, 16'h0000, LA_BIDIR, 16'h0000, 16'd4},
{16'h0000, 16'h0000, LA_BIDIR, 16'h0000, 16'd5},
{16'h0000, 16'h0000, LA_BIDIR, 16'h0000, 16'd6},
{16'h0000, 16'h0000, LA_BIDIR, 16'h0000, 16'd7},
{16'h0000, 16'h0000, LA_BIDIR, 16'h0000, 16'd8},
{16'h0000, 16'h0000, LA_BIDIR, 16'h0000, 16'd9}
};
All four sides reuse this map here. A real part rarely has such tidy symmetry – different sides carry different pin counts, and each gets its own map.
Placing it#
The RTL settled which cells exist. A TCL script settles where they go, and the order of operations matters because each step depends on the last: make the IO rows, place the pads into them, place the corners, fill the gaps, connect the ring by abutment, then add the bond pads.
# North and south run left to right; east and west are built bottom to top,
# so their declaration order is reversed relative to the row.
place_pads -row IO_NORTH {*}[padring_side_insts north]
place_pads -row IO_SOUTH {*}[padring_side_insts south]
place_pads -row IO_WEST {*}[lreverse [padring_side_insts west]]
place_pads -row IO_EAST {*}[lreverse [padring_side_insts east]]
connect_by_abutment is the step people miss. The supply rails and the
configuration signals pass between neighbouring cells through touching pins
rather than through routed wire, so it has to come after the fill has closed
the gaps – otherwise the ring is a ring of islands.
See also
Every command in that script belongs to OpenROAD, not to SiliconCompiler, and
each takes more arguments than this example uses. OpenROAD’s
pad module documents make_io_sites, place_pads,
place_corners, place_io_fill, connect_by_abutment and
place_bondpad; its pdn module documents the power grid
commands in the next section.
Wiring it to the flow#
None of those scripts do anything until a task is told to read them. These are task variables rather than schema keys, because each configures one step:
InitFloorplanTask.find_task(project).add_openroad_padringfileset("padring.sky130")
PowerGridTask.find_task(project).add_openroad_powergridfileset("padring", "pdn.sky130")
for task in APRTask.find_task(project):
task.add_openroad_globalconnectfileset("padring", "globalconns.sky130")
Note
The pad ring hook takes only a fileset name, while the power grid and global connect hooks also take the library that owns the fileset.
The power grid then needs one addition an ordinary design does not: the core’s
rings are tied out to the supply pads with -connect_to_pads, so the core is
fed through the ring rather than through pins on the die edge.
How big does the die have to be?#
The ring sets a floor: fourteen cells plus two corners need about 1520 µm a side. A die sized by that is pad limited – set by how many pins must fit around the edge rather than by the logic inside.
Here the memory pushes it further. sky130’s SRAM macro is 1041 µm wide at any depth and needs a placement channel around it, so with a 250 µm margin to clear the pads this die lands at 1900 µm.
What to look at#
Open the result with project.show(), or the GDS directly, and check the ring
before anything else:
The finished die. Ten signal pads and four supply pads on each side, corners turning the ring, IO fill closing the gaps between cells, and bond pads over the top. Inside, the SRAM macro is the wide block across the lower core and the processor is the dense region above it.#
Four populated sides. The floorplan log reports what it placed –
Placed 14 pads in IO_NORTHand so on. A ring that is missing pads on one side usually means the selection in the placement script stopped matching the instance names the RTL generates.Continuous supplies. The fill cells should leave no gaps, and the rails should run unbroken through the corners.
Bond pads sitting over their cells, offset to clear the passivation.
Warning
This example stops short of a tapeout in one respect worth naming: the supply
pads carry ESD clamp pins (DRN_HVC, SRC_BDY_HVC) which are left
unconnected, and the power grid step says so. Where those clamps belong
depends on the chip’s ESD strategy and on foundry guidance, so the example
does not guess. A real part has to resolve them.
Next#
Example designs – the full source for this build.
How do I…? has the same steps in condensed form.
Instantiating a hardened module in a design covers macros inside the core, which is the other half of a floorplan like this one.