Generated banner for Matlab Model Ams Systems
Generated logo for Matlab Model Ams Systems

Matlab Model Ams Systems

MathWorksOfficialCreated by MathWorks

  • Skill
  • Workflow
  • Software development
  • BSD-2-Clause
  • No issues found

From the author

Model a Phase-Locked Loop (PLL) IC from its datasheet or system specs using Mixed-Signal Blockset. Without this skill, agents universally select the wrong solver and produce non-functional PLL models — 100% of unguided attempts fail. Covers Integer-N, Fractional-N, Dual Modulus architectures, loop filter design, lock time optimization, VCO phase noise configuration, and msbPllArchitectures/msbPllFoundation block assembly. Use when: PLL modeling, frequency synthesizer design, phase noise simulation, lock time analysis, charge pump design, loop filter tuning, datasheet-to-model, Mixed-Signal Blockset PLL, msbPllArchitectures.
— the description by MathWorks

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured, automated workflow for modeling Phase-Locked Loop (PLL) integrated circuits in Simulink. It leverages the Mixed-Signal Blockset to ensure accurate simulation of frequency synthesizers, including Integer-N, Fractional-N, and Dual Modulus architectures. The skill guides the user through parameter extraction from datasheets, architecture selection, loop filter design, and performance validation, preventing common simulation failures caused by incorrect solver or block configuration.

What it does

  • Extracts technical parameters from PLL IC datasheets
  • Automates the assembly of behavioral PLL models using Mixed-Signal Blockset
  • Calculates loop filter components for stability and lock time targets
  • Validates VCO phase noise performance against datasheet specifications
  • Generates HTML reports with Bode plots, transient analysis, and design parameters
  • Provides a standardized iterative workflow for meeting performance targets

When to use it

  • Modeling a PLL IC from a datasheet
  • Designing a frequency synthesizer from system specifications
  • Performing lock time analysis or phase noise simulation
  • Tuning an existing PLL model to meet new performance requirements

Advantages

  • Prevents common modeling failures by enforcing correct solver and block usage
  • Accelerates simulation setup through batch assembly scripts
  • Ensures design stability via automated Bode plot analysis
  • Provides a consistent, repeatable methodology for datasheet-to-model conversion

Usage examples

“Model a 5GHz Integer-N PLL from this datasheet.”
The agent extracts parameters, selects the Integer-N architecture block, designs the loop filter, and generates a simulation report.
“My PLL lock time is too slow, how do I fix it?”
The agent probes the current model, identifies the loop bandwidth as the lever, adjusts the filter parameters, and re-simulates to verify the new lock time.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/matlab/matlab-model-ams-systems/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip matlab-model-ams-systems.zip -d .agents/skills

Documentation by MathWorks

Skill: PLL Datasheet Modeling -- Core (Phases 1-4)

Model a PLL IC from its datasheet or system specifications using Simulink building blocks from the Mixed-Signal Blockset (MSB) foundation library msbPllFoundation.

Companion file: modeling-pll-datasheet-validate.md covers Phases 5-9 (loop filter design, validation, measurement, iteration, pitfalls).

When to Use

  • Modeling a PLL IC from its datasheet (extracting parameters, selecting architecture)

  • Designing a frequency synthesizer from system specs (fVco, fRef, lock time, phase noise targets)

  • Building a behavioral PLL model in Simulink using Mixed-Signal Blockset

  • Validating phase noise performance against datasheet measurements

  • Selecting between Integer-N, Fractional-N, or Dual Modulus PLL architectures

When NOT to Use

  • Circuit-level PLL design (transistor-level VCO, charge pump schematic)

  • PLL analysis without building a Simulink model (use estimatePLLPhaseNoise directly)

  • Clock distribution or jitter cleaning (not frequency synthesis)

  • Non-MSB PLL modeling (e.g., custom Simulink blocks without the Mixed-Signal Blockset)


