Compare commits

...

2 Commits

Author SHA1 Message Date
morgan 4b098c0a54 add README.md
readme: add quick start and installation docs
readme: add sim limitation with images
2023-12-05 14:58:41 +08:00
morgan 3f6e525108 add notebook examples with 3 PLL modes 2023-12-05 14:58:20 +08:00
6 changed files with 532 additions and 0 deletions

68
README.md Normal file
View File

@ -0,0 +1,68 @@
# WRPLL simulation
A time domain simulation for WRPLL
## Installing dependencies
### Nix:
```bash
nix develop
```
### Others:
```bash
python -m venv .venv
source .venv/bin/activate
(venv) pip install -r requirements.txt
```
## Quick start
- Three notebook examples are included
1. `helper_PLL_example.ipynb` : helper PLL only
2. `main_PLL_example.ipynb` : main PLL only with a user specific helper frequency
3. `both_PLL_example.ipynb` : main and helper PLL
- RAM usage and execution time estimate for simulation **ONLY**
1. 100,000,000 time steps: 6GiB RAM and 12 seconds
2. 200,000,000 time steps: 11GiB RAM and 20 seconds
3. 300,000,000 time steps: 16GiB RAM and 35 seconds
<br>
<details><summary><b>WRPLL formulas</b></summary>
- Assume the difference between $f_{main}, f_{gtx}$ is very small
- Let $f_{in} = f_{main} = f_{gtx}$
- $f_{helper} = f_{in} * \dfrac{N-1}{N}$
- $f_{beat} = f_{in} - f_{helper} = \dfrac{f_{in}}{N}$
- Main and helper Si549 DCXO **ADPLL** setting
- $ADPLL = \dfrac{\Delta f_{outppm}}{0.0001164}$
</details><br>
## Limitation
As the simulation is not cycle nor delay accurate, there will be more glitches than the hardware implementation
### Helper PLL glitches (remedies are added to follow hardware behavior)
- Cycle slipping issue will appear as $|\Delta{period}| \sim N$
- During hardware testing, slipping issue is not common
- It's recommended to turn cycle_slip_comp ON to reduce slipping and have a more accurate simulation
![cycle_slip](img/cycle_slipping.png)
- Deglitcher fail issue will appear as $|\Delta{period}| \sim N/2$
- There are no such issue for hardware
- It's recommended to set blind_period higher than the hardware setting (around 300 is sufficient)
![deglitch_fail](img/deglitch_fail.png)

BIN
img/cycle_slipping.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
img/deglitch_fail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

156
src/both_PLL_example.ipynb Normal file
View File

