---
name: matlab/matlab-analyze-pcb-pdn
source: https://app.decimal.ai/s/matlab-matlab-analyze-pcb-pdn@1/SKILL.md
source_sha256: 4ca4527ba7dc
---

# Analyzing Power Distribution Networks (PDN)

## When to Use

- Analyzing DC voltage and current distribution on PCB power rails
- Checking design rules (max current density, voltage margins, via current limits)
- Discovering and listing power nets on an imported PCB layout
- Assigning source/load/sense topology for PDN analysis
- Running batch analysis across multiple power rails on a board
- Inferring nominal voltage from standard PCB net naming conventions

## When NOT to Use

- Importing PCB layouts (Gerber, ODB++, Allegro) — use `matlab-read-pcb-layout`
- Analyzing S-parameters, fields, or EM performance — use `matlab-analyze-em`
- Analyzing crosstalk between signal traces — use `matlab-design-pcb-txline`
- Defining dielectric or conductor materials — use `matlab-manage-pcb-material`
- Modeling via structures — use `matlab-model-via`

## Typical Workflow

1. **Before:** `matlab-read-pcb-layout` — import the PCB layout from Gerber/ODB++/Allegro
2. **This skill:** Run DC analysis, check IR drop, evaluate design rules, batch-analyze nets
3. **After:** Iterate on the physical design in CAD and re-import, or use results to inform stackup changes via `matlab-manage-pcb-material`

## Quick Reference

| Task | Code |
|------|------|
| Import PCB layout | `pcb = pcbFileRead('board_native')` |
| List all nets | `netList = cadnetList(pcb)` |
| Find power nets | Filter `cadnetList(pcb)` with `regexpi` (see below) |
| Find specific rail | `idx = ~cellfun(@isempty, regexpi(netList.CadnetName, "P0V8"))` |
| Infer rail voltage | Parse net name with regex helper `parseNetVoltage` (see below) |
| Create cadnet | `cnet = cadnet(pcb, 'P0V8')` |
| Show cadnet layout | `show(cnet)` |
| Find components on net | `comps = findComponents(cnet)` |
| Filter by type | `inductors = findComponents(cnet, "ComponentType", "Inductor")` |
| Create PDN model | `PDN = powerDistributionNetwork(cnet)` |
| Assign topology | `setNetworkParameters(PDN, Source=src, Load=load, Sense=sense)` |
| Auto-assign topology | `setNetworkParameters(PDN, AutoAssignDefault='True')` |
| Set DC parameters | `setDCParameters(PDN, "NominalVoltage", 0.8, "LoadCurrent", 1)  % placeholder — ask user` |
| Set DC rules | `setDCRules(PDN, "MaxCurrentDensity", 0.5, "MinVoltage", 0.784)` |
| Voltage distribution | `voltage(PDN)` |
| Voltage with violations | `voltage(PDN, ShowViolation=true)` |
| Current distribution | `current(PDN)` |
| Current with arrows | `current(PDN, Direction='on')` |

## PCB Import and Net Discovery

### Importing a PCB Layout

`pcbFileRead` imports a PCB file and returns an object for hierarchical inspection. Supported formats: native directory (CSV files), ODB++ (zipped or unzipped), and Cadence Allegro `.brd` (requires one-time `extractaSetup()`).

```matlab
% Native format (directory containing CSV files)
pcb = pcbFileRead(fullfile(boardDir, 'pcie5_native'));

% ODB++ format
pcb = pcbFileRead(fullfile(boardDir, 'myboard.zip'));

% Allegro .brd (run extractaSetup() once first)
extractaSetup();  % one-time setup for Allegro support
pcb = pcbFileRead(fullfile(boardDir, 'myboard.brd'));
```

The returned object exposes: `NumLayers`, `NumCadnets`, `NumPadStacks`, `NumComponents`, `NumParts`, `LayerHeight`.

### Listing All Nets

```matlab
NetList = cadnetList(pcb);
disp(NetList);
```

Returns a table with columns: `CadnetIdx`, `CadnetName`, `NumPins`, `Length`. A real board may have 3000+ nets.