Workflow Directives (MANDATORY)

  1. Gather specs ONE AT A TIME (MANDATORY — no exceptions) -- For ANY PLL or frequency synthesizer design request (spec-driven or exploratory), ask exactly ONE question per response. Do NOT list multiple questions or present a bulleted requirements checklist. This reduces cognitive load and makes the interaction conversational. Sequence:

    1. Output frequency (or frequency range)

    2. Reference frequency (flag if N > 200 — high in-band noise penalty)

    3. Architecture (Integer-N / Frac-N / auto — may be predetermined by N)

    4. Lock time target

    5. Phase noise / jitter / spur targets (or confirm "none")

    6. Charge pump current (offer typical value as default)

    7. VCO requirements (Kvco, phase noise profile — offer rule-of-thumb)

    8. Loop filter preference (order, BW override)

    9. Any other requirements?

    10. Save location (folder path for .slx and results)

    Rules:

    • Ask ONE question, wait for answer, then ask the next.

    • Skip questions already answered in the user's initial prompt.

    • Offer a sensible default in parentheses so user can just confirm.

    • After all specs are gathered, present the Design Plan (Directive 5, Step A).

  2. Iterate autonomously ONLY when targets are NOT met -- If the first attempt PASSES with >3x margin, STOP. The BW formula already gives a good design — do NOT sweep BW or iterate "for completeness." Only sweep/iterate when the first attempt FAILS or margin is < 2x. When iteration IS needed, sweep parameters and re-simulate until met.

  3. Report progress and generate HTML summary -- Print brief status per attempt (e.g., "BW=1MHz, PM=70 -> lock=5.2us X"). After ALL targets pass, generate an HTML report saved alongside the model. Required sections:

    Report contents (mandatory):

    • Summary box: pass/fail verdict with margin

    • Model screenshot: print(['-s' model], path, '-dpng', '-r150')

    • Architecture diagram (text-based)

    • Design parameters grid (fVCO, fRef, N, Icp, Kvco, filter type, etc.)

    • Loop filter component table with time constants (τ_z = R2·C2, τ_p3 = R3·C3)

    • Transfer function box: Z(s), G(s)=Icp·Kvco·2π·Z(s)/(N·s), H(s)=G/(1+G), plus key values: fc, PM, zero freq, pole freqs

    • Bode plots (open-loop + closed-loop): export from pllOpenLoopPlot/ pllCloseLoopPlot via exportgraphics(fig, path, 'Resolution', 150). Find figures by Tag: 'PllOpenLoopDynamicPlot', 'PllCloseLoopDynamicPlot'

    • Vctrl transient: plot from simOut timeseries with lock time marker

    • Simulation results table (spec vs measured)

    • Simulation config (solver, stopTime, holdOff, averages)

    • Session metrics: include [COST] and [DURATION] placeholders in the report footer. The user fills these in from the Claude Code UI after the task completes (visible at session end).

    How to export plots:

    pllOpenLoopPlot(Icp, Kvco, N, 0, R2, R3, 0, C1, C2, C3, 0);
    fig = findobj('Type','figure','Tag','PllOpenLoopDynamicPlot');
    exportgraphics(fig, fullfile(outDir,'open_loop_bode.png'), 'Resolution', 150);

    Use relative src="filename.png" paths in HTML. Open report with web(reportPath, '-browser').

  4. Figures must be visible -- After simulation, call set(0,'DefaultFigureVisible','on') and ensure all plot figures have 'Visible','on'. Call drawnow to force rendering. The MCP MATLAB server defaults to Visible='off'.

  5. Show progress on screen at each step -- At key milestones, print status:

    • Step A — Design plan: ASCII block diagram + params before building

      [PLL TB]──▶[PFD]──▶[CP]──▶[Loop Filter]──▶[VCO]──┐
         ▲                                               │
         └─────────────[Divider ÷N]◀─────────────────────┘

      Include: architecture, fRef, N, Icp, Kvco, Fc, PM, filter type, solver. When adding impairments, show updated diagram BEFORE implementing.

    • Step B — Filter + Bode: component values, then pllOpenLoopPlot/pllCloseLoopPlot

    • Step C — Sim start: Simulating Pass 1 (lock time)... StopTime=9µs

    • Step D — Results: Lock time = 2.1 µs (target < 3 µs) ✓ [1.4× margin]

    • Step E — Pass 2 (only if PN spec): offsets, measured vs target, pass/fail


Prerequisites

  • Access to the target PLL IC datasheet (PDF) -- OR basic specs (fVco, fRef, lock time)

  • Mixed-Signal Blockset installed (provides msbPllFoundation library)

  • Control System Toolbox (for estimatePLLPhaseNoise validation, R2026b+)

Entry Points

A. Datasheet-Driven (Full workflow, Phases 1-4)

Use when you have a PLL IC datasheet. Follow all phases below.

B. Spec-Driven (No datasheet)

Use when you have basic PLL specs but no datasheet. Follow Directive 1 to gather specs, then derive remaining parameters.

Parameter derivation rules:

N = fVco / fRef  (or P*N+S for dual modulus)
BW = min(12/t_lock, fPFD/10)  (capped at fPFD/10 for stability)
Kvco: fVco/50 typical if not specified (e.g., 6 GHz → 120 MHz/V)
Icp: 1-5 mA typical (higher Icp → wider achievable BW with smaller R2)
PM: 50° default (60° if adding 4th-order pole)

Architecture selection:

ConditionArchitecture
N is integer, single prescalerInteger N PLL with Single Modulus Prescaler
N is integer, need P/P+1 flexibilityInteger N PLL with Dual Modulus Prescaler
N is fractional, low spur requirementFractional N PLL with Delta Sigma Modulator
N is fractional, simple designFractional N PLL with Accumulator

DSM order selection (when using Frac-N DSM):

  • Order 1: simplest, highest spurs at fPFD/denom

  • Order 2: good balance for most designs

  • Order 3-4: lowest spurs, but more quantization noise energy pushed to high offsets (requires adequate filter attenuation)

  • Match datasheet DSM order if available; default to order 3

R-divider tradeoff (when fRef ≠ fComp):

  • Using R-counter: fComp = fRef/R → N_eff = fVco/fComp = N×R

  • In-band noise penalty: +20×log10(N_eff) — larger N hurts in-band PN

  • Only use R > 1 when channel spacing requires it (fComp = channel step)

Spec-driven steps:

  1. Gather specs (Directive 1) → present Design Plan (Directive 5, Step A)

  2. If VCO PN data available: validate VCO standalone first (see below)

  3. Select architecture block from msbPllArchitectures (Strategy A — DEFAULT)

  4. Design loop filter:

    • N ≤ 50: CompSelectionMethod='Automatic' with Fc and Phi

    • N > 50 (P79): thirdOrderPassiveFilterDesign → CompSelectionMethod='Manual'

    • N threshold applies to effective N (including fractional part)

  5. Build model, simulate, present results

VCO standalone validation (when PN data provided):

% 1. Create VCO testbench model
vcoModel = 'VCO_Validation';
new_system(vcoModel); open_system(vcoModel);
set_param(vcoModel, 'Solver', 'VariableStepDiscrete');
add_block('msbPllFoundation/Ring Oscillator VCO', [vcoModel '/VCO']);
add_block('msbPllMeasurements/VCO Testbench', [vcoModel '/VCO TB']);
add_line(vcoModel, 'VCO TB/1', 'VCO/1', 'autorouting', 'smart');
add_line(vcoModel, 'VCO/1', 'VCO TB/1', 'autorouting', 'smart');

% 2. Get PeriodJitter and CornerFrequency from PN data
[pJitter, cFreq] = msblks.VCO.estimatePhaseNoiseCore(fVco, Foffset, PN_dBc);
set_param([vcoModel '/VCO'], 'Fo', num2str(fVco), ...
    'PeriodJitter', num2str(pJitter), 'CornerFrequency', num2str(cFreq));

% 3. Simulate and compare to datasheet (accept ±3 dB)
sim(vcoModel);
ud = get_param([vcoModel '/VCO TB'], 'UserData');