@ -0,0 +1,156 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Both PLL mode example\n",
"\n",
"Time domain simulation with helper and main PLL mode. \n",
"\n",
"- Period error (**Helper PLL**)\n",
" - $\\Delta{period} = N - (tag_{gtx}[n] - tag_{gtx}[n-1]) $\n",
" - where ideally $f_{helper} = \\dfrac{f_{in} * (N-1)}{N}$ \n",
"\n",
"- Phase error (**Main PLL**)\n",
" - $\\text{Let } \\Delta tag[n] = tag_{main}[n] - tag_{gtx}[n] \\text{ mod N}$\n",
"\n",
" - $\\Delta\\phi[n] = \\begin{cases}\n",
" \\Delta tag[n] - N, & \\text{if } \\Delta tag > N/2 \\\\\n",
" \\Delta tag[n], & otherwise\n",
" \\end{cases} \\quad$\n",
" \n",
"\n",
"- ADPLL PID (common for main and helper PLL)\n",
" - $P[n] = err[n] * K_P$\n",
" - $I[n] = I[n-1] + err[n] * K_I$\n",
" - $D[n] = (err[n] - err[n-1]) * K_D$\n",
" - $adpll[n] = \\text{base adpll} + P[i] + I[n] + D[n] $\n",
" - where $\\text{base adpll}$ is constant and obtain from frequency counter in HW"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from plotly_resampler import FigureResampler, FigureWidgetResampler\n",
"from plotly.subplots import make_subplots\n",
"import plotly.graph_objects as go\n",
"import numpy as np\n",
"from wave_gen import square_arr\n",
"from wrpll import WRPLL_simulator\n",
"\n",
"# settings\n",
"timestep = 4e-10 # even number is recommended to avoid strange glitches\n",
"total_steps = 300_000_000\n",
"sim_mode = \"both\"\n",
"adpll_period = int(100e-6/timestep) # in simulation steps, 100μs is minimum, smaller = more frequency adjustment and filter calulation per unit time\n",
"start_up_delay = int(100e-6/timestep) # in simulation steps, the frequency adjustment is DISABLE until steps > start_up_delay\n",
"\n",
"gtx_freq = 125_001_519\n",
"\n",
"helper_init_freq = gtx_freq * (4096-1)/4096\n",
"\n",
"helper_filter = {\n",
" \"KP\": 2,\n",
" \"KI\": 4,\n",
" \"KD\": 0,\n",
"}\n",
"\n",
"main_filter = { \n",
" \"KP\": 12,\n",
" \"KI\": 0,\n",
" \"KD\": 0,\n",
"}\n",
"\n",
"\n",
"t = np.linspace(0, timestep*total_steps, total_steps)\n",
"\n",
"# simulation will start with\n",
"# - random phase for main & helper\n",
"# - gussian based base_adpll error\n",
"# - gussian jitter for gtx, main and helper with the set standard deviation\n",
"\n",
"wrpll_sim = WRPLL_simulator(\n",
" time=t,\n",
" sim_mode=sim_mode,\n",
" helper_filter=helper_filter,\n",
" main_filter=main_filter,\n",
" gtx_freq=gtx_freq,\n",
" adpll_write_period=adpll_period,\n",
" start_up_delay=start_up_delay,\n",
" helper_init_freq=helper_init_freq,\n",
" # preset\n",
" gtx_jitter_SD=19e-12, # 0 = no jitter\n",
" dcxo_jitter_SD=9e-12,\n",
" dcxo_freq=125_000_000,\n",
" freq_acquisition_SD=100,\n",
" N=4096, # hardware used 4096\n",
" blind_period=300, # 300 is used to remove most glitches in simulation (for details see README 'Limitation')\n",
" cycle_slip_comp=True,\n",
")\n",
"wrpll_sim.run()\n",
"\n",
"# faster than pyplot with resampling feature\n",
"# see https://github.com/predict-idlab/plotly-resampler\n",
"\n",
"fig = FigureWidgetResampler(make_subplots(rows=4, shared_xaxes=True))\n",
"fig.add_trace(go.Scattergl(name='phase error'), hf_x=t, hf_y=wrpll_sim.phase_err, row=1, col=1)\n",
"\n",
"fig.add_trace(go.Scattergl(name='freq error (ppm)'), hf_x=t, hf_y=(\n",
" wrpll_sim.mainfreq-gtx_freq) * (1e6/gtx_freq), row=2, col=1)\n",
"\n",
"fig.add_trace(go.Scattergl(name='period error'), hf_x=t, hf_y=wrpll_sim.period_err, row=3, col=1)\n",
"\n",
"fig.add_trace(go.Scattergl(name='gtx'), hf_x=t, hf_y=wrpll_sim.gtx+1, row=4, col=1)\n",
"fig.add_trace(go.Scattergl(name='main'), hf_x=t, hf_y=wrpll_sim.main, row=4, col=1)\n",
"fig.add_trace(go.Scattergl(name='helper'), hf_x=t, hf_y=wrpll_sim.helper-1, row=4, col=1)\n",
"\n",
"\n",
"fig.update_layout(\n",
" xaxis4=dict(title=\"time (sec)\"),\n",
" yaxis1=dict(title=\"phase error\"),\n",
" yaxis2=dict(title=\"freq error (ppm)\"),\n",
" yaxis3=dict(title=\"beating period error\"),\n",
" yaxis4=dict(title=\"Signal\"),\n",
"\n",
" height=1000,\n",
" showlegend=True,\n",
" title_text=\"PLL example\",\n",
" legend=dict(\n",
" orientation=\"h\",\n",
" yanchor=\"bottom\",\n",
" y=1.02,\n",
" xanchor=\"right\",\n",
" x=1,\n",
" ),\n",
")\n",
"\n",
"fig"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.6"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,149 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Helper PLL mode example\n",
"\n",
"Time domain simulation with helper PLL mode\n",
"\n",
"- Period error (**Helper PLL**)\n",
" - $\\Delta{period} = N - (tag_{gtx}[n] - tag_{gtx}[n-1]) $\n",
" - where ideally $f_{helper} = \\dfrac{f_{in} * (N-1)}{N}$ \n",
"\n",
"- Phase error (**Main PLL**)\n",
" - $\\text{Let } \\Delta tag[n] = tag_{main}[n] - tag_{gtx}[n] \\text{ mod N}$\n",
"\n",
" - $\\Delta\\phi[n] = \\begin{cases}\n",
" \\Delta tag[n] - N, & \\text{if } \\Delta tag > N/2 \\\\\n",
" \\Delta tag[n], & otherwise\n",
" \\end{cases} \\quad$\n",
" \n",
"\n",
"- ADPLL PID (common for main and helper PLL)\n",
" - $P[n] = err[n] * K_P$\n",
" - $I[n] = I[n-1] + err[n] * K_I$\n",
" - $D[n] = (err[n] - err[n-1]) * K_D$\n",
" - $adpll[n] = \\text{base adpll} + P[i] + I[n] + D[n] $\n",
" - where $\\text{base adpll}$ is constant and obtain from frequency counter in HW"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from plotly_resampler import FigureResampler, FigureWidgetResampler\n",
"from plotly.subplots import make_subplots\n",
"import plotly.graph_objects as go\n",
"import numpy as np\n",
"from wave_gen import square_arr\n",
"from wrpll import WRPLL_simulator\n",
"\n",
"# settings\n",
"timestep = 4e-10 # even number is recommended to avoid strange glitches\n",
"total_steps = 200_000_000\n",
"sim_mode = \"helper_pll\"\n",
"adpll_period = int(100e-6/timestep) # in simulation steps, 100μs is minimum, smaller = more frequency adjustment and filter calulation per unit time\n",
"start_up_delay = int(100e-6/timestep) # in simulation steps, the frequency adjustment is DISABLE until steps > start_up_delay\n",
"\n",
"gtx_freq = 125_001_519\n",
"\n",
"helper_filter = {\n",
" \"KP\": 2,\n",
" \"KI\": 4,\n",
" \"KD\": 0,\n",
"}\n",
"\n",
"main_filter = { # unused\n",
" \"KP\": 12,\n",
" \"KI\": 0,\n",
" \"KD\": 0,\n",
"}\n",
"\n",
"\n",
"t = np.linspace(0, timestep*total_steps, total_steps)\n",
"\n",
"# simulation will start with\n",
"# - random phase for main & helper\n",
"# - gussian based base_adpll error\n",
"# - gussian jitter for gtx, main and helper with the set standard deviation\n",
"\n",
"wrpll_sim = WRPLL_simulator(\n",
" time=t,\n",
" sim_mode=sim_mode,\n",
" helper_filter=helper_filter,\n",
" main_filter=main_filter,\n",
" gtx_freq=gtx_freq,\n",
" adpll_write_period=adpll_period,\n",
" start_up_delay=start_up_delay,\n",
" # preset\n",
" gtx_jitter_SD=19e-12, # 0 = no jitter\n",
" dcxo_jitter_SD=9e-12,\n",
" dcxo_freq=125_000_000,\n",
" freq_acquisition_SD=500,\n",
" N=4096, # hardware used 4096\n",
" blind_period=300, # 300 is used to remove most glitches in simulation (for details see README 'Limitation')\n",
" cycle_slip_comp=True,\n",
")\n",
"\n",
"wrpll_sim.run()\n",
"\n",
"# faster than pyplot with resampling feature\n",
"# see https://github.com/predict-idlab/plotly-resampler\n",
"\n",
"fig = FigureWidgetResampler(make_subplots(rows=3, shared_xaxes=True))\n",
"fig.add_trace(go.Scattergl(name='period error'), hf_x=t, hf_y=wrpll_sim.period_err, row=1, col=1)\n",
"\n",
"fig.add_trace(go.Scattergl(name='gtx'), hf_x=t, hf_y=wrpll_sim.gtx+1, row=2, col=1)\n",
"fig.add_trace(go.Scattergl(name='main'), hf_x=t, hf_y=wrpll_sim.main, row=2, col=1)\n",
"fig.add_trace(go.Scattergl(name='helper'), hf_x=t, hf_y=wrpll_sim.helper-1, row=2, col=1)\n",
"\n",
"fig.add_trace(go.Scattergl(name='helper'), hf_x=t, hf_y=wrpll_sim.helper_adpll, row=3, col=1)\n",
"\n",
"\n",
"fig.update_layout(\n",
" xaxis2=dict(title=\"time (sec)\"),\n",
"\n",
" yaxis1=dict(title=\"beating period error\"),\n",
" yaxis2=dict(title=\"Signal\"),\n",
" height=500,\n",
" showlegend=True,\n",
" title_text=\"PLL example\",\n",
" legend=dict(\n",
" orientation=\"h\",\n",
" yanchor=\"bottom\",\n",
" y=1.02,\n",
" xanchor=\"right\",\n",
" x=1,\n",
" ),\n",
")\n",
"\n",
"fig"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.6"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