### Finding Power Nets

There is no built-in `findPowerNets` function. Filter the `cadnetList` output using regex to identify power and ground nets by name:

```matlab
netList = cadnetList(pcb);

% Define naming patterns (case-insensitive)
powerPatterns = ["^P\d+V", "^VDD", "^VCC", "^AVDD", "^DVDD", "^VDDO"];
groundPatterns = ["^GND", "^AGND", "^DGND", "^PGND", "^VSS", "^AVSS", "^DVSS"];

% Match power nets
isPower = false(height(netList), 1);
for p = powerPatterns
    isPower = isPower | ~cellfun(@isempty, regexpi(netList.CadnetName, p));
end
powerNets = sortrows(netList(isPower, :), 'NumPins', 'descend');

% Match ground nets
isGround = false(height(netList), 1);
for g = groundPatterns
    isGround = isGround | ~cellfun(@isempty, regexpi(netList.CadnetName, g));
end
groundNets = sortrows(netList(isGround, :), 'NumPins', 'descend');

% Filter by minimum pin count
minPins = 5;
powerNets = powerNets(powerNets.NumPins >= minPins, :);

% Search for a specific pattern (e.g., 0.8V rails)
idx = ~cellfun(@isempty, regexpi(powerNets.CadnetName, "P0V8"));
rails_0v8 = powerNets(idx, :);
```

**Common power net naming conventions (case-insensitive):**
- Power rails: `P<digit>V<digit>` (P0V8, P3V3_AUX, P12V), `VDD*`, `VCC*`, `AVDD*`, `DVDD*`, `VDDO*`
- Ground nets: `GND*`, `AGND*`, `DGND*`, `PGND*`, `VSS*`, `AVSS*`, `DVSS*`

### Inferring Rail Voltage from Net Name

There is no built-in `inferRailVoltage` function. Parse voltage from net names using regex:

```matlab
function nomV = parseNetVoltage(netName)
    netName = string(netName);
    % Pattern: P<int>V<frac> (e.g., P0V8 → 0.8, P3V3 → 3.3, P12V → 12.0)
    tok = regexp(netName, '(?i)P(\d+)V(\d*)', 'tokens');
    if ~isempty(tok)
        intPart = str2double(tok{1}{1});
        fracStr = tok{1}{2};
        if isempty(fracStr)
            nomV = intPart;
        else
            nomV = intPart + str2double(fracStr) / 10^numel(fracStr);
        end
        return;
    end
    % Pattern: explicit decimal (e.g., 3.3V, 1.8V)
    tok = regexp(netName, '(\d+\.\d+)\s*V', 'tokens');
    if ~isempty(tok)
        nomV = str2double(tok{1}{1});
        return;
    end
    % Pattern: millivolt (e.g., 800MV → 0.8)
    tok = regexp(netName, '(\d+)\s*MV', 'tokens', 'ignorecase');
    if ~isempty(tok)
        nomV = str2double(tok{1}{1}) / 1000;
        return;
    end
    nomV = NaN;
end
```

Usage in a loop:

```matlab
for k = 1:height(powerNets)
    netName = powerNets.CadnetName{k};
    nomV = parseNetVoltage(netName);
    fprintf('%s → %.2f V\n', netName, nomV);
end
```

## cadnet Object

### Creating a Cadnet

```matlab
cnet = cadnet(pcb, 'P0V8');
```

**Properties:**

| Property | Description |
|----------|-------------|
| `NumPins` | Number of pins on the net |
| `NumSurfaces` | Number of copper surfaces |
| `NumVias` | Number of vias |
| `NumTraces` | Number of traces |
| `TotalLength` | Total trace length |
| `EntityList` | List of all entities |
| `Voltage` | Nominal voltage |
| `LayerRange` | Layers spanned by the net |

### Visualizing a Cadnet

```matlab
show(cnet);
```

### Finding Connected Components

`findComponents` returns a table with columns: `Refdes`, `PinList`, `ComponentType`, `Part`.