Skip to Phase 4.0 (Strategy A assembly) after deriving parameters.

C. Tune Existing Model (Meet a new spec)

Use when the user provides an existing .slx model and wants to meet a target (lock time, phase noise, spurs) without rebuilding from scratch.

Workflow:

  1. Probe — extract current params: Fc, Phi, N, OutputCurrent, Kvco, CompSelectionMethod, filter components via get_param

  2. Check for PLL Testbench — if missing or PLL input unconnected, ASK the user for fComp (P104). Add a PLL Testbench if needed.

  3. Baseline sim — sim(model), read get_param(tbBlk, 'UserData') for lock time, frequency, phase noise. This is the ONLY valid baseline (P103).

  4. Identify the lever:

    • Lock time too slow → increase Fc (BW ≈ 12/t_lock)

    • Phase noise too high in-band → decrease Fc, increase Icp, or reduce N

    • Spurs too high → increase filter order or narrow Fc

  5. Redesign — set new Fc (and Phi if needed), keep CompSelectionMethod='Automatic' so the block recomputes filter components

  6. Re-simulate — read testbench UserData. Iterate until spec is met.

  7. Report — before/after comparison with trade-off notes

Key rules:

  • NEVER estimate lock time from Vctrl settling (P103)

  • NEVER guess fComp from Fo/N or RefFreq param (P104)

  • Cap Fc at fPFD/10 for stability

  • Use lock_time ≈ 12/Fc only for initial sizing, then verify with testbench


Availability Check (MANDATORY)

Before using ANY function or block, verify it exists. Check exist(func,'file') for key functions (thirdOrderPassiveFilterDesign, estimatePLLPhaseNoise, phaseNoiseMeasure, phaseNoiseToJitter) and exist(lib,'file')==4 for libraries (msbPllFoundation, msbPllMeasurements, msbPllArchitectures). If not found, do NOT use — skip dependent steps.


Phase 1: Extract Datasheet Parameters

1.0 Reading the Datasheet PDF

Use MATLAB's extractFileText (never read PDFs directly with the Read tool):

pdfPath = 'path/to/datasheet.pdf';
txtContent = extractFileText(pdfPath);
txtPath = strrep(pdfPath, '.pdf', '_extracted.txt');
fid = fopen(txtPath, 'w'); fprintf(fid, '%s', txtContent); fclose(fid);
fprintf('Extracted %d characters to: %s\n', strlength(txtContent), txtPath);
1.1 Architecture Identification

Determine the PLL topology from the functional block diagram:

QuestionTypical Options
Integer-N or Fractional-N?Integer-only, Fractional with accumulator, Fractional with DSM
DSM order (if fractional)?1st, 2nd, 3rd, 4th
Prescaler type?Single modulus, Dual modulus (P/P+1)
Integrated VCO?Yes / No (external)
Reference path?Direct, with R counter, with doubler, with divider
Output dividers?None, programmable divide chain
Feedback tap point?Before output divider (VCO), after output divider
1.2-1.4 Detailed Parameter Extraction

See references/datasheet-extraction.md for the full parameter tables:

  • 1.2: Core PLL parameters (PFD, CP, dividers, VCO, output stage)

  • 1.3: Noise parameters (VCO PN, PNSYNTH, flicker, jitter, spurs)

  • 1.4: Frequency plan worked example


Phase 2: Select Assembly Strategy

Decision Tree (ALWAYS follow this)
START
  │
  ├─ Does the PLL topology match an msbPllArchitectures template?
  │   ├─ YES ──► Strategy A (Architecture block) ◄── DEFAULT
  │   └─ NO ───► Strategy B (Foundation blocks)
  │
  └─ Do you need EXTERNAL custom noise injection (BLWN, spur sources wired
     into the signal path OUTSIDE the PLL subsystem)?
      ├─ NO ───► Strategy A (Architecture block) ◄── DEFAULT
      └─ YES ──► Strategy A + editSystem (flatten, then inject)
                 OR Strategy B (if injection point is before CP or after VCO)

Strategy A is the default for ALL designs — spec-driven or datasheet-driven. Foundation blocks (Strategy B) are only needed when topology has no matching architecture template (e.g., dual-loop, injection-locked, external VCO with non-standard feedback).

StrategyWhen to UsePerformanceComplexity
A: Architecture blockDefault. Any standard Int-N, Frac-N, Dual-Modulus PLL3.3x faster sim (65s vs 212s for 5 GHz PLL)4 blocks, 4 connections
A + editSystemNeed to inject CP broadband noise or add custom impairmentsSame speed until flattenedFlatten adds ~5 internal blocks
B: Foundation blocksNon-standard topology, external VCO, dual-loop, or educational/visualization purposesBaseline (slowest)9+ blocks, 11+ connections
Probe-First Pattern (MANDATORY before set_param)

Before setting ANY block parameter, probe the mask to discover exact parameter names. Never guess parameter names from documentation or memory. See references/probing-simulink-models.md for the full probe workflow.

blk = [model '/PLL'];
m = Simulink.Mask.get(blk);
paramNames = {m.Parameters.Name};
fprintf('Available params (%d):\n', numel(paramNames));
cellfun(@(p) fprintf('  %s\n', p), paramNames);

This eliminates errors like using 'Icp' (wrong) instead of 'OutputCurrent' (correct), or 'DividerRatio' (wrong) instead of 'N' (correct).

2.1 msbPllArchitectures -- Pre-built PLL Templates (Strategy A)
Architecture BlockTopologyDivider Params
Integer N PLL with Single Modulus PrescalerPFD->CP->LF->VCO->Single PrescalerNmin, N (integer) — set N FIRST
Integer N PLL with Dual Modulus PrescalerPFD->CP->LF->VCO->Dual PrescalerProgramCounter(P), PrescalerDivider(N), SwallowCounter(S) — constraints: P > S > 0 (P105)
Fractional N PLL with AccumulatorPFD->CP->LF->VCO->Frac Divider (Accum)N (fractional), Nmin
Fractional N PLL with Delta Sigma ModulatorPFD->CP->LF->VCO->Frac Divider (DSM)N (fractional), Nmin, dsm (order)