159
src/main_PLL_example.ipynb Normal file
View File

@ -0,0 +1,159 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Main PLL mode example\n",
"\n",
"Time domain simulation with main PLL mode, and helper PLL is assumed to be locked \n",
"\n",
"(`helper_init_freq` variable need to be set)\n",
"\n",
"\n",
"- Period error (**Helper PLL**)\n",
" - $\\Delta{period} = N - (tag_{gtx}[n] - tag_{gtx}[n-1]) $\n",
" - where ideally $f_{helper} = \\dfrac{f_{in} * (N-1)}{N}$ \n",
"\n",
"- Phase error (**Main PLL**)\n",
" - $\\text{Let } \\Delta tag[n] = tag_{main}[n] - tag_{gtx}[n] \\text{ mod N}$\n",
"\n",
" - $\\Delta\\phi[n] = \\begin{cases}\n",
" \\Delta tag[n] - N, & \\text{if } \\Delta tag > N/2 \\\\\n",
" \\Delta tag[n], & otherwise\n",
" \\end{cases} \\quad$\n",
" \n",
"\n",
"- ADPLL PID (common for main and helper PLL)\n",
" - $P[n] = err[n] * K_P$\n",
" - $I[n] = I[n-1] + err[n] * K_I$\n",
" - $D[n] = (err[n] - err[n-1]) * K_D$\n",
" - $adpll[n] = \\text{base adpll} + P[i] + I[n] + D[n] $\n",
" - where $\\text{base adpll}$ is constant and obtain from frequency counter in HW"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from plotly_resampler import FigureResampler, FigureWidgetResampler\n",
"from plotly.subplots import make_subplots\n",
"import plotly.graph_objects as go\n",
"import numpy as np\n",
"from wave_gen import square_arr\n",
"from wrpll import WRPLL_simulator\n",
"\n",
"# settings\n",
"timestep = 4e-10 # even number is recommended to avoid strange glitches\n",
"total_steps = 200_000_000\n",
"sim_mode = \"main_pll\"\n",
"adpll_period = int(100e-6/timestep) # in simulation steps, 100μs is minimum, smaller = more frequency adjustment and filter calulation per unit time\n",
"start_up_delay = int(100e-6/timestep) # in simulation steps, the frequency adjustment is DISABLE until steps > start_up_delay\n",
"\n",
"gtx_freq = 125_001_519\n",
"\n",
"helper_init_freq = gtx_freq * (4096-1)/4096\n",
"\n",
"helper_filter = { # unused\n",
" \"KP\": 2,\n",
" \"KI\": 4,\n",
" \"KD\": 0,\n",
"}\n",
"\n",
"main_filter = { \n",
" \"KP\": 12,\n",
" \"KI\": 0,\n",
" \"KD\": 0,\n",
"}\n",
"\n",
"\n",
"t = np.linspace(0, timestep*total_steps, total_steps)\n",
"\n",
"# simulation will start with\n",
"# - random phase for main & helper\n",
"# - gussian based base_adpll error\n",
"# - gussian jitter for gtx, main and helper with the set standard deviation\n",
"\n",
"wrpll_sim = WRPLL_simulator(\n",
" time=t,\n",
" sim_mode=sim_mode,\n",
" helper_filter=helper_filter,\n",
" main_filter=main_filter,\n",
" gtx_freq=gtx_freq,\n",
" adpll_write_period=adpll_period,\n",
" start_up_delay=start_up_delay,\n",
" helper_init_freq=helper_init_freq,\n",
" # preset\n",
" gtx_jitter_SD=19e-12, # 0 = no jitter\n",
" dcxo_jitter_SD=9e-12,\n",
" dcxo_freq=125_000_000,\n",
" freq_acquisition_SD=100,\n",
" N=4096, # hardware used 4096\n",
" blind_period=300, # 300 is used to remove most glitches in simulation (for details see README 'Limitation')\n",
" cycle_slip_comp=True,\n",
")\n",
"wrpll_sim.run()\n",
"\n",
"# faster than pyplot with resampling feature\n",
"# see https://github.com/predict-idlab/plotly-resampler\n",
"\n",
"fig = FigureWidgetResampler(make_subplots(rows=4, shared_xaxes=True))\n",
"fig.add_trace(go.Scattergl(name='phase error'), hf_x=t, hf_y=wrpll_sim.phase_err, row=1, col=1)\n",
"\n",
"fig.add_trace(go.Scattergl(name='freq error (ppm)'), hf_x=t, hf_y=(\n",
" wrpll_sim.mainfreq-gtx_freq) * (1e6/gtx_freq), row=2, col=1)\n",
"\n",
"fig.add_trace(go.Scattergl(name='period error'), hf_x=t, hf_y=wrpll_sim.period_err, row=3, col=1)\n",
"\n",
"fig.add_trace(go.Scattergl(name='gtx'), hf_x=t, hf_y=wrpll_sim.gtx+1, row=4, col=1)\n",
"fig.add_trace(go.Scattergl(name='main'), hf_x=t, hf_y=wrpll_sim.main, row=4, col=1)\n",
"fig.add_trace(go.Scattergl(name='helper'), hf_x=t, hf_y=wrpll_sim.helper-1, row=4, col=1)\n",
"\n",
"\n",
"fig.update_layout(\n",
" xaxis4=dict(title=\"time (sec)\"),\n",
" yaxis1=dict(title=\"phase error\"),\n",
" yaxis2=dict(title=\"freq error (ppm)\"),\n",
" yaxis3=dict(title=\"beating period error\"),\n",
" yaxis4=dict(title=\"Signal\"),\n",
"\n",
" height=1000,\n",
" showlegend=True,\n",
" title_text=\"PLL example\",\n",
" legend=dict(\n",
" orientation=\"h\",\n",
" yanchor=\"bottom\",\n",
" y=1.02,\n",
" xanchor=\"right\",\n",
" x=1,\n",
" ),\n",
")\n",
"\n",
"fig"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.6"
}
},
"nbformat": 4,
"nbformat_minor": 4
}