```matlab
% All components on the net
allComps = findComponents(cnet);

% Filter by component type
inductors = findComponents(cnet, "ComponentType", "Inductor");
ics       = findComponents(cnet, "ComponentType", "IC");
resistors = findComponents(cnet, "ComponentType", "Resistor");
caps      = findComponents(cnet, "ComponentType", "Capacitor");
```

The `Refdes` values are strings -- use them directly for Source/Load/Sense assignment in `setNetworkParameters`.

### Getting Detailed Cadnet Data

```matlab
data = cadnetData(cnet);
s = shapes(cnet);
```

## powerDistributionNetwork Object

### Creating a PDN Model

```matlab
PDN = powerDistributionNetwork(cnet);
```

**Properties:**

| Property | Description |
|----------|-------------|
| `NetType` | Type of net |
| `Source` | Source component(s) |
| `Load` | Load component(s) |
| `Sense` | Sense component(s) |
| `PlatingThickness` | Via barrel plating thickness (inches) |
| `NominalVoltage` | Nominal voltage (V) |
| `LoadCurrent` | Load current (A) |
| `MaxCurrentDensity` | Max current density (mA/mil²) |
| `MinVoltage` | Minimum allowable voltage (V) |
| `MaxVoltage` | Maximum allowable voltage (V) |
| `MaxViaCurrent` | Max via current (mA) |

## PDN Configuration

### setNetworkParameters -- Assign Source, Load, Sense, Plating

Use `findComponents` output to assign topology:

```matlab
% Manual assignment using RefDes from findComponents
setNetworkParameters(PDN, ...
    Source=sourceRefDes, ...
    Load=sinkRefDes, ...
    Sense=senseRefDes, ...
    PlatingThickness=0.002);

% Auto-assign defaults (fallback when topology is unclear)
setNetworkParameters(PDN, AutoAssignDefault='True');
```

- **Source** -- RefDes of the power source (typically an inductor). Use all inductors for multiphase rails.
- **Load** -- RefDes of the load (typically an IC). Use all ICs on the net.
- **Sense** -- RefDes of the sense component (typically a resistor or test point).
- **PlatingThickness** -- Via barrel plating thickness in **inches** (e.g., `0.002` = 2 mil ≈ 1.4 oz copper).

#### Sense Component Resolution

The `Sense` parameter is required. When no test point is available on the net, use a resistor as the sense component:

```matlab
tp = findComponents(cnet, 'ComponentType', 'Test Point');
if ~isempty(tp)
    senseRef = tp.Refdes;
else
    res = findComponents(cnet, 'ComponentType', 'Resistor');
    senseRef = res.Refdes(1);  % use first resistor as sense fallback
end
setNetworkParameters(PDN, Source=src, Load=load, Sense=senseRef, ...
    PlatingThickness=0.002);
```

#### Multiphase Rails

For multiphase VRM designs, multiple inductors feed the same rail. Always use all inductors as Source, not just the first:

```matlab
inductors = findComponents(cnet, "ComponentType", "Inductor");
setNetworkParameters(PDN, Source=inductors.Refdes);  % handles multiphase
```

### setDCParameters -- Set Electrical Parameters

```matlab
setDCParameters(PDN, "NominalVoltage", 0.8, "LoadCurrent", 1);  % placeholder — ask user for actual value
```

### setDCRules -- Set DC Design Rules

```matlab
setDCRules(PDN, ...
    "MaxCurrentDensity", 0.5, ...
    "MaxVoltage", 0.816, ...
    "MinVoltage", 0.784, ...
    "MaxViaCurrent", 500);
```

**DC rules units (mixed — specific to this API):**

| Property | Units | Description |
|----------|-------|-------------|
| `PlatingThickness` | inches | Via barrel plating thickness (0.002 = 2 mil) |
| `NominalVoltage` | V | Nominal rail voltage |
| `LoadCurrent` | A | Expected load current per sink |
| `MaxCurrentDensity` | mA/mil² | Current density thermal limit |
| `MinVoltage` | V | Minimum allowable absolute voltage |
| `MaxVoltage` | V | Maximum allowable absolute voltage |
| `MaxViaCurrent` | mA | Max current through a single via |