Key promoted parameters (common to all):

CategoryParameters
VCOKvco, Fo, Amplitude, AddPhaseNoise, Foffset, PhaseNoise, PeriodJitter, CornerFrequency, FlickerExponent
Charge PumpOutputCurrent, EnableCurrentImpairments, CurrentImbalance, LeakageCurrent, EnableTimingImpairments
Loop FilterCompSelectionMethod(Automatic/Manual), Fc, Phi, FilterType, C1-C4, R2-R4, LfEnableImpairments, Temperature
PFDDeadbandCompensation, EnableImpairments
Analysisol_opt, cl_opt, estimatePn
Probepfd_up_dn, cp_out, lf_out, ps_out

Built-in callbacks:

  • msblks.PLL.editSystem(gcb) -- flatten to editable subsystem

  • msblks.PLL.estimatePhaseNoise(gcb) -- analytical PN estimation

  • msblks.VCO.plotMaskFigure(gcb) -- plot PN fit vs data

2.2 msbPllFoundation -- Individual Building Blocks (Strategy B)

See references/assembly-code.md for the full block table and parameters. Key blocks: PFD, Charge Pump, Loop Filter, Ring Oscillator VCO, Fractional Clock Divider with DSM.

2.3 Gap Analysis

Architecture blocks cover PFD, CP, LF, VCO, and Dividers. For R counter, ref doubler, RF output divider, or CP broadband noise: flatten with editSystem, then add custom blocks inside the subsystem.


Phase 3: Create Custom Blocks / Customize Architecture

3.0 Flattening (msblks.PLL.editSystem)

Set all mask parameters FIRST, THEN flatten. After flattening, the subsystem contains individual blocks (PFD, CP, LF, VCO, Divider) that you can modify.

blk = [model '/PLL'];
set_param(blk, 'Kvco','40e6', 'Fo','4.225e9', 'OutputCurrent','5e-3', ...
    'N','422.52', 'CompSelectionMethod','Automatic', 'Fc','60e3', 'Phi','48');
msblks.PLL.editSystem(blk);  % Flatten AFTER setting params
3.1-3.4 Custom Block Recipes

See references/assembly-code.md for: CP broadband noise (3.1), reference path (3.2), RF output divider (3.3), feedback select mux (3.4).


Phase 4: Assemble the Model

PERFORMANCE DIRECTIVE — Batch Model Assembly Execute the ENTIRE model assembly in ONE mcp__matlab__evaluate_matlab_code call (new_system, set_param, add_block, add_line, scope setup — ALL in one script). Each MCP round-trip = ~10-15s overhead. Batched = ~30s vs individual = 5+ min. Pattern: (1) compute params, (2) write assembly script to .m file in save folder, (3) execute via mcp__matlab__run_matlab_file (keeps terminal clean — no code dump), (4) verify. Using run_matlab_file instead of evaluate_matlab_code for large scripts prevents raw code from cluttering the user's screen during live demos.

4.0 Strategy A: Architecture Block Assembly (DEFAULT)

Architecture blocks: 3.3x faster sim, 56% fewer blocks, 64% fewer connections. Full assembly code template in references/assembly-code.md. Key sequence:

  1. new_system + solver config (VariableStepDiscrete, ReturnWorkspaceOutputs='on')

  2. add_block from msbPllArchitectures/<type>

  3. Set divider: Nmin='1' first, then N, then Nmin to final value (P57/P92)

  4. Set VCO params: Kvco, Fo, OutputCurrent, AddPhaseNoise, RefFreq

  5. Loop filter: thirdOrderPassiveFilterDesign for N>50 (P79), else Automatic

  6. PFD timing: PropDelay = max(50e-12, min(5e-12, 1/(2*fVCO)/10)), MaxFreqInterest = 2*fVCO (50ps floor — block rejects smaller values)

  7. Enable lf_out='on' for Vctrl probe

  8. Add PLL Testbench (Fo=fPFD, ExpectedFreq=fVCO, SampleRate=8*fVCO)

  9. Set SpectralAverages='2' (P102: mask default is 4), LockTimeOption='on', PhaseNoiseOption='off' (P83)

  10. StopTime = min(3*t_lock, 50e-6), HoldOffTime = min(1.5*estLock, 0.8*StopTime)

  11. Set paired vectors via Simulink.Mask.get: PhaseNoiseFreqOffset, TargetPhaseNoiseVector (P71, P100: use -999 if no targets — mask rejects -inf)

  12. Connect: TB/1→PLL/1, PLL/1→TB/1, PLL/2→Scope+ToWorkspace

  13. Simulink.BlockDiagram.arrangeSystem(model); drawnow; set_param(model,'ZoomFactor','FitSystem'); drawnow; (P95)

4.1 Strategy B: Foundation Blocks

Use ONLY for non-standard topologies. See references/assembly-code.md.

4.2 Stability Analysis (ALWAYS before time-domain sim)

Confirm PM > 45° and no closed-loop peaking > 1 dB before running full sim.

  • pllOpenLoopPlot(Icp,Kvco,N,Fc,R2,R3,R4,C1,C2,C3,C4) / pllCloseLoopPlot(...)

  • For 3rd-order passive: R4=0, C4=0. See references/stability-analysis.md.

  • If pllOpenLoopPlot crashes (ylim error at phase <= -180°): use manual Bode fallback per P106 — compute Z(s), G(s) via logspace sweep. Do NOT use Control System Toolbox (tf, bode) — it is not required.

4.3 Simulate and Read Results
simOut = sim(model);
delete(findall(0,'Type','figure','Tag','Msgbox_Warning'));
% Read lock time from PLL Testbench model workspace
sid = Simulink.ID.getSID([model '/PLL Testbench']);
sidParts = split(sid, ':'); sidSuffix = sidParts{2};
mdlWs = get_param(model, 'ModelWorkspace');
lockTime = evalin(mdlWs, ['LockTime_' sidSuffix]);
freq = evalin(mdlWs, ['Frequency_' sidSuffix]);

