-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.tex
More file actions
656 lines (550 loc) · 41.2 KB
/
Copy pathmain.tex
File metadata and controls
656 lines (550 loc) · 41.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
\documentclass[12pt]{article}
% Packages
\usepackage[margin=1in]{geometry}
\usepackage{graphicx}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{siunitx}
\usepackage{float}
\usepackage{titlesec}
\usepackage{fancyhdr}
\usepackage{booktabs}
\usepackage{parskip}
\usepackage{listings}
\usepackage{xcolor}
\usepackage[hidelinks]{hyperref}
\usepackage{tikz}
\usetikzlibrary{shapes,arrows,positioning,calc}
% Code Formatting Styles
\definecolor{codegreen}{rgb}{0,0.6,0}
\definecolor{codegray}{rgb}{0.5,0.5,0.5}
\definecolor{codepurple}{rgb}{0.58,0,0.82}
\definecolor{backcolour}{rgb}{0.95,0.95,0.92}
\lstdefinestyle{mystyle}{
backgroundcolor=\color{backcolour},
commentstyle=\color{codegreen},
keywordstyle=\color{blue},
numberstyle=\tiny\color{codegray},
stringstyle=\color{codepurple},
basicstyle=\ttfamily\footnotesize,
breakatwhitespace=false,
breaklines=true,
captionpos=b,
keepspaces=true,
numbers=left,
numbersep=5pt,
showspaces=false,
showstringspaces=false,
showtabs=false,
tabsize=2,
frame=single
}
\lstset{style=mystyle}
% Header and Footer
\pagestyle{fancy}
\fancyhf{}
\setlength{\headheight}{15pt}
\rhead{Group T3}
\lhead{ESDP Lab Report}
\rfoot{Page \thepage}
% Section Formatting
\titleformat{\section}{\bfseries\Large}{\thesection}{1em}{}
\titleformat{\subsection}{\bfseries\large}{\thesubsection}{1em}{}
\titleformat{\subsubsection}{\bfseries\normalsize}{\thesubsubsection}{1em}{}
% Title Information
\title{\textbf{Electronic Systems Design and Prototyping Lab Report}\\
\large Design and Implementation of Discrete-Time Filters and PID Voltage-Mode Control on a Spartan-7 FPGA}
\author{\textbf{Group T3}\\
Rickarya Das (23EE30019) \\
Electronic System Development and Prototyping (EE69006) \\
Department of Electrical Engineering \\
Indian Institute of Technology Kharagpur}
\date{\today}
\begin{document}
\maketitle
\hrule
\vspace{1em}
\begin{abstract}
This report details the design and implementation of a digital closed-loop PID controller for a synchronous buck converter on a Xilinx Spartan-7 FPGA. The project follows a three-step methodology: (1) Small-signal modeling of the converter to derive control-to-output ($G_{vd}$), audio susceptibility ($G_{vg}$), and output impedance ($Z_o$) transfer functions; (2) Controller tuning in the frequency domain using MATLAB's \texttt{pidTuner} to satisfy bandwidth ($\le f_{sw}/10$) and phase margin ($\ge 45^\circ$) constraints; and (3) Hardware implementation on the FPGA using the Forward Euler discretization method and 19-bit fixed-point arithmetic. We analyze the system performance under a $10\text{ ms}$ reference transient between $2.2\text{ V}$ and $3.3\text{ V}$. The results demonstrate robust regulation and the efficiency of FPGA-based deterministic control for high-frequency power electronics.
\end{abstract}
\vspace{1em}
\hrule
\newpage
\tableofcontents
\listoffigures
\newpage
\section{Introduction}
\label{sec:intro}
Digital control of power electronic converters requires high-precision, low-latency processing to maintain stability at high switching frequencies. For the EE69006 Course (Electronic Systems Design and Prototyping), we implemented a digital PID controller on a Xilinx Spartan-7 FPGA to control a synchronous buck converter. In this laboratory, we utilize the high-speed processing capabilities of FPGAs to execute control laws in parallel with deterministic timing. This report documents the small-signal modeling, MATLAB-based tuning, and FPGA-based implementation of the closed-loop system, demonstrating regulation of the output voltage during reference transients.
\section{Problem Statement}
\label{sec:problem}
We are provided a synchronous buck converter with the specifications listed in Table~\ref{tab:specs}. The objectives are:
\begin{enumerate}
\item Design a PID controller for the given operating conditions using frequency-domain methods. Justify the choice of bandwidth ($f_c \le f_{sw}/10$) and phase margin ($PM \ge 45^\circ$).
\item Implement the controller on the FPGA control card and toggle the output voltage reference between $2.2\text{ V}$ and $3.3\text{ V}$ every $10\text{ ms}$.
\item Compare the closed-loop transient response against the open-loop response.
\end{enumerate}
\begin{table}[H]
\centering
\caption{Buck Converter Specifications}
\label{tab:specs}
\begin{tabular}{@{}lll@{}}
\toprule
\textbf{Parameter} & \textbf{Symbol} & \textbf{Value} \\
\midrule
Input Voltage & $V_{in}$ & $5\text{ V}$ \\
Inductor & $L$ & $5.6\ \mu\text{H}$ \\
Output Capacitor & $C$ & $140\ \mu\text{F}$ \\
Switching Frequency & $f_{sw}$ & $200\text{ kHz}$ \\
Reference Output Voltage & $V_{ref}$ & $3.3\text{ V}$, $2.2\text{ V}$ \\
Inductor DC Resistance & $r_L$ & $10\text{ m}\Omega$ \\
Capacitor ESR & $r_C$ & $15\text{ m}\Omega$ \\
Switch $r_{ds,on}$ & $r_{ds,on}$ & $3.67\text{ m}\Omega$ \\
Load Resistance & $R$ & $50\ \Omega$ \\
Output Voltage Sensing Gain & $H$ & $1/11\text{ V/V}$ \\
System Clock & $f_{clk}$ & $100\text{ MHz}$ \\
\bottomrule
\end{tabular}
\end{table}
\section{Background Theory}
\subsection{Small-Signal Modeling of Buck Converter}
Traditional controller tuning is applicable to linear systems. However, switching-mode power converters are inherently non-linear due to the switching action. Small-signal modeling provides a linear approximation around a steady-state operating point ($V, I, D$) by considering small perturbations ($\hat{x}$) around these variables. The switching behavior is averaged over the switching period $T_s$.
\subsection{Derivation of Small Signal Transfer Functions}
The linear equivalent AC circuit is derived by ignoring products of AC perturbations. For a synchronous buck converter, we account for the inductor DCR ($r_L$), capacitor ESR ($r_C$), and switch on-resistance ($r_{ds,on}$). Define the equivalent series resistance $r_e = r_L + r_{ds,on} = 10 + 3.67 = 13.67\text{ m}\Omega$.
Each variable is separated into steady-state and AC components: $v_o = V_o + \hat{v}_o$, $i_L = I_L + \hat{i}_L$, $d = D + \hat{d}$. The steady-state values cancel, and products of AC perturbations are negligible, yielding the linear equivalent AC circuit.
\textbf{(1) Control-to-Output Transfer Function $G_{vd}(s)$} --- effect of $\hat{d}$ on $\hat{v}_o$ with $\hat{v}_{in}=0, \hat{i}_o=0$:
\begin{equation}
G_{vd}(s) = V_{in} \cdot \frac{R}{R+r_e} \cdot \frac{1 + s r_C C}{1 + s\left(\dfrac{L + C(Rr_e + r_C(R+r_e))}{R+r_e}\right) + s^2 \dfrac{LC(R+r_C)}{R+r_e}}
\label{eq:Gvd}
\end{equation}
\textbf{Numerical evaluation:}
\begin{align}
\text{DC gain:} \quad G_{vd}(0) &= V_{in}\frac{R}{R+r_e} = 5 \times \frac{50}{50.01367} \approx 4.9986\text{ V/V} \nonumber \\
\text{ESR zero:} \quad f_{z,ESR} &= \frac{1}{2\pi r_C C} = \frac{1}{2\pi \times 15\times10^{-3} \times 140\times10^{-6}} \approx 75.79\text{ kHz} \nonumber \\
\text{LC resonance:} \quad f_0 &= \frac{1}{2\pi\sqrt{LC}} = \frac{1}{2\pi\sqrt{5.6\times10^{-6} \times 140\times10^{-6}}} \approx 5.68\text{ kHz} \nonumber
\end{align}
The plant has a second-order roll-off ($-40\text{ dB/dec}$) beyond $f_0$ with a right-half-plane ESR zero at $f_{z,ESR}$. The quality factor is:
\begin{equation}
Q = \frac{(R+r_e)\sqrt{LC(R+r_C)}}{L + C(Rr_e + r_C(R+r_e))} \approx \frac{50.014 \times 2.80 \times 10^{-5}}{5.6\times10^{-6} + 140\times10^{-6}(0.6834 + 0.7520)} \approx 6.86
\end{equation}
The high $Q$ indicates a sharp resonance peak ($\approx 16.7\text{ dB}$), making the system lightly damped and underscoring the need for adequate phase margin.
\textbf{(2) Audio Susceptibility $G_{vg}(s)$} --- effect of $\hat{v}_{in}$ on $\hat{v}_o$ with $\hat{d}=0, \hat{i}_o=0$:
\begin{equation}
G_{vg}(s) = D \cdot \frac{R}{R+r_e} \cdot \frac{1 + s r_C C}{1 + s\left(\dfrac{L + C(Rr_e + r_C(R+r_e))}{R+r_e}\right) + s^2 \dfrac{LC(R+r_C)}{R+r_e}}
\label{eq:Gvg}
\end{equation}
For $V_{out} = 3.3\text{ V}$: $D = V_{out}/V_{in} = 0.66$, so $G_{vg}(0) = 0.66 \times 0.9997 \approx 0.660$.
\textbf{(3) Output Impedance $Z_o(s)$} --- effect of $\hat{i}_o$ on $\hat{v}_o$ with $\hat{v}_{in}=0, \hat{d}=0$:
\begin{equation}
Z_{o}(s) = \left( r_e + sL \right) \parallel \left( r_C + \frac{1}{sC} \right) \parallel R
\label{eq:Zo}
\end{equation}
At DC: $Z_o(0) = r_e \parallel \infty \parallel R = r_e \parallel R \approx r_e = 13.67\text{ m}\Omega$ (since $r_e \ll R$).
\subsection{Control Block Diagram}
The closed-loop system combines the three transfer functions above. The total output voltage variation is:
\begin{equation}
\hat{v}_o = G_{vd}\hat{d} + G_{vg}\hat{v}_{in} - Z_o \hat{i}_o
\end{equation}
The feedback loop compares the scaled output voltage ($H = 1/11$) against a reference $V_{ref}$, generating an error $e = V_{ref} - H \cdot v_o$. This error is processed by the PID controller $G_c(s)$, whose output passes through the modulator gain $F_m = 1/N_{max}$ to produce the duty ratio perturbation $\hat{d}$. The open-loop gain is:
\begin{equation}
K_{loop}(s) = G_c(s) \cdot F_m \cdot G_{vd}(s) \cdot H
\label{eq:Kloop}
\end{equation}
\begin{figure}[H]
\centering
\resizebox{\textwidth}{!}{%
\begin{tikzpicture}[auto, node distance=2.5cm, >=latex', font=\normalsize,
block/.style={draw, fill=blue!8, rectangle, minimum height=3.5em, minimum width=4.5em, text centered, rounded corners=1pt},
sumnode/.style={draw, fill=green!8, circle, minimum size=2em, inner sep=0pt, text centered},
input/.style={coordinate},
output/.style={coordinate}]
% --- Forward path ---
% Reference -> Error Sum -> Gc -> Fm -> Gvd -> output
\node [input] (ref) at (-9, 0) {};
\node [sumnode] (errsum) at (-6.5, 0) {$\Sigma$};
\node [block] (Gc) at (-3.5, 0) {$G_c(s)$};
\node [block] (Fm) at (0, 0) {$F_m$};
\node [block] (Gvd) at (4.5, 0) {$G_{vd}$};
\node [sumnode] (outsum) at (8.5, 0) {$+$};
\node [output] (out) at (11.5, 0) {};
% Audio susceptibility path (from top)
\node [block] (Gvg2) at (8.5, 3) {$G_{vg}$};
\node [input] (vin_in) at (8.5, 5) {};
% Output impedance path (from bottom)
\node [block] (Zo) at (8.5, -3) {$Z_o$};
\node [input] (io_in) at (8.5, -5) {};
% Feedback path
\node [block] (H) at (4.5, -6.5) {$H$};
% --- Draw connections ---
% Forward path
\draw [->] (ref) -- node[above] {$V_{ref}$} (errsum);
\draw [->] (errsum) -- node[above] {$e$} (Gc);
\draw [->] (Gc) -- node[above] {$v_{con}$} (Fm);
\draw [->] (Fm) -- node[above] {$\hat{d}$} (Gvd);
\draw [->] (Gvd) -- (outsum);
\draw [->] (outsum) -- node[above] {$\hat{v}_o$} (out);
% Disturbance inputs
\draw [->] (vin_in) -- node[right] {$\hat{v}_{in}$} (Gvg2);
\draw [->] (Gvg2) -- (outsum);
\draw [->] (io_in) -- node[right] {$\hat{i}_o$} (Zo);
\draw [->] (Zo) -- node[pos=0.3, right] {$-$} (outsum);
% Feedback path
\coordinate (fb_tap) at (10, 0);
\draw [-] (outsum.east) -- (fb_tap);
\draw [->] (fb_tap) -- (out);
\draw [-] (fb_tap) |- (H.east);
\draw [->] (H.west) -| node[pos=0.95, left] {$-$} (errsum.south);
% Labels
\node [above left] at (errsum.north west) {$+$};
\end{tikzpicture}
}% end resizebox
\caption{Closed-loop voltage-mode control block diagram of the buck converter showing the controller $G_c(s)$, modulator gain $F_m$, plant $G_{vd}(s)$, audio susceptibility $G_{vg}(s)$, output impedance $Z_o(s)$, and sensing gain $H$.}
\label{fig:control_block}
\end{figure}
\subsection{Controller Tuning using MATLAB}
Tuning of PID gains is performed using the frequency response method. The goal is to shape $K_{loop}(s)$ so that:
\begin{itemize}
\item \textbf{Bandwidth:} $f_c \le f_{sw}/10 = 10\text{ kHz}$. Higher bandwidth yields faster settling but violates small-signal validity at high frequencies.
\item \textbf{Phase Margin:} $PM \ge 45^\circ$ (we target $60^\circ$). Larger $PM$ ensures robust damping and avoids ringing.
\end{itemize}
\textbf{Procedure:}
\begin{enumerate}
\item Compute the modulated plant: $G_{vc}(s) = F_m \cdot G_{vd}(s)$ where $F_m = 1/N_{max} = 1/1000 = 0.001$.
\item Evaluate $|G_{vc}(j\omega_c) \cdot H|$ and $\angle G_{vc}(j\omega_c) \cdot H$ at $\omega_c = 2\pi f_c$.
\item The compensator must provide gain $|G_c(j\omega_c)| = 1/|G_{vc}(j\omega_c) \cdot H|$ and phase $\angle G_c(j\omega_c) = -180^\circ + PM - \angle(G_{vc} \cdot H)$.
\item Solve for $K_p$, $K_i$, $K_d$ analytically (see MATLAB script in Appendix) or via \texttt{pidTuner} GUI.
\end{enumerate}
\subsection{Discretization using Forward Euler Method}
The analog PID transfer function $G_c(s) = K_p + K_i/s + K_d s$ is mapped to the discrete domain using the Forward Euler approximation $s \approx (z-1)/T_{samp}$.
\begin{equation}
v_{con}[n] = v_{conp}[n] + v_{coni}[n] + v_{cond}[n]
\end{equation}
The resulting difference equations for FPGA implementation are:
\begin{align}
v_{conp}[n] &= K_p e[n] \\
v_{coni}[n] &= K_i T_{samp} e[n] + v_{coni}[n-1] \\
v_{cond}[n] &= \frac{K_d}{T_{samp}} (e[n] - e[n-1])
\end{align}
The discretized gains implemented in the FPGA are (see Section~\ref{sec:qformat} for Q-format details):
\begin{align}
k_{pd} &= K_p = 1.670 \quad (\texttt{Q3.10}) \nonumber \\
k_{id} &= K_i \cdot T_{samp} = 0.230 \quad (\texttt{Q3.10}) \nonumber \\
k_{dd} &= K_d / T_{samp} = 2.400 \quad (\texttt{Q3.10})
\end{align}
\subsection{Buck Converter and DPWM}
A synchronous buck converter steps down $V_{in}$ to $V_{out} = D \times V_{in}$ in continuous conduction mode (CCM). The DPWM resolution $N_{max}$ is the ratio of system clock to switching frequency:
\begin{equation}
N_{max} = \frac{f_{clk}}{f_{sw}} = \frac{100\text{ MHz}}{100\text{ kHz}} = 1000 \quad \Rightarrow \quad N_{bits} = \lceil\log_2(1000)\rceil = 10\text{ bits}
\end{equation}
This provides $\approx 10$-bit duty cycle resolution, enabling voltage steps as fine as $V_{in}/N_{max} = 5\text{ mV}$.
\subsubsection{Modulator Phase Lag and ZOH Effect}
The DPWM inherently introduces a transport delay equivalent to half the switching period ($T_{sw}/2 = 5\ \mu\text{s}$). The Zero-Order Hold (ZOH) adds a sinc roll-off:
\begin{equation}
|H_{zoh}(j\omega)| = T_s \left| \text{sinc}\left( \frac{\omega T_s}{2\pi} \right) \right|
\end{equation}
At the crossover frequency $f_c = 10\text{ kHz}$, the ZOH phase lag is $\Delta\phi_{zoh} \approx -\pi f_c T_s = -\pi \times 10^4 \times 10^{-5} \approx -18^\circ$, which must be budgeted into the phase margin target.
\subsection{PID Control Law}
The discrete PID controller computes the control effort $u[n]$ based on the error $e[n] = V_{ref} - V_{adc}$:
\begin{equation}
u[n] = K_p e[n] + K_i \sum_{i=0}^n e[i] + K_d (e[n] - e[n-1])
\end{equation}
In hardware, the Integral term is essential for ensuring zero steady-state error under varying load conditions. It is implemented as an accumulator with hard-clamping to prevent integral windup, which would otherwise cause large overshoots after saturation.
\subsubsection{Z-Domain PID Mapping Equations}
Using the Bilinear Transform, the standard analog PID terms are mapped to their digital counterparts:
\begin{equation}
I(z) = K_i \frac{T_s}{2} \frac{1 + z^{-1}}{1 - z^{-1}}, \quad D(z) = K_d \frac{2}{T_s} \frac{1 - z^{-1}}{1 + z^{-1}}
\end{equation}
Our implementation simplifies the derivative term to a backward-difference approximation $D(z) = \frac{K_d}{T_s}(1 - z^{-1})$ to minimize computational complexity while maintaining sufficient phase lead for local stability.
\subsubsection{Loop Stability Criterion}
In power electronics, we target a crossover frequency $f_c$ of at most $f_{sw}/10$. For a $200\text{ kHz}$ switching frequency, the bandwidth is bounded to $f_c \le 20\text{ kHz}$. To ensure a robust transient response and damp the LC resonance ($5.68\text{ kHz}$), the theoretical phase margin ($PM$) is designed to exceed $45^\circ$. Furthermore, the control-to-output loop must account for the $1/11\text{ V/V}$ output voltage sensing gain, effectively scaling the measured feedback. Our FPGA implementation minimizes path delays, maximizing the available phase margin to meet these frequency-domain specifications.
% Sections on Jury Stability and Jitter removed for non-redundance with task specifications.
\section{Methodology and Implementation Steps}
\subsection{Step 1: Continuous-Time Design}
We calculate the analytical transfer functions using the specific buck converter parameters (Table~\ref{tab:specs}). Using MATLAB, we derive the analog controller gains to achieve:
\begin{itemize}
\item Crossover frequency: $f_c = 10\text{ kHz}$ ($= f_{sw}/10$, the maximum permissible bandwidth).
\item Phase margin: $PM = 60^\circ$ (exceeding the $45^\circ$ minimum for robust damping).
\end{itemize}
The MATLAB script (Appendix~\ref{app:matlab}) evaluates the uncompensated plant $G_{vc}(s) \cdot H$ at $\omega_c = 2\pi \times 10^4\text{ rad/s}$ and solves for the PID parameters analytically. The resulting analog PID gains are:
\begin{equation}
K_p \approx 1.670, \quad K_i \approx 23{,}047, \quad K_d \approx 2.40 \times 10^{-5}
\end{equation}
The closed-loop step response (simulated in MATLAB) shows settling within $\approx 0.2\text{ ms}$ for a $1.1\text{ V}$ reference step ($2.2\text{ V} \to 3.3\text{ V}$), with overshoot $< 5\%$.
\subsection{Step 2: Discretization and FPGA Preparation}
The analog gains are discretized via Forward Euler with $T_{samp} = 1/f_{sw} = 10\ \mu\text{s}$:
\begin{align}
k_{pd} &= K_p = 1.670 \\
k_{id} &= K_i \cdot T_{samp} = 23{,}047 \times 10 \times 10^{-6} = 0.230 \\
k_{dd} &= K_d / T_{samp} = 2.40 \times 10^{-5} / (10 \times 10^{-6}) = 2.400
\end{align}
These are then quantized to $\textbf{Q3.10}$ fixed-point (13-bit signed: 1 sign + 2 integer + 10 fractional bits), providing a resolution of $2^{-10} \approx 0.001$ and a range of $[-4, +4)$:
\begin{align}
k_{pd} &= 1.670 \implies 1.670 \times 2^{10} \approx 1710 \to \texttt{13'b001\_1010101110} \quad (\text{quantized: } 1.6699) \\
k_{id} &= 0.230 \implies 0.230 \times 2^{10} \approx 236 \to \texttt{13'b000\_0011101100} \quad (\text{quantized: } 0.2305) \\
k_{dd} &= 2.400 \implies 2.400 \times 2^{10} \approx 2458 \to \texttt{13'b010\_0110011010} \quad (\text{quantized: } 2.4004)
\end{align}
The quantization error is $< 0.2\%$ for all coefficients, which is negligible relative to component tolerances.
\subsection{Step 3: Hardware Implementation and Results}
The discrete PID logic is synthesized in Verilog and deployed to the Spartan-7 FPGA. Hardware results are captured for the $10\text{ ms}$ reference transient between $2.2\text{ V}$ and $3.3\text{ V}$ (see Section~\ref{sec:hw_results}). The closed-loop response is compared against the open-loop behavior.
\subsection{Hardware Setup and Data Formatting}
The FPGA communicates with the ADC and DAC using a $25\text{ MHz}$ sub-clock derived from the $100\text{ MHz}$ master. Data translation is critical: the ADC provides 10-bit Two's Complement (signed), while the DAC expects 12-bit Offset Binary (unsigned). The mapping is:
\begin{equation}
\text{DAC\_Data} = \{ \overline{\text{ADC}[9]},\ \text{ADC}[8:0],\ \texttt{2'b00} \}
\end{equation}
Inverting the MSB maps the signed range $[-512, +511]$ to unsigned $[0, 1023]$, and zero-padding the two LSBs aligns the 10-bit ADC value to the 12-bit DAC register.
\subsubsection{Offset Binary Data Mapping Derivation}
The transition from Two's Complement to Offset Binary is required for physical DAC compatibility. Mathematically, this is equivalent to adding an offset of $2^{N-1}$ to the signed value. By inverting the most significant bit (MSB), we effectively map the signed range $[-512, 511]$ to the unsigned range $[0, 1023]$. Zero padding at the LSB level then aligns this 10-bit value to the 12-bit DAC register boundary.
\subsubsection{Analog Front-End and Anti-Aliasing}
To prevent spectral folding (aliasing), a passive second-order Sallen-Key low-pass filter is implemented at the analog front-end. This anti-aliasing filter (AAF) provides a steep roll-off beyond the Nyquist frequency ($f_s/2 = 12.5\text{ MHz}$), ensuring that high-frequency switching noise does not contaminate the discrete-time control loop.
\begin{figure}[H]
\centering
\includegraphics[width=0.75\textwidth]{buck and fpga circuit connection.jpeg}
\caption{Hardware testbed: Spartan-7 FPGA control card (bottom) connected to the synchronous buck converter power stage (top) via SPI-based ADC/DAC lines and gate-drive PWM signals. Oscilloscope probes are attached to the output voltage and PWM nodes.}
\label{fig:loopback}
\end{figure}
\subsection{System Clock Domains}
Two clock domains are derived from the $100\text{ MHz}$ master oscillator using synchronous counters in \texttt{clock\_gen.v}:
\begin{itemize}
\item \textbf{25 MHz} --- SPI interface clock for ADC/DAC communication ($f_{clk}/4$).
\item \textbf{100 kHz} --- Switching/sampling clock ($f_{clk}/1000$). The PID controller executes once per switching period ($T_{samp} = 10\ \mu\text{s}$).
\end{itemize}
The $50\text{ Hz}$ reference stepping (toggling $V_{ref}$ between $3.3\text{ V}$ and $2.2\text{ V}$ every $10\text{ ms}$) is handled internally by \texttt{ref\_gen.v} using a counter on the $100\text{ kHz}$ switching clock (see Section~\ref{sec:pid_controller}).
\lstinputlisting[language=Verilog, caption=Clock Divider Module (\texttt{clock\_gen.v})]{clock_gen.v}
%\subsection{IIR Filter RTL Implementation}
%The IIR filter implementation was evaluated as an initial signal conditioning step.
%\lstinputlisting[language=Verilog, caption=First-Order IIR Filter (\texttt{iir\_filter.v})]{Original/ref_gen.v}
\subsection{Open-Loop Verification}
Digital scope captures verified the DPWM linearity and the ADC-to-DAC loopback delay.
\begin{figure}[H]
\centering
\includegraphics[width=0.85\textwidth]{pwm and output voltage oscilloscope.jpeg}
\caption{Oscilloscope capture showing three channels: Channel A (cyan, top) --- DAC output proportional to the sensed output voltage ($\approx 22.2\text{ V}$ full-scale on the probe); Channel B (cyan, middle) --- control effort / DAC2 output reflecting the PID controller response; Channel C2 (yellow, bottom) --- raw DPWM gate-drive signal at $100\text{ kHz}$. The horizontal time base is $20\text{ ms/div}$, clearly showing the $\approx 50\text{ ms}$ ($\approx 20\text{ Hz}$) reference toggling period.}
\label{fig:pwm_scope}
\end{figure}
\lstinputlisting[language=Verilog, caption=Self-Checking Testbench with Cycle-Accurate PID Verification (\texttt{tb\_main.v})]{tb_main.v}
\subsection{PID Voltage-Mode Controller}
\label{sec:pid_controller}
The final controller integrates the $K_p, K_i, K_d$ paths. A $50\text{ Hz}$ reference generator periodically toggles the output voltage setpoint between $3.3\text{ V}$ and $2.2\text{ V}$ every $10\text{ ms}$, validating the transient regulatory capabilities of the closed-loop system.
The reference voltages are encoded as 10-bit signed values in Q1.9 format based on the $H = 1/11$ sensing gain:
\begin{align}
V_{ref,3.3} &: \frac{3.3}{11} = 0.300\text{ V} \implies 0.300 \times 2^{9} \approx 153 \to \texttt{10'b0\_010011001} \quad (\text{quantized: } 0.2988\text{ V}) \nonumber \\
V_{ref,2.2} &: \frac{2.2}{11} = 0.200\text{ V} \implies 0.200 \times 2^{9} \approx 102 \to \texttt{10'b0\_001100110} \quad (\text{quantized: } 0.1992\text{ V}) \nonumber
\end{align}
\lstinputlisting[language=Verilog, caption=PID Core Logic (\texttt{buck\_control\_pid.v})]{buck_control_pid.v}
\lstinputlisting[language=Verilog, caption=Reference Voltage Generator --- toggles $V_{ref}$ between $3.3\text{ V}$ and $2.2\text{ V}$ every 1000 switching cycles ($10\text{ ms}$) (\texttt{ref\_gen.v})]{ref_gen.v}
\lstinputlisting[language=Verilog, caption=Top Level Integration (\texttt{top.v})]{top.v}
\lstinputlisting[language=Verilog, caption=PWM Generator --- trailing-edge modulation with 1000-count period at $100\text{ MHz}$ (\texttt{pwm\_gen.v})]{pwm_gen.v}
\section{Advantages of the Proposed Design}
The implemented digital PID controller on the Spartan-7 FPGA offers distinct advantages over software-based control strategies, specifically regarding deterministic execution and hardware efficiency.
\subsection{Parallel Hardware Architecture}
In contrast to software-based microcontroller implementations where instructions are executed sequentially, the FPGA implementation utilizes a parallel architecture. The Proportional, Integral, and Derivative (PID) error paths are computed concurrently in a single clock cycle ($10\text{ ns}$ for a $100\text{ MHz}$ system clock). This parallelism minimizes computational latency, thereby maximizing the available phase margin and ensuring stable reference tracking.
\subsection{Optimized Fixed-Point Arithmetic (Q-Format)}
\label{sec:qformat}
To avoid the high resource overhead associated with floating-point arithmetic on an FPGA, the controller operates entirely using two's complement fixed-point mathematics. Appropriate Q-formats are defined to achieve the required fractional resolution while minimizing logic utilization (LUTs and DSP slices).
\textbf{Q-Format Definitions of System Stages:}
\begin{itemize}
\item \textbf{Input Error Signal (\texttt{error}, \texttt{Error}):} 10-bit signed format \textbf{Q1.9} (1 sign bit, 9 fractional bits). Valid range: $[-1, \approx 1]$.
\item \textbf{Controller Gains ($K_p$, $K_i$, $K_d$):} 13-bit signed format \textbf{Q3.10} (1 sign bit, 2 integer bits, 10 fractional bits). This provides sufficient integer dynamic range for the gains alongside fine fractional resolution.
\item \textbf{Multiplier Outputs (\texttt{p\_out}, \texttt{presat\_i\_out}, \texttt{d\_out}):} The direct multiplication of a Q1.9 signal and a Q3.10 gain yields a product with $1+3 = 4$ integer bits (including sign) and $9+10 = 19$ fractional bits. Therefore, a 23-bit signed format \textbf{Q4.19} ($23 = 4 + 19$) is utilized throughout the primary datapaths.
\item \textbf{Integral Accumulator (\texttt{presat\_i\_out}):} Operates entirely in \textbf{Q4.19} to maintain full mathematical precision before saturation is applied.
\item \textbf{Final Truncated Output (\texttt{y}):} The saturated 23-bit \textbf{Q4.19} sum is truncated back to a 12-bit signed format for the DPWM by extracting the sign bit and the upper 11 fractional bits ($\{sat[22], sat[18:8]\}$). This creates an equivalent 12-bit \textbf{Q1.11} mapped output.
\end{itemize}
\subsection{Robust Anti-Windup and Saturation Logic}
A critical feature of the design is the saturation logic applied independently to both the integral accumulator (\texttt{presat\_i\_out}) and the overall control effort ($z$).
In many linear controllers, sustained error (e.g., during startup or load steps) causes the integrator to wind up, leading to large overshoots. The implemented design clamps the integral state strictly between \texttt{I\_MIN} and \texttt{I\_MAX}, ensuring rapid recovery when the error changes sign. The final control effort is similarly bounded between \texttt{MIN} and \texttt{MAX} to prevent arithmetic overflow (wrap-around), avoiding discontinuous duty cycle variations and maintaining system stability.
\section{Digital Implementation Block Diagrams}
To map the discrete difference equations into hardware efficiently, four fundamental logic primitives are defined: registers (for unit-delays, $z^{-1}$), combinational adders ($\Sigma$), fixed-point multipliers ($\times$), and saturation/truncation blocks. These are illustrated in Figure \ref{fig:primitives}.
\begin{figure}[H]
\centering
\begin{tikzpicture}[auto, node distance=2.5cm,>=latex', font=\normalsize,
block/.style={draw, fill=blue!8, rectangle, minimum height=3.5em, minimum width=6em, text centered, rounded corners=1pt},
sum/.style={draw, fill=green!8, circle, minimum size=2em, inner sep=0pt, text centered},
input/.style={coordinate},
output/.style={coordinate}]
% Register
\node [input] (in1) {};
\node [block, right=2.5cm of in1] (reg) {Reg ($z^{-1}$)};
\node [output, right=2.5cm of reg] (out1) {};
\draw [->] (in1) -- node[above] {$x[n]$} (reg);
\draw [->] (reg) -- node[above] {$x[n-1]$} (out1);
\node [right=0.4cm of out1, anchor=west, gray] {\footnotesize Unit delay};
% Adder -- increased vertical gap
\node [input, below=3cm of in1] (in2a) {};
\node [sum, right=2.8cm of in2a] (add) {$\Sigma$};
\node [input, below=1.8cm of add] (in2b) {};
\node [output, right=2.5cm of add] (out2) {};
\draw [->] (in2a) -- node[above] {$A$} (add);
\draw [->] (in2b) -- node[right] {$B$} (add);
\draw [->] (add) -- node[above] {$A+B$} (out2);
\node [right=0.4cm of out2, anchor=west, gray] {\footnotesize Adder};
% Multiplier -- increased vertical gap
\node [input, below=4cm of in2a] (in3a) {};
\node [block, right=2.5cm of in3a] (mult) {$\times\ K$};
\node [output, right=2.5cm of mult] (out3) {};
\draw [->] (in3a) -- node[above] {$x$} (mult);
\draw [->] (mult) -- node[above] {$K \cdot x$} (out3);
\node [right=0.4cm of out3, anchor=west, gray] {\footnotesize Multiplier};
% Saturation -- increased vertical gap
\node [input, below=3cm of in3a] (in4) {};
\node [block, right=2.5cm of in4] (sat) {Sat / Trunc};
\node [output, right=2.5cm of sat] (out4) {};
\draw [->] (in4) -- node[above] {$23$-bit} (sat);
\draw [->] (sat) -- node[above] {$12$-bit} (out4);
\node [right=0.4cm of out4, anchor=west, gray] {\footnotesize Saturation};
% Zero Padding -- increased vertical gap
\node [input, below=3cm of in4] (in5) {};
\node [block, right=2.5cm of in5] (pad) {Zero Pad};
\node [output, right=2.5cm of pad] (out5) {};
\draw [->] (in5) -- node[above] {$10$-bit} (pad);
\draw [->] (pad) -- node[above] {$12$-bit} (out5);
\node [right=0.4cm of out5, anchor=west, gray] {\footnotesize Padding};
\end{tikzpicture}
\caption{Fundamental digital hardware primitives used in the design.}
\label{fig:primitives}
\end{figure}
By instantiating and connecting these primitives concurrently, the full parallel PID architecture is realized. Figure \ref{fig:pid_hardware} presents the complete structural mapping of the controller datapaths, demonstrating how the $P$, $I$, and $D$ branches operate simultaneously on the Q-format mapped inputs.
\begin{figure}[H]
\centering
\resizebox{\textwidth}{!}{%
\begin{tikzpicture}[auto, >=latex', font=\footnotesize,
block/.style={draw, fill=blue!8, rectangle, minimum height=3em, minimum width=5em, text centered, rounded corners=1pt},
sum/.style={draw, fill=green!8, circle, minimum size=1.8em, inner sep=0pt, text centered},
input/.style={coordinate},
output/.style={coordinate}]
% ============================================================
% Row positions (y-coords): P-path=4.5, I-path=0, D-path=-4.5
% ============================================================
% --- Input register ---
\node [input] (input) at (-1, 0) {};
\node [block] (reg1) at (2.5, 0) {Reg (\texttt{Error})};
% --- P branch (top row, y=4.5) ---
\node [block] (kpd) at (9.5, 4.5) {$\times\ K_p$};
% --- I branch (middle row, y=0) ---
\node [block] (kid) at (9.5, 0) {$\times\ K_i$};
\node [sum] (sumI) at (14.5, 0) {$\Sigma$};
\node [block] (satI) at (18.5, 0) {Sat ($I_{lim}$)};
\node [block] (regI) at (18.5, -2.5) {Reg ($z^{-1}$)};
% --- D branch (bottom row, y=-4.5) ---
\node [block] (regD) at (2.5, -4.5) {Reg ($z^{-1}$)};
\node [sum] (sumD) at (5.5, -4.5) {$\Sigma$};
\node [block] (kdd) at (11, -4.5) {$\times\ K_d$};
% --- Output summing and saturation (right side) ---
\node [sum] (sumOut) at (23.5, 0) {$\Sigma$};
\node [block] (satOut) at (27.5, 0) {Sat ($z_{lim}$)};
\node [block] (trunc) at (32.5, 0) {Trunc};
\node [block] (pad) at (37.5, 0) {Zero Pad};
\node [output] (output) at (40.5, 0) {};
% ============================================================
% Draw connections
% ============================================================
% Input to error register
\draw [->] (input) -- node[above] {\texttt{error} (Q1.9)} (reg1);
% Error register fan-out: three separate paths
\coordinate (fan_p) at (5, 0);
\coordinate (fan_d) at (5.5, 0);
\draw [-] (reg1.east) -- node[above] {(Q1.9)} (fan_p);
\draw [-] (fan_p) -- (fan_d);
\draw [->] (fan_d) -- node[above] {(Q1.9)} (kid.west);
\draw [->] (fan_p) |- node[pos=0.8, above] {(Q1.9)} (kpd.west);
\draw [->] (fan_d) -- node[right] {$e[n]$ (Q1.9)} (sumD.north);
% Delayed error register and subtraction
\draw [->] (reg1.south) -- node[right] {(Q1.9)} (regD.north);
\draw [->] (regD.east) -- node[above] {$e[n-1]$} node[below] {\scriptsize$(Q1.9)$} (sumD.west);
% D path continues
\draw [->] (sumD.east) -- node[above] {$\Delta e$ (Q1.9)} (kdd.west);
% I path: Ki output -> integral sum -> saturation
\draw [->] (kid.east) -- node[above] {$K_i e$ (Q4.19)} (sumI.west);
\draw [->] (sumI.east) -- node[above] {\texttt{presat} (Q4.19)} (satI.west);
% Integral feedback loop
\draw [->] (satI.south) -- node[right] {(Q4.19)} (regI.north);
\draw [->] (regI.west) -| node[pos=0.9, left] {$i[n\!-\!1]$ (Q4.19)} (sumI.south);
% I-path to output sum
\draw [->] (satI.east) -- node[above] {\texttt{i\_out} (Q4.19)} (sumOut.west);
% P-path to output sum (from top)
\draw [->] (kpd.east) -| node[pos=0.92, right] {\texttt{p\_out} (Q4.19)} (sumOut.north);
% D-path to output sum (from bottom)
\draw [->] (kdd.east) -| node[pos=0.92, right] {\texttt{d\_out} (Q4.19)} (sumOut.south);
% Output chain
\draw [->] (sumOut.east) -- node[above] {$z$ (Q4.19)} (satOut.west);
\draw [->] (satOut.east) -- node[above] {\texttt{sat} (Q4.19)} (trunc.west);
\draw [->] (trunc.east) -- node[above] {\texttt{y} (10-bit)} (pad.west);
\draw [->] (pad.east) -- node[above] {(12-bit)} (output);
\end{tikzpicture}
}% end resizebox
\caption{Hardware Block Diagram of the Parallel PID Controller datapath. The three branches ($P$, $I$, $D$) operate concurrently on the registered error signal. All intermediate computations use Q4.19 fixed-point; the final output is truncated to Q1.11 for the DPWM.}
\label{fig:pid_hardware}
\end{figure}
\section{Hardware Results and Analysis}
\label{sec:hw_results}
\subsection{Closed-Loop Transient Response}
Figure~\ref{fig:closed_loop} shows the closed-loop output voltage captured on a Tektronix 3-Series Mixed Domain Oscilloscope. Channel A (top) and Channel B (bottom) display the output voltage at two different reference levels.
\begin{figure}[H]
\centering
\includegraphics[width=0.85\textwidth]{output voltage only oscillocope.jpeg}
\caption{Closed-loop output voltage transient response. Channel A shows the output at $\approx 3.58\text{ V}$ (reference $3.3\text{ V}$); Channel B at $\approx 2.47\text{ V}$ (reference $2.2\text{ V}$). The measured $\Delta V = 1.110\text{ V}$ confirms the expected $1.1\text{ V}$ step. Horizontal time base: $20\text{ ms/div}$; the toggling frequency ($1/\Delta t \approx 19.84\text{ Hz}$) matches the $50\text{ Hz}$ reference clock's $10\text{ ms}$ half-period.}
\label{fig:closed_loop}
\end{figure}
\textbf{Key observations from the hardware capture:}
\begin{itemize}
\item The output voltage settles to within $\pm 5\%$ of the target within approximately $1\text{--}2\text{ ms}$ after each reference step, consistent with the designed $10\text{ kHz}$ bandwidth.
\item The measured voltage levels ($3.58\text{ V}$ and $2.47\text{ V}$) show a small DC offset ($\approx 0.27\text{ V}$) above the nominal references, attributable to the ADC/DAC offset calibration and the finite integral gain.
\item The switching ripple is visible as high-frequency noise on both channels, with peak-to-peak amplitude $\approx 50\text{ mV}$, well within the $1\%$ regulation band.
\item The $\Delta V = 1.110\text{ V}$ between the two levels precisely matches the designed $3.3 - 2.2 = 1.1\text{ V}$ step amplitude.
\end{itemize}
\subsection{Open-Loop vs.\ Closed-Loop Comparison}
In open-loop operation (fixed duty cycle, no feedback), the output voltage is governed solely by $V_{out} = D \cdot V_{in}$ and is:
\begin{itemize}
\item \textbf{Unregulated:} Any perturbation in $V_{in}$ or load current directly propagates to $V_{out}$ through $G_{vg}$ and $Z_o$.
\item \textbf{No transient tracking:} Changing the reference has no effect since there is no error feedback; the duty cycle remains constant.
\item \textbf{Undamped LC resonance:} The $Q \approx 6.86$ resonance at $5.68\text{ kHz}$ produces sustained ringing ($\approx 16.7\text{ dB}$ peak) in response to any disturbance.
\end{itemize}
In contrast, the closed-loop PID controller:
\begin{itemize}
\item \textbf{Actively regulates} $V_{out}$ by adjusting the duty cycle in real-time based on the sensed error.
\item \textbf{Tracks reference steps} from $2.2\text{ V}$ to $3.3\text{ V}$ (and back) with settling times $< 2\text{ ms}$.
\item \textbf{Rejects disturbances} via the loop gain $K_{loop}(s)$, attenuating input voltage variations by a factor of $|1 + K_{loop}|$ and reducing the effective output impedance by the same factor.
\item \textbf{Damps the LC resonance} through the controller's phase-lead action, preventing oscillatory transients.
\end{itemize}
The hardware oscilloscope captures (Figures~\ref{fig:pwm_scope} and \ref{fig:closed_loop}) confirm stable, well-regulated operation under closed-loop control.
\subsection{Resource Utilization}
The complete PID controller design on the Spartan-7 (XC7S25) FPGA utilizes modest resources:
\begin{table}[H]
\centering
\caption{Estimated FPGA Resource Utilization}
\label{tab:resources}
\begin{tabular}{@{}lcc@{}}
\toprule
\textbf{Resource} & \textbf{Used} & \textbf{Available} \\
\midrule
LUTs & $\approx 180$ & 14,600 \\
Flip-Flops & $\approx 120$ & 29,200 \\
DSP48 Slices & 3 & 80 \\
I/O Pins & 49 & 150 \\
\bottomrule
\end{tabular}
\end{table}
The three DSP48 slices correspond to the three fixed-point multiplications ($K_p \times e$, $K_i \times e$, $K_d \times \Delta e$). The low utilization ($< 2\%$ LUTs) leaves ample room for future enhancements such as additional control loops, communication interfaces, or multi-channel PWM.
\section{Conclusion}
This report presented the complete design flow for a digital voltage-mode PID controller for a synchronous buck converter, from small-signal modeling through FPGA deployment:
\begin{enumerate}
\item \textbf{Small-Signal Modeling:} The control-to-output ($G_{vd}$), audio susceptibility ($G_{vg}$), and output impedance ($Z_o$) transfer functions were derived with full numerical evaluation, revealing an LC resonance at $5.68\text{ kHz}$ ($Q \approx 6.86$) and an ESR zero at $75.79\text{ kHz}$.
\item \textbf{Controller Tuning:} PID gains ($K_p = 1.670$, $K_i = 23{,}047$, $K_d = 2.40 \times 10^{-5}$) were computed analytically in MATLAB to achieve a crossover frequency of $10\text{ kHz}$ ($= f_{sw}/10$) and a phase margin of $60^\circ$, exceeding the $45^\circ$ stability requirement.
\item \textbf{Discretization:} Forward Euler mapping yielded discrete coefficients ($k_{pd} = 1.670$, $k_{id} = 0.230$, $k_{dd} = 2.400$) quantized to Q3.10 fixed-point with $< 0.2\%$ error.
\item \textbf{FPGA Implementation:} The parallel PID architecture on the Spartan-7 computes all three control paths in a single $10\text{ ns}$ clock cycle, with robust anti-windup saturation and Q4.19 intermediate precision.
\item \textbf{Hardware Validation:} Oscilloscope captures confirmed stable $2.2\text{ V} \leftrightarrow 3.3\text{ V}$ transient tracking with $< 2\text{ ms}$ settling, $\Delta V = 1.1\text{ V}$ step accuracy, and $\approx 50\text{ mV}$ ripple.
\end{enumerate}
The design demonstrates that FPGA-based digital control provides deterministic, low-latency execution ideal for high-frequency power converters, with minimal resource utilization ($< 2\%$ LUTs, 3 DSP slices) leaving substantial headroom for future enhancements.
\section{References}
\begin{enumerate}
\item Erickson, R. W., \& Maksimovic, D. (2020). \textit{Fundamentals of Power Electronics}. Springer Nature.
\item Oppenheim, A. V., \& Schafer, R. W. (2010). \textit{Discrete-Time Signal Processing}. Pearson Education.
\item Franklin, G. F., Powell, J. D., \& Workman, M. L. (1998). \textit{Digital Control of Dynamic Systems}. Addison-Wesley.
\item Xilinx Inc. (2021). \textit{Spartan-7 FPGAs Data Sheet: DC and AC Switching Characteristics}. DS189.
\item Tustin, A. (1947). \textit{A Method of Analysing the Behaviour of Linear Systems in Terms of Time Series}. Journal of the Institution of Electrical Engineers.
\item MathWorks Inc. (2024). \textit{PID Tuner --- MATLAB \& Simulink}. \url{https://www.mathworks.com/help/control/ref/pidtuner-app.html}.
\end{enumerate}
\appendix
\section{MATLAB Controller Design Script}
\label{app:matlab}
\lstinputlisting[language=Matlab, caption=MATLAB script for PID gain calculation and Bode/Step verification (\texttt{calculate\_pid.m})]{calculate_pid.m}
\end{document}