**Voltage tolerance guidelines:**

| Rail Voltage | Tolerance | MinVoltage | MaxVoltage |
|---|---|---|---|
| < 1 V | 1–2% | P0V8: 0.8 × 0.98 = **0.784 V** | 0.8 × 1.02 = **0.816 V** |
| 1–3.3 V | 2–3% | P1V8: 1.8 × 0.975 = **1.755 V** | 1.8 × 1.025 = **1.845 V** |
| 3.3–5 V | 3–5% | P3V3: 3.3 × 0.97 = **3.201 V** | 3.3 × 1.03 = **3.399 V** |

## DC Analysis

### Voltage Distribution

```matlab
voltage(PDN);

% Show design rule violations
voltage(PDN, ShowViolation=true);
```

### Current Distribution

```matlab
current(PDN);

% Show current direction arrows
current(PDN, Direction='on');
```

### Inspecting PDN Configuration Before Analysis

Check the PDN model properties after setup to verify assignments:

```matlab
PDN.Source
PDN.Load
PDN.Sense
PDN.NominalVoltage
PDN.LoadCurrent
```

## Workflow: Single-Net PDN Analysis

Interactive workflow for analyzing one power net end-to-end:

```matlab
%% Step 1: Import the board
pcb = pcbFileRead(fullfile(boardDir, 'pcie5_native'));

%% Step 2: Identify power nets
netList = cadnetList(pcb);
powerPatterns = ["^P\d+V", "^VDD", "^VCC", "^AVDD", "^DVDD"];
isPower = false(height(netList), 1);
for p = powerPatterns
    isPower = isPower | ~cellfun(@isempty, regexpi(netList.CadnetName, p));
end
powerNets = sortrows(netList(isPower, :), 'NumPins', 'descend');
disp(powerNets);

%% Step 3: Create cadnet and inspect
cnet = cadnet(pcb, 'P0V8');
show(cnet);

%% Step 4: Discover components for topology assignment
allComps = findComponents(cnet);
inductors = findComponents(cnet, "ComponentType", "Inductor");
ics       = findComponents(cnet, "ComponentType", "IC");
resistors = findComponents(cnet, "ComponentType", "Resistor");

%% Step 5: Resolve voltage from net name
nomV = parseNetVoltage('P0V8');  % 0.8 V (see helper function above)

%% Step 6: Create and configure PDN
PDN = powerDistributionNetwork(cnet);

setNetworkParameters(PDN, ...
    Source=inductors.Refdes, ...
    Load=ics.Refdes, ...
    Sense=resistors.Refdes(1), ...
    PlatingThickness=0.002);

setDCParameters(PDN, "NominalVoltage", nomV, "LoadCurrent", 1);  % placeholder — ask user for actual value

tolerancePct = 0.02;  % 2% for <1V rails
setDCRules(PDN, ...
    "MaxCurrentDensity", 0.5, ...
    "MaxVoltage", nomV * (1 + tolerancePct), ...
    "MinVoltage", nomV * (1 - tolerancePct), ...
    "MaxViaCurrent", 500);

%% Step 7: Run analysis
voltage(PDN, ShowViolation=true);
current(PDN, Direction='on');
```

## Workflow: Multi-Net Batch Analysis

For batch analysis of all power rails on a board (loop with skip logic, per-rail spec tables), see [references/batch-analysis.md](references/batch-analysis.md).

## Common Patterns

### Component Refdes Usage

`findComponents` returns `Refdes` as a string -- use directly in `setNetworkParameters`. Use all matching components, not just the first:

```matlab
inductors = findComponents(cnet, "ComponentType", "Inductor");
setNetworkParameters(PDN, Source=inductors.Refdes);  % all inductors
```

### When to Use AutoAssignDefault

Use `AutoAssignDefault='True'` only when:
- No inductors found on the net (LDO rails, connector-fed rails)
- User explicitly requests automatic topology assignment
- Quick screening mode where accuracy is secondary

Always prefer explicit `findComponents`-based assignment.

### Z-Axis Visualization