% Alternative: read from UserData (works after sim completes)
ud = get_param([model '/PLL Testbench'], 'UserData');
lockTime = ud.lockTime;  % seconds
freq = ud.freq;          % Hz
pnLevels = ud.phaseNoiseLevel; % dBc/Hz vector

P103: NEVER estimate lock time from Vctrl settling. The PLL Testbench uses frequency-error-based detection (FreqErrorTol) — this is the ONLY valid lock time measurement. Manual Vctrl analysis gives incorrect results.

P104: If the model has NO PLL Testbench or unconnected reference input, you CANNOT determine fComp. The RefFreq parameter is for PN estimation only — it does NOT define the actual reference clock. ASK the user for fComp before proceeding. Do not guess or calculate it from Fo/N.

Probe tab ports (after VCO out port 1): pfd_up, pfd_dn, cp_out, lf_out, ps_out. HoldOffTime must be < StopTime, otherwise no measurements.


Quick Reference

f_PFD = f_REFIN*(1+D)/(R*(1+T))  |  f_VCO = f_PFD*(INT+FRAC/MOD)  |  f_OUT = f_VCO/RF_DIV
N_eff = INT+FRAC/MOD (from VCO)  |  N_eff = (INT+FRAC/MOD)*RF_DIV (from divider output)
In-band PN  = PNSYNTH + 10*log10(f_PFD) + 20*log10(N)
1/f PN at f = PN1_f + 10*log10(10kHz/f) + 20*log10(f_RF/1GHz)
Divider effect = -20*log10(RF_DIV) on output phase noise

Happy Path Cheat Sheet (Spec → Lock Time Verified)

Most common workflow in ~20 steps:

1. User gives: fVCO, fRef, lock time target
2. Derive: N = fVCO/fRef, Fc = 12/t_lock (cap at fPFD/10), Kvco = fVCO/50
3. Present Design Plan (block diagram + params)
4. Build model (ONE mcp call):
   - new_system, VariableStepDiscrete solver
   - add_block msbPllArchitectures/Integer N PLL...
   - set Nmin='1', N, Nmin=N
   - set Kvco, Fo, OutputCurrent, Fc, Phi='50'
   - add PLL Testbench (Fo=fRef, ExpectedFreq=fVCO, SampleRate=8*fVCO)
   - set LockTimeOption='on', SpectralAverages='2'
   - connect TB↔PLL, enable lf_out, add scope
   - StopTime = min(3*12/Fc, 50e-6), HoldOff = 1.5*12/Fc
   - arrangeSystem + FitSystem
5. Plot Bode: pllOpenLoopPlot(...), confirm PM > 45° (if ylim crash, use manual fallback P106)
6. sim(model)
7. ud = get_param(tbBlk, 'UserData'); lockTime = ud.lockTime;
8. Report: lock_time vs target, margin, PASS/FAIL
9. If margin > 3x → DONE. If not → increase Fc by 50%, repeat from step 5.

Library names (canonical): msbPllArchitectures, msbPllFoundation, msbPllMeasurements, msbUtilities


Copyright 2026 The MathWorks, Inc.

Files

references/modeling-pll-datasheet-validate.md

PLL Datasheet Modeling — Validate & Measure (Phases 5-9)

PLL modeling validation and measurement (Phases 5-9): loop filter design, estimatePLLPhaseNoise, post-sim measurement, measurement blocks, iteration, and pitfalls. Companion to SKILL.md.

Companion file: modeling-pll-datasheet-core.md covers Phases 1-4 (extract params, map blocks, assemble model).


Availability Check (MANDATORY)

Before executing any phase below, the availability probe from modeling-pll-datasheet-core.md MUST have been run. If a function or block printed [SKIP], skip the entire step that depends on it.

Key release requirements:

Function / BlockAvailable From
estimatePLLPhaseNoiseR2026b
phaseNoiseMeasureR2021a
clockJitterMeasureR2021a
phaseNoiseToJitterR2021a
lockTimeMeasureR2021a
secondOrderPassiveFilterDesignR2021a
thirdOrderPassiveFilterDesignR2021a
fourthOrderPassiveFilterDesignR2021a
msbPllFoundation libraryR2021a
msbPllMeasurements libraryR2021a

If estimatePLLPhaseNoise is unavailable (pre-R2026b), skip Phase 6 entirely and rely on Phase 7/8 post-simulation measurements instead.


Phase 4.5: Baseband PLL — Fast Exploration (ON REQUEST ONLY)

Trigger: User explicitly asks for rapid BW/PM sweeps or fast loop exploration.

The msbPllArchitectures/Baseband PLL block is a linearized phase-domain model (100-1000x faster). Use it to sweep BW/PM rapidly, then build full model with the chosen parameters.

fastModel = 'PLL_FastExplore';
new_system(fastModel); open_system(fastModel);
set_param(fastModel, 'Solver', 'VariableStepDiscrete');
add_block('msbPllArchitectures/Baseband PLL', [fastModel '/BB_PLL']);
set_param([fastModel '/BB_PLL'], ...
    'Fo', num2str(fVco), 'ReferenceFrequency', num2str(fRef), ...
    'DividerRatio', num2str(N), 'ChargePumpCurrent', num2str(Icp), ...
    'VCOSensitivity', num2str(Kvco), 'FilterType', '3rd order passive', ...
    'CompSelectionMethod', 'Automatic', 'Fc', num2str(Fc), 'Phi', num2str(Phi));

% Sweep BW (completes in seconds)
Fc_sweep = linspace(Fc/3, Fc*3, 10);
for k = 1:numel(Fc_sweep)
    set_param([fastModel '/BB_PLL'], 'Fc', num2str(Fc_sweep(k)));
    sim(fastModel, 'StopTime', num2str(20/Fc_sweep(k)));
end
close_system(fastModel, 0);

Limitations: No spurs, no PN measurement, no nonlinear effects (cycle slip). Use ONLY for loop dynamics. Build full event-driven model for final validation.


Phase 5: Design the Loop Filter

5.1 Key Relationships
Open-loop gain:     L(s) = (ICP / 2pi) * Z_LF(s) * (2pi * Kvco / s) * (1/N)
Loop bandwidth:     f_BW  (where |L(j*2pi*f_BW)| = 1)
Phase margin:       PM = 180 + angle(L(j*2pi*f_BW))
5.2 MSB Loop Filter Design Functions

All three share the same five input arguments:

ArgumentDescriptionGuideline
FcLoop bandwidth (Hz)Start at 1/10th of f_PFD
PhiPhase margin (degrees)40 < Phi < 60 recommended
IcpCharge pump current (A)From datasheet
KvcoVCO sensitivity (Hz/V)From datasheet
NminMinimum divider ratioWorst-case N for stability
% 2nd order
[C1, C2, R2] = secondOrderPassiveFilterDesign(Fc, Phi, Icp, Kvco, Nmin)

% 3rd order (most common)
[C1, C2, C3, R2, R3] = thirdOrderPassiveFilterDesign(Fc, Phi, Icp, Kvco, Nmin)

% 4th order (maximum spur rejection)
[C1, C2, C3, C4, R2, R3, R4] = fourthOrderPassiveFilterDesign(Fc, Phi, Icp, Kvco, Nmin)
5.3 Automatic Mode (Preferred)
set_param(blk, 'CompSelectionMethod', 'Automatic', ...
    'Fc', num2str(Fc), 'Phi', num2str(Phi));
5.4 4th-Order Filter: Manual Design Workaround

fourthOrderPassiveFilterDesign often fails at wide BW (P58). Workaround:

Phi_base = 60;  % elevated PM to compensate for 4th pole
[C1, C2, C3, R2, R3] = thirdOrderPassiveFilterDesign(Fc, Phi_base, Icp, Kvco, Nmin);
f4 = 50*Fc; T4 = 1/(2*pi*f4); C4 = C3; R4 = T4/C4;
set_param(blk, 'CompSelectionMethod', 'Manual', ...
    'FilterType', '4th order passive', ...
    'C1',num2str(C1),'R2',num2str(R2),'C2',num2str(C2), ...
    'R3',num2str(R3),'C3',num2str(C3),'R4',num2str(R4),'C4',num2str(C4));

4th pole at 50x BW erodes ~8-10 deg of PM.

5.5 Active Loop Filter (ON REQUEST ONLY)

Trigger: User asks for active filter, or supply < 1.8V, or BW > fPFD/20.

set_param(lfBlk, 'FilterType', '3rd order active', ...
    'CompSelectionMethod', 'Automatic', 'Fc', num2str(Fc), 'Phi', num2str(Phi));

Use when: supply < 1.8V, BW > fPFD/20, or Icp limited. Avoid when: standard 2.5-3.3V supply (passive is simpler, less noise).

5.6 Design Guidelines
  • Bandwidth: Fc <= f_PFD / 10 for loop stability

  • Phase margin: 45-60 degrees typical; higher = more stable but slower

  • Nmin: Use the smallest N the PLL will operate at (worst-case gain)

  • Nonlinear lock time: 2-4x longer than linear prediction (P55 in pitfalls)

5.7 Filter Realizability Check (MANDATORY)

After computing filter components, verify they are physically realizable:

% Bounds: C in [0.1pF, 100µF], R in [1Ω, 10MΩ]
comps = [C1 C2 C3 R2 R3];
names = {'C1','C2','C3','R2','R3'};
for k=1:numel(comps)
    if startsWith(names{k},'C') && (comps(k)<0.1e-12 || comps(k)>100e-6)
        fprintf('WARNING: %s=%.3g out of range\n', names{k}, comps(k));
    elseif startsWith(names{k},'R') && (comps(k)<1 || comps(k)>10e6)
        fprintf('WARNING: %s=%.3g out of range\n', names{k}, comps(k));
    end
end

Fix: C too small → decrease Icp. R too large → increase Icp. Doubling Icp roughly halves R and doubles C for same BW/PM.

5.8 Jitter-Aware BW Selection (Lock Time vs. Phase Noise Tradeoff)

The 12/t_lock formula (P55) optimizes for lock time only. When a jitter or phase noise target is also specified, wider BW lets more VCO noise through. Use this decision tree:

  1. Lock time is the binding constraint (no jitter spec, or jitter is easy):

    Fc = min(12/t_lock, fPFD/10)
  2. Jitter is the binding constraint (lock time is relaxed):

    Fc = BW where analytical jitter ≈ target × 0.7 (30% margin)

    Use phaseNoiseToJitter or estimatePLLPhaseNoise to sweep BW analytically before committing to simulation.

  3. Both are tight (most common in real datasheets):

    Fc_lock = 12/t_lock          % BW needed for lock time
    Fc_jitter = (find from sweep) % BW needed for jitter
    if Fc_jitter >= Fc_lock
        Fc = Fc_lock              % lock time dominates, jitter met easily
    elseif Fc_jitter < Fc_lock
        Fc = Fc_jitter            % start narrow for jitter, verify lock time
        if lock_time > target
            flag tradeoff conflict to user
        end
    end

Rule of thumb: For ultra-low jitter parts (< 50 fs target), start with BW = 500 kHz–1 MHz regardless of lock time formula, verify jitter analytically, then widen only if lock time fails.


Phase 6: Validate with estimatePLLPhaseNoise

[out, cfg] = estimatePLLPhaseNoise(gcb);
6.2 Standalone (No Block Required)
VCOpn.f_Hz         = [10e3, 100e3, 1e6, 5e6];
VCOpn.L_dBc_per_Hz = [-86,  -111,  -134, -145];