Use `daspect` to see Z-axis detail in 3D views:

```matlab
ax = gca;
daspect(ax, [1, 1, 0.05]);
```

## Pitfalls

1. **`inferRailVoltage` returns `NaN` for unrecognized nets.** Guard with `isnan()` before using the result in calculations or display (e.g., `string(NaN)` produces `"NaN"`, not `missing`).

2. **`Sense` parameter is required in `setNetworkParameters`.** When no test point exists on the net, use a resistor as the sense component. Omitting `Sense` will cause errors during analysis.

3. **`AutoAssignDefault` is a fallback, not a first choice.** It may produce incorrect topology assignments on complex rails (LDO, connector-fed). Prefer explicit assignment via `findComponents` output.

4. **`LoadCurrent` cannot be inferred from board data — STOP and ask.** Unlike `NominalVoltage` (which can be parsed from net names), load current must come from IC datasheets or system power budgets. Before calling `setDCParameters`, ask the user for the actual load current and **STOP execution — do not proceed until the user responds.** Do NOT assume 1 A or any default without explicit user confirmation. Once the user responds that they don't have it or asks you to proceed, present these estimation options and let them choose:
   - **Per-pin heuristic:** 0.5 A × number of load pins (e.g., 130 pins → 65 A)
   - **TDP-based:** total power budget ÷ rail voltage (e.g., 40 W ÷ 0.8 V = 50 A)
   - **Fixed conservative:** 10 A per load IC (quick screening)
   - **1 A token:** minimal value to verify the workflow runs end-to-end

   Only after the user selects an option or provides a value, proceed with `setDCParameters`.

5. **`LoadCurrent` must be a vector, not a scalar total.** When multiple load ICs exist on a rail, `setDCParameters` requires one current value per load. For example, with loads U1 (4.8 A) and U9 (0.2 A): `setDCParameters(PDN, LoadCurrent=[4.8, 0.2])`. Passing a scalar (e.g., `LoadCurrent=5`) errors when the topology has more than one load.

6. **PDN units are mixed — not all SI.** `PlatingThickness` is in **inches** (not meters): `0.002` = 2 mil ≈ 1.4 oz copper. `MaxCurrentDensity` is **mA/mil²** (not A/mm²). `MaxViaCurrent` is **mA** (not A). `MinVoltage`/`MaxVoltage` are absolute **volts**. Using meters for plating (e.g., `35e-6`) or amps for via current (e.g., `1`) produces wildly incorrect results.

7. **Batch mode: skip rather than block.** When a rail is missing voltage, source, or load information, skip it and report which rails were skipped and why. Do not halt the entire batch for one incomplete rail.

8. **Large boards have 3000+ nets.** `cadnetList` returns all nets. Filter with regex on `CadnetName` to narrow to power/ground nets, then further filter by `NumPins` or specific patterns.

9. **`cnet.Voltage` is unreliable.** The `Voltage` property on the cadnet object is populated heuristically from the PCB file and often returns `'0.000'` even for valid power rails. Always parse the voltage from the net name using regex instead of relying on this property.

10. **No built-in `findPowerNets` or `inferRailVoltage`.** These do not exist as MATLAB functions. Use `cadnetList(pcb)` + `regexpi` filtering for net discovery, and the `parseNetVoltage` helper (defined in this skill) for voltage inference from net names.

11. **`.brd` files require extracta — STOP if unavailable.** If the user only has a `.brd` file and `extractaSetup()` returns `[]` or errors, STOP and inform the user: extracta (from a Cadence install) is required. Offer alternatives: (a) provide the path to `extracta.exe`, (b) export from Allegro as ODB++ or native CSV format, (c) use a colleague's Cadence install to convert. Do not attempt workarounds.

## Related Skills

- `matlab-read-pcb-layout` -- Importing PCB/package layouts for PDN analysis
- `matlab-manage-pcb-material` -- Substrate and conductor material setup
- `matlab-model-via` -- Via modeling for power delivery paths
- `matlab-analyze-em` -- EM analysis fundamentals

----

Copyright 2026 The MathWorks, Inc.