% Derive charge pump noise from datasheet PNSYNTH and PN1_f
Icp = 5e-3; fPFD = 10e6; PNSYNTH = -220; PN1_f = -116;
Si_cp = 10^((PNSYNTH + 10*log10(fPFD) - 10*log10(2*pi^2) + 20*log10(Icp)) / 10);
fc_cp = 10e3 / 10^((PNSYNTH - PN1_f - 10*log10(fPFD) + 180) / 10);

[out, cfg] = estimatePLLPhaseNoise( ...
    Architecture            = "frac-dsm", ...
    ReferenceFrequency      = fPFD, ...
    ChargePumpCurrent       = Icp, ...
    VCOSensitivity          = 40e6, ...
    FractionalDividerRatio  = 422.52, ...
    UseDSMDividerNoise      = true, ...
    DSMOrder                = 3, ...
    C1 = 1.31e-12, C2 = 1.44e-11, C3 = 9.41e-14, ...
    R2 = 1.33e4,   R3 = 1.7e5, ...
    VCOPhaseNoise           = VCOpn, ...
    ChargePumpNoiseDensity  = Si_cp, ...
    ChargePumpFlickerCorner = fc_cp, ...
    Temperature             = 25, ...
    FrequencyOffset         = [1e3 10e3 100e3 1e6 10e6]);
6.3 Validation Checklist

Compare model output against datasheet specs only:

[ ] Phase noise at key offsets matches datasheet values
[ ] RMS jitter matches datasheet spec
[ ] PFD spur level at fPFD offset meets spec

Phase 7: Post-Simulation Measurement (MSB Utilities)

7.1 Phase Noise Measurement
[PnAtOffsets, freqAxis, pnProfile] = phaseNoiseMeasure( ...
    time, vcoSignal, Rbw, FrOffset, PlotOption, Tag, PnTarget);
7.2 Lock Time Measurement
LockTime = lockTimeMeasure(Vctrl, Time, ErrorTol)
7.3 Clock Jitter Measurement
[periodJitter, c2cJitter] = clockJitterMeasure(t, v, threshold, clockFreq)
7.4 Phase Noise to Jitter Conversion
Jrms_sec = phaseNoiseToJitter(freqOffset, phaseNoise, carrierFreq, intBandLow, intBandHigh)
7.5 Reference Spur Measurement (ON REQUEST ONLY)

Trigger: User asks for spur level or has a spur spec (e.g., "spurs < -60 dBc").

% From PLL Testbench logged PN profile
ud = get_param(tbBlk, 'UserData');
pnFreq = ud.PnFOffset; pnLevel = ud.Pn;
fPFD = fRef / R;  % comparison frequency

% Find peak near fPFD (+/- 5 bins)
[~, spurIdx] = min(abs(pnFreq - fPFD));
searchRange = max(1,spurIdx-5) : min(numel(pnLevel),spurIdx+5);
spurPeak_dBc = max(pnLevel(searchRange));

% Floor from neighboring region
floorIdx = [searchRange(1)-10:searchRange(1)-3, searchRange(end)+3:searchRange(end)+10];
floorIdx = floorIdx(floorIdx>0 & floorIdx<=numel(pnLevel));
pnFloor_dBc = mean(pnLevel(floorIdx));

% Spur in dBc (integrate over RBW)
spurLevel_dBc = spurPeak_dBc + 10*log10(Rbw);
fprintf('Ref spur @ %.0f MHz: %.1f dBc\n', fPFD/1e6, spurLevel_dBc);

Fractional spurs (Frac-N): appear at fPFD/denom multiples. Higher DSM order suppresses but pushes quantization noise to high offsets.

If spur fails: narrow BW, increase filter order (3rd→4th), check CP mismatch.


Phase 8: Validate with Measurement Blocks (msbPllMeasurements)

Key points:

  • PLL Testbench output port provides the reference clock stimulus

  • PLL Testbench input port receives VCO output for measurement

  • Enable PlotAndLogPhaseNoise = 'on' on testbench blocks

  • Set RBW = min(FreqOffset)/2 and SpectralAverages=2 for faster sims

  • TB targets must come from datasheet, not analytical prediction

VCO Testbench Mask Parameters (P29)
mask = Simulink.Mask.get([model '/VCO Testbench']);
mask.getParameter('PhaseNoiseFreqOffset').Value = mat2str(Foffset);
mask.getParameter('PhaseNoiseVector').Value = mat2str(phaseNoise);
mask.getParameter('ResBandwidth').Value = num2str(min(Foffset)/2);

set_param([model '/VCO Testbench'], ...
    'ControlVoltage', '2', ...
    'SpectralAverages', '2', ...
    'PlotAndLogPhaseNoise', 'on');
Stop Time Calculation (P5)
simStopTime = msblks.utilities.acStopTime(avg, RBW, holdoff);
set_param(model, 'StopTime', num2str(simStopTime));
Testbench Logged Data (pll_phase_noise_out)
  • PnFOffset/Pn -- full spectral profile (301 pts)

  • phaseNoiseFreq/phaseNoiseLevel -- discrete measurement offsets

  • zcData -- zero-crossing timestamps (for spur analysis)

  • CenterFreq, Avg, simStopTime

Zero-Crossing Capture (P30, P33)

Preferred: Triggered Subsystem with Digital Clock inside. See references/validate.md for full code.

Fallback: rising_idx = find(diff(v) > 0.5); zc_times = t(rising_idx + 1);


Phase 9: Iterate and Refine

9.1 Iteration Decision Table
ObservationAdjustment
In-band noise too highIncrease f_PFD (use doubler), decrease N
VCO noise dominates at high offsetChoose lower-noise VCO operating point
CP noise dominates in-bandIncrease ICP (wider BW helps too)
Spurs too highNarrow loop BW, increase filter order
Lock time too slowWiden loop BW (nonlinear = 2-4x linear)
Phase margin too lowAdjust filter components (increase R2)
9.2 Autonomous Iteration Algorithm (MANDATORY when targets specified)

When PN or lock time targets are specified, iterate BEFORE simulation until analytical checks pass. Key logic:

  1. Compute analytical PN using actual 3rd-order filter TF (NOT 2nd-order approx)

  2. Check ALL target offsets

  3. Corrective action by failure location:

    • In-band fail: increase fRef (decrease N) or increase Icp

    • Out-of-band fail: narrow BW (check lock time tradeoff)

  4. Recompute filter, update model, repeat

Termination criteria:

  • Success: ALL targets met with ≥ 2 dB margin → STOP, proceed to sim

  • Max iterations: 10 attempts → STOP, report best result and ask user

  • Conflicting targets: lock time requires wider BW but PN requires narrower → STOP, inform user of tradeoff and ask for priority

  • Diminishing returns: improvement < 0.5 dB between iterations → STOP

Step-size rules:

  • Lock time lever (Fc): increase by 50% per iteration (e.g., 500k → 750k → 1.1M)

  • Phase noise lever (Fc): decrease by 30% per iteration

  • Icp lever: double per iteration (1mA → 2mA → 4mA), cap at 10 mA

  • Filter order: try 3rd → 4th only once (not iterative)

  • PM adjustment: ±5° per iteration (range 45°–65°)

When to switch levers:

  • If 3 iterations of Fc adjustment don't converge → try Icp

  • If Icp at max and still failing → inform user (may need different architecture)

9.3 Accurate Phase Noise Calculation

Use actual 3rd-order filter impedance Z(s) = parallel(C1, series(R2,C2), series(R3,C3)). See references/iteration-algorithm.md for accuratePhaseNoise() function. Never use 2nd-order approximation (P72: can be 3-5 dB off near loop BW).

9.4 Two-Pass Simulation Strategy (P86 — MANDATORY)
  • Pass 1 (ALWAYS): Lock time only. StopTime = min(3*t_lock_target, 50µs). If PASSES with >3x margin → DONE. Do NOT sweep or iterate.

  • Pass 2 (ONLY if user specified PN/jitter/spur targets or asks for PN):

    1. Set SpectralAverages='2' explicitly (P102: mask default is 4)

    2. Set HoldOffTime = 1.5× measured lock time from Pass 1 (P101), NOT the pre-sim estimate

    3. StopTime = msblks.utilities.acStopTime(2, RBW, measuredHoldoff) (P98)

    4. Verify StopTime >= "Recommended min. simulation stop time" shown in TB mask (P75)

    5. Enable PhaseNoiseOption='on', PlotAndLogPhaseNoise='on'

Critical rules:

  1. 3x margin on first attempt → STOP. No sweeps "for completeness."

  2. No PN/jitter spec → NEVER run Pass 2.

  3. Use acStopTime for PN StopTime (P98), not manual calculation.

9.5 Testbench Parameter Ordering (P71)

PLL Testbench mask callbacks validate vector lengths when enabling measurement options. You MUST set vectors BEFORE enabling the corresponding option:

% CORRECT ORDER:
% 1. Set offset and target vectors (via Simulink.Mask.get)
tbMask = Simulink.Mask.get(tb);
tbMask.getParameter('PhaseNoiseFreqOffset').Value = mat2str(pnOffsets);
tbMask.getParameter('TargetPhaseNoiseVector').Value = mat2str(pnTargets);  % SAME LENGTH as pnOffsets!
tbMask.getParameter('ResBandwidth').Value = num2str(min(pnOffsets)/2);

% 2. Set scalar params
set_param(tb, 'TargetLockTime', num2str(lockTimeTarget));

% 3. THEN enable measurement options (triggers validation callbacks)
set_param(tb, 'FrequencyMeasureOption', 'on');
set_param(tb, 'LockTimeOption', 'on');
set_param(tb, 'PhaseNoiseOption', 'on');
set_param(tb, 'PlotAndLogPhaseNoise', 'on');

Phase 10: Simulation Troubleshooting

Error / SymptomCauseFix
"Algebraic loop"Zero-delay feedbackCheck PropDelay > 0 on PFD/divider
"Step size too small" / hangsFilter component = 0Verify all C, R > 0
NaN in VctrlUnstable (PM<0 or BW>fPFD/5)Reduce Fc, increase PM
Sim very slowHigh fVco + wrong solverUse VariableStepDiscrete
Lock time = 0 from TBRipple > FreqErrorTolKvco*peak2peak(Vctrl) — relax tol or narrow BW
PN shows flat lineHoldOffTime >= StopTimeFix holdoff; check VCO toggling
"Apply or cancel" dialogBlock dialog openclose_system(blk) first (P97)

Pre-sim check (always run before sim()):

assert(strcmp(get_param(model,'Solver'),'VariableStepDiscrete'));
assert(str2double(get_param(model,'StopTime')) > 0);

Pitfalls Quick Reference

See references/pitfalls.md for the full catalog (P1-P103).

Simulation failures: P39 (PFD timing), P57/P92 (Nmin ordering), P71 (vector length), P79 (manual filter N>50), P87 (HoldOff<StopTime), P93 (LockTime=0), P97 (close dialog), P105 (dual modulus P>S>0)

Wrong results: P43 (Fo=fComp), P48 (OL=Gofs/N), P74 (no PN extrapolation), P94/P100 (-999 not -inf), P98 (acStopTime), P99 (DSM spurs), P101 (measured holdoff), P102 (SpectralAverages=2), P103 (UserData lock time)

Plotting/analysis: P106 (pllOpenLoopPlot ylim crash — manual Bode fallback, no CST needed)

Performance: P83 (PN off by default), P85/P95 (arrangeSystem), P86 (two-pass, no over-iteration)


Copyright 2026 The MathWorks, Inc.

Requirements and compatibility

Written for: Claude Code

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Names Claude Code tools, Needs an MCP server

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
MathWorks
Publisher
MathWorks (Official)
Source
matlab/matlab-agentic-toolkit · skills-catalog/rf-and-mixed-signal/matlab-model-ams-systems · commit 2a7f215
Licence
BSD 2-Clause License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE.md)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by MathWorks or MathWorks.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download matlab-model-ams-systems.zip