T-coil

coupling factor

image-20251023214723225

note CTAP is grounded

\[ L_{12} = \frac{\text{im}[Z_{diff}]}{2\pi f} \]

where \(Z_{diff} = Z_{11} - Z_{12} - Z_{21} + Z_{22}\)

image-20251023214042381

Min-Sun Keel. Design of reliable and energy-efficient high-speed interface circuits. University of Illinois Urbana-Champaign, USA, 2015 [https://files.core.ac.uk/download/pdf/158312105.pdf]

image-20251124205353311

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
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt


df_emx = pd.read_csv('./tcoil-emx.csv')
L1sim = df_emx['L1sim Y'].to_numpy()/1e-12 # pH
L2sim = df_emx['L2sim Y'].to_numpy()/1e-12 # pH
Q1sim = df_emx['Q1sim Y'].to_numpy()
Q2sim = df_emx['Q2sim Y'].to_numpy()
frqsim = df_emx['ksim X'].to_numpy()/1e9 # GHz
Ksim = df_emx['ksim Y'].to_numpy()

df_calc = pd.read_csv('./tcoil-calc.csv')
frqcalc = df_calc.iloc[:,0].to_numpy()/1e9 # GHz
L1calc = df_calc.iloc[:,1].to_numpy()/1e-12 # pH
L2calc = df_calc.iloc[:,3].to_numpy()/1e-12 # pH
Q1calc = df_calc.iloc[:,5].to_numpy()
Q2calc = df_calc.iloc[:,7].to_numpy()
L12calc = df_calc.iloc[:,9].to_numpy()/1e-12 # pH

kcalc = []
for l1i, l2i, l12i in zip(L1calc, L2calc, L12calc):
l1il2i = l1i * l2i
kk = (l12i - l1i -l2i)/2/np.sqrt(l1il2i) if l1il2i > 0.0 else 0.0 # L1 * L2 < 0 is meaningless
kk = kk if np.abs(kk) < 2.0 else 0.0 # constrain |k| < 2
kcalc.append(-kk) # opposite due to kk assuming differential stimulus

plt.figure(figsize=(20,8))
plt.subplot(3, 2, 1)
plt.plot(frqsim, L1sim, frqcalc, L1calc, 'r--', linewidth=3); plt.legend(['L1sim', 'L1calc'], loc='upper right'); plt.grid()
plt.subplot(3, 2, 2)
plt.plot(frqsim, Q1sim, frqcalc, Q1calc, 'r--', linewidth=3); plt.legend(['Q1sim', 'Q1calc'], loc='upper right'); plt.grid()
plt.subplot(3, 2, 3)
plt.plot(frqsim, L2sim, frqcalc, L2calc, 'r--', linewidth=3); plt.legend(['L2sim', 'L2calc'], loc='upper right'); plt.grid()
plt.subplot(3, 2, 4)
plt.plot(frqsim, Q2sim, frqcalc, Q2calc, 'r--', linewidth=3); plt.legend(['Q2sim', 'Q2calc'], loc='upper right'); plt.grid()
plt.subplot(3, 1, 3)
plt.plot(frqsim, Ksim, frqcalc, kcalc, 'r--', linewidth=3); plt.legend(['Ksim', 'kcalc'], loc='upper right'); plt.grid()

plt.show()

image-20251023213942643

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
  (get_k
(lambda (l12 l1122)
(letseq ((xvec (drGetWaveformXVec l12))
(n (drVectorLength xvec))
(l12v (drGetWaveformYVec l12))
(l1122v (drGetWaveformYVec l1122))
(resultv (drCreateVec 'double n)))
(do ((i 0 i+1))
((i >= n))
(letseq ((l12i (drGetElem l12v i))
(l1122i (drGetElem l1122v i))
(kk (if (l1122i > 0.0)
l12i/(sqrt l1122i)
0.0))
(k (if ((abs kk) < 2.0) kk 0.0)))
(drSetElem resultv i k)))
(drCreateWaveform xvec resultv)))))
(EMX_plot_aux bgui wid what 3
'("Inductance" "Q" "k")
'("Henry" "" "")
(lambda (ys)
(letseq ((zs (reduce ys))
(z11 (nth 0 zs))
(z12 (nth 1 zs))
(z22 (nth 2 zs))
(pi 3.14159265358979)
(f (xval z11))
(l11 (imag z11)/(2*pi*f))
(q11 (imag z11)/(real z11))
(l12 (imag z12)/(2*pi*f))
(l22 (imag z22)/(2*pi*f))
(q22 (imag z22)/(real z22))
(k (get_k l12 l11*l22)))
`((,l11 ,l22) (,q11 ,q22) (,k))))
'(("L1" "L2") ("Q1" "Q2") ("k")))))

In recent versions (specifically EMX2024 and later), (k (if ((abs kk) < 2.0) kk 0.0))) was updated to (k (if ((abs kk) < 2.0) -kk 0.0))) to ensure the correct sign for k


[https://wiki.icprophet.com/doku.php?id=wiki#t-coil]

image-20251206101447859

\[ k = \frac{L_{tot}-L_1-L_2}{2M} = \frac{\text{im}[Z_{diff}]/2\pi f - \text{im}[Z_{11}]/2\pi f-\text{im}[Z_{22}]/2\pi f}{2\sqrt{\text{im}[Z_{11}]/2\pi f\times \text{im}[Z_{22}]/2\pi f}} = \frac{- \text{im}[Z_{12}] - \text{im}[Z_{21}]}{2\sqrt{\text{im}[Z_{11}]\times \text{im}[Z_{22}]}} \] if \(\text{im}[Z_{12}] = \text{im}[Z_{21}]\), then \[ \color{red}k =- \frac{\text{im}[Z_{21}]}{\sqrt{\text{im}[Z_{11}]\times \text{im}[Z_{22}]}} \] image-20251206101226005

\(C_B\) from Nport

Chapter 4.5. High Frequency Passive Devices [https://www.cambridge.org/il/files/7713/6698/2369/HFIC_chapter_4_passives.pdf]

image-20251121000517142


Min-Sun Keel. Design of reliable and energy-efficient high-speed interface circuits. University of Illinois Urbana-Champaign, USA, 2015 [https://files.core.ac.uk/download/pdf/158312105.pdf]

Measuring Self Resonant Frequency [https://www.coilcraft.com/getmedia/8ef1bd18-d092-40e8-a3c8-929bec6adfc9/doc363_measuringsrf.pdf?srsltid=AfmBOoqdBJ_CTB-N_wOVp2_7zIDXPEwOYLm7S4RLuws1CEcEWZUijblK]

image-20251121002136639

image-20251121002228554

T-coil vs tapped inductor

tcoil and tapped inductor share same EM simulation result, and use modelgen with different model formula.

The relationship is \[ L_{\text{sim}} = L1_{\text{sim}}+L2_{\text{sim}}+2\times k_{\text{sim}} \times \sqrt{L1_{\text{sim}}\cdot L2_{\text{sim}}} \] where \(L1_{\text{sim}}\), \(L2_{\text{sim}}\) and \(k_{\text{sim}}\) come from tcoil model result, \(L_{\text{sim}}\) comes from tapped inductor model result

\(k_{\text{sim}}\) in EMX have assumption, induce current from P1 and P2 Given Dot Convention:

Same direction : k > 0

Opposite direction : k < 0

So, the \(k_{\text{sim}}\) is negative if routing coil in same direction

image-20260619193536112

image-20220623013923263

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
% EMX - shield tcoil model
L1 = csvread('./L1sim.csv', 1, 0);
L2 = csvread('./L2sim.csv', 1, 0);
k = csvread('./ksim.csv', 1, 0);

% EMX - Tapped shield inductor
L = csvread('./Lsim.csv', 1, 0);

freq = L1(:, 1)/1e9; % GHz
L1 = L1(:, 2);
L2 = L2(:, 2);
k = -k(:, 2); % Caution: minus of EMX ksim due to same current direction

L = L(:, 2);

Lcalc = L1 + L2 + 2*k.*(L1.*L2).^0.5;

plot(freq, L*1e9, 'r', 'LineWidth', 3);
hold on;
plot(freq, Lcalc*1e9, '--b', 'LineWidth', 3);
grid on;
xlabel('Freq (GHz)');
ylabel('Inductance (nH)');
legend('Tapped inductor model', 'tcoil model calc');

Transformer

J. R. Long, "On-chip transformer design and application to RF and mm-wave front-ends," 2017 IEEE Custom Integrated Circuits Conference (CICC), Austin, TX, USA, 2017

A. Bevilacqua, "Tutorial: Fundamentals of Integrated Transformers: from Principles to Applications," 2020 IEEE International Solid-State Circuits Conference - (ISSCC), San Francisco, CA, USA, 2020

—, "Fundamentals of Integrated Transformers: From Principles to Applications," in IEEE Solid-State Circuits Magazine, vol. 12, no. 4, pp. 86-100, Fall 2020

[https://wiki.icprophet.com/doku.php?id=wiki#%E5%8F%98%E5%8E%8B%E5%99%A8]

变压器差分输入、差分输出 image-20251206101920561
变压器单端输入、差分输出 image-20251206102009723
变压器差分输入、单端输出 image-20251206102047381
变压器单端输入、单端输出 image-20251206102150510

image-20251206102240524

the formula is same with T-coil's, except k


Luong, H. C., & Yin, J. (2016). Transformer-based design techniques for oscillators and frequency dividers. Springer International Publishing

image-20260704084701048


image-20251206083550560

image-20251206090819816

PGS (Patterned ground Shields)

Ring Pattern, Star Pattern

image-20251206005014571

Common-Mode Rejection

TODO 📅

image-20251206005603725

Transmission Zero

image-20251206103623404

like shunt-peaking, the impedance of \(C_o/n\), \(L_s\), \(r_s\) have resonant peak at resonant frequency, which block signal transmission to S

Mixed-Mode Z/Y-Parameters

Mixed-Mode Y/Z-Parameters [http://zeptoblog.com/2024/03/09/mixed-mode-yz-parameters.html]

image-20260807214809889

For an AC simulation:

When a differential-voltage stimulus, i.e., \(V_1=-V_2\), is applied, the differential-mode current can be calculated from the simulated branch currents as \(I_{DM}=(I_1-I_2)/2\).

When a differential-current stimulus, i.e., \(I_1=-I_2\), is applied, the differential-mode voltage can be calculated from the simulated node voltages as \(V_{DM}=V_1-V_2\).


For a center-tapped (CTAP) inductor with the center tap AC-grounded, define

\[ R_1=\frac{\omega L_1}{Q_1}, \qquad R_2=\frac{\omega L_2}{Q_2}, \qquad M=k\sqrt{L_1L_2}. \]

For the usual CT winding orientation, when both port currents are defined from the outer terminals toward the grounded center tap, the two halves have opposite dot orientation. Therefore, the 2-port impedance matrix is

\[ \mathbf Z= \begin{bmatrix} R_1+j\omega L_1 & -j\omega M\\ -j\omega M & R_2+j\omega L_2 \end{bmatrix}. \]

The differential impedance is

\[ Z_{\mathrm{diff}} = Z_{11}+Z_{22}-Z_{12}-Z_{21}. \]

Therefore,

\[ \begin{aligned} Z_{\mathrm{diff}} &= R_1+j\omega L_1 + R_2+j\omega L_2 - (-j\omega M) - (-j\omega M) \\ &= R_1+R_2 + j\omega(L_1+L_2+2M). \end{aligned} \]

Using

\[ M=k\sqrt{L_1L_2}, \]

we obtain

\[ \boxed{ Z_{\mathrm{diff}} = R_1+R_2 + j\omega \left( L_1+L_2+2k\sqrt{L_1L_2} \right) }. \]

Hence, the differential inductance is

\[ \boxed{ L_{\mathrm{diff}} = L_1+L_2+2k\sqrt{L_1L_2} }. \]

The differential series resistance is

\[ R_{\mathrm{diff}} = R_1+R_2 = \frac{\omega L_1}{Q_1} + \frac{\omega L_2}{Q_2}. \]

Therefore, the differential quality factor is

\[ Q_{\mathrm{diff}} = \frac{\operatorname{Im}\{Z_{\mathrm{diff}}\}} {\operatorname{Re}\{Z_{\mathrm{diff}}\}}. \]

Thus,

\[ \boxed{ Q_{\mathrm{diff}} = \frac{ L_1+L_2+2k\sqrt{L_1L_2} }{ L_1/Q_1+L_2/Q_2 } }. \]

DE & SE excitation

Min-Sun Keel. Design of reliable and energy-efficient high-speed interface circuits. University of Illinois Urbana-Champaign, USA, 2015 [https://files.core.ac.uk/download/pdf/158312105.pdf]

image-20251120230342210

differential impedance

Y parameters to Z parameters

\[\begin{align} |Y| &= Y_{11}*Y_{22} - Y_{12}*Y_{22} \\ \begin{bmatrix} Z_{11} & Z_{12}\\ Z_{21} & Z_{22} \end{bmatrix} &= \begin{bmatrix} \frac{Y_{22}}{|Y|} & \frac{-Y_{12}}{|Y|}\\ \frac{-Y_{21}}{|Y|} & \frac{Y_{11}}{|Y|} \end{bmatrix} \end{align}\]

Then differential impedance is \[ Z_{diff} = Z_{11} - Z_{12} - Z_{21} + Z_{22} \]

image-20220330234833756

1
2
3
4
5
6
7
(define (EMX_differential y11 y12 y21 y22)
(letseq ((det y11*y22-y12*y21)
(z11 y22/det)
(z12 -y12/det)
(z21 -y21/det)
(z22 y11/det))
z11+z22-z12-z21))

similarly, Z parameters to Y parameters \[ \begin{bmatrix} Y_{11} & Y_{12}\\ Y_{21} & Y_{22} \end{bmatrix} = \begin{bmatrix} \frac{Z_{22}}{|Z|} & \frac{-Z_{12}}{|Z|}\\ \frac{-Z_{21}}{|Z|} & \frac{Z_{11}}{|Z|} \end{bmatrix} \] where \[ |Z| = Z_{11}Z_{22} - Z_{12}Z_{21} \]


Inductor EM simulation: 1-port or 2-port? [https://muehlhaus.com/support/ads-application-notes/inductor-em-ports]

image-20251220084731053

image-20251220083030590

image-20251220084025043

image-20251220085006044

image-20251220084559347

EMX Ports Setup

plain labels

  • pin layer
  • uncheck Cadence pins in Advanced options

rectangle pins

  • drawing layer rectangle pin and specify Access Direction as intended
  • check Cadence pins in Advanced options

The rectangle pins are always selected as driven port while there are only rectangle pin whether Cadence pins checked or not.


check ports used for simulation use GDS view - EMX


EMX Synthesis Kits

Synthesis is a capability of the EMX Pcell library and uses scalable model data pre-generated by Continuum for a specific process and metal scheme combination.

Synthesis is supported by the Pcells that are suffixed _scalable, and these Pcells have the additional fields and buttons needed for synthesis.

port order (signals)

/path/to/EMX/share/emx/virtuoso_ui/emxinterface/emxskill/emxform.ils

type Port order
inductor P1 P2
shield inductor P1 P2 SHIELD
tapped inductor P1 P2 CT
tapped shield inductor P1 P2 CT SHIELD
mom/mim capacitor P1 P2
tcoil P1 P2 TAP
shield tcoil P1 P2 TAP SHIELD
tline P1 P2
differential tline P1 P2 P3 P4

EMX device info

name menu_selection (split with _ ) num_ports modelgen_type generic_model_type plot_fn
Single-ended inductor inductor_no tap_no shield_single-ended 2 inductor inductor EMX_plot_se_ind
Differential inductor inductor_no tap_no shield_differential 2 inductor inductor EMX_plot_diff_ind
Single-ended shield inductor inductor_no tap_with shield_single-ended 3 shield_inductor shield_inductor EMX_plot_se_ind
Differential shield inductor inductor_no tap_with shield_differential 3 shield_inductor shield_inductor EMX_plot_diff_ind
Tapped inductor (diff mode only) inductor_with tap_no shield_differential mode only 3 center_tapped_inductor tapped_inductor EMX_plot_ct_ind
Tapped inductor (common mode too) inductor_with tap_no shield_also fit common mode 3 center_tapped_inductor_common_mode tapped_inductor EMX_plot_ct_ind
Tapped shield inductor (diff only) inductor_with tap_with shield_differential mode only 4 center_tapped_well_inductor_common_mode tapped_shield_inductor EMX_plot_ct_ind
Single-ended cap (symm) capacitor_symmetric single-ended 2 complex_mom_capacitor mom_capacitor EMX_plot_se_cap
Differential cap (symm) capacitor_symmetric differential 2 complex_mom_capacitor mom_capacitor EMX_plot_diff_cap
Single-ended cap (asymm) capacitor_asymmetric single-ended 2 complex_asymmetric_mom_capacitor mom_capacitor EMX_plot_se_cap
Differential cap (asymm) capacitor_asymmetric differential 2 complex_asymmetric_mom_capacitor mom_capacitor EMX_plot_diff_cap
MiM capacitor capacitor_MiM 2 mim_capacitor mim_capacitor EMX_plot_se_cap
Tcoil (simple model) tcoil_simple model 3 tcoil tcoil EMX_plot_tcoil
Tcoil (complex model) tcoil_complex model 3 complex_tcoil complex_tcoil EMX_plot_tcoil
Shield tcoil tcoil_with shield 4 shield_complex_tcoil shield_tcoil EMX_plot_shield_tcoil
Transmission line transmission line_single 2 xline xline EMX_plot_xline
Diff transmission line transmission line_coupled (differential) 4 coupled_xline diff_xline EMX_plot_diff_xline

EMX plot function

EMX's formulation is defined in

/path/to/EMX/share/emx/virtuoso_ui/emxinterface/emxskill/emxform.ils

EMX import this file at Virtuoso startup, you have to relaunch Virtuoso if you change this file

Single-ended inductor

Both with and without shield apply

  • port-1 impedance when port-2 short

\[ Z_1 = \frac{1}{Y_{11}} \]

  • port-2 impedance when port-1 short

\[ Z_2 = \frac{1}{Y_{22}} \] Then \[\begin{align} L1 &= \frac{Im(Z_1)}{2\pi f} \qquad Q1 = \frac{Im(Z_1)}{Re(Z_1)} \\ L2 &= \frac{Im(Z_2)}{2\pi f} \qquad Q2 = \frac{Im(Z_2)}{Re(Z_2)} \end{align}\]

EMX only plot L1 and Q1

Differential inductor

Both with and without shield apply

\[\begin{align} L_{diff} = \frac{Im(Z_{diff})}{2\pi f} \qquad Q_{diff} = \frac{Im(Z_{diff})}{Re(Z_{diff})} \end{align}\]

Center-tapped inductor

\[ Y = \begin{bmatrix} Y_{11} & Y_{12} & Y_{13}\\ Y_{21} & Y_{22} & Y_{23}\\ Y_{31} & Y_{32} & Y_{33} \end{bmatrix} \]

where port order is P1 P2 CT.

1
2
3
4
5
6
7
8
9
10
11
(define (EMX_plot_ct_ind bgui wid what)
(EMX_plot_aux bgui wid what 3
'("Differential inductance" "Differential Q")
'("Henry" "")
(lambda (ys)
(letseq ((z (EMX_differential (nth 0 ys) (nth 1 ys) (nth 3 ys) (nth 4 ys)))
(f (xval z))
(L (imag z)/(2*3.14159265358979*f))
(Q (imag z)/(real z)))
`((,L) (,Q))))
'(("L") ("Q"))))

Assume CT i.e. port 3 in S-parameter is grounded, (z (EMX_differential (nth 0 ys) (nth 1 ys) (nth 3 ys) (nth 4 ys))) obtain differential impedance with \(Y_{11}\), \(Y_{12}\), \(Y_{21}\) and \(Y_{22}\). \[ Y = \begin{bmatrix} Y_{11} & Y_{12}\\ Y_{21} & Y_{22} \end{bmatrix} \] Finally, differential inductance and Q are obtained, shown as below

\[\begin{align} L_{diff} = \frac{Im(Z_{diff})}{2\pi f} \qquad Q_{diff} = \frac{Im(Z_{diff})}{Re(Z_{diff})} \end{align}\]

image-20220331013735370

Single-ended cap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(define (EMX_plot_se_cap bgui wid what)
(EMX_plot_aux bgui wid what 2
'("Capacitance" "Q" "Capacitance" "Q")
'("Farad" "" "Farad" "")
(lambda (ys)
(letseq ((z1 1.0/(nth 0 ys))
(y12 (nth 1 ys))
(z2 1.0/(nth 3 ys))
(f (xval z1))
(C1 (-1.0/(imag z1))/(2*3.14159265358979*f))
(C12 -(imag y12)/(2*3.14159265358979*f))
(C2 (-1.0/(imag z2))/(2*3.14159265358979*f))
(Q1 -(imag z1)/(real z1))
(Q12 (imag y12)/(real y12))
(Q2 -(imag z2)/(real z2)))
`((,C1) (,Q1) (,C12))))
'(("Cse") ("Qse") ("C12"))))

We define Port-1 impedance \(Z_1\), Port-2 impedance \(Z_2\)

\[\begin{align} Z_1 = \frac {1}{Y_{11}} \qquad Z_2 = \frac {1}{Y_{22}} \end{align}\]

Caution above \(\color{red}z_1 \neq Z_{11}\), but \(z_1=\frac{1}{Y_{11}}\)

Then single-ended cap and Q \[\begin{align} C_1 &= -\frac{1/Im(Z_1)}{2\pi f} \qquad Q_1 = -\frac{Im(Z_1)}{Re(Z_1)} \\ C_2 &= -\frac{1/Im(Z_2)}{2\pi f} \qquad Q_2 = -\frac{Im(Z_2)}{Re(Z_2)} \\ C_{12} &= -\frac{Im(Y_{12})}{2\pi f} \qquad Q_{12} = \frac{Im(Y_{12})}{Re(Y_{12})} \end{align}\]

  • Series equivalent model is used in \(C_1\), \(Q_1\), \(C_2\) and \(Q_2\)
    • \(Z_1 = R + \frac{1}{sC_1}\) and \(Z_2 = R + \frac{1}{sC_2}\)
  • Parallel model is used in \(C_{12}\) and \(Q_{12}\)
    • \(Y_{12} = \frac{1}{R} + sC_{12}\)

EMX plot \(C_{se}\), \(Q_{se}\) and \(C_{12}\), i.e. \(C_1\), \(Q_1\) and \(C_{12}\)

image-20220331020334023

Differential cap

1
2
3
4
5
6
7
8
9
10
11
(define (EMX_plot_diff_cap bgui wid what)
(EMX_plot_aux bgui wid what 2
'("Differential capacitance" "Differential Q")
'("Farad" "")
(lambda (ys)
(letseq ((z (apply EMX_differential ys))
(f (xval z))
(C (-1.0/(imag z))/(2*3.14159265358979*f))
(Q -(imag z)/(real z)))
`((,C) (,Q))))
'(("C") ("Q"))))

First obtain differential impedance, \(Z_{diff}\) then apply series equivalent model \[\begin{align} C_{diff} = -\frac{1/Im(Z_{diff})}{2\pi f} \qquad Q_{diff} = -\frac{Im(Z_{diff})}{Re(Z_{diff})} \end{align}\]

image-20220331022224865

Tline

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(define (EMX_plot_xline bgui wid what)
(EMX_plot_aux bgui wid what 2
'("Z0" "gamma*length")
'("Ohm" "")
(lambda (ys)
(letseq ((y11 (nth 0 ys))
(y12 (nth 1 ys))
(y21 (nth 2 ys))
(y22 (nth 3 ys))
(det y11*y22-y12*y21)
(z11 y22/det)
(o z11) ; Open circuit impedance
(s 1.0/y11) ; Short circuit impedance
(Z0 (sqrt o*s))
(gammal (atanh s/Z0)))
`(((,(real Z0) ,(imag Z0)) (,(real gammal) ,(imag gammal)))))
'(("Re" "Im") ("Re (alpha*l)" "Im (beta*l)"))))

image-20260530101942861

Open circuit impedance \(Z_o\), short circuit impedance \(Z_s\) and characteristic impedance \(Z_0\)

\[ Z_o = Z_{11} \qquad Z_s = \frac{1}{Y_{11}} \qquad \boxed{Z_0 = \sqrt{Z_o*Z_s}} \qquad \boxed{\gamma l = \operatorname{atanh} {\frac{Z_s}{Z_0}}} \]

propagation constant is given as \[ \gamma \cdot l = \frac{1}{2}\log\left( \frac{Z_0+Z_s}{Z_0-Z_s} \right) \]

\(Z_s\) depends on the length of tline

The relationship between these parameter and geometry of the transmission line \[ Z_0 = \sqrt{\frac{R+j\omega L}{G+j\omega C}} \qquad \gamma = \sqrt{(G+j\omega C)(R+j\omega L)} \] EMX plot the real and imaginary part of \(Z_0\), \(\alpha\) and \(\beta\) of \(\gamma\)

Note EMX plot \(\operatorname{Re}\{Z_0\}\) ,\(\operatorname{Im}\{Z_0\}\), \(\alpha\cdot l\) and \(\beta\cdot l\)

image-20260530102835660

image-20220630215418372

image-20220630215630849

EMX autoplot

using AC simulation, and inductor's parallel model or series model

That is to say: both sp (network parameter) and ac (impedance) can be used to plot inductance, Q value.

usually EMX choose ac method

image-20220501173856442

image-20220501173930035

left 2 figures are used for AC simulation, \(Y_{nn}\) can be obtained conveniently

Model Parameter Extraction

Chapter 4.5. High Frequency Passive Devices [https://www.cambridge.org/il/files/7713/6698/2369/HFIC_chapter_4_passives.pdf]

image-20221217141519947

for single-end capicator \[\begin{align} Q_1 &= -\frac{Im(Z_1)}{Re(Z_1)} = -\frac{Im(1/Y_{11})}{Re(1/Y_{11})} = -\frac{Im(Y_{11}^*)/|Y_{11}|^2}{Re(Y_{11}^*)/|Y_{11}|^2} = \frac{Im(Y_{11})}{Re(Y_{11})} \end{align}\]

So, the EMX model and foundary model is consistent.


O. Hanay, J. Hulsman and R. Negra, "Three-Port S-Parameter based characterization of integrated bridged-T-Coils," 2019 12th German Microwave Conference (GeMiC), Stuttgart, Germany, 2019, pp. 268-271 [https://sci-hub.se/10.23919/GEMIC.2019.8698123]

image-20251121002530468

pad & bump

EMX process file contain M0 up to RDL-AP

PM, CB2_FC, UBM is in the chip package

image-20250613235639258

PEX extract up to RDL-AP as expected

DC model

quick find routing resistance

image-20250705231456546

image-20250705231907284

GPDK045 metal resistor model is not consistent with its process file on sheet resistance

image-20250705233530802

Ports & Ground Reference

The ground pin confusion in EM transmission line models [https://muehlhaus.com/support/ads-application-notes/em_line_ground]

Momentum port: global ground or differential? [https://muehlhaus.com/support/ads-application-notes/momentum-port-global-ground-or-differential]

Effect of ground cutout size on RFIC inductor performance [https://muehlhaus.com/support/rfic-em-appnotes/inductor-ground-cutout]

img

image-20251220090704827

dummy metal fill

60 GHz on-chip balun transformer: effect of dummy metal fill [https://muehlhaus.com/support/rfic-em-appnotes/60-ghz-balun-filler-effect]

TODO 📅

Edge/Area pins in Momentum

Edge/Area pins in Momentum EM simulation [https://muehlhaus.com/support/ads-application-notes/edge-area-pins]

For accurate results from EM, the current in the model needs to flow in the physically correct way, similar to the hardware. With edge/area pins, we can help Momentum to create the physically correct current flow in complex port configurations.

Edge port with user defined location and size

img

Area pins with user defined location and size

If we manually control the area pin size, Momentum will equally distribute the injected current across that area

img

Port Referencing in S-Parameters

Peter J. Pupalaikis, DesignCon 2026: Port referencing in S-parameters - Critical Insights You Need to Know

TODO 📅

same Pin names in EMX

It remains shrouded in myth

image-20251220123154767

EMX Setup Tricks

Process file*

Process file encryption mostly for advanced nodes, like TSMC 16nm Finfet, whose process file is encrypted.

  • Use --key=EMXkey in the EMX Advanced options

GDSviewer has two options

  • EMX: shows the final gds sent to EMX for simulation after it has been processed by EMX
  • Raw: shows the raw gds

If there are port name with the # sign, it means EMX sees a port but it is not in the signal list.

EMX Accuracy

  • Edge mesh: controls layout discretization in the X-Y plane

    • For MoM capacitors, use the edge mesh to be the same as the width of the finger (for example, 0.1um).
  • Thickness: controls layout discretization in the Z dimension

  • 3D metals: skips all 2D assumptions about conductors and their currents and charges

    • If you set 3D metals to * then all metals are treated as 3D
      • For Inductor type structures, only thick metal needs 3D.
      • For MoM, all layers are needed.

Ports entered in Grounds will cause these nets to be grounded; these ports will not show up in the S-parameter result.

Setup Temperature

  • EMX: --temperature=100

ParaView

  • If check ParaView related options when ParaView is not setup properly, EMX simulation stop at Creating mesh... without waring or errors (version 6.2).

Paraview & stimulus

image-20251023000524293

LVS check

LVS issue for circuits with customized devices

  • auCdl: Analog and Microwave CDL, is a netlister used for creating CDL netlist for analog circuits
  • auLVS: Analog and Microwave LVS, is used for analog circuit LVS

Dr. Muehlhaus

Dr. Muehlhaus Consulting & Software GmbH, RFIC-Inductor-Toolkit-Open [https://github.com/VolkerMuehlhaus/RFIC-Inductor-Toolkit-Open]

—, lumpedmodel [https://github.com/VolkerMuehlhaus/lumpedmodel]

—, plot_inductor [https://github.com/VolkerMuehlhaus/plot_inductor]

—, plot_snp [https://github.com/VolkerMuehlhaus/plot_snp]

TODO 📅

gsim

Electromagnetic simulation for photonics and electronics, powered by GDSFactory+ [https://github.com/gdsfactory/gsim]

—, [https://gdsfactory.github.io/gsim/nbs/palace_inductor/]

Extract Differential Impedance

compute the differential impedance \(Z_\text{diff} = Z_{11} - Z_{12} - Z_{21} + Z_{22}\), which is the impedance seen between the two ports of the inductor under differential excitation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np
import skrf as rf

f = results.freq * 1e9 # GHz -> Hz
w = 2 * np.pi * f

ports = results.port_names
n = len(ports)

S = np.zeros((len(f), n, n), dtype=complex)
for i, pi in enumerate(ports):
for j, pj in enumerate(ports):
S[:, i, j] = results[(pi, pj)].complex

ntwk = rf.Network(f=f, s=S, f_unit="hz")
Z = ntwk.z

Z_sim = Z[:, 0, 0] - Z[:, 0, 1] - Z[:, 1, 0] + Z[:, 1, 1]
f_sim = f

Analytical RLC Model Fit of Inductor

RLC circuit

The total impedance and total admittance:

\[ Z(f) = \frac{1}{j2\pi f C + \frac{1}{R + j2\pi f L}} \]

Defining \(\tilde\omega = \omega/\omega_0\) with undamped resonance \(\omega_0 = 1/\sqrt{LC}\) , \(L = QR/\omega_0\)

\[ z(\tilde\omega, Q) = \frac{1 + j\tilde\omega Q}{1 - \tilde\omega^2 + j\tilde\omega/Q} \qquad Z(f) = R \cdot z(f/f_0, Q) \]

The loss function measures the total squared error between the model and the simulated data across all frequencies: \[ \mathcal{L}(f_0, Q, R) = \sum_k \left| Z_\text{sim}(f_k) - R \cdot z(f_k/f_0, Q) \right|^2 \]

1
2
3
4
5
6
7
8
9
10
11
def z_rlc(w, Q):
return (1 + 1j * w * Q) / (1 - w**2 + 1j * w / Q)

f_jnp = jnp.array(f_sim, dtype=jnp.float64)
Z_target = jnp.array(Z_sim, dtype=jnp.complex128)

@jax.jit
def loss_fn(param):
z_fit = param[2] * z_rlc(f_jnp / param[0], param[1])
z_err = Z_target - z_fit
return jnp.real(jnp.sum(z_err * jnp.conj(z_err)))

initial guesses

1
2
3
4
5
6
absZ = np.abs(Z_sim)
f0_ini = float(f_sim[np.argmax(absZ)]) # peak of |Z| -> resonance
R_ini = float(Z_sim.real[0]) # low-frequency resistance
mask = absZ > np.max(absZ) / np.sqrt(2) # -3 dB bandwidth
Q_ini = f0_ini / np.ptp(f_sim[mask]) if mask.sum() > 1 else 5.0
par_ini = jnp.array([f0_ini, Q_ini, R_ini])

f0_ini / np.ptp(f_sim[mask]) i.e. \(\mathbf{BW}= \omega_0/Q\)

Optimization

1
2
3
4
5
6
7
8
9
10
11
par = par_ini
for step in range(1000):
loss, grads = vg_fn(par)
if step % 200 == 0:
print(
f"step {step:4d}: f0={float(par[0]) / 1e9:.4f} GHz Q={float(par[1]):.3f} R={float(par[2]):.4f} loss={float(loss):.3e}"
)
updates, opt_state = optimizer.update(grads, opt_state)
par = optax.apply_updates(par, updates)

f0_fit, Q_fit, R_fit = float(par[0]), float(par[1]), float(par[2])

Recover L & C \[ L = \frac{Q \cdot R}{\omega_0}, \quad C = \frac{1}{L\omega_0^2} \]

1
2
3
4
w0 = 2 * np.pi * f0_fit
tau = Q_fit / w0
L_fit = tau * R_fit
C_fit = 1 / (L_fit * w0**2)

image-20260619203919002

image-20260619205641832


suppose R = 4.0 Ω, L = 112pH, and C = 7.0 fF

image-20260619204453137

image-20260619205739324

reference

Tips on Specifying Ports in EMX

Using 'Cadence pins' as ports with access direction in EMX simulations

EMX miscellaneous features [https://picture.iczhiku.com/resource/eetop/WyIFKleSLTRIuvCb.pdf]

Cadence Rapid Adoption Kit (RAK). Analysis of a Figure-Eight Inductor with EMX

image-20250817101208717


image-20250606203026237

discrete time jitter impulse response (normalized to the input jitter stimulus similar to the procedure used to represent a conventional system impulse response)

image-20250606203531304

When impulsive jitter is injected into clock distribution circuits (i.e., a small incremental time delay or advance applied to an individual clock edge), it results in jitter in multiple subsequent edges in the output clock

image-20250606215100752

image-20250606215204625

transient noise and rms_jitter function

image-20220608233610066

image-20220313230930333

RJ(rms): single Edge or Both Edge?

RJ(seed): what is it?

phase noise method

Directly compare the input phase noise and output phase noise, the input waveform maybe is the PLL output or other clock distribution end point

Jitter Impulse Response & Jitter Transfer Function

assuming linear, time-invariant phase response

image-20250522223530464

n =5 buffers, fclk = 10GHz

[Alphawave’s CTO, Tony Chan Carusone, High Speed Communications Part 8 – On Die CMOS Clock Distribution]

image-20250818230528344

Theoretically, the DC gain of JTF of low pass filter shall be 1. Unfortunately, the gain less than 1 due to numerical error or nonlinearity

image-20220313231027512

JTF by impulse response

Low Pass Filter

image-20220322124344158

1
2
3
4
5
6
7
8
9
10
11
N = 32;
x = zeros(N,1);
x(1) = 6;
x(2) = -2;
x(3) = 0.5;
x = x/5;
figure(1)
stem(x)
Y = fft(x, N);
figure(2)
plot(abs(Y(1:N/2)));

image-20220322124902394

image-20220322124932584

discrete time jitter impulse response

both input and output are discrete time signal, i.e. no sampling in the input, that's why ratio \(1/T_s\) is not in the jtf

High Pass Filter

image-20220327010223664

1
2
3
4
5
6
7
8
9
10
11
12
N = 128;
j_hp = zeros(N, 1);
j_hp(1)= 1;
j_hp(2) = 0.5;
j_hp(3) = -0.3;
j_hp(4) = 0.3;
j_hp(5) = -0.1;
jtf_hp = abs(fft(j_hp));
semilogx(jtf_hp(1:N/2+1));
xlabel('Freq');
ylabel('Jitter Amplification Factor');
grid on;

image-20220327010421475

inverter chain

image-20220608232251056

image-20220608232658054

image-20220608232438188

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ji = 1e-12; % 1ps
data = importdata('/path/to/jir.csv');
jo = data.data(:, 2);
Ts = 31.25e-12;
Fs = 1/Ts;
jir = jo/ji;
N = 2^(nextpow2(length(jir)-1));
Y = fft(jir, N);
jtf = abs(Y(1:N/2+1));
freqs= Fs/N*(0:N/2);
plot(feqs/1e9, jtf, 'linewidth', 2);
grid on;
xlabel('Freq (GHz)');
ylabel('JTF');
title('JTF of inverter chain');

image-20220608233303576

JTF by step response

t0 t1 t2 t3 t4 t5 t6 t7
h0 h1 h2 h3 h4 h5 h6 h7
h0 h1 h2 h3 h4 h5 h6
h0 h1 h2 h3 h4 h5
h0 h1 h2 h3 h4
h0 h1 h2 h3
h0 h1 h2
h0 h1
h0
h0 h0+h1 h0+...h2 h0+...h3 h0+...h4 h0+...h5 h0+...h6 h0+...h7

For a unit-step input, the cumulative sum h0+...+hn converges to 1, ensuring a unity DC gain — \(X(e^{j0})=\sum_{n=-\infty}^{+\infty}x[n]e^{-j0 n}=\sum_{n=-\infty}^{+\infty}x[n]\)

\[ \boxed{h_\text{impulse}[n] = h_\text{step}[n+1] - h_\text{step}[n]} \]

Colored Jitter Amplification

image-20250522225506217

[Alphawave’s CTO, Tony Chan Carusone, High Speed Communications Part 8 – On Die CMOS Clock Distribution]

image-20250522234522208

Four major noise sources are included in the modeling: Input noise, DAC quantization noise (DAC QN), DCO random noise (DCO RN), and delay line random noise (DL RN).

H. Kang et al., "A 42.7Gb/s Optical Receiver with Digital CDR in 28nm CMOS," 2023 IEEE Radio Frequency Integrated Circuits Symposium (RFIC), San Diego, CA, USA, 2023 [https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=10630516]

Dirac impulse at edge position

image-20250606205753885

Full-rate JTF

Singe edge is using

Half-rate

image-20250606220410050

image-20250607113251021

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ji = 10;
jir = readmatrix("~/cargo/jir.csv");
jir = jir / ji;
jir_2e = jir(:, 2); % both edge
jir_1e = jir(1:2:end, 2); % single edge

[mag, w] = freqz(jir_2e, 1, [], 1);
plot(w, abs(mag), LineWidth=2);
hold on
[mag, w] = freqz(jir_1e, 1, [], 0.5);
plot(w, abs(mag), LineWidth=2);
grid on;
legend("2Edge", "1Edge")
title("JTF")

image-20250607113553089

image-20250607114048892

fck = 1GHz

Note: Phase Noise dBc is SSB, that's why we add 10log10(2)

image-20250607114018289

The calculated JTF from JIR is too small compared with Pnoise simualtion

245.1/272.4 = 89.98%


image-20250607123008345

image-20250607123126301

245.1/272.4 = 83.74%

image-20250607123235447

Reference

Sam Palermo, ECEN 720, Lecture 13 - Forwarded Clock Deskew Circuits

B. Casper and F. O'Mahony, "Clocking Analysis, Implementation and Measurement Techniques for High-Speed Data Links-A Tutorial," in IEEE Transactions on Circuits and Systems I. [https://people.engr.tamu.edu/spalermo/ecen689/clocking_analysis_hs_links_casper_tcas1_2009.pdf]

Rhee, W. (2020). Phase-locked frequency generation and clocking : architectures and circuits for modern wireless and wireline systems. The Institution of Engineering and Technology

Mathuranathan Viswanathan, Digital Modulations using Matlab : Build Simulation Models from Scratch

Tony Chan Carusone, University of Toronto, Canada, 2022 CICC Educational Sessions "Architectural Considerations in 100+ Gbps Wireline Transceivers"

X. Mo, J. Wu, N. Wary and T. Chan Carusone, "Design Methodologies for Low-Jitter CMOS Clock Distribution," in IEEE Open Journal of the Solid-State Circuits Society, 2021 [https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=9559395]

Y. Zhao and B. Razavi, "Phase Noise Integration Limits for Jitter Calculation," 2022 IEEE International Symposium on Circuits and Systems (ISCAS), Austin, TX, USA, 2022 [https://www.seas.ucla.edu/brweb/papers/Conferences/YZ_ISCAS_22.pdf]

Thomas Toifl. TWEPP 2012. Low-power High-Speed CMOS I/Os: Design Challenges and Solutions [https://indico.cern.ch/event/170595/contributions/266344/attachments/212179/297391/twepp_sept2012_final_v2.pdf]

Ganesh Balamurugan and Naresh Shanbhag, "Modeling and mitigation of jitter in multiGbps source-synchronous I/O links," Proceedings 21st International Conference on Computer Design, San Jose, CA, USA, 2003, pp. 254-260, doi: 10.1109/ICCD.2003 [https://shanbhag.ece.illinois.edu/publications/ganesh-ICCD2203.pdf]

Balamurugan, G. & Casper, Bryan & Jaussi, James & Mansuri, Mozhgan & O'Mahony, Frank & Kennedy, Joseph. (2009). Modeling and Analysis of High-Speed I/O Links. Advanced Packaging, IEEE Transactions on. [https://sci-hub.se/10.1109/TADVP.2008.2011366]

Jihwan Kim, ISSCC2019 F5: Design Techniques for a 112Gbs PAM-4 Transmitter

Process Variation

image-20220302000743092

[https://ewh.ieee.org/r5/denver/sscs/Presentations/2004_12_Loke.pdf]

image-20251213113351611

Global/Local Variation

image-20220302000808679

Timing and RC Modeling with Process Corners

image-20220302000916728

Global and Local variation by Gaussian

image-20230111003336823

Local Monte-Carlo (SSG, FFG with Local Gaussian) as Signoff golden

image-20231215234014594

Process Corner Model Limitations

image-20220323232119661

image-20230111003705417

Variation section

  • Total corner (TT/SS/FF/SF/FS)
    • E.g. TTMacro_MOS_MOS_MOSCAP
  • Global Corner (TTG/SSG/FFG/SFG/FSG) + Local MC
    • E.g. TTGlobalCorner_LocalMC_MOS_MOSCAP
  • Local MC
    • E.g. LocalMCOnly_MOS_MOSCAP
  • Global MC + Local MC (Total MC)
    • GlobalMC_LocalMC_MOS_MOSCAP

image-20230308003005235

image-20230111010426775

img

image-20230111011757049

SSGNP, FFGNP:

When N/P global correlation is weak (R^2=0.15), the corner of N/PMOS balance circuit (e.g. inverter) can be tightened (3sigma -> 2.5sgma) due to the cancellation between NMOS and PMOS

SSGNP, FFGNP usually used in Digital STA

  • Global variation validation with global corner
    • 3-sigma of global MC simulation is aligned with global corner
  • Total variation validation with total corner
    • 3-sigma of global MC + local MC (total) simulation is aligned with total corner

Global Corner

https://community.cadence.com/cadence_technology_forums/f/custom-ic-design/20466/monte-carlo-simulation-global-local-vs-local-and-process-vs-mismatch/1365101#1365101

The "total corner" is representative of the maximum device parameter variation including local device variation effects. However, it is not a statistical corner.

The "global" corner is defined as the "total" corner minus the impact of "local variation"

Hence, if you were to examine simulation results for a parameter using a "total" and "global" corner, you would find the range of variation will be less with the "global" corner than with the "total" corner.

The "global" corner is provided for use in statistical simulations. Hence, when performing a Monte-Carlo simulation, the "global" corner is selected - NOT the "total" corner.

image-20230511234313012

\[ \Delta V_{T,\sigma_{total}} = \sqrt{\Delta^2 _{T, \sigma_{global}}+\Delta^2 _{T, \sigma_{local}}} \]

RC corner

Traditional RC Corners

Metal width variation (\(\Delta W\)), Metal thickness variation (\(\Delta T\)), IMD thickness variation (\(\Delta H\))

image-20230511232521956

Capacitance Dominant: C-best, C-worst

Resistance Dominant: RC-best, RC-worst

DPT effect

When using two masks per layer (Double Patterning Technology, DPT) there is an issue of mask alignment where any mis-alignment will cause layer spacing values to change, therefore changing the parasitic coupling capacitance values.

Misalignment scale and direction are not deterministic facts: coupling cap and total cap may be increased or decreased.

Five new corners are added in a DPT flow to account for RC variations accurately:

sapced-dependent side-wall dielectric constant also affect coupling cap

and CC_worst means to increase both K1 and K2

CC_best means to decrease both K1 and K2

  • Setup time sign-off would use:

    Cworst_CCworst / RCworst_CCworst

  • Hold time sign-off would use:

    Cbest_CCbest / RCbest_CCbest / Cworst_CCworst / RCworst_CCworst

image-20230416143108231

Signoff corner

with misalignment effect without misalignment effect
cworst_CCworst, cworst_CCworst_T cworst, cworst_T
cbest_CCbest, cbest_CCbest_T cbest, cbest_T
rcworst_CCworst, rcworst_CCworst_T rcworst, rcworst_T
rcbest_CCbest, rcbest_CCbest_T rcbest, rcbest_T

BEOL Target: typical

The recommended RC corner:

cworst_CCworst, cbest_CCbest, rcworst_CCworst, rcbest_CCbest and typical

The others are for pre-color RC calculation purpose

_T stands for "Tighten DPT corner"; these are less pessimistic 1.5 sigma corners

image-20230513001426737

tmp_fohu0q4

Below table is caputre of Aragio's TSMC16: LVDS datasheet

image-20230416130433181

BEOL corner

image-20230416144136870

Spacing variation is implicitly defined by \(\Delta W_m\).

We denote the conductor width and thickness of the layer m by \(W_m\) and \(T_m\), respectively.

Similarly, we denote the thickness of the layer's interlayer dielectric (i.e., the distance between layer m and layer m +1) by \(H_m\)

  • C-based means worst and best caps
  • RC-based means worst and best R in adjustment with C (RC product)

Based on experience, it was found that C-based extraction provides worst and best case over RC for internal timing paths because Capacitance dominates short wire.

However, for large design, inter-block timing paths were often worst with RC worst parasitic since R dominates for long wires.

image-20230416155654008

signoff corners for setup & hold

tmpwlgwrlq0

reference

Process Variation

Eric J.-W. Fang, T5: Fundamentals of Process Monitors for Signoff-Oriented Circuit Design, 2022 IEEE International Solid-State Circuits Conference

Alvin Loke, Device and Physical Design Considerations for Circuits in FinFET Technology, ISSCC 2020 Short Course

簡報 Cln16ffcll Sr V1d0 2p1 Usage Guide URL: https://usermanual.wiki/Document/cln16ffcllsrv1d02p1usageguide.1649731847/view

Radojcic, Riko, Dan Perry and Mark Nakamoto. “Design for manufacturability for fabless manufactuers.” IEEE Solid-State Circuits Magazine 1 (2009): n. pag.

How To Reduce Implementation Headaches In FinFET Processes URL: https://semiengineering.com/how-to-reduce-implementation-headaches-in-finfet-processes/

陌上风骑驴看IC, STA | SSGNP, FFGNP. https://mp.weixin.qq.com/s/eJ8fYRJBR1E9XbfH95OUOg

陌上风骑驴看IC, STA | ssg 跟ss corner 的区别——谬误更正版 https://mp.weixin.qq.com/s?__biz=MzUzODczODg2NQ==&mid=2247486225&idx=1&sn=e9c68f6108ae6c9958d47ca0b29373ca&chksm=fad262cfcda5ebd949cc91353c7cbfaf4ba61179306f7d8e98461a4f4ca9d8a9baef5e9f2cc1&scene=178&cur_album_id=1326356275000705025#rd

The Evolution, Pitfalls, and Cargo Cult Engineering of Advanced Digital Timing Sign-off https://www.tauworkshop.com/2021/speaker_slides/christian_l.pdf

Don O'Riordan Cadence Design Systems. Recommended Spectre Monte Carlo modeling methodology [https://designers-guide.org/modeling/montecarlo.pdf]


RC corner

Modeling Sub-90nm On-Chip Variation Using Monte Carlo Method for DFM https://www.aspdac.com/aspdac2007/pdf/archive/2D-1.pdf

Double Patterning for IC Design, Extraction and Signoff https://semiwiki.com/eda/synopsys/1974-double-patterning-for-ic-design-extraction-and-signoff/

抽刀断水水更流,RC Corner不再愁:STA之RC Corner URL: http://mp.weixin.qq.com/s?__biz=MzUzODczODg2NQ==&mid=2247484115&idx=1&sn=de99f27aadf58ea316c284dad9000b7c&chksm=fad26b0dcda5e21b8c9750f738b55053f695843a66c3c202ff0ba586c738f45aa270254c3722&scene=21#wechat_redirect

一曲新词酒一杯,RC Corner继续飞: STA之RC Corner拾遗 URL:https://mp.weixin.qq.com/s?__biz=MzUzODczODg2NQ==&mid=2247484135&idx=2&sn=bddc632850bd10c32b5688fd7af46218&chksm=fad26b39cda5e22f1c3970f8c8c2e1287c9492c526c4caf02b61f61faffdf829381c392d6ea1&scene=21#wechat_redirect

且将新火试新茶,深究趁年华:STA之RC Corner再论 URL:https://mp.weixin.qq.com/s?__biz=MzUzODczODg2NQ==&mid=2247484144&idx=1&sn=059843381e77cd4008d25166db388d02&chksm=fad26b2ecda5e23816b33b3a949f34d4ca09118bad76f1089dfcb228b7da6491f423a5f4e703&cur_album_id=1326356275000705025&scene=189#wechat_redirect

LDP_IN_800_25V_DN: 1.2GHz LVDS Receiver http://aragiosolutions.com/pdf/rgo_tsmc16_18v25_lvds_product_brief_rev_1a.pdf

Parasitic extraction technologies Advanced node and 3D-IC design https://static.sw.cdn.siemens.com/siemens-disw-assets/public/81845/en-US/Siemens-SW-Parasitic-extraction-technologies-for-advanced-WP-81845-C2.pdf

New Game, New Goal Posts: A Recent History of Timing Closure https://pdfs.semanticscholar.org/9360/5ce48f9bd3b7527ae8979f41a9c7e310efa4.pdf

The Evolution, Pitfalls, and Cargo Cult Engineering of Advanced Digital Timing Sign-off https://www.tauworkshop.com/2021/speaker_slides/christian_l.pdf

T. -B. Chan, S. Dobre and A. B. Kahng, "Improved signoff methodology with tightened BEOL corners," 2014 IEEE 32nd International Conference on Computer Design (ICCD), Seoul, Korea (South), 2014, pp. 311-316, doi: 10.1109/ICCD.2014.6974699.

Chan, T. (2014). Mitigation of Variability and Reliability Margins in IC Implementation /. UC San Diego. ProQuest ID: Chan_ucsd_0033D_14269. Merritt ID: ark:/20775/bb52916761. Retrieved from https://escholarship.org/uc/item/35r1m001

Dr. Adam Temanm, Digital VLSI Design:Lecture 10: Routing https://www.eng.biu.ac.il/temanad/files/2017/02/Lecture-10-Routing.pdf

Article (20487193) Title: Setting Pegasus - LVS to Quantus av_extracted view Flow with TSMC16 packages

primary clock, generated clock and virtual clock in SDC

primary clocks

  • Primary clocks should be created at input ports and output pins of black boxes.
  • Never create clocks on hierarchy pins. Creating clocks on hierarchy will cause problems when reading SDF. The net timing arc becomes segmented at the hierarchy and PrimeTime will be unable to annotate the net successfully.

generated clocks

  • Generated clocks are generally created for waveform modifications of a primary clock (not including simple inversions). PrimeTime does not simulate a design and thus will not derive internally generated clocks automatically - these clocks must be created by the user and applied as a constraint.
  • PrimeTime caculate source latency for generated clocks if primary clock is propagated, otherwise its source latency is zero.
1
2
3
4
5
# primary clock
create_clock -period 4 [get_ports Clk]
set_clock_latency -source 2 [get_clocks Clk]
set_propagated_clock [get_clocks Clk]
create_generated_clock -divide_by 2 -name div_clk -source [get_ports Clk] FF3/Q

virtual clocks

  • Are clock objects without a source
  • Do not clock sequential devices within the current_design
  • Serve as references of input or output delays
1
2
3
4
# create a virtual clock, vclk, for input and output delay constraints
create_clock -period 5 -name vclk
set_input_delay -max 2 -clock vclk [get_ports in1]
set_output_delay -max 1 -clock vclk [get_ports out2]

There is no network latency to calculate, even if a virtual clock is propagated.

Clock Edges used for Setup and Hold in PrimeTime

PT picks the most restrictive pair of edges for setup and for hold. It determines which edges to be used as follows:

  1. Evaluate waveforms over the smallest common base period

  2. For each capture edge, find the closest setup launch edge. Call these the primary pairs

  3. Out of the primary pairs, pick the most restrictive setup launch and capture edges.

  4. For each primary pair, draw two hold relationships:

    • Launch to (capture - 1)

    • (Launch + 1) to Capture

      From all of these hold relationships, pick the most restrictive.

PrimeTime uses the ideal clock waveform (as reported in report_clock) to determine the appropriate clock edges for inter-clock analysis.

image-20220301203135490

The most restrictive setup pair is from Clk1 8ns to Clk2 9ns

The most restrictive hold pair is from Clk1 0ns to Clk2 0ns

Multicycle Paths Constraints in PrimeTime

PrimeTime® User Guide Version O-2018.06-SP4 Chapter 1: Introduction to PrimeTime Overview of Static Timing Analysis - Timing Exceptions

PrimeTime does not automatically identifies multicycle paths

specifying multicycle path for setup

1
2
3
4
5
set_multicycle_path 6 -from reg[26]/CP -to reg/D
# or
set_multicycle_path -setup 6 -from reg[26]/CP -to reg/D
# check the exception
report_exception

specifying multicycle path for hold (new data every 6 cycles)

1
2
set_multicycle_path -setup 6 -to [get_pins "*reg[*]/D"]
set_multicycle_path -hold [expr 6-1] -to [get_pins "*reg[*]/D"]

image-20220301215938296

MH stands for Hold Multiplier, MS for Setup Multiplier. The Setup multiplier counts up with increasing clock cycles, the Hold Multiplier counts up with decreasing cycles. The origin (0) for the Hold Multiplier is always at the Setup Multiplier - 1 position.

Reporting a multicycle path with report_timing

1
report_timing -exceptions all -from *reg[26]/CP -to *reg/D

Timing Exceptions

If certain paths are not intended to operate according to the default setup and hold behavior assumed by the PrimeTime tool, you need to specify those paths as timing exceptions. Otherwise, the tool might incorrectly report those paths as having timing violations.

The PrimeTime tool lets you specify the following types of exceptions:

  • False path – A path that is never sensitized due to the logic configuration, expected data sequence, or operating mode.
  • Multicycle path – A path designed to take more than one clock cycle from launch to capture.
  • Minimum or maximum delay path – A path that must meet a delay constraint that you explicitly specify as a time value.

package import in SystemVerilog compilation

class forward declaration [https://verificationacademy.com/forums/systemverilog/class-forward-declaration#answer-41383]

import and compile order

project

yapp_pkg.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package yapp_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"

typedef uvm_config_db#(virtual yapp_if) yapp_vif_config;

`include "yapp_packet.sv"
`include "yapp_tx_monitor.sv"
`include "yapp_tx_sequencer.sv"
`include "yapp_tx_seqs.sv"
`include "yapp_tx_driver.sv"
`include "yapp_tx_agent.sv"
`include "yapp_env.sv"

endpackage

yapp_tx_monitor.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class yapp_tx_monitor extends uvm_monitor;

// Collected Data handle
yapp_packet pkt;

// Count packets collected
int num_pkt_col;

// component macro
`uvm_component_utils_begin(yapp_tx_monitor)
`uvm_field_int(num_pkt_col, UVM_ALL_ON + UVM_NOCOMPARE)
`uvm_component_utils_end
/////////////////// virtual interface ////////////////////
virtual interface yapp_if vif;
/////////////////////////////////////////////////////////

function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
...

endclass : yapp_tx_monitor

yapp_tx_driver.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class yapp_tx_driver extends uvm_driver #(yapp_packet);

////////////////// virtual interface ////////////////////
virtual interface yapp_if vif;
/////////////////////////////////////////////////////

int num_sent;

// component macro
`uvm_component_utils_begin(yapp_tx_driver)
`uvm_field_int(num_sent, UVM_ALL_ON + UVM_NOCOMPARE)
`uvm_component_utils_end

// Constructor - required syntax for UVM automation and utilities
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new

function void start_of_simulation_phase(uvm_phase phase);
`uvm_info(get_type_name(), {"start of simulation for ", get_full_name()}, UVM_HIGH)
endfunction : start_of_simulation_phase
...

endclass : yapp_tx_driver

yapp_if.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
interface yapp_if (input clock, input reset );
timeunit 1ns;
timeprecision 100ps;

import uvm_pkg::*;
`include "uvm_macros.svh"

////////////////// import package //////////////////////
import yapp_pkg::*;
//////////////////////////////////////////////////////

// Actual Signals
logic in_data_vld;
logic in_suspend;
logic [7:0] in_data;

// signal for transaction recording
bit monstart, drvstart;

// local storage for payload
logic [7:0] payload_mem [0:63];
...

endinterface : yapp_if

virtual interface is in package;

yapp_pkg is imported into interface

!!! typedef uvm_config_db#(virtual yapp_if) yapp_vif_config; is forward declaration

A forward typedef declares an identifier as a type in advance of the full definition of that type

VCS compile

1
vcs -full64 -R -sverilog  -ntb_opts uvm-1.2 +UVM_TESTNAME=short_yapp_012_test  -f vcs.f  

vcs.f

1
2
3
4
5
6
7
8
9
-timescale=1ns/1ns

+incdir+../sv

../sv/yapp_if.sv
../sv/yapp_pkg.sv
./clkgen.sv
./hw_top.sv
./tb_top.sv

output ERROR

1
2
3
4
5
6
Error-[SV-LCM-PND] Package not defined
../sv/yapp_if.sv, 18
yapp_if, "yapp_pkg::"
Package scope resolution failed. Token 'yapp_pkg' is not a package.
Originating module 'yapp_if'.
Move package definition before the use of the package.

xrun compile

1
xrun -f xrun.f

xrun.f

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-64

-uvmhome CDNS-1.2

// incdir for include files
-incdir ../sv

+UVM_TESTNAME=short_yapp_012_test

-timescale 1ns/1ns

// compile files
../sv/yapp_if.sv
../sv/yapp_pkg.sv
./clkgen.sv
./hw_top.sv
./tb_top.sv

output Error

1
2
3
4
5
6
file: ../sv/yapp_if.sv
import yapp_pkg::*;
|
xmvlog: *E,NOPBIND (../sv/yapp_if.sv,18|14): Package yapp_pkg could not be bound.
interface worklib.yapp_if:sv
errors: 1, warnings: 0

solution

place ../sv/yapp_pkg.sv before ../sv/yapp_if.sv.

In this particular example, yapp_pkg is NOT needed in interface. just delete import yapp_pkg::* is enough in ../sv/yapp_if.sv

plain example

The order of compilation unit DON'T matter

project

hw_top.sv

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
module hw_top;

// Clock and reset signals
logic [31:0] clock_period;
logic run_clock;
logic clock;
logic reset;

// YAPP Interface to the DUT
yapp_if in0(clock, reset);

// CLKGEN module generates clock
clkgen clkgen (
.clock(clock),
.run_clock(1'b1),
.clock_period(32'd10)
);

yapp_router dut(
.reset(reset),
.clock(clock),
.error(),
// YAPP interface signals connection
.in_data(in0.in_data),
.in_data_vld(in0.in_data_vld),
.in_suspend(in0.in_suspend),
// Output Channels
//Channel 0
.data_0(),
.data_vld_0(),
.suspend_0(1'b0),
//Channel 1
.data_1(),
.data_vld_1(),
.suspend_1(1'b0),
//Channel 2
.data_2(),
.data_vld_2(),
.suspend_2(1'b0),
// Host Interface Signals
.haddr(),
.hdata(),
.hen(),
.hwr_rd());

...

endmodule

yapp_router.sv

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
module yapp_router (input clock,                              
input reset,
output error,

// Input channel
input [7:0] in_data,
input in_data_vld,
output in_suspend,
// Output Channels
output [7:0] data_0, //Channel 0
output reg data_vld_0,
input suspend_0,
output [7:0] data_1, //Channel 1
output reg data_vld_1,
input suspend_1,
output [7:0] data_2, //Channel 2
output reg data_vld_2,
input suspend_2,

// Host Interface Signals
input [15:0] haddr,
inout [7:0] hdata,
input hen,
input hwr_rd);
...
endmodule

compile

place hw_top.sv before or after yapp_router.sv doesn't matter, the compiler (xrun, vcs) can compile them successfully.

Conclusion

Always place package before DUT is preferred choice during compiling.

Transaction Level Modeling (TLM)

image-20220403094350570

image-20220403094404891

  • Blocking methods are defined with get() or put() tasks to allow them to consume time
  • Non-blocking methods are defined with try_get() or try_put() functions as they execute in zero time

Uni-Directional TLM Methods Reference

Method Description Syntax
put() Blocking put virtual task put(iput TR t);
try_put() Nonblocking put
-return 1 if successful
-return 0 if not
virtual function bit try_put(input TR t);
can_put() Nonblocking test put
-return 1 if put would be successful
-return 0 if not
virtual function bit can_put();
get() Blocking get virtual task get(output TR t);
try_get() Nonblocking get
-return 1 if successful
-return 0 if not
virtual function bit try_get(output TR t);
can_get() Nonblocking test get
-return 1 if get would be successful
-return 0 if not
virtual function bit can_get();
peek() Blocking peek virtual task peek(output TR t);
try_peek() Nonblocking peek
-return 1 if successful
-return 0 if not
virtual function bit try_peek(output TR t);
can_peek Nonblocking test peek
-return 1 if peek would be successful
-return 0 if not
virtual function bit can_peek();

The peek() methods are similarly to the get() methods, but copy the transaction instead of removing it. The transaction is not consumed, and a subsequent get or peek operation will return the same transaction

Selected Connector and Method Options

put try_put can_put get try_get can_get peek try_peed can_peek
uvm_put_*
uvm_blocking_put_*
uvm_nonblocking_put_*
uvm_get_*
uvm_blocking_get_*
uvm_nonblocking_get_*
uvm_get_peek_*
uvm_blocking_get_peek_*
uvm_nonblocking_get_peek_*

in the connectors above, * can be replaced by port, imp, or export

All the methods for a specific connector type MUST be implemented. If you define an uvm_put connection between two compoents, then the component with the uvm_put_imp object must provide implementations of ALL three put methods, put, try_put and can_put, even if these methods are not explicitly called

TLM FIFO

The TLM FIFO is a FIFO component wrapped in get and put imp connectors. This has the benefit of data storage as well as providing implementations of the communication methods. Components connected to the TLM FIFO are in control of data transfer and can simply defined port connectors to initiate read and write operations on the FIFO

uvm_tlm_fifo

image-20220403141222126

The TLM FIFO object is effectively a FIFO component instantiated between and connected to two components. The FIFO contains imp connectors for the standard TLM put and get/peek interfaces, therefore the user does not have to defineimp ports or communication methods and the FIRO takes care of data storage

The advantages are:

  • The user does not need to define communication methods or imp connectors
  • The FIFO provides data storage between the write (put) and read (get/peek) components
  • There are a number of built-in methods for checking FIFO status

The disadvantages are:

  • The user must now initiate both sides of the transfer (both get/peek and put) to complete the transaction
  • Two connections must be made (both sides of the FIFO) rather than one

The put_export and get_peek_export connection objects have alternatives which provide subsets of the full connector. For example, blocking_put_export and nonblocking_put_export can replace put_export. blocking_get_export, nonblocking_get_export and get_export (as well as others) can replace get_peek_export.

built-in methods
Method Description Syntax
new Standard component constructor with an additional third argument, size, which sets the maximum FIFO size. Default size is 1. A size of 0 is an unbounded FIFO function new(string name, uvm_component parent=null, int size=1);
size Return size of FIFO. 0 indicates unbounded FIFO virtual function int size()
used Return number of entries written to the FIFO virtual function int used();
is_empty Return 1 if used() is 0, otherwise 0 virtual function bit is empty();
is_full Return 1 if used() is equal to size, otherwise 0 virtual function bit is_full()
flush Delete all entries from the FIFO, upon which used() is 0 and is_empty() is 1 virtual funciton void flush();
1
2
3
class uvm_tlm_fifo #(type T=int) extends uvm_tlm_fifo_base #(T);
...
endclass
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
virtual class uvm_tlm_fifo_base #(type T=int) extends uvm_component;
uvm_put_imp #(T, this_type) put_export;

uvm_get_peek_imp #(T, this_type) get_peek_export;

uvm_analysis_port #(T) put_ap;

uvm_analysis_port #(T) get_ap;

// The following are aliases to the above put_export
uvm_put_imp #(T, this_type) blocking_put_export;
uvm_put_imp #(T, this_type) nonblocking_put_export;

// The following are all aliased to the above get_peek_export, which provides
// the superset of these interfaces.
uvm_get_peek_imp #(T, this_type) blocking_get_export;
uvm_get_peek_imp #(T, this_type) nonblocking_get_export;
uvm_get_peek_imp #(T, this_type) get_export;

uvm_get_peek_imp #(T, this_type) blocking_peek_export;
uvm_get_peek_imp #(T, this_type) nonblocking_peek_export;
uvm_get_peek_imp #(T, this_type) peek_export;

uvm_get_peek_imp #(T, this_type) blocking_get_peek_export;
uvm_get_peek_imp #(T, this_type) nonblocking_get_peek_export;

function new(string name, uvm_component parent = null);
super.new(name, parent);

put_export = new("put_export", this);
blocking_put_export = put_export;
nonblocking_put_export = put_export;

get_peek_export = new("get_peek_export", this);
blocking_get_peek_export = get_peek_export;
nonblocking_get_peek_export = get_peek_export;
blocking_get_export = get_peek_export;
nonblocking_get_export = get_peek_export;
get_export = get_peek_export;
blocking_peek_export = get_peek_export;
nonblocking_peek_export = get_peek_export;
peek_export = get_peek_export;

put_ap = new("put_ap", this);
get_ap = new("get_ap", this);

endfunction

Analysis FIFO

uvm_tlm_analysis_fifo

image-20220403144951278

uvm_tlm_analysis_fifo is a specialization of uvm_tlm_fifo

  • Intended to buffer write transactions between the UVC monitor analysis port and scoreboard

It has the following characteristics:

  • Unbounded (size=0)
  • analysis_export connector replaces put_export
    • Support the analysis write method

image-20220403150326750

By declaring the FIFO in the scoreboard, we can get directly from the FIFO output.

However the write side connection of the FIFO to the interface UVC monitor analysis port must be made (usually) in the testbench which has visibility of both UVC and scoreboard components. The connection is made using a connect method call inside the connect phase method

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
class uvm_tlm_analysis_fifo #(type T = int) extends uvm_tlm_fifo #(T);

// Port: analysis_export #(T)
//
// The analysis_export provides the write method to all connected analysis
// ports and parent exports:
//
//| function void write (T t)
//
// Access via ports bound to this export is the normal mechanism for writing
// to an analysis FIFO.
// See write method of <uvm_tlm_if_base #(T1,T2)> for more information.

uvm_analysis_imp #(T, uvm_tlm_analysis_fifo #(T)) analysis_export;


// Function: new
//
// This is the standard uvm_component constructor. ~name~ is the local name
// of this component. The ~parent~ should be left unspecified when this
// component is instantiated in statically elaborated constructs and must be
// specified when this component is a child of another UVM component.

function new(string name , uvm_component parent = null);
super.new(name, parent, 0); // analysis fifo must be unbounded
analysis_export = new("analysis_export", this);
endfunction

const static string type_name = "uvm_tlm_analysis_fifo #(T)";

virtual function string get_type_name();
return type_name;
endfunction

function void write(input T t);
void'(this.try_put(t)); // unbounded => must succeed
endfunction

endclass

Analysis Port Broadcast

image-20220403152922101

Analysis ports can (uniquely) be connected to any number of imp connectors, including zero

image-20220403153148158

The analysis port in component yapp_monitor will be connected to both monitor_one and monitor_two components. Each of the receiving components has an analysis imp object and a write communication method declared. The write method must have the same signature - i.e., they must be void functions called write with a single input argument of type yapp_packet, but their implementations can be completely different.

When the send_yapp port is connected to both mone_in and mtwo_in imps, then a single write call from yapp_monitor executes both write implementations in monitor_one and monitor_two

image-20220403153742706

For non-analysis connections

  • the TLM object must be connected to a single destination only, i.e., each non-analysis object has exactly one connect call. An object may be used multiple times as the argument to a connect, but exactly once as the caller. This is called many-to-one connection. For example, many ports can be connected to a one imp connector

For analysis connections

  • a TLM object can be connected to any number of destinations i.e., one analysis object can call connect many times. This is called one-to-many connection. For example, one port can be connected to many imp connectors. one-to-many is only allowed for analysis connections. An analysis connection can also NOT call connect. An unconnected TLM object is also only allowed for analysis connections

Bi-Directional TLM Transport Connection

image-20220403160248099

Connector syntax

1
2
3
uvm_blocking_transport_XXX #(type REA, type RSP)
uvm_nonblocking_transport_XXX #(type REQ, type RSP)
uvm_transport_XXX #(type REQ, type RSP)

Communication methods

1
2
task transport(REQ request, output RSP response)
function bit nb_transport(REQ request, output RSP response)

Gotchas

FIFO/analysis FIFOs do not perform any cloning on input transactions. Therefore, you will need to check that the UVC monitors collect every transaction into a different instance to avoid overwriting data in the FIFOs

reg_verifier of Cadence

Generation

image-20220404125201390

1
reg_verifier -domain uvmreg -top yapp_router_regs.xml -dut_name yapp_router_regs -out_file yapp_router_regs -cov -quicktest -pkg yapp_router_reg_pkg

image-20220404125541442

image-20220404125421201

generated file description
yapp_router_regs_config.dat Configuration information
yapp_router_regs_hdlpaths.dat Path information for backddoor access
yapp_router_regs_rdb.sv Register Model
cdns_uvmreg_utils_pkg.sv Cadence utility package
quicktest.sv UVM test to verify model

Sanity Check

1
make run_test
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
class yapp_router_regs_t extends cdns_uvm_reg_block;
uvm_reg_map default_map;
uvm_reg_map router;
rand yapp_regs_c router_yapp_regs;
rand yapp_mem_c router_yapp_mem;
rand yapp_pkt_mem_c router_yapp_pkt_mem;
...
virtual function void build();
router = create_map("router", `UVM_REG_ADDR_WIDTH'h0, 1, UVM_LITTLE_ENDIAN, 1);
default_map = router;
...
//Mapping router map
router.add_submap(router_yapp_regs.default_map, `UVM_REG_ADDR_WIDTH'h1000)
router.add_mem(router_yapp_mem, `UVM_REG_ADDR_WIDTH'h1100);
router.add_mem(router_yapp_pkt_mem, `UVM_REG_ADDR_WIDTH'h1010);
...
endfunction

endclass


class qt_test extends uvm_test;
yapp_router_regs_t model;
...
task run_phase(uvm_phase phase);
...
model.print();
model.default_map.print();
model.router_yapp_regs.default_map.print();
endtask
...
endclass

yapp_mem_c

1
2
3
4
5
6
7
class yapp_mem_c extends uvm_mem;                                                                                                                                                                                                                                
`uvm_object_utils(yapp_mem_c)

function new(input string name="router_yapp_mem");
super.new(name, 'h100, 8, "RW", UVM_NO_COVERAGE);
endfunction
endclass

yapp_pkt_mem_c

1
2
3
4
5
6
7
class yapp_pkt_mem_c extends uvm_mem;                                                                                                                                                                                                                                      
`uvm_object_utils(yapp_pkt_mem_c)
function new(input string name="router_yapp_pkt_mem");
super.new(name, 'h40, 8, "RO", UVM_NO_COVERAGE);
endfunction

endclass

yapp_regs_c

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
class yapp_regs_c extends cdns_uvm_reg_block;
`uvm_object_utils(yapp_regs_c)
rand addr0_cnt_reg_c addr0_cnt_reg;
rand addr1_cnt_reg_c addr1_cnt_reg;
rand addr2_cnt_reg_c addr2_cnt_reg;
rand illegal_addr_cnt_reg_c addr3_cnt_reg;
rand ctrl_reg_c ctrl_reg;
rand en_reg_c en_reg;
rand mem_size_reg_c mem_size_reg;
rand oversized_pkt_cnt_reg_c oversized_pkt_cnt_reg;
rand parity_err_cnt_reg_c parity_err_cnt_reg;
virtual function void build();
uvm_reg reg_set[$];
default_map = create_map(get_name(), `UVM_REG_ADDR_WIDTH'h1000, 1, UVM_LITTLE_ENDIAN, 1);
begin
uvm_reg_config_ta ta = get_reg_config("yapp_router_regs.router_yapp_regs");
build_uvm_regs(default_map, this, null, ta, reg_se);
end
if(! $cast(addr0_cnt_reg, reg_set[0]))
`uvm_error("UVM_REG", "addr0_cnt_reg register casting error")
if(! $cast(addr1_cnt_reg, reg_set[1]))
`uvm_error("UVM_REG", "addr1_cnt_reg register casting error")
if(! $cast(addr2_cnt_reg, reg_set[2]))
`uvm_error("UVM_REG", "addr2_cnt_reg register casting error")
if(! $cast(addr3_cnt_reg, reg_set[3]))
`uvm_error("UVM_REG", "addr3_cnt_reg register casting error")
if(! $cast(ctrl_reg, reg_set[4]))
`uvm_error("UVM_REG", "ctrl_reg register casting error")
if(! $cast(en_reg, reg_set[5]))
`uvm_error("UVM_REG", "en_reg register casting error")
if(! $cast(mem_size_reg, reg_set[6]))
`uvm_error("UVM_REG", "mem_size_reg register casting error")
if(! $cast(oversized_pkt_cnt_reg, reg_set[7]))
`uvm_error("UVM_REG", "oversized_pkt_cnt_reg register casting error")
if(! $cast(parity_err_cnt_reg, reg_set[8]))
`uvm_error("UVM_REG", "parity_err_cnt_reg register casting error")
endfunction

function new(input string name="router_yapp_regs");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
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
class uvm_reg_map extends uvm_object;
// Function: add_submap
//
// Add an address map
//
// Add the specified address map instance to this address map.
// The address map is located at the specified base address.
// The number of consecutive physical addresses occupied by the submap
// depends on the number of bytes in the physical interface
// that corresponds to the submap,
// the number of addresses used in the submap and
// the number of bytes in the
// physical interface corresponding to this address map.
//
// An address map may be added to multiple address maps
// if it is accessible from multiple physical interfaces.
// An address map may only be added to an address map
// in the grand-parent block of the address submap.
//
extern virtual function void add_submap (uvm_reg_map child_map,
uvm_reg_addr_t offset);


// Function: add_mem
//
// Add a memory
//
// Add the specified memory instance to this address map.
// The memory is located at the specified base address and has the
// specified access rights ("RW", "RO" or "WO").
// The number of consecutive physical addresses occupied by the memory
// depends on the width and size of the memory and the number of bytes in the
// physical interface corresponding to this address map.
//
// If ~unmapped~ is TRUE, the memory does not occupy any
// physical addresses and the base address is ignored.
// Unmapped memories require a user-defined ~frontdoor~ to be specified.
//
// A memory may be added to multiple address maps
// if it is accessible from multiple physical interfaces.
// A memory may only be added to an address map whose parent block
// is the same as the memory's parent block.
//
extern virtual function void add_mem (uvm_mem mem,
uvm_reg_addr_t offset,
string rights = "RW",
bit unmapped=0,
uvm_reg_frontdoor frontdoor=null);


endclass: uvm_reg_map

register model print

// test run_phase

model.default_map.print();

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
----------------------------------------------------------------------------------------------------------------------------            
Name Type Size Value
----------------------------------------------------------------------------------------------------------------------------
model yapp_router_regs_t - @3724
router_yapp_regs yapp_regs_c - @3792
addr0_cnt_reg addr0_cnt_reg_c - @3827
addr0_cnt_reg_fld uvm_reg_field ... RO addr0_cnt_reg[7:0]=8'h00
addr1_cnt_reg addr1_cnt_reg_c - @3839
addr1_cnt_reg_fld uvm_reg_field ... RO addr1_cnt_reg[7:0]=8'h00
addr2_cnt_reg addr2_cnt_reg_c - @3857
addr2_cnt_reg_fld uvm_reg_field ... RO addr2_cnt_reg[7:0]=8'h00
addr3_cnt_reg illegal_addr_cnt_reg_c - @3874
addr3_cnt_reg_fld uvm_reg_field ... RO addr3_cnt_reg[7:0]=8'h00
ctrl_reg ctrl_reg_c - @3891
plen uvm_reg_field ... RW ctrl_reg[5:0]=6'h3f
rsvd_0 uvm_reg_field ... RW ctrl_reg[7:6]=2'h0
en_reg en_reg_c - @3908
router_en uvm_reg_field ... RW en_reg[0:0]=1'h1
parity_err_cnt_en uvm_reg_field ... RW en_reg[1:1]=1'h0
oversized_pkt_cnt_en uvm_reg_field ... RW en_reg[2:2]=1'h0
rsvd_0 uvm_reg_field ... RW en_reg[3:3]=1'h0
addr0_cnt_en uvm_reg_field ... RW en_reg[4:4]=1'h0
addr1_cnt_en uvm_reg_field ... RW en_reg[5:5]=1'h0
addr2_cnt_en uvm_reg_field ... RW en_reg[6:6]=1'h0
addr3_cnt_en uvm_reg_field ... RW en_reg[7:7]=1'h0
mem_size_reg mem_size_reg_c - @3928
mem_size_reg_fld uvm_reg_field ... RO mem_size_reg[7:0]=8'h00
oversized_pkt_cnt_reg oversized_pkt_cnt_reg_c - @3966
oversized_pkt_cnt_reg_fld uvm_reg_field ... RO oversized_pkt_cnt_reg[7:0]=8'h00
parity_err_cnt_reg parity_err_cnt_reg_c - @3983
parity_err_cnt_reg_fld uvm_reg_field ... RO parity_err_cnt_reg[7:0]=8'h00
router_yapp_mem yapp_mem_c - @3757
n_bits integral 32 'd8
size integral 32 'd256
router_yapp_pkt_mem yapp_pkt_mem_c - @3779
n_bits integral 32 'd8
size integral 32 'd64
router uvm_reg_map 0 id=@3737 seqr=HI0.sqr offset=0x0 size=0x1200 baseaddr=0x0
router_yapp_regs uvm_reg_map 0 id=@3816 seqr=HI0.sqr offset=0x1000 size=0x5 baseaddr=0x1000
----------------------------------------------------------------------------------------------------------------------------

address map print

// test run_phase

model.router_yapp_regs.default_map.print();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
---------------------------------------------------------------------------
Name Type Size Value
---------------------------------------------------------------------------
router uvm_reg_map - @3737
endian ... UVM_LITTLE_ENDIAN
effective sequencer uvm_sequencer ... HI0.sqr
router_yapp_mem yapp_mem_c ... @3757 +'h1100
router_yapp_pkt_mem yapp_pkt_mem_c ... @3779 +'h1010
router_yapp_regs uvm_reg_map - @3816
endian ... UVM_LITTLE_ENDIAN
effective sequencer uvm_sequencer ... HI0.sqr
addr0_cnt_reg addr0_cnt_reg_c ... @3827 +'h1009
addr1_cnt_reg addr1_cnt_reg_c ... @3839 +'h100a
addr2_cnt_reg addr2_cnt_reg_c ... @3857 +'h100b
addr3_cnt_reg illegal_addr_cnt_reg_c ... @3874 +'h1006
ctrl_reg ctrl_reg_c ... @3891 +'h1000
en_reg en_reg_c ... @3908 +'h1001
mem_size_reg mem_size_reg_c ... @3928 +'h100d
oversized_pkt_cnt_reg oversized_pkt_cnt_reg_c ... @3966 +'h1005
parity_err_cnt_reg parity_err_cnt_reg_c ... @3983 +'h1004
---------------------------------------------------------------------------

submap print

// test run_phase

model.router_yapp_regs.default_map.print();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-------------------------------------------------------------------------
Name Type Size Value
-------------------------------------------------------------------------
router_yapp_regs uvm_reg_map - @3814
endian ... UVM_LITTLE_ENDIAN
effective sequencer uvm_sequencer ... HI0.sqr
addr0_cnt_reg addr0_cnt_reg_c ... @3825 +'h1009
addr1_cnt_reg addr1_cnt_reg_c ... @3837 +'h100a
addr2_cnt_reg addr2_cnt_reg_c ... @3855 +'h100b
addr3_cnt_reg illegal_addr_cnt_reg_c ... @3872 +'h1006
ctrl_reg ctrl_reg_c ... @3889 +'h1000
en_reg en_reg_c ... @3906 +'h1001
mem_size_reg mem_size_reg_c ... @3926 +'h100d
oversized_pkt_cnt_reg oversized_pkt_cnt_reg_c ... @3964 +'h1005
parity_err_cnt_reg parity_err_cnt_reg_c ... @3981 +'h1004
-------------------------------------------------------------------------

cdns_uvmreg_utils_pkg

uvm_reg_field_config_t

1
2
3
4
5
6
7
8
9
10
11
typedef struct  {                                                                         
string name;
int unsigned size;
int unsigned lsb_pos;
string access;
bit volatile;
uvm_reg_data_t reset;
bit has_reset;
bit is_rand;
bit individually_accessible;
} uvm_reg_field_config_t;

uvm_reg_config_t

1
2
3
4
5
6
7
8
9
10
11
typedef struct  {
string reg_type_name;
string configUID;
string name;
uvm_reg_addr_t offset;
int unsigned size_bytes;
int unsigned addr_bits;
string access;
int is_array;
string range;
} uvm_reg_config_t;

uvm_blk_config_t

1
2
3
4
5
6
typedef struct {                             
uvm_reg_addr_t base_addr;
int n_bytes;
uvm_endianness_e endianess;
bit byte_addr_t;
} uvm_blk_config_t;

uvm_reg_field_config_ta

1
typedef uvm_reg_field_config_t uvm_reg_field_config_ta[];

uvm_reg_field_config_tq

1
typedef uvm_reg_field_config_ta uvm_reg_field_config_tq[string];

build_uvm_regs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function automatic void build_uvm_regs(uvm_reg_map map, uvm_reg_block pblock, uvm_reg_file pfile, ref uvm_reg_config_t f_props[], output uvm_reg out[$]);
foreach(f_props[idx]) begin
uvm_reg_config_t f= f_props[idx];
if(f.is_array == 0) begin
cdns_uvm_reg reg_t;
uvm_object obj = factory.create(f.reg_type_name, pfile!=null ? pfile.get_full_name(): "",f.name);
assert($cast(reg_t,obj));
reg_t.configure(pblock, pfile, "");
reg_t.setconfigUID(f.configUID);
reg_t.build();
// !!! f.offset !!!
map.add_reg(reg_t, f.offset, f.access);
out.push_back(reg_t);
// !!! print the offset
`uvm_info("[build_uvm_regs]", $sformatf("### %s offset: %0x", f.reg_type_name, f.offset), UVM_LOW)
end

else if (f.is_array == 1) begin
int unsigned array_offset;
string range_str = f.range;
int indx, arr_idx = 0;
int range_arr [];
int arr_size = 1;
  • dynamic array
    • need new[size]
  • queue
    • DONT need new
1
2
3
4
5
6
7
8
9
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### addr0_cnt_reg_c offset: 9                                 
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### addr1_cnt_reg_c offset: a
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### addr2_cnt_reg_c offset: b
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### illegal_addr_cnt_reg_c offset: 6
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### ctrl_reg_c offset: 0
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### en_reg_c offset: 1
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### mem_size_reg_c offset: d
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### oversized_pkt_cnt_reg_c offset: 5
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### parity_err_cnt_reg_c offset: 4

In submap, offset is 0-based, router.add_submap(router_yapp_regs.default_map, ``UVM_REG_ADDR_WIDTH'h1000) configure base addr for submap

IP-XACT XML : yapp_router_regs.xml

image-20220404194550165

memory

yapp_pkt_mem

image-20220404194628354

yapp_mem

image-20220404194721416

register

image-20220404194941613

yapp_regs

ctrl_reg

image-20220404195052310

en_reg

image-20220404195156776

UVM Register Abstraction Layer (RAL) - overview

Within an UVM testbench a register model is used

  • either as a means of looking up a mirror of the current DUT hardware state

  • or as means of accessing the hardware via the front or back door

    and updating the register model database.

image-20220312125516500

Register Frontdoor Write

1
model.r0.write(status, value, [UVM_FRONTDOOR], .parent(this));
  • Sequence sets uvm_reg with value
  • uvm_reg content is translated into bus transaction
  • Driver gets bus transaction and writes DUT register
  • Mirror can be updated either implicitly or explicitly (predictor)

Register Backdoor Write

1
model.r0.write(status, value, UVM_BACKDOOR, .parent(this));

uvm_reg uses DPI/XMR to set DUT register with value

  • Physical interface is bypassed
  • Register behavior is mimicked

Register Backdoor Poke

1
model.r0.poke(status, value, .parent(this));

uvm_reg used DPI/XMR to set DUT register with value as is

  • Physical interface is bypassed
  • Register behavior is NOT mimicked

Register Frontdoor Read

1
model.r0.read(status, value, [UVM_FRONTDOOR], .parent(this));
  • Sequence executes uvm_reg READ
  • uvm_reg is translated into bus transaction
  • Driver gets bus transaction and read DUT register
  • Read value is translated into uvm_reg data and returned to sequence
  • Mirror updates

Register Backdoor Read

1
model.r0.read(status, value, UVM_BACKDOOR, .parent(this));

uvm_reg used DPI/XMR to get DUT register value

  • Physical interface is bypassed
  • Register behavior is mimicked (acc)
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
// EXECUTE READ...
case (rw.path)

// ...VIA USER BACKDOOR
UVM_BACKDOOR: begin
uvm_reg_backdoor bkdr = get_backdoor();

uvm_reg_map map = uvm_reg_map::backdoor();
if (map.get_check_on_read()) exp = get();

if (bkdr != null)
bkdr.read(rw);
else
backdoor_read(rw);

value = rw.value[0];

// Need to clear RC fields, set RS fields and mask WO fields
if (rw.status != UVM_NOT_OK) begin

uvm_reg_data_t wo_mask;

foreach (m_fields[i]) begin
string acc = m_fields[i].get_access(uvm_reg_map::backdoor());
if (acc == "RC" ||
acc == "WRC" ||
acc == "WSRC" ||
acc == "W1SRC" ||
acc == "W0SRC") begin
value &= ~(((1<<m_fields[i].get_n_bits())-1)
<< m_fields[i].get_lsb_pos());
end
else if (acc == "RS" ||
acc == "WRS" ||
acc == "WCRS" ||
acc == "W1CRS" ||
acc == "W0CRS") begin
value |= (((1<<m_fields[i].get_n_bits())-1)
<< m_fields[i].get_lsb_pos());
end
else if (acc == "WO" ||
acc == "WOC" ||
acc == "WOS" ||
acc == "WO1") begin
wo_mask |= ((1<<m_fields[i].get_n_bits())-1)
<< m_fields[i].get_lsb_pos();
end
end

if (value != rw.value[0]) begin
uvm_reg_data_t saved;
saved = rw.value[0];
rw.value[0] = value;
if (bkdr != null)
bkdr.write(rw);
else
backdoor_write(rw);
rw.value[0] = saved;
end

rw.value[0] &= ~wo_mask;

if (map.get_check_on_read() &&
rw.status != UVM_NOT_OK) begin
void'(do_check(exp, rw.value[0], map));
end

do_predict(rw, UVM_PREDICT_READ);
end
end

Register Backdoor Peek

1
model.r0.peek(status, value, .parent(this));

uvm_reg uses DPI/XMR to get DUT register value as is

  • Physical interface is bypassed
  • Register behavior is NOT mimicked
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
task uvm_reg::peek(output uvm_status_e      status,
output uvm_reg_data_t value,
input string kind = "",
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);

uvm_reg_backdoor bkdr = get_backdoor();
uvm_reg_item rw;

m_fname = fname;
m_lineno = lineno;

// create an abstract transaction for this operation
rw = uvm_reg_item::type_id::create("mem_peek_item",,get_full_name());
rw.element = this;
rw.path = UVM_BACKDOOR;
rw.element_kind = UVM_REG;
rw.kind = UVM_READ;
rw.bd_kind = kind;
rw.parent = parent;
rw.extension = extension;
rw.fname = fname;
rw.lineno = lineno;

do_predict(rw, UVM_PREDICT_READ);

endtask: peek

Methods to handle property of uvm_reg or uvm_reg_block

mirror

1
2
model.mirror(status, [check], [path], .parent(this));
model.r0.mirro(status, [check], [path], .parent(this));

Update mirrored and desired properties with DUT content

set

1
model.r0.set(value);

Set value in desired properties

randomize

1
2
model.randomize();
model.r0.randomize();

Populate desired property with random value

get

1
value = model.r0.get();

Get value from desired property

update

1
2
model.update(status, [path], .parent(this));
model.r0.update(status, [path], .parent(this));

Update DUT and mirrored property with desired property if mirrored property is different from desired

predict

1
model.r0.predict(value);

Set the value of mirrored property

get_mirrored_value

1
value = model.r0.get_mirrored_value();

Get value from mirrored property

Backdoor Access

  • Two ways to generate the backdoor access:
    • Via SystemVerilog Cross Module Reference (XMR)
    • Via SystemVerilog DPI call
  • Both allow register model to be part of SystemVerilog package
  • XMR implementation is faster
    • Requires user to compile one additional file and at compile-time provide top level path to DUT
    • VCS only
  • DPI implementation is slower
    • No additional file is needed and top level path can be provided at run-time
    • Portable to other simulators

UVM Register Classes: uvm_reg_bus_op & uvm_reg_item

The generic register item is implemented as a struct in order to minimise the amount of memory resource it uses. The struct is defined as type uvm_reg_bus_op and this contains 6 fields:

Property Type Comment/Description
addr uvm_reg_addr_t Address field, defaults to 64 bits
data uvm_reg_data_t Read or write data, defaults to 64 bits
kind uvm_access_e UVM_READ or UVM_WRITE
n_bits unsigned int Number of bits being transferred
byte_en uvm_reg_byte_en_t Byte enable
status uvm_status_e UVM_IS_OK, UVM_IS_X, UVM_NOT_OK
1
2
3
4
5
6
7
8
9
typedef struct {
uvm_access_e kind; // Kind of access: READ or WRITE.
uvm_reg_addr_t addr; // The bus address.
uvm_reg_data_t data; // The data to write.
// The number of bits of <uvm_reg_item::value> being transferred by this transaction.
int n_bits;
uvm_reg_byte_en_t byte_en; // Enables for the byte lanes on the bus.
uvm_status_e status; // The result of the transaction: UVM_IS_OK, UVM_HAS_X, UVM_NOT_OK.
} uvm_reg_bus_op;
1
2
3
4
5
6
7
class uvm_reg_item extends uvm_sequence_item;
rand uvm_access_e kind;
rand uvm_reg_data_t value[];
rand uvm_reg_addr_t offset;
uvm_status_e status;
uvm_reg_map map;
endclass

Registers

The register class contains a build method which is used to create and configure the fields.

this build method is not called by the UVM build_phase, since the register is an uvm_object rather than an uvm_component

1
2
3
4
5
6
//
// uvm_reg constructor prototype:
//
function new (string name="", // Register name
int unsigned n_bits, // Register width in bits
int has_coverage); // Coverage model supported by the register
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
// Function: new
//
function new(string name = "ctrl_reg");
super.new(name, 32, build_coverage(UVM_CVR_FIELD_VALS));
add_coverage(build_coverage(UVM_CVR_FIELD_VALS));
if(has_coverage(UVM_CVR_FIELD_VALS))
cg_vals = new();
endfunction


// Function: sample_values
//
virtual function void sample_values();
super.sample_values();
if (get_coverage(UVM_CVR_FIELD_VALS))
cg_vals.sample();
endfunction


// Function: build
//
virtual function void build();
ass = uvm_reg_field::type_id::create("ass");
ie = uvm_reg_field::type_id::create("ie");
lsb = uvm_reg_field::type_id::create("lsb");
tx_neg = uvm_reg_field::type_id::create("tx_neg");
rx_neg = uvm_reg_field::type_id::create("rx_neg");
go_bsy = uvm_reg_field::type_id::create("go_bsy");
reserved2 = uvm_reg_field::type_id::create("reserved2");
char_len = uvm_reg_field::type_id::create("char_len");

ass.configure(this, 1, 13, "RW", 0, 1'b0, 1, 1, 0);
ie.configure(this, 1, 12, "RW", 0, 1'b0, 1, 1, 0);
lsb.configure(this, 1, 11, "RW", 0, 1'b0, 1, 1, 0);
tx_neg.configure(this, 1, 10, "RW", 0, 1'b0, 1, 1, 0);
rx_neg.configure(this, 1, 9, "RW", 0, 1'b0, 1, 1, 0);
go_bsy.configure(this, 1, 8, "RW", 0, 1'b0, 1, 1, 0);
reserved2.configure(this, 1, 7, "RW", 0, 1'b0, 1, 1, 0);
char_len.configure(this, 7, 0, "RW", 0, 7'b0000000, 1, 1, 0);
endfunction

As shown above, Register width is 32 same with the bus width, lower 14 bit is configured.

RTL

1
2
`define SPI_CTRL_BIT_NB         14
reg [`SPI_CTRL_BIT_NB-1:0] ctrl; // Control and status register

Register Maps

Two purpose of the register map

  • provide information on the offset of the registers, memories and/or register blocks
  • identify bus agent based sequences to be executed ???

There can be several register maps within a block, each one can specify a different address map and a different target bus agent

register map has to be created which the register block using the create_map method

1
2
3
4
5
6
7
8
9
10
11
12
13
//
// Prototype for the create_map method
//
function uvm_reg_map create_map(string name, // Name of the map handle
uvm_reg_addr_t base_addr, // The maps base address
int unsigned n_bytes, // Map access width in bytes
uvm_endianness_e endian, // The endianess of the map
bit byte_addressing=1); // Whether byte_addressing is supported

//
// Example:
//
AHB_map = create_map("AHB_map", 'h0, 4, UVM_LITTLE_ENDIAN);
  • The n_bytes parameter is the word size (bus width) of the bus to which the map is associated. If a register's width exceeds the bus width, more than one bus access is needed to read and write that register over that bus.

  • he byte_addressing argument affects how the address is incremented in these consecutive accesses. For example, if n_bytes=4 and byte_addressing=0, then an access to a register that is 64-bits wide and at offset 0 will result in two bus accesses at addresses 0 and 1. With byte_addressing=1, that same access will result in two bus accesses at addresses 0 and 4.

    The default for byte_addressing is 1

  • The first map to be created within a register block is assigned to the default_map member of the register block

byte_addressing.drawio

Register Adapter

uvm_reg_adapter
Methods Description
reg2bus Overload to convert generic register access items to target bus agent sequence items
bus2reg Overload to convert target bus sequence items to register model items
Properties (Of type bit) Description
supports_byte_enable Set to 1 if the target bus and the target bus agent supports byte enables, else set to 0
provides_responses Set to 1 if the target agent driver sends separate response sequence_items that require response handling

The provides_responses bit should be set if the agent driver returns a separate response item (i.e. put(response), or item_done(response)) from its request item

Prediction

the update, or prediction, of the register model content can occur using one of three models

Auto Prediction

This mode of operation is the simplest to implement, but suffers from the drawback that it can only keep the register model up to date with the transfers that it initiates. If any other sequences directly access the target sequencer to update register content, or if there are register accesses from other DUT interfaces, then the register model will not be updated.

1
2
3
4
function void uvm_reg_map::set_auto_predict(bit on=1); m_auto_predict = on; endfunction

// Gets the auto-predict mode setting for this map.
function bit uvm_reg_map::get_auto_predict(); return m_auto_predict; endfunction

// Function: set_auto_predict

//

// Sets the auto-predict mode for his map.

//

// When on is TRUE,

// the register model will automatically update its mirror (what it thinks should be in the DUT)

immediately after any bus read or write operation via this map. Before a uvm_reg::write

// or uvm_reg::read operation returns, the register's uvm_reg::predict method is called to update

the mirrored value in the register.

//

// When on is FALSE, bus reads and writes via this map do not

// automatically update the mirror. For real-time updates to the mirror

// in this mode, you connect a uvm_reg_predictor instance to the bus

// monitor. The predictor takes observed bus transactions from the

// bus monitor, looks up the associated uvm_reg register given

// the address, then calls that register's uvm_reg::predict method.

// While more complex, this mode will capture all register read/write

// activity, including that not directly descendant from calls to

// uvm_reg::write and uvm_reg::read.

//

// By default, auto-prediction is turned off.

//

Reg auto predict.gif

The register model content is updated based on the register accesses it initiates

Explicit prediction is the default mode of prediction

Reg explicit prediction.gif

The register model content is updated via the predictor component based on all observed bus transactions, ensuring that register accesses made without the register model are mirrored correctly. The predictor looks up the accessed register by address then calls its predict() method

uvm_reg::predict & uvm_reg::do_predict
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
// predict
function bit uvm_reg::predict (uvm_reg_data_t value,
uvm_reg_byte_en_t be = -1,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_path_e path = UVM_FRONTDOOR,
uvm_reg_map map = null,
string fname = "",
int lineno = 0);
uvm_reg_item rw = new;
rw.value[0] = value;
rw.path = path;
rw.map = map;
rw.fname = fname;
rw.lineno = lineno;
do_predict(rw, kind, be);
predict = (rw.status == UVM_NOT_OK) ? 0 : 1;
endfunction: predict


// do_predict
function void uvm_reg::do_predict(uvm_reg_item rw,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_reg_byte_en_t be = -1);

uvm_reg_data_t reg_value = rw.value[0];
m_fname = rw.fname;
m_lineno = rw.lineno;

if (rw.status ==UVM_IS_OK )
rw.status = UVM_IS_OK;

if (m_is_busy && kind == UVM_PREDICT_DIRECT) begin
`uvm_warning("RegModel", {"Trying to predict value of register '",
get_full_name(),"' while it is being accessed"})
rw.status = UVM_NOT_OK;
return;
end

foreach (m_fields[i]) begin
rw.value[0] = (reg_value >> m_fields[i].get_lsb_pos()) &
((1 << m_fields[i].get_n_bits())-1);
m_fields[i].do_predict(rw, kind, be>>(m_fields[i].get_lsb_pos()/8));
end

rw.value[0] = reg_value;

endfunction: do_predict
uvm_reg_field::predict & uvm_reg_field::do_predict
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
// predict

function bit uvm_reg_field::predict (uvm_reg_data_t value,
uvm_reg_byte_en_t be = -1,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_path_e path = UVM_FRONTDOOR,
uvm_reg_map map = null,
string fname = "",
int lineno = 0);
uvm_reg_item rw = new;
rw.value[0] = value;
rw.path = path;
rw.map = map;
rw.fname = fname;
rw.lineno = lineno;
do_predict(rw, kind, be);
predict = (rw.status == UVM_NOT_OK) ? 0 : 1;
endfunction: predict


// do_predict

function void uvm_reg_field::do_predict(uvm_reg_item rw,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_reg_byte_en_t be = -1);

uvm_reg_data_t field_val = rw.value[0] & ((1 << m_size)-1);

if (rw.status != UVM_NOT_OK)
rw.status = UVM_IS_OK;

// Assume that the entire field is enabled
if (!be[0])
return;

m_fname = rw.fname;
m_lineno = rw.lineno;

case (kind)

UVM_PREDICT_WRITE:
begin
uvm_reg_field_cb_iter cbs = new(this);

if (rw.path == UVM_FRONTDOOR || rw.path == UVM_PREDICT)
field_val = XpredictX(m_mirrored, field_val, rw.map);

m_written = 1;

for (uvm_reg_cbs cb = cbs.first(); cb != null; cb = cbs.next())
cb.post_predict(this, m_mirrored, field_val,
UVM_PREDICT_WRITE, rw.path, rw.map);

field_val &= ('b1 << m_size)-1;

end

UVM_PREDICT_READ:
begin
uvm_reg_field_cb_iter cbs = new(this);

if (rw.path == UVM_FRONTDOOR || rw.path == UVM_PREDICT) begin

string acc = get_access(rw.map);

if (acc == "RC" ||
acc == "WRC" ||
acc == "WSRC" ||
acc == "W1SRC" ||
acc == "W0SRC")
field_val = 0; // (clear)

else if (acc == "RS" ||
acc == "WRS" ||
acc == "WCRS" ||
acc == "W1CRS" ||
acc == "W0CRS")
field_val = ('b1 << m_size)-1; // all 1's (set)

else if (acc == "WO" ||
acc == "WOC" ||
acc == "WOS" ||
acc == "WO1" ||
acc == "NOACCESS")
return;
end

for (uvm_reg_cbs cb = cbs.first(); cb != null; cb = cbs.next())
cb.post_predict(this, m_mirrored, field_val,
UVM_PREDICT_READ, rw.path, rw.map);

field_val &= ('b1 << m_size)-1;

end

UVM_PREDICT_DIRECT:
begin
if (m_parent.is_busy()) begin
`uvm_warning("RegModel", {"Trying to predict value of field '",
get_name(),"' while register '",m_parent.get_full_name(),
"' is being accessed"})
rw.status = UVM_NOT_OK;
end
end
endcase

// update the mirror with predicted value
m_mirrored = field_val;
m_desired = field_val;
this.value = field_val;

endfunction: do_predict
uvm_access_e
1
2
3
4
5
6
7
8
9
10
11
12
13
// Enum: uvm_access_e
//
// Type of operation begin performed
//
// UVM_READ - Read operation
// UVM_WRITE - Write operation
//
typedef enum {
UVM_READ,
UVM_WRITE,
UVM_BURST_READ,
UVM_BURST_WRITE
} uvm_access_e;
uvm_predict_e
1
2
3
4
5
6
7
8
9
10
11
12
13
// Enum: uvm_predict_e
//
// How the mirror is to be updated
//
// UVM_PREDICT_DIRECT - Predicted value is as-is
// UVM_PREDICT_READ - Predict based on the specified value having been read
// UVM_PREDICT_WRITE - Predict based on the specified value having been written
//
typedef enum {
UVM_PREDICT_DIRECT,
UVM_PREDICT_READ,
UVM_PREDICT_WRITE
} uvm_predict_e;
uvm_path_e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Enum: uvm_path_e
//
// Path used for register operation
//
// UVM_FRONTDOOR - Use the front door
// UVM_BACKDOOR - Use the back door
// UVM_PREDICT - Operation derived from observations by a bus monitor via
// the <uvm_reg_predictor> class.
// UVM_DEFAULT_PATH - Operation specified by the context
//
typedef enum {
UVM_FRONTDOOR,
UVM_BACKDOOR,
UVM_PREDICT,
UVM_DEFAULT_PATH
} uvm_path_e;

Passive Prediction

UVM Register Abstraction Layer (RAL) - source code reading

Field Access Policies

image-20220526092125583

whether a register field can be read or written depends on both the field's configured access policy and the register's rights in the map being used to access the field

http://www.verilab.com/files/litterick_register_final.pdf

https://www.verilab.com/files/litterick_register_slides_sm.pdf

uvm_reg::write

  • If a back-door access path is used, the effect of writing the register through a physical access is mimicked. For example, read-only bits in the registers will not be written.

  • The mirrored value will be updated using the uvm_reg::predict() method.

1
2
3
4
5
6
7
8
9
10
11
extern virtual task write(output uvm_status_e      status,
input uvm_reg_data_t value,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);

extern virtual task do_write(uvm_reg_item rw);
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
task uvm_reg::write(output uvm_status_e      status,
input uvm_reg_data_t value,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);

// create an abstract transaction for this operation
uvm_reg_item rw;

XatomicX(1);

set(value);

rw = uvm_reg_item::type_id::create("write_item",,get_full_name());
rw.element = this;
rw.element_kind = UVM_REG;
rw.kind = UVM_WRITE;
rw.value[0] = value;
rw.path = path;
rw.map = map;
rw.parent = parent;
rw.prior = prior;
rw.extension = extension;
rw.fname = fname;
rw.lineno = lineno;

do_write(rw);

status = rw.status;

XatomicX(0);

endtask

uvm_reg::read

  • If a back-door access path is used, the effect of reading the register through a physical access is mimicked. For example, clear-on-read bits in the registers will be set to zero.

  • The mirrored value will be updated using the uvm_reg::predict() method.

1
2
3
4
5
6
7
8
9
10
11
extern virtual task read(output uvm_status_e      status,
output uvm_reg_data_t value,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);

extern virtual task do_read(uvm_reg_item rw);

uvmreg_read.drawio

readback value can be different from m_mirrored , m_desired or value

uvm_reg::poke

  • Deposit the value in the DUT register corresponding to this abstraction class instance, as-is, using a back-door access.

  • Uses the HDL path for the design abstraction specified by kind.

  • The mirrored value will be updated using the uvm_reg::predict() method.

1
2
3
4
5
6
7
extern virtual task poke(output uvm_status_e      status,
input uvm_reg_data_t value,
input string kind = "",
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);

uvm_reg::peek

  • Sample the value in the DUT register corresponding to this abstraction class instance using a back-door access. The register value is sampled, not modified.
  • Uses the HDL path for the design abstraction specified by kind.
  • The mirrored value will be updated using the uvm_reg::predict() method.
1
2
3
4
5
6
7
extern virtual task peek(output uvm_status_e      status,
output uvm_reg_data_t value,
input string kind = "",
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);

uvm_reg::mirror

  • Read the register and optionally compared the readback value with the current mirrored value if check is UVM_CHECK.

  • The mirrored value will be updated using the uvm_reg::predict() method based on the readback value.

  • The mirroring can be performed using the physical interfaces (frontdoor) or uvm_reg::peek() (backdoor).

  • If the register contains write-only fields, their content is mirrored and optionally checked only if a UVM_BACKDOOR access path is used to read the register.

1
2
3
4
5
6
7
8
9
extern virtual task mirror(output uvm_status_e      status,
input uvm_check_e check = UVM_NO_CHECK,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);

uvm_reg::update

  • Write this register if the DUT register is out-of-date with the desired/mirrored value in the abstraction class, as determined by the uvm_reg::needs_update() method.
  • The update can be performed using the using the physical interfaces (frontdoor) or uvm_reg::poke() (backdoor) access.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
extern virtual task update(output uvm_status_e      status,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);


function bit uvm_reg::needs_update();
needs_update = 0;
foreach (m_fields[i]) begin
if (m_fields[i].needs_update()) begin
return 1;
end
end
endfunction: needs_update
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
task uvm_reg::update(output uvm_status_e      status,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
uvm_reg_data_t upd;

status = UVM_IS_OK;

if (!needs_update()) return;

// Concatenate the write-to-update values from each field
// Fields are stored in LSB or MSB order
upd = 0;
foreach (m_fields[i])
upd |= m_fields[i].XupdateX() << m_fields[i].get_lsb_pos();

write(status, upd, path, map, parent, prior, extension, fname, lineno);
endtask: update

uvm_reg::predict

  • Update the mirrored and desired value for this register.
  • Predict the mirror (and desired) value of the fields in the register based on the specified observed value on a specified address map, or based on a calculated value.
  • See uvm_reg_field::predict() for more details.
1
2
3
4
5
6
7
8
9
10
11
12
13
extern virtual function bit predict (uvm_reg_data_t    value,
uvm_reg_byte_en_t be = -1,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_path_e path = UVM_FRONTDOOR,
uvm_reg_map map = null,
string fname = "",
int lineno = 0);

extern virtual function void do_predict
(uvm_reg_item rw,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_reg_byte_en_t be = -1);

uvm_reg::do_predict

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
function void uvm_reg::do_predict(uvm_reg_item      rw,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_reg_byte_en_t be = -1);

uvm_reg_data_t reg_value = rw.value[0];
m_fname = rw.fname;
m_lineno = rw.lineno;

if (rw.status ==UVM_IS_OK )
rw.status = UVM_IS_OK;

if (m_is_busy && kind == UVM_PREDICT_DIRECT) begin
`uvm_warning("RegModel", {"Trying to predict value of register '",
get_full_name(),"' while it is being accessed"})
rw.status = UVM_NOT_OK;
return;
end

foreach (m_fields[i]) begin
rw.value[0] = (reg_value >> m_fields[i].get_lsb_pos()) &
((1 << m_fields[i].get_n_bits())-1);
m_fields[i].do_predict(rw, kind, be>>(m_fields[i].get_lsb_pos()/8));
end

rw.value[0] = reg_value;

endfunction: do_predict

uvm_reg_field::do_predict

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
function void uvm_reg_field::do_predict(uvm_reg_item      rw,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_reg_byte_en_t be = -1);

uvm_reg_data_t field_val = rw.value[0] & ((1 << m_size)-1);

if (rw.status != UVM_NOT_OK)
rw.status = UVM_IS_OK;

// Assume that the entire field is enabled
if (!be[0])
return;

m_fname = rw.fname;
m_lineno = rw.lineno;

case (kind)

UVM_PREDICT_WRITE:
begin
uvm_reg_field_cb_iter cbs = new(this);

if (rw.path == UVM_FRONTDOOR || rw.path == UVM_PREDICT)
field_val = XpredictX(m_mirrored, field_val, rw.map);

m_written = 1;

for (uvm_reg_cbs cb = cbs.first(); cb != null; cb = cbs.next())
cb.post_predict(this, m_mirrored, field_val,
UVM_PREDICT_WRITE, rw.path, rw.map);

field_val &= ('b1 << m_size)-1;

end

UVM_PREDICT_READ:
begin
uvm_reg_field_cb_iter cbs = new(this);

if (rw.path == UVM_FRONTDOOR || rw.path == UVM_PREDICT) begin

string acc = get_access(rw.map);

if (acc == "RC" ||
acc == "WRC" ||
acc == "WSRC" ||
acc == "W1SRC" ||
acc == "W0SRC")
field_val = 0; // (clear)

else if (acc == "RS" ||
acc == "WRS" ||
acc == "WCRS" ||
acc == "W1CRS" ||
acc == "W0CRS")
field_val = ('b1 << m_size)-1; // all 1's (set)

else if (acc == "WO" ||
acc == "WOC" ||
acc == "WOS" ||
acc == "WO1" ||
acc == "NOACCESS")
return;
end

for (uvm_reg_cbs cb = cbs.first(); cb != null; cb = cbs.next())
cb.post_predict(this, m_mirrored, field_val,
UVM_PREDICT_READ, rw.path, rw.map);

field_val &= ('b1 << m_size)-1;

end

UVM_PREDICT_DIRECT:
begin
if (m_parent.is_busy()) begin
`uvm_warning("RegModel", {"Trying to predict value of field '",
get_name(),"' while register '",m_parent.get_full_name(),
"' is being accessed"})
rw.status = UVM_NOT_OK;
end
end
endcase

// update the mirror with predicted value
m_mirrored = field_val;
m_desired = field_val;
this.value = field_val;

endfunction: do_predict

uvm_reg_field::XpredictX

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
function uvm_reg_data_t uvm_reg_field::XpredictX (uvm_reg_data_t cur_val,
uvm_reg_data_t wr_val,
uvm_reg_map map);
uvm_reg_data_t mask = ('b1 << m_size)-1;

case (get_access(map))
"RO": return cur_val;
"RW": return wr_val;
"RC": return cur_val;
"RS": return cur_val;
"WC": return '0;
"WS": return mask;
"WRC": return wr_val;
"WRS": return wr_val;
"WSRC": return mask;
"WCRS": return '0;
"W1C": return cur_val & (~wr_val);
"W1S": return cur_val | wr_val;
"W1T": return cur_val ^ wr_val;
"W0C": return cur_val & wr_val;
"W0S": return cur_val | (~wr_val & mask);
"W0T": return cur_val ^ (~wr_val & mask);
"W1SRC": return cur_val | wr_val;
"W1CRS": return cur_val & (~wr_val);
"W0SRC": return cur_val | (~wr_val & mask);
"W0CRS": return cur_val & wr_val;
"WO": return wr_val;
"WOC": return '0;
"WOS": return mask;
"W1": return (m_written) ? cur_val : wr_val;
"WO1": return (m_written) ? cur_val : wr_val;
"NOACCESS": return cur_val;
default: return wr_val;
endcase

`uvm_fatal("RegModel", "uvm_reg_field::XpredictX(): Internal error");
return 0;
endfunction: XpredictX

uvm_reg::reset , uvm_reg_field::reset

Resetting a register model sets the mirror to the reset value specified in the model

uvm_reg::reset

1
2
3
4
5
6
7
8
9
10
function void uvm_reg::reset(string kind = "HARD");
foreach (m_fields[i])
m_fields[i].reset(kind);
// Put back a key in the semaphore if it is checked out
// in case a thread was killed during an operation
void'(m_atomic.try_get(1));
m_atomic.put(1);
m_process = null;
Xset_busyX(0);
endfunction: reset

uvm_reg_field::reset

1
2
3
4
5
6
7
8
9
10
11
12
13
function void uvm_reg_field::reset(string kind = "HARD");

if (!m_reset.exists(kind))
return;

m_mirrored = m_reset[kind];
m_desired = m_mirrored;
value = m_mirrored;

if (kind == "HARD")
m_written = 0;

endfunction: reset

uvm_reg_field::randomize

uvm_reg_field::pre_randomize()

Update the only publicly known property value with the current desired value so it can be used as a state variable should the rand_mode of the field be turned off.

value is m_desired if rand_mode is off.

1
2
3
function void uvm_reg_field::pre_randomize();
value = m_desired;
endfunction: pre_randomize

uvm_reg_field::post_randomize

1
2
3
function void uvm_reg_field::post_randomize();
m_desired = value;
endfunction: post_randomize

misc

1
typedef  bit unsigned [`UVM_REG_DATA_WIDTH-1:0]  uvm_reg_data_t ;
1
2
3
4
5
6
7
8
9
10
11
12
13
// Enum: uvm_predict_e
//
// How the mirror is to be updated
//
// UVM_PREDICT_DIRECT - Predicted value is as-is
// UVM_PREDICT_READ - Predict based on the specified value having been read
// UVM_PREDICT_WRITE - Predict based on the specified value having been written
//
typedef enum {
UVM_PREDICT_DIRECT,
UVM_PREDICT_READ,
UVM_PREDICT_WRITE
} uvm_predict_e;

UVM REG RALF & IP-XACT

UVM Register Abstraction Layer Generator User Guide, S-2021.09-SP1, December 2021

User guide for the IEEE 1685 Standard for IP-XACT

uvm_reg_field

There are no properties for unused or reserved fields, and unlike register arrays

ralf

  • bytes: the register size, default is N*8 > all field
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
register CTRL {
left_to_right;
field unused {bits 6;}
field HC1R {
bits 1; access rw ;
coverpoint { bins x = {0,1}}
}
field HC1E {bits 1 ; access rw }
field unused {bits 2; }
field HC0R {bits 1; access rw }
field HCOE {bits 1 ; access rw }
field unused {bits 2; }
field BL {bits 1; access rw }
field CSL {bits 1 ; access rw}
field VSL {bits 1 ; access rw}
field HSL {bits 1 ; access rw}
field PC {bits 1 ; access rw}
field CD {bits 2 ; access rw}
field VBL {bits 2 ; access rw}
field CBSWE {bits 1 ; access rw}
field VBSWE {bits 1 ; access rw}
field CBSIE {bits 1 ; access rw}
field VBSIE {bits 1 ; access rw}
field HIE {bits 1 ; access rw}
field VIE {bits 1 ; access rw}
field VEN {bits 1 ; access rw}
}

generated sv

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
function void build();
this.HC1R = uvm_reg_field::type_id::create("HC1R",,get_full_name());
this.HC1R.configure(this, 1, 23, "RW", 0, 1'h0, 0, 0, 0);
this.HC1E = uvm_reg_field::type_id::create("HC1E",,get_full_name());
this.HC1E.configure(this, 1, 22, "RW", 0, 1'h0, 0, 0, 0);
this.HC0R = uvm_reg_field::type_id::create("HC0R",,get_full_name());
this.HC0R.configure(this, 1, 19, "RW", 0, 1'h0, 0, 0, 0);
this.HCOE = uvm_reg_field::type_id::create("HCOE",,get_full_name());
this.HCOE.configure(this, 1, 18, "RW", 0, 1'h0, 0, 0, 0);
this.BL = uvm_reg_field::type_id::create("BL",,get_full_name());
this.BL.configure(this, 1, 15, "RW", 0, 1'h0, 0, 0, 0);
this.CSL = uvm_reg_field::type_id::create("CSL",,get_full_name());
this.CSL.configure(this, 1, 14, "RW", 0, 1'h0, 0, 0, 0);
this.VSL = uvm_reg_field::type_id::create("VSL",,get_full_name());
this.VSL.configure(this, 1, 13, "RW", 0, 1'h0, 0, 0, 0);
this.HSL = uvm_reg_field::type_id::create("HSL",,get_full_name());
this.HSL.configure(this, 1, 12, "RW", 0, 1'h0, 0, 0, 0);
this.PC = uvm_reg_field::type_id::create("PC",,get_full_name());
this.PC.configure(this, 1, 11, "RW", 0, 1'h0, 0, 0, 0);
this.CD = uvm_reg_field::type_id::create("CD",,get_full_name());
this.CD.configure(this, 2, 9, "RW", 0, 2'h0, 0, 0, 0);
this.VBL = uvm_reg_field::type_id::create("VBL",,get_full_name());
this.VBL.configure(this, 2, 7, "RW", 0, 2'h0, 0, 0, 0);
this.CBSWE = uvm_reg_field::type_id::create("CBSWE",,get_full_name());
this.CBSWE.configure(this, 1, 6, "RW", 0, 1'h0, 0, 0, 0);
this.VBSWE = uvm_reg_field::type_id::create("VBSWE",,get_full_name());
this.VBSWE.configure(this, 1, 5, "RW", 0, 1'h0, 0, 0, 0);
this.CBSIE = uvm_reg_field::type_id::create("CBSIE",,get_full_name());
this.CBSIE.configure(this, 1, 4, "RW", 0, 1'h0, 0, 0, 0);
this.VBSIE = uvm_reg_field::type_id::create("VBSIE",,get_full_name());
this.VBSIE.configure(this, 1, 3, "RW", 0, 1'h0, 0, 0, 0);
this.HIE = uvm_reg_field::type_id::create("HIE",,get_full_name());
this.HIE.configure(this, 1, 2, "RW", 0, 1'h0, 0, 0, 0);
this.VIE = uvm_reg_field::type_id::create("VIE",,get_full_name());
this.VIE.configure(this, 1, 1, "RW", 0, 1'h0, 0, 0, 0);
this.VEN = uvm_reg_field::type_id::create("VEN",,get_full_name());
this.VEN.configure(this, 1, 0, "RW", 0, 1'h0, 0, 0, 0);
endfunction: build

uvm_reg_block

ralf

  • bytes : bus width
1
2
3
4
5
6
7
8
9
10
11
block vga_lcd  {
bytes 4;
endian little;

register STAT (`RAL_HDL_PATH.wbs.stat[31:0]) @0004;
register HTIM (`RAL_HDL_PATH.htim[31:0]) @0008;
register CTRL (`RAL_HDL_PATH.wbs.ctrl[31:0]) @00010;
register VTIM (`RAL_HDL_PATH.vtim[31:0]) @0012;
register C1CR (`RAL_HDL_PATH.c1cr[31:0]) @0016;
memory CLUT1 @'h0C00;
}

generated sv

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
this.default_map = create_map("", 0, 4, UVM_LITTLE_ENDIAN, 0);

this.STAT = ral_reg_STAT::type_id::create("STAT",,get_full_name());
this.STAT.configure(this, null, "");
this.STAT.build();
this.STAT.add_hdl_path('{ '{"wbs.stat[31:0]", -1, -1}});
this.default_map.add_reg(this.STAT, `UVM_REG_ADDR_WIDTH'h4, "RW", 0);

this.HTIM = ral_reg_HTIM::type_id::create("HTIM",,get_full_name());
this.HTIM.configure(this, null, "");
this.HTIM.build();
this.HTIM.add_hdl_path('{'{"htim[31:0]", -1, -1} });
this.default_map.add_reg(this.HTIM, `UVM_REG_ADDR_WIDTH'h8, "RW", 0);

this.CTRL = ral_reg_CTRL::type_id::create("CTRL",,get_full_name());
this.CTRL.configure(this, null, "");
this.CTRL.build();
this.CTRL.add_hdl_path('{'{"wbs.ctrl[31:0]", -1, -1} });
this.default_map.add_reg(this.CTRL, `UVM_REG_ADDR_WIDTH'hA, "RW", 0);

this.VTIM = ral_reg_VTIM::type_id::create("VTIM",,get_full_name());
this.VTIM.configure(this, null, "");
this.VTIM.build();
this.VTIM.add_hdl_path('{ '{"vtim[31:0]", -1, -1} });
this.default_map.add_reg(this.VTIM, `UVM_REG_ADDR_WIDTH'hC, "RW", 0);

this.C1CR = ral_reg_C1CR::type_id::create("C1CR",,get_full_name());
this.C1CR.configure(this, null, "");
this.C1CR.build();
this.C1CR.add_hdl_path('{ '{"c1cr[31:0]", -1, -1} });
this.default_map.add_reg(this.C1CR, `UVM_REG_ADDR_WIDTH'h10, "RW", 0);

this.CLUT1 = ral_mem_CLUT1::type_id::create("CLUT1",,get_full_name());
this.CLUT1.configure(this, "");
this.CLUT1.build();
this.default_map.add_mem(this.CLUT1, `UVM_REG_ADDR_WIDTH'hC00, "RW", 0);

1
2
3
4
5
6
7
class ral_reg_CTRL extends uvm_reg;
class ral_reg_STAT extends uvm_reg;
class ral_reg_HTIM extends uvm_reg;
class ral_reg_VTIM extends uvm_reg;
class ral_reg_C1CR extends uvm_reg;
class ral_mem_CLUT1 extends uvm_mem;
class ral_block_vga_lcd extends uvm_reg_block;

ralgen command

1
ralgen -uvm -t dut_regmodel0 vga_lcd_env.ralf

BYTE or HALFWORD access

User is verifying 32 bit registers and the design also allows the BYTE (8 bits) and HALFWORD (16 bits) accesses.

this is achieved by setting the bit_addressing=0 field in the uvm_reg_block::create_map function.

Using create_map in uvm_reg_block, you can change the type of addressing scheme you want to use; namely BYTE or HALFWORD.

create_map

1
2
3
4
5
virtual function uvm_reg_map create_map( string name,
uvm_reg_addr_t base_addr,
int unsigned n_bytes,
uvm_endianness_e endian,
bit byte_addressing)

Creates an address map with the specified name, then configures it with the following properties:

Parameter Description
base_addr It is the base address for the map. All registers, memories, and sub-blocks within the map will be at offsets to this address.
n_bytes It is the byte-width of the bus on which this map is used
endian It is the endian format. See uvm_endianness_e for possible values.
byte_addressing It specifies whether consecutive addresses referred are 1 byte apart (TRUE) or n_bytes apart (FALSE). Default is TRUE.
  • For HALFWORD addressing, you should call create_map the following way:
1
default_map = create_map(get_name(), 0, 2, UVM_LITTLE_ENDIAN, 0); // 32 bit registers offset are 0x00, 0x02, 0x04
  • For WORD addressing
1
default_map = create_map(get_name(), 0, 4, UVM_LITTLE_ENDIAN, 0); // 32 bit registers offset are 0x00, 0x01, 0x02
  • For BYTE addressing (default) :
1
default_map = create_map(get_name(), 0, 4, UVM_LITTLE_ENDIAN, 1); // 32 bit registers offset are 0x00, 0x04, 0x08
  • BYTE width and byte addressing
1
default_map = create_map(get_name(), 0, 1, UVM_LITTLE_ENDIAN, 1); // 32 bit registers offset are 0x00, 0x04, 0x08 

uvm_reg_block::create_map

Create an address map in this block

n_bytes - the byte-width of the bus on which this map is used

byte_addressing - specifies whether consecutive addresses refer are 1 byte apart (TRUE) or n_bytes apart (FALSE). Default is TRUE.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function uvm_reg_map uvm_reg_block::create_map(string name,
uvm_reg_addr_t base_addr,
int unsigned n_bytes,
uvm_endianness_e endian,
bit byte_addressing=1);

uvm_reg_map map;

if (this.locked) begin
`uvm_error("RegModel", "Cannot add map to locked model");
return null;
end

map = uvm_reg_map::type_id::create(name,,this.get_full_name());
map.configure(this,base_addr,n_bytes,endian,byte_addressing);

this.maps[map] = 1;
if (maps.num() == 1)
default_map = map;

return map;
endfunction

uvm_reg_map::add_reg

The register is located at the specified address offset from this maps configured base address.

The number of consecutive physical addresses occupied by the register depends on the width of the register and the number of bytes in the physical interface corresponding to this address map.

If unmapped is TRUE, the register does not occupy any physical addresses and the base address is ignored. Unmapped registers require a user-defined frontdoor to be specified.

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
function void uvm_reg_map::add_reg(uvm_reg rg,  
uvm_reg_addr_t offset,
string rights = "RW",
bit unmapped=0,
uvm_reg_frontdoor frontdoor=null);

if (m_regs_info.exists(rg)) begin
`uvm_error("RegModel", {"Register '",rg.get_name(),
"' has already been added to map '",get_name(),"'"})
return;
end

if (rg.get_parent() != get_parent()) begin
`uvm_error("RegModel",
{"Register '",rg.get_full_name(),"' may not be added to address map '",
get_full_name(),"' : they are not in the same block"})
return;
end

rg.add_map(this);

begin
uvm_reg_map_info info = new;
info.offset = offset;
info.rights = rights;
info.unmapped = unmapped;
info.frontdoor = frontdoor;
m_regs_info[rg] = info;
end
endfunction

Register Defines

`UVM_REG_ADDR_WIDTH

Maximum address width in bits

Default value is 64. Used to define the type.

1
2
3
`ifndef UVM_REG_ADDR_WIDTH
`define UVM_REG_ADDR_WIDTH 64
`endif

`UVM_REG_DATA_WIDTH

Maximum data width in bits

Default value is 64. Used to define the type.

1
2
3
`ifndef UVM_REG_DATA_WIDTH
`define UVM_REG_DATA_WIDTH 64
`endif

Generic RALF Features and IP-XACT Mapping

field

image-20220314212141988

register

addressOffset

a register has an addressOffset that describes the location of the register expressed in addressUnitBits as offset to the starting address of the containing addressBlock or the containing registerFile

addressUnitBits

The addressUnitBits element describes the number of bits of an address increment between two consecutive addressable units in the addressSpace. If addressUnitBits is not described, then its value defaults to 8, indicating a byte-addressable addressSpace

image-20220314212217722

block

image-20220314212251012

memory

image-20220314212427347

Global Handles in UVM

Mechanism in UVM-1.1

1
2
3
4
5
function void test_base::start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
uvm_top.print_topology(); // Will not compile in UVM-1.2
factory.print(); // Will not compile in UVM-1.2
endfunction

Global handles uvm_top and factory in uvm_pkg have been removed in UVM-1.2 and later

Mechanism in UVM-1.1 and UVM-1.2

Call the get() method of the class to retrieve the singleton handle.

1
2
3
4
5
function void test_base::start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
uvm_root::get().print_topology(); // Works in UVM-1.1 & UVM-1.2
uvm_factory::get().print(); // Works in UVM-1.1 & UVM-1.2
endfunction

Mechanism Only in UVM-1.2

1
2
3
4
5
function void test_base::start_of_simulation_phase(uvm_phase phase);
uvm_coreservice_t cs = uvm_coreservice_t::get();
cs.get_root().print_topology();
cs.get_factory().print();
endfunction

uvm_coreservice_t is the uvm-1.2 mechanism for accessing all the central UVM services such as uvm_root,uvm_factory, uvm_report_server, etc.

1
2
3
4
5
6
7
// Using the uvm_coreservice_t:
uvm_coreservice_t cs;
uvm_factory f;
uvm_root top;
cs = uvm_coreservice_t::get();
f = cs.get_factory();
top = cs.get_root();

uvm_config_db::set & precedence rule

https://vlsiverify.com/uvm/uvm_config_db-in-uvm#Precedence_Rule

Two Precedence rules

There are two precedence rules applicable to uvm_config_db. In the build_phase,

  1. A set() call in a context higher up the component hierarchy takes precedence over a set() call that occurs lower in the hierarchical path.
  2. On having same context field, the last set() call takes precedence over the earlier set() call.

Example for rule - 1

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
`include "uvm_macros.svh"
import uvm_pkg::*;

class component_A extends uvm_component;
int id;
`uvm_component_utils(component_A)

function new(string name = "component_A", uvm_component parent = null);
super.new(name, parent);
id = 1;
endfunction

function display();
`uvm_info(get_type_name(), $sformatf("inside component_A: id = %0d", id), UVM_LOW);
endfunction
endclass

class component_B extends component_A;
int receive_value;
int id;

`uvm_component_utils(component_B)

function new(string name = "component_B", uvm_component parent = null);
super.new(name, parent);
id = 2;
endfunction

function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db #(int)::get(this, "*", "value", receive_value))
`uvm_fatal(get_type_name(), "get failed for resource in this scope");
endfunction

function display();
`uvm_info(get_type_name(), $sformatf("inside component_B: id = %0d, receive_value = %0d", id, receive_value), UVM_LOW);
endfunction
endclass

class env extends uvm_env;
`uvm_component_utils(env)
component_A comp_A;
component_B comp_B;

function new(string name = "env", uvm_component parent = null);
super.new(name, parent);
endfunction

function void build_phase(uvm_phase phase);
super.build_phase(phase);
comp_A = component_A ::type_id::create("comp_A", this);
comp_B = component_B ::type_id::create("comp_B", this);

uvm_config_db #(int)::set(this, "*", "value", 200);
endfunction

task run_phase(uvm_phase phase);
super.run_phase(phase);
void'(comp_A.display());
void'(comp_B.display());
endtask
endclass

class my_test extends uvm_test;
bit control;
`uvm_component_utils(my_test)
env env_o;

function new(string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction

function void build_phase(uvm_phase phase);
super.build_phase(phase);
////////////////////////////////////////////////////
env_o = env::type_id::create("env_o", this); // line 99

uvm_config_db #(int)::set(null, "*", "value", 100); // line 101
////////////////////////////////////////////////////
endfunction

function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
uvm_top.print_topology();
endfunction
endclass

module tb_top;
initial begin
run_test("my_test");
end
endmodule
1
2
3
4
5
6
7
8
9
10
11
12
UVM_INFO /xcelium20.09/tools//methodology/UVM/CDNS-1.2/sv/src/base/uvm_root.svh(605) @ 0: reporter [UVMTOP] UVM testbench topology:
--------------------------------------
Name Type Size Value
--------------------------------------
uvm_test_top my_test - @1810
env_o env - @1877
comp_A component_A - @1922
comp_B component_B - @1953
--------------------------------------

UVM_INFO testbench.sv(14) @ 0: uvm_test_top.env_o.comp_A [component_A] inside component_A: id = 1
UVM_INFO testbench.sv(36) @ 0: uvm_test_top.env_o.comp_B [component_B] inside component_B: id = 2, receive_value = 100

As you can see , the uvm_config_db::set is after the factory type_id::create, but receive_value is still 100, I believe that build_phase of upper hierarchy finish first before create lower components.

But the recommendation in literature and many web is place uvm_config_db::set before create

Simple Example for rule - 2

1
2
uvm_config_db #(int)::set(null, "*", "value", 100);
uvm_config_db #(int)::set(null, "*", "value", 200);

[complete code]

1
2
3
4
5
6
7
8
9
10
11
12
UVM_INFO /xcelium20.09/tools//methodology/UVM/CDNS-1.2/sv/src/base/uvm_root.svh(605) @ 0: reporter [UVMTOP] UVM testbench topology:
--------------------------------------
Name Type Size Value
--------------------------------------
uvm_test_top my_test - @1810
env_o env - @1877
comp_A component_A - @1924
comp_B component_B - @1955
--------------------------------------

UVM_INFO testbench.sv(14) @ 0: uvm_test_top.env_o.comp_A [component_A] inside component_A: id = 1
UVM_INFO testbench.sv(36) @ 0: uvm_test_top.env_o.comp_B [component_B] inside component_B: id = 2, receive_value = 200

Another Example for rule - 2

The set in super.build_phase and set in current build_phase are at the same hierarchy, so rule-2 apply

base_test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class base_test extends uvm_test;

// component macro
`uvm_component_utils(base_test)

router_tb tb;

// component constructor
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new

// UVM build_phase()
function void build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_int::set(this, "*", "recording_detail", 1);
uvm_config_wrapper::set(this, "tb.yapp.tx_agent.sequencer.run_phase",
"default_sequence",
yapp_5_packets::get_type());
tb = router_tb::type_id::create("tb", this);
endfunction : build_phase

endclass : base_test

test2 - One

yapp_incr_payload_seq override the yapp_5_packets in super.build_phase(phase), yapp_incr_payload_seq is used.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class test2 extends base_test;

// component macro
`uvm_component_utils(test2)

// component constructor
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new

function void build_phase(uvm_phase phase);
yapp_packet::type_id::set_type_override(short_yapp_packet::get_type());
super.build_phase(phase);
uvm_config_wrapper::set(this, "tb.yapp.tx_agent.sequencer.run_phase",
"default_sequence",
yapp_incr_payload_seq::get_type());
endfunction : build_phase

endclass : test2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
---------------------------------------------------------------------------------------------------------------------                   
Name Type Size Value
---------------------------------------------------------------------------------------------------------------------
req short_yapp_packet - @3045
length integral 6 'h5
addr integral 2 'h1
payload da(integral) 5 -
[0] integral 8 'h0
[1] integral 8 'h1
[2] integral 8 'h2
[3] integral 8 'h3
[4] integral 8 'h4
parity integral 8 'h11
parity_type parity_t 1 GOOD_PARITY
packet_delay integral 32 'd19
begin_time time 64 0
depth int 32 'd2
parent sequence (name) string 21 yapp_incr_payload_seq
parent sequence (full name) string 61 uvm_test_top.tb.yapp.tx_agent.sequencer.yapp_incr_payload_seq
sequencer string 39 uvm_test_top.tb.yapp.tx_agent.sequencer
---------------------------------------------------------------------------------------------------------------------

test2 - Two

the yapp_5_packets in super.build_phase(phase) overide yapp_incr_payload_seq in test2's build_phase, yapp_5_packets is used

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class test2 extends base_test;

// component macro
`uvm_component_utils(test2)

// component constructor
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new

function void build_phase(uvm_phase phase);
yapp_packet::type_id::set_type_override(short_yapp_packet::get_type());
uvm_config_wrapper::set(this, "tb.yapp.tx_agent.sequencer.run_phase",
"default_sequence",
yapp_incr_payload_seq::get_type());
super.build_phase(phase);
endfunction : build_phase

endclass : test2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
--------------------------------------------------------------------------------------------------------------                          
Name Type Size Value
--------------------------------------------------------------------------------------------------------------
req short_yapp_packet - @3176
length integral 6 'h6
addr integral 2 'h1
payload da(integral) 6 -
[0] integral 8 'h4f
[1] integral 8 'hbe
[2] integral 8 'hb
[3] integral 8 'h1d
[4] integral 8 'h72
[5] integral 8 'hc5
parity integral 8 'h49
parity_type parity_t 1 GOOD_PARITY
packet_delay integral 32 'd0
begin_time time 64 40
depth int 32 'd2
parent sequence (name) string 14 yapp_5_packets
parent sequence (full name) string 54 uvm_test_top.tb.yapp.tx_agent.sequencer.yapp_5_packets
sequencer string 39 uvm_test_top.tb.yapp.tx_agent.sequencer
--------------------------------------------------------------------------------------------------------------

complex project topology in UVM

topo.drawio

compile

  • -incdir for all UVC's `include
  • all pkg.sv
  • all if.sv
  • all RTL DUT
  • uvm top module

uvm top.sv

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
module tb_top;

// import the UVM library
import uvm_pkg::*;

// include the UVM macros
`include "uvm_macros.svh"

// import the yapp UVC
import yapp_pkg::*;
import hbus_pkg::*;
import channel_pkg::*;
import clock_and_reset_pkg::*;

// include the test library file
`include "router_tb.sv"
`include "router_test_lib.sv"

initial begin
yapp_vif_config::set(null, "uvm_test_top.tb.yapp.tx_agent.*", "vif", hw_top.in0);
hbus_vif_config::set(null, "uvm_test_top.tb.hbus.*", "vif", hw_top.hif );
channel_vif_config::set(null, "uvm_test_top.tb.chan0.*", "vif", hw_top.ch0);
channel_vif_config::set(null, "uvm_test_top.tb.chan1.*", "vif", hw_top.ch1);
channel_vif_config::set(null, "uvm_test_top.tb.chan2.*", "vif", hw_top.ch2);
clock_and_reset_vif_config::set(null, "uvm_test_top.tb.clock_and_reset.*", "vif", hw_top.clk_rst_if);
run_test();
end


endmodule : tb_top

RTL top

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
module hw_top;

// Clock and reset signals
logic [31:0] clock_period;
logic run_clock;
logic clock;
logic reset;

// YAPP Interface to the DUT
yapp_if in0(clock, reset);
// reset will now be generated by the Clock and Reset UVC
// input: clock; output: reset, run_clock, clock_period
clock_and_reset_if clk_rst_if(.clock(clock), .reset(reset), .run_clock(run_clock), .clock_period(clock_period));
hbus_if hif(.clock(clock), .reset(reset));

channel_if ch0(.clock(clock), .reset(reset));
channel_if ch1(.clock(clock), .reset(reset));
channel_if ch2(.clock(clock), .reset(reset));

// CLKGEN module generates clock
clkgen clkgen (
.clock(clock),
.run_clock(run_clock),
.clock_period(clock_period)
);

yapp_router dut(
...


endmodule

pkg.sv

`include all related uvm class in pgk.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package yapp_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"

typedef uvm_config_db#(virtual yapp_if) yapp_vif_config;

`include "yapp_packet.sv"
`include "yapp_tx_monitor.sv"
`include "yapp_tx_sequencer.sv"
`include "yapp_tx_seqs.sv"
`include "yapp_tx_driver.sv"
`include "yapp_tx_agent.sv"
`include "yapp_env.sv"

endpackage

if.sv

1
2
3
4
5
6
7
8
9
10
11
interface yapp_if (input clock, input reset );
timeunit 1ns;
timeprecision 100ps;

import uvm_pkg::*;
`include "uvm_macros.svh"

import yapp_pkg::*;
...

endinterface : yapp_if

Hierarchical Connections and Exports in UVM

imp and port connectors are sufficient for modeling most connections, but there is third connector, export, which is used exclusively in hierarchical connections.

Normally hierarchical routing is not required. A port on an UVC monitor can be connected directly to an imp in a scoreboard by specifying full hierarchical pathname (e.g., env.agent.monitor.analysis_port). The structure of an UVC is fixed and so the user knows to look in the monitor component for the analysis ports.

However the internal hierarchy of a module UVC is more arbitrary, and it may be convenient to route all the module UVC connectors to the top level to allow use without knowledge of the internal structure.

On the initiator side

  • ports are routed up the hierarchy via other port instances

On the target side

  • only the component which defines the communication method is allowed to have an imp instance. So we need a third object to route connections up the target side hierarchy - export

The hierarchical route must be connected at each level in the direction of control flow:

1
initiator.connect(target)

Connection rules are as follows:

  • port initiators can be connected to port, export or imp targets
  • export initiators can be connected to export or imp targets
  • imp cannot be connection initiator. imp is target only and is always the last connection object on a route

uvm_analysis_port

1
2
3
class uvm_analysis_port # (
type T = int
) extends uvm_port_base # (uvm_tlm_if_base #(T,T))

uvm_analysis_port

1
2
3
class uvm_analysis_port # (
type T = int
) extends uvm_port_base # (uvm_tlm_if_base #(T,T))

uvm_analysis_imp

1
2
3
4
class uvm_analysis_imp #(
type T = int,
type IMP = int
) extends uvm_port_base #(uvm_tlm_if_base #(T,T))

QA

  1. What are the three distinct functions of a scoreboard?

    Reference model, expected data storage and comparison

  2. To how many consumer components can an analysis port be connected?

    Any number, including zero

  3. What are the names of the two declarations made available by the following macro:

    1
    `uvm_analysis_imp_decl(_possible)

    Subclass uvm_analysis_imp_possible

    Method write_possible

  4. Why should a scoreboard clone the data received by a write method?

    The write method only sends a reference, therefore if the sending component uses the same reference for every data item, overwriting of data in the storage function of the scoreboard is possible without cloning.

Debug topology and Check config usage in UVM

Topology of the test

1
2
3
function void test_base::start_of_simulation_phase(uvm_phase phase);
uvm_root::get().print_topology(); // defaults to table printer
endfunction

The default printer policy is uvm_default_table_printer

There are three default printer policies that the uvm_pkg provides:

uvm_default_table_printer uvm_default_tree_printer uvm_default_line_printer

Check all configuration settings

Check all configuration settings in a components configuration table to determine if the setting has been used, overridden or not used. When recurse is 1 (default), configuration for this and all child components are recursively checked. This function is automatically called in the check phase, but can be manually called at any time.

To get all configuration information prior to the run phase, do something like this in your top object:

1
2
3
function void start_of_simulation_phase(uvm_phase phase);
check_config_usage();
endfunction

UVM phase

image-20220221230440017

  • Functions are executed bottom-up (Except for build and final phases , which are executed top-down)
  • Tasks are forked into concurrent executing threads

project topology in UVM

Most user-defined UVM classes are placed in separate files

Classes can only be compiled from a module or package scope

Recommendation:

  • Include related class files into a package
  • Import the package into modules where required

The UVM library is supplied in the package uvm_pkg

  • Import package to access library
  • The macro file uvm_macros.svh must be included separately

yapp_packet.sv

1
2
3
class yapp_packet extends uvm_sequence_item;
...
endclass : yapp_packet

yapp_pkg.sv

1
2
3
4
5
6
package  yapp_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"

`include "yapp_packet.sv"
endpackage : yapp_pkg

run.f for xrun

1
2
3
4
5
6
7
8
9
10
11
// 64 bit option for AWS labs
-64

-uvmhome CDNS-1.2

// include directories
-incdir ../sv

// compile files
../sv/yapp_pkg.sv
./top.sv

Functional Coverage Modeling

Coverage Options

  • Explicit (user defined)
    • Fully and clearly expressed within sources
      • Sequence and temporal coverage using assertions
      • Data-oriented coverage using covergroup
    • Planned and defined by the verification team
  • Implicit
    • Derived or computed from source
      • Code coverage measured by the simulator
    • May be defined outside the verification team
    • May be implied by the verification interface
      • e.g., an industry standard protocol

Explicit Coverage in Systemverilog

Assertions for control-oriented coverage

  • Defined as procedural statements
  • CANNOT be defined in a class
1
2
3
4
5
6
7
8
property req_gnt (cyc);
@(posedge clk)
$rose(req) ##0 (req && !gnt)[cyc] ##1 gnt;
endproperty

cover property (req_gnt(3));
cover property (req_gnt(4));
cover property (req_gnt(5));

Control-oriented coverage uses SystemVerilog Assertion (SVA) syntax and the cover directive. It is used to cover sequences of signal values over time. Assertions cannot be declared or "covered" in a class declaration, so their use in an UVM verification environment is restricted to interface only

Covergroup for data-oriented coverage

  • CAN be declared as a class member and created in class constructor
  • Used in interface and module UVCs
1
2
3
4
5
6
7
8
9
10
covergroup cg @(posedge clk);
len: coverpoint pkt.lenght {
illegal_bins zero = {0};
bins sml = {[1:10]}
...
}
addrxlen : cross pkt.addr, len;
endgroup

cg cg1 = new();

Data-oriented coverage uses the covergroup construct. Covergroups can be declared and created in classes, therefore they are essential for explicit coverage in a UVM verification environment.

Interface Monitor Coverage

covergroup new constructor is called directly off the declaration of the covergroup, it does not have a separate instance name

1
collected_pkts_cq = new();

The covergroup instance must be created in the class constructor, not a UVM build_phase or other phase method

image-20220403210225837

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class yapp_tx_monitor extends uvm_monitor;
// constructor

covergroup collected_pkts_cg;
...
endgroup

yapp_packet packet_collected;

task run_phase(uvm_phase phase);
collect_packet();
...
endtask

task collect_packet();
// read RTL signals via interface
// reconstruct packet
collected_pkts_cg.sample();
...
endtask

...
endclass
Module UVC Coverage

Typical module UVC coverage:

  • Routing - packets flow from input ports to all legal outputs
  • latency - all packets received within speicied delay

The module UVC monitor receives data from the interface monitors via analysis ports, so the implementation of the analysis write function is a convenient place to put coverage code

image-20220403212529981

run_test search testcase in UVM

It depends on the simulator:

  • QuestaSim, Xcelium: You have to import pkg or `include file in top testbench
  • VCS: VCS automatically search testcase in other Compilation Units
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
// test_pkg.sv
package sim_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"

class my_test extends uvm_test;
`uvm_component_utils(my_test)

function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new

task run_phase(uvm_phase phase);
`uvm_info(get_type_name(), "this is run_phase of my_test", UVM_LOW)
endtask : run_phase
endclass : my_test

class its_test extends uvm_test;
`uvm_component_utils(its_test)

function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new

task run_phase(uvm_phase phase);
`uvm_info(get_type_name(), "this is run_phase of its_test", UVM_LOW)
endtask : run_phase
endclass : its_test

endpackage : sim_pkg
1
2
3
4
5
6
7
8
9
// tb_top.sv
module tb_top;
import uvm_pkg::*;
//import sim_pkg::*;

initial begin
run_test();
end
endmodule
simulator cmd result
VCS vcs -sverilog -ntb_opts uvm-1.2 test_pkg.sv tb_top.sv
./simv +UVM_TESTNAME=my_test
Xcelium xrun -64 -uvmhome CDNS-1.2 test_pkg.sv tb_top.sv +UVM_TESTNAME=my_test
QuestaSim vlog test_pkg.sv tb_top.sv -L $QUESTA_HOME/uvm-1.2
vsim -c -do "run -all;exit" +UVM_TESTNAME=my_test work.tb_top -L $QUESTA_HOME/uvm-1.2

Xcelium log:

UVM_WARNING @ 0: reporter [BDTYP] Cannot create a component of type 'my_test' because it is not registered with the factory. UVM_FATAL @ 0: reporter [INVTST] Requested test from command line +UVM_TESTNAME=my_test not found. UVM_INFO /home/EDA/Cadence/XCELIUM2109/tools/methodology/UVM/CDNS-1.2/sv/src/base/uvm_report_catcher.svh(705) @ 0: reporter [UVM/REPORT/CATCHER]

QuestaSim log:

# UVM_INFO verilog_src/questa_uvm_pkg-1.2/src/questa_uvm_pkg.sv(277) @ 0: reporter [Questa UVM] QUESTA_UVM-1.2.3 # UVM_INFO verilog_src/questa_uvm_pkg-1.2/src/questa_uvm_pkg.sv(278) @ 0: reporter [Questa UVM] questa_uvm::init(+struct) # UVM_WARNING @ 0: reporter [BDTYP] Cannot create a component of type 'my_test' because it is not registered with the factory. # UVM_FATAL @ 0: reporter [INVTST] Requested test from command line +UVM_TESTNAME=my_test not found. # UVM_INFO verilog_src/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER]

solution:

uncomment //import sim_pkg::*; in tb_top.sv

Efficient Sequence Objection in UVM

Objections are handled in pre/post body decalared in a base sequence class

This is efficient for all sequence execution options:

  • Default sequences use body objections

  • Test sequences use test objections

  • Subsequences use objections of the root sequence which calls them

1
2
3
4
5
6
7
8
9
10
11
12
13
class yapp_base_seq extends uvm_sequence#(yapp_packet);

task pre_body();
if(starting_phase != null);
starting_phase.raise_objection(this, get_type_name());
endtask
task post_body();
if(starting_phase != null);
starting_phase.drop_objection(this, get_type_name());
endtask

class yapp012 extends yapp_base_seq;

Set as default sequence of sequencer:

A default sequence is a root sequence, so the pre/post body methods are executed and objection are raised/dropped there

Test sequence:

A test sequence is a root sequence, so the pre/post body methods are executed. However, for a test sequence, starting_phase is null and so the objection is not handled in the sequence. The test must raise and drop objections

Subsequence:

Not a root sequence, so pre/post body methods are not executed. The root sequence which ultimately called the subsequence handles the objections, using one of the two options above.

If a sequence is call via a `uvm_do variant, the it is defined as a subsequence and its pre/post_body() methods are not executed.

objection change in UVM1.2

Raising or dropping objections directly from starting_phase is deprecated

  • must use get_starting_phase() method

  • prevents modification of phase during sequence

1
2
3
4
5
task pre_body();
uvm_phase sp = get_starting_phase();
if (sp != null)
sp.raise_objection(this, get_type_name());
endtask

VCS with customized UVM version

1
2
3
4
# uvm 1.1 customized
$ export VCS_UVM_HOME="path/to/uvm-1.1d/src"
$ vcs -full64 -debug_access+all -kdb -sverilog -ntb_opts uvm -timescale=1ns/1ps -f filelist.f
$ ./simv -gui=verdi

VCS with release UVM

1
$ vcs -full64 -debug_access+all -kdb -sverilog -ntb_opts uvm-1.2

VCS compile-time options

-kdb: Enables generating Verdi KDB database

-lca: Enables Limited Customer Availability feature, which is not fully test

+vpi: Enables the use of VPI PLI access routines.

Verilog PLI (Programming Language Interface) is a mechanism to invoke C or C++ functions from Verilog code.

-P <pli.tab>: Specifies a PLI table file

${VERDI_HOME}/share/PLI/VCS/LINUX64/novas.tb

image-20220603221739973

+define+=: Define a text macro, Test for this definition in your Verilog source code using the `ifdef compiler directive

+define+SIMULATION when compiling

`ifdef SIMULATOIN in code

-debug_access: Enables dumping to FSDB/VPD, and limited read/callback capability. Use -debug_access+classs for testbench debug, and debug_access+all for all debug capabilities. Refer the VCS user guide for more granular options for debug control under the switch debug_access and refer to debug_region for region control

-y : Specifies a Verilog library directory to search for module definitons

-v <filename>: Specifies a Verilog library file to search for module definitons

+nospecify: Suppresses module path delays and time checks in specify blocks

-l <filename>: (lower case L) Specifies a log file where VCS records compilation message and runtime messages if you include the -R, -RI, or -RIG option

+vcs+fsdbon: A compile-time substitute for $fsdbDumpvars system task. The +vcs+fsdbon switch enables dumping for the entire design. If you do not add a corresponding -debug_access* switch, then -debug_access is automatically added. Note that you must also set VERDI_HOME.

$ ./simv

FSDB Dumper for VCS, Release Verdi_S-2021.09-SP2-2, Linux x86_64/64bit, 05/22/2022 (C) 1996 - 2022 by Synopsys, Inc. *Verdi* : Create FSDB file 'novas.fsdb' *Verdi* : Begin traversing the scopes, layer (0). *Verdi* : End of traversing.

+vcs+vcdpluson: A compile-time substitute for $vcdpluson system task. The +vcs+vcdpluson switch enables dumping for the entire design. If you do not add a corresponding -debug_access* switch, then -debug_access is automatically added

$ ./simv

VCD+ Writer S-2021.09-SP2-2_Full64 Copyright (c) 1991-2021 by Synopsys Inc.

+incdir+<directory>: Specifies the directories that contain the files you specified with the `include compiler directive. You can specify more than on directory, separating each path name with the + character.

Compile time Use Model

Just add the -kdb option to VCS executables when running simulation

  • Three steps flow:

    • vlogan/vhdlan/syscan -kdb

      Compile design and generate un-resolved KDB to ./work

    • vcs -kdb -debug_access+all <other option>

      Generate elaborated KDB to ./sim.dadir

  • Two steps flow:

    • vcs -kdb -debug_access+all <other option>

      Compile design and generate elaborated KDB to ./simv.dadir

Common simv Option

-gv <gen=value>: override runtime VHDL generics *

-ucli: stop at Tcl prompt upon start-up

-i <run.tcl>: execute specified Tcl script upon start-up

-l <logfile>: create runtime logfile

-gui: create runtime logfile

-xlrm: allow relaxed/non-LRM compliant code

-cm <options>: enable coverate options

verdi binkey

SHIFT+A: Find Signal/Find Instance/Find Instport

SHIFT+S: Find Scope

module traverse

image-20220527165858163

Show Calling

Show Definition

Double-Click instance name is same with click Show Definition

Double-Click module name is same with click Show Calling

signal traverse

image-20220527201122708

Driver

Load

Double-Click signal name is same with click Driver

Verdi options

-ssf fastFile(s)|dumpFile(s)|fastFile list(s): Load FSDB (*.fsdb), virtual FSDB (*.vf) , gzipped FSDB (*.fsdb.gz), bzip2 FSDB (*.fsdb.bz2), waveform dump (*.vcd, *.vcd.gz) files, or FSDB file list (*.flst)

-simBin <simv_executable>: Specify the path of the simulation binary file.

image-20220604220225513

-dbdir: Specify the daidir (simv.daidir ) directory to load

In the VCS two-step flow, the VCS generated KDB (kdb.elab++) is saved under the simv.daidir/ directory (like simv.daidir/kdb.elab++).

-f file_name / -file file_name: Load an ASCII file containing design source files and additional simulator options

Import Design from UFE

Knowledge Database (KDB): As it compiles the design, the Verdi platform uses its internal synthesis technology to recognize and extract specific structural, logical, and functional information about the design and stores the resulting detailed design information in the KDB

The Unified Compiler Flow (UFE) uses VCS with the -kdb option and the generated simv.daidar includes the KDB information

  1. verdi -dbdir simv.daidir

    Use the new -dbdir option to specify the simv.daidir directory

  2. verdi -simBin simv

    Load simv.daidir from the same directory as simv and invoke Verdi if simv.daidir is available

  3. verdi -ssf novas.fsdb

    Load KDB automatically from FSDB,

For 2 and 3, use the -dbdir option to load simv.dadir if you have move it to somewhere else


module load vcs

module load verdi

both vcs and verdi are needed for design import

Reference Design and FSDB on the Command Line

1
verdi -f <source_file_name> -ssf <fsdb_file_name>

Where, source_file_name is the source file name and fsdb_file_name is the name of the FSDB file

reference

Verdi使用总结 URL: https://www.wenhui.space/docs/07-ic-verify/tools/verdi_userguide/

Using Verdi for Design Understanding - Driver/Load Tracing in Verdi | Synopsys

Using Verdi for Design Understanding - Connectivity Tracing and FSM Extraction in Verdi | Synopsys

Julia

create a new project

1
$ julia

]

1
(@v1.12) pkg> generate MyNewProj

;

1
shell> cd MyNewProj/

]

1
(@v1.12) pkg> activate .

pkg> dev|develop: add a local package, which not initialized by git

load custom module:

1
2
3
include("path/to/MyModule.jl")

struct0 = MyModule.Mystruct()
1
2
3
4
include("path/to/MyModule.jl")
import .MyModule:Mystruct

struct0 = Mystruct()
1
2
3
4
include("path/to/MyModule.jl")
using .MyModule # !!! don't work in Pluto

struct0 = Mystruct()

activate a Julia environment and execute a file using the command line

1
julia --project=. your_script.jl

The --project=. argument tells Julia to look for a Project.toml and Manifest.toml file in the current directory (indicated by .)


Sharing Project Environments

instantiate command to download and install all packages and their dependencies listed in Project.toml (and Manifest.toml if present)

Feature / Command activate generate resolve instantiate
Primary Goal Switch to a specific project folder's context. Create a brand new project skeleton from scratch. Recalculate the dependency tree for the current Julia version. Download and install the specific versions listed in the Manifest.
Julia Version Role Does not check version; just points Julia to a directory. Creates a Project.toml compatible with the running Julia version. Crucial: Filters package versions based on the [compat] julia field. Uses the Julia version to ensure the Manifest is valid before downloading.
Manifest Impact None. None (a manifest is only created once you add packages). Rewrites the Manifest.toml to match the current Julia environment. Follows the existing Manifest.toml to recreate the environment.
Cross-Version Use Same command works across all Julia versions. Standard way to start a project regardless of Julia version. Used to "fix" a Manifest when moving a project to a newer/older Julia version. Used to "build" the project once resolve has created a valid Manifest.
Error Handling Rarely fails (unless the path doesn't exist). Fails if the directory name is invalid or already exists. Fails if no package versions support your running Julia version. Fails if the Manifest was built for a different Julia version (pre-1.11).

Basics

parentmodule: determine the package a function in Julia originates from

names: Get a vector of the public names of a Module, excluding deprecated names

undef: undef is a special marker used when constructing arrays (including vectors) to indicate that the elements of the array should not be initialized to any specific value.

collect: return an Array of all items in a collection or iterator, collect(2:5), collect('B':'D'), collect("HELLO")

!: a function naming convention to indicate that a function mutates its arguments in place, meaning the changes will be visible outside the function. When a function is designed to modify its arguments, it is good practice to append a ! (exclamation mark) to its name

eltype: To find the type of the elements that are iterated over in a collection

typeof: To determine the specific type of any given value

1
2
3
4
5
6
7
pizza_tuple = ("hawaiian", 'S', 10.5)
typeof(pizza_tuple) # Tuple{String, Char, Float64}
eltype(pizza_tuple) # Any

arr = [1,2,3]
typeof(arr) # Vector{Int64} (alias for Array{Int64, 1})
eltype(arr) # Int64

Any: It is used to construct a heterogeneous array that can hold elements of any type, like Any[1, "hello", 3.14]

^: Repeat a regex n times (s^n is same with repeat(s, n)); Exponentiation operator

parse: convert a text string to anything else. parse(Int, "42"), parse(Float64, "42")

String * : concatenate strings, "The " * engine *

String $: string interpolation, use $(variable) instead of $variable when there is no whitespace that can clearly distinguish the variable name from the surrounding text

vec: Reshape the array as a one-dimensional column vector


dictionary

1
pizza = Dict("name" => "hawaiian", "size" => 'S', "price" => 10.5)

A problem with using a dictionary is that it requires every value to be of the same type

1
typeof(pizza)	# Dict{String, Any}

symbols

It is denoted by : (colon), followed by the name of the symbol, built-in Julia type to represent identifiers


named tuples

1
pizza = (name = "hawaiian", size = 'S', price = 10.5)

A named tuple only allows you to use symbols as keys

All types of tuples are immutable, meaning you cannot change them

implicit naming from identifiers

1
2
3
x = 0
t = (; x) # t is (x = 0,)
u = (; t.x) # u is (x = 0,)

composite type (struct) & type annotation

:: is used to annotate variables and expressions with their type. x::T means variable x should have type T. It helps Julia figure out how many bytes are needed to hold all fields in a struct

1
2
3
4
5
struct Archer
name::String
health::Int
arrows::Int
end

closure


varargs

"varargs" (variable arguments) refers to the ability of a function to accept an arbitrary number of arguments. This is achieved using the splat operator (...) in the function definition.

1
2
3
4
5
6
7
function my_sum(a, b, rest...)
total = a + b
for x in rest
total += x
end
return total
end
1
my_sum(1, 2, 3, 4, 5)	# 15

keyword arguments

a semicolon (;) separates positional arguments from keyword arguments in the function signature. All arguments to the right of the semicolon are treated as keyword arguments. They can optionally have default values


views

A view is essentially a pointer to a sub-section of another vector, but not a standalone vector itself

1
2
3
4
5
6
7
8
9
10
11
12
13
one2ten = collect(1:10);
println(one2ten)

one2five = one2ten[1:5]
one2five_view = view(one2ten, 1:5)

one2ten[1] = 10
println(one2five)
println(one2five_view)

# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# [1, 2, 3, 4, 5]
# [10, 2, 3, 4, 5]

@views is a macro that converts sliced arrays into views (pointers are much cheaper than creating copies of arrays). For more information on how to use the view syntax correctly


semicolon after steprange

placing a semicolon ; after a step range expression inside square brackets, e.g., [1:10;], changes the resulting object from a UnitRange to a Vector

1
2
3
4
5
b = [1:2:10]	# Vector{StepRange{Int64, Int64}}
a = [1:2:10;] # Vector{Int64}

size(b) # (1)
size(a) # (5)

element-wise operations

Dot syntax for operators: For binary operators like +, -, *, /, ^

Dot syntax for functions: For functions, the dot is placed after the function name.


Property destructuring

1
2
3
4
5
6
7
8
julia> (; b, a) = (a=1, b=2, c=3)
(a = 1, b = 2, c = 3)

julia> a
1

julia> b
2

One-line functions

"one-line function" also known as the compact "assignment form"

1
function_name(parameters) = expression

Anonymous Functions (Lambda Functions)

functions without a name (parameters) -> expression, often defined inline for use with higher-order functions like map, filter, or reduce

1
2
numbers = [1, 2, 3]
doubled_numbers = map(x -> 2 * x, numbers) # doubled_numbers will be [2, 4, 6]

do keyword is syntactic sugar for creating an anonymous function and passing it as the first argument to another function

1
2
3
4
map([1, 2, 3]) do x
y = x * 2
return y + 1
end
  • do starts the block.
  • x, y (optional) following do are the arguments the anonymous function receives.
  • end closes the block

CircularBuffer

Feature Vector (Standard) CircularBuffer (DataStructures.jl)
Push/Pop Front \(O(n)\) (Shifts all elements) \(O(1)\) (Updates head pointer)
Push/Pop Back \(O(1)\) (Amortized) \(O(1)\)
Random Access \(O(1)\) (Direct) \(O(1)\) (With index wrapping logic)
Capacity Dynamic: Grows as needed Fixed: Initialized at set size
Overflow Reallocates memory Overwrites the oldest data
Memory Layout Contiguous Contiguous (internally wraps)
Best Use Case General collections / Appending Sliding windows / Real-time buffers
1
2
3
4
5
6
7
8
9
10
11
12
using DataStructures

# Initialize a CircularBuffer of type Int with capacity 3
cb = CircularBuffer{Int}(3)

# Add elements
push!(cb, 1) # [1]
push!(cb, 2) # [1, 2]
append!(cb, [3, 4]) # [2, 3, 4] (1 is overwritten because capacity is 3)

# Add to the front
pushfirst!(cb, 0) # [0, 2, 3] (4 is overwritten)
  • push!(cb, item): Adds a single item to the back of the buffer. If the buffer is at its capacity, the item at the front (the oldest) is removed to make room.
  • append!(cb, collection): Adds multiple items from another collection to the back of the buffer. This is essentially a shorthand for calling push! on each element of the collection.
Feature push! append!
Input A single item A collection of items
Action Adds one element as a single entry Unpacks all elements from the input

using vs import

that’s the difference between using and import - the former brings all exported names into scope, while the latter only brings NiceStuff (the module identifier) into scope.

[https://discourse.julialang.org/t/difference-between-include-use-and-import/65918/5]


PlutoUI.Slider

image-20260118010932797



multiple dispatch

A function can take arguments of different types, and share the same name


@. : used for automatic broadcasting across an entire expression

1
2
3
4
5
6
# Manual broadcasting
y = exp.(sin.(x)) .+ 2 .* x


# Automatic broadcasting
@. y = exp(sin(x)) + 2 * x

Revise.jl Context Summary

Context Role Key Mechanism
REPL Hot-reloads functions and modules without restarting Julia. using Revise then using MyPackage
Scripts Watches standalone .jl files for changes on save. includet("path/to/script.jl")
Pluto.jl Tracks updates to external packages used in the notebook. using Revise in a cell (Internal cells are natively reactive).
Jupyter/IJulia Updates package code without needing to restart the kernel. using Revise in the first cell.
VS Code Powers the integrated "Execute" and "Debugger" workflows. Usually auto-enabled by the Julia Extension.

Makie.jl & Observables.jl

Storopoli, Huijzer and Alonso (2021). Julia Data Science. [https://juliadatascience.github.io/JuliaDataScience/]

non-mutating methods of plotting function: Figure Axis plot object in collectionFigureAxisPlot

mutating methods of plotting function: plot object

Figure + Axis + plot + axislegend

1
2
3
4
5
fig = Figure()
ax = Axis(fig[1,1])
scatterlines!(ax, 0.5:0.2:3pi, x -> -cos(x)/x, label = "cos(x)/x")
axislegend(; position=:rt) # plot function's label rather xlabel, ylabel of Axis
fig

Observable

The argument to the Observable() constructor provides both an initial value and determines the type of the observable variable.

There are two ways to access/update an observable's value - w/ .val or []

The difference is that only using [] will trigger the listener event when updating the observable value

Feature lift on
Return Value A new Observable A Handler (for disconnecting later)
Purpose To transform data (create a dependency) To perform an action (side effect)
Data Flow Value flows from parent to child Value triggers an external event
1
2
3
4
5
6
7
8
9
using GLMakie

x = Observable(0.0)

on(x) do x
println("New value of x is $x")
end

x[] = 5.0

DifferentialEquations.jl & Callback Functions

image-20260510174019169

Python

np.fft.fft vs np.fft.rfft

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import numpy as np

def custom_rfft(x):
# Ensure input is a NumPy array
x = np.asarray(x)

# 1. Compute the full standard FFT
full_fft = np.fft.fft(x)

# 2. Calculate the required output length: (n // 2) + 1
n = len(x)
rfft_len = (n // 2) + 1

# 3. Slice and return only the unique positive frequencies
return full_fft[:rfft_len]


# Test data
even_signal = [1.0, 2.0, 3.0, 4.0]
odd_signal = [1.0, 2.0, 3.0, 4.0, 5.0]

# Verification
print(np.allclose(custom_rfft(even_signal), np.fft.rfft(even_signal))) # True
print(np.allclose(custom_rfft(odd_signal), np.fft.rfft(odd_signal))) # True

np.fft.irfft

Since the discrete Fourier Transform of real input is Hermitian-symmetric, the negative frequency terms are taken to be the complex conjugates of the corresponding positive frequency terms


  • scipy.signal.residue: (H(s) = ) Converts numerator/denominator arrays into residues, poles, and a direct polynomial
  • scipy.signal.invres: (H(s) ) Converts residues, poles, and a direct polynomial back into numerator/denominator arrays

\[ H(s) = \frac{s + z}{(s + p_1)(s + p_2)} = \frac{z-p_1}{p_2-p_1}\frac{1}{s + p_1} + \frac{z-p_2}{p_1-p_2}\frac{1}{s + p_2}=\frac{r_1}{s+p_1} + \frac{r_2}{s+p_2} \]

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
# https://github.com/capn-freako/PyBERT/blob/master/src/pybert/utility/sigproc.p


def make_ctle(rx_bw: float, peak_freq: float, peak_mag: float, w: Rvec) -> tuple[Rvec, Cvec]: # pylint: disable=too-many-arguments # noqa: F405
"""
Generate the frequency response of a continuous time linear equalizer (CTLE), given the:

- signal path bandwidth,
- peaking specification, and
- list of frequencies of interest.

Args:
rx_bw: The natural (or, unequalized) signal path bandwidth (Hz).
peak_freq: The location of the desired peak in the frequency response (Hz).
peak_mag: The desired relative magnitude of the peak (dB).
w: The list of frequencies of interest (rads./s).

- The zero location is chosen, so as to provide the desired degree of peaking.
"""

p2 = -2.0 * pi * rx_bw
p1 = -2.0 * pi * peak_freq
z = p1 / pow(10.0, peak_mag / 20.0)
if p2 != p1:
r1 = (z - p1) / (p2 - p1)
r2 = 1 - r1
else:
r1 = -1.0
r2 = z - p1
b, a = invres([r1, r2], [p1, p2], [])
w, H = freqs(b, a, w)
H /= max(abs(H))

return (w, H)

np.where

There are actually two distinct call signatures:

Call Returns Use
np.where(cond, a, b) array, a/b chosen element-wise select values
np.where(cond) tuple of index arrays where cond is True find positions

Matlab

LaTeX code for the expression

1
2
3
title('$c(T) = (\eta - m) \frac{k}{q} \left( T - T_r - T \cdot \ln \frac{T}{T_r} \right) < 0$', 'Interpreter', 'latex');

ylabel('$c(T)$ mV', 'Interpreter', 'latex');

Dollar Signs ($): Wrapping the equation in $...$ tells the LaTeX engine to use "inline math mode," which ensures proper spacing and formatting for symbols.

Interpreter: Adding 'Interpreter', 'latex' at the end is the "switch" that tells MATLAB not to use its basic text engine.

image-20260502005228622



identify class of an object or variable

1
2
3
x = 42;
type_name = class(x); % Returns 'double'
disp(type_name)


zpk

Use zpk to create zero-pole-gain models

sys = zpk(zeros,poles,gain) creates a continuous-time zero-pole-gain model with zeros and poles specified as vectors and the scalar value of gain.

sys = zpk(zeros,poles,gain,ts) creates a discrete-time zero-pole-gain model with sample time ts. Set ts to -1 or [] to leave the sample time unspecified.


zpkdata

Access zero-pole-gain data

1
[z,p,k] = zpkdata(sys,'v')

the sampling time associated with that ZPK data

1
Ts = sys_d.Ts; % Returns the sampling interval (1/fs)
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
% 1. Define Continuous ZPK Model
fc = 10; % Cutoff frequency in Hz
wc = 2 * pi * fc; % Cutoff frequency in rad/s

z = []; % No zeros
p = -wc; % Single pole at -wc
k = wc; % Gain (to ensure DC gain = 1)

sys_c = zpk(z, p, k);

% 2. Convert to Discrete via Bilinear Transformation
fs = 100; % Sampling frequency in Hz
dt = 1/fs;

% 'tustin' flag is the standard name for the bilinear transformation in control theory
sys_d = c2d(sys_c, dt, 'tustin'); % 'tustin' is the Bilinear method in MATLAB

% Extract zeros, poles, and gain from sys_c
[zc, pc, kc] = zpkdata(sys_c, 'v');
[zd, pd, kd] = bilinear(zc, pc, kc, fs);
sys_dbli = zpk(zd, pd, kd, dt);


% 3. Plot and Compare Step Response
[yc, tc] = step(sys_c, 0:dt/100:dt*10);
tt = 0:dt:dt*10;
[yd, ~] = step(sys_d, tt);
[ydbli, ~] = step(sys_dbli, tt);

plot(tc/dt, yc, LineWidth=2); hold on
stairs(tt/dt, yd, 'bs--', LineWidth=2); stairs(tt/dt, ydbli, 'r*--', LineWidth=2)
title(['Step Response Comparison (fc = ', num2str(fc), 'Hz, fs = ', num2str(fs), 'Hz)']);
legend('Continuous LPF', 'Discrete LPF (c2d tustin)', 'Discrete LPF (zpk Bilinear)');
grid on; xlabel('Time (Ts)')

image-20260503212334349



syms and sym in MATLAB's Symbolic Math Toolbox both create symbolic objects, but they differ in usage and behavior.

syms: The command to create symbolic objects.

define symbolic scalar variables

1
syms t x a b;

define symbolic scalar functions that depend on the variable t

1
syms f(t) g(t) h(t) vc0(t) vc1(t) vc2(t);

create a set of symbolic row vectors with sym

1
2
m=2500;
ic=zeros(1,m);


3D surface plot, importing data from a CSV file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
clear; clc
df = readmatrix('/path/to/core_lod_sodab_sab.csv');
nsab = df(:, 3);
nsodab = df(:,4);

svtN = df(:,8)*1e3; % mV
lvtN = df(:,9)*1e3;
ulvtN = df(:,10)*1e3;
elvtN = df(:,11)*1e3;

vth_plot = svtN;

% Uses logical indexing to find rows where nsab is greater than nsodab.
% set to NaN to filter them out completely
vth_plot(nsab>nsodab) = NaN;

[nsabq, nsodabq] = meshgrid(linspace(min(nsab), max(nsab), 20), linspace(min(nsodab), max(nsodab), 20));

vth_plotq = griddata(nsab, nsodab, vth_plot, nsabq, nsodabq);

surf(nsabq, nsodabq, vth_plotq);
xlabel('CPODE(SA/SB)'); ylabel('PODE(SODA/SODB)'); title('svtN (mV)'); view(135,30)

By using NaN, the surf command will simply not render any polygons over the invalid coordinates. Your 3D plot will sharply truncate exactly at the physical layout boundary rule line (\(nsab = nsodab\))



  • [num_cell, den_cell] = tfdata(sys)

    returns cell arrays to accommodate Multi-Input Multi-Output (MIMO) systems

  • [num, den] = tfdata(sys, 'v')

    returns row vectors to accommodate Single-Input Single-Output (SISO) system

Using 'v' simplifies data handling for single systems by eliminating the need to extract data from cells using curly braces {}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
% Create a transfer function model
s = tf('s');
sys = (s + 2) / (s^2 + 3*s + 2);

% Default output (returns cell arrays)
[num_cell, den_cell] = tfdata(sys);
% num_cell 1×1 cell array is {[0 1 2]}, num_cell{1} is [0 1 2]
% den_cell 1×1 cell array is {[1 3 2]}, den_cell{1} is [1 3 2]


% Using the 'v' option (returns numerical row vectors)
[num, den] = tfdata(sys, 'v');
% num is [0 1 2]
% den is [1 3 2]

Quick Insert menu

  • Single-click anywhere on the empty white space of your model canvas.
  • Type the name of the block you want (e.g., "Gain", "Scope", or "Triggered Subsystem")

image-20260505170224276



Configure Model Layout

Rotate Clockwise: Ctrl+R.

Rotate Counterclockwise: Ctrl+Shift+R.

Flip Horizontally: Ctrl+I.

Flip Vertically: Right-click block () Rotate & Flip () Flip Block (or apply clockwise rotation twice)

Mathematica

/.: ReplaceAll command



WhenEvent[event,action]

Sequential Execution: If multiple actions are assigned to a single event (e.g., WhenEvent[cond, {action1, action2}]), they are evaluated in order.



DiscreteVariables in NDSolve: handle state variables that only change at specific, discontinuous moments rather than changing continuously with the independent variable (usually time \(t\)), solution returned will be a piecewise constant

Markdown

> [!NOTE]: General useful information

[!NOTE]

> [!IMPORTANT]: Crucial information for user success

[!IMPORTANT]

> [!WARNING]: Urgent information to avoid potential problems

[!WARNING]

> [!TIP]: Helpful advice or better ways to do things

[!TIP]

> [!CAUTION]: Negative consequences or risks of certain actions

[!CAUTION]


subscript and superscript

  1. HTML Tags (Universal Compatibility)

    Subscript syntax: text<sub>subscript</sub>

    Superscript syntax: text<sup>superscript</sup>

H<sub>2</sub>O

H2O

X<sup>2</sup>

X2

  1. LaTeX Math Blocks

text color by embedding inline HTML tags

Style Type HTML Snippet Example
Color Name <span style="color:blue">Blue Text</span> Blue Text
Hex Code <span style="color:#FF5733">Orange Text</span> Orange Text
RGB Value <span style="color:rgb(0, 128, 0)">Green Text</span> Green Text
Background Color <span style="background-color:yellow">Highlighted Text</span> Highlighted Text
Combined <span style="color:white; background-color:black">White text on black</span> White text on black

Latex

In math mode, LaTeX provides several commands to insert horizontal space of specific widths:

Command Width Name
\, 3/18 em thin space
\: 4/18 em medium space
\; 5/18 em thick space
\! −3/18 em negative thin space
\quad 1 em quad
\qquad 2 em double quad


\operatorname{...}math operator in an upright (roman) font with appropriate mathematical spacing \[ \operatorname{sinc}(x) \]



\textcolor (Argument-Based)

  • How it works: It takes exactly two arguments: \textcolor{color}{content}.
  • Scope: It limits the color strictly to the content inside the second set of curly braces.
  • Spacing: It automatically preserves proper mathematical spacing around the colored elements.
  • Best for: Coloring individual variables, specific numbers, or small terms.

\color (Switch-Based)

  • How it works: It acts as a toggle switch: \color{color}.
  • Scope: It colors everything that follows it until it hits the end of the current environment or a closing curly brace }.
  • Spacing: It can sometimes disrupt standard mathematical spacing if not enclosed in braces.
  • Best for: Coloring large sections, full equations, or entire document blocks.

Using \textcolor (Precise Targeting): takes the content as an argument

1
\textcolor{red}{E} = \textcolor{blue}{mc^2}

\[ \textcolor{red}{E} = \textcolor{blue}{mc^2} \]

Using \color (The "Bleeding" Switch): changes the color of everything after it If you do not isolate it

1
{\color{red}{E}} = \color{blue}mc^2

\[ {\color{red}{E}} = \color{blue}mc^2 \]

To make \color behave like \textcolor, you must manually wrap the command and the target character inside a hidden group using extra curly braces { ... }



Set-Builder Notation and Conditional Probability

1
P(A \mid B)

\[ P(A \mid B) \]

1
\left\{ x \in \mathbb{R} \mid x > 0 \right\}

\[ \left\{ x \in \mathbb{R} \mid x > 0 \right\} \]



evaluation bar

1
\bigg\rvert_{…}

\[ \frac{df}{dx}\bigg\rvert_{x=0} \]

1
\left. \right|

\[ \left. \frac{df}{dx}\right|_{x=0} \]



math alphabet commands

Blackboard Bold Roman/Upright Bold face Calligraphic/Script
\mathbb \mathrm \mathbf \mathcal
\(\mathbb{F}\mathbb{L}\) \(\mathrm{F}\mathrm{L}\) \(\mathbf{F}\mathbf{L}\) \(\mathcal{F}\mathcal{L}\)


implication arrow

\implies produces \(\implies\) (displays a long arrow with proper math spacing).

\Rightarrow produces \(\Rightarrow\) (displays a standard-length double right arrow).

\longrightarrow produces \(\longrightarrow\) (displays a long single right arrow)

mermaid

Mermaid is a JavaScript based diagramming and charting tool that takes Markdown-inspired text definitions and creates diagrams dynamically in the browser

graph TD;A-->B;A-->C;B-->D;C-->D;

C++

Using g++ only

conditional.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

#define na 4

int main() {
int a[na];

a[0] = 2;
for (int n = 1; n < na; n++) a[n] = a[n-1] + 1;

#ifdef DEBUG
// Only kept by preprocessor if DEBUG defined
for (int n = 0; n < na; n++) {
std::cout << "a[" << n << "] = " << a[n] << std::endl;
}
#endif

return 0;
}

-DDEBUG args

1
2
3
4
5
6
7
8
$ g++ -Wall -Wextra -Wconversion conditional.cpp -o conditional
$ ./conditional
$ g++ -Wall -Wextra -Wconversion conditional.cpp -o conditional -DDEBUG
$ ./conditional
a[0] = 2
a[1] = 3
a[2] = 4
a[3] = 5


add_definitions in CMakeLists.txt

1
2
3
4
5
6
7
8
9
cmake_minimum_required(VERSION 3.2)

option(DEBUG "Option description" OFF)

if(DEBUG)
add_definitions(-DDEBUG)
endif(DEBUG)

add_executable(cond conditional.cpp)

without debug

1
2
3
$ cmake ..
$ make
$ ./cond

with debug

1
2
3
4
5
6
7
$ cmake -DDEBUG=ON ..
$ make
$ ./cond
a[0] = 2
a[1] = 3
a[2] = 4
a[3] = 5

create & assign bump

power/ground bump

1
2
3
4
5
6
7
create_bump –cell PAD90UBMBOP –loc_type cell_center –loc $x0 $y  
create_bump –cell PAD90UBMBOP –loc_type cell_center –loc $x1 $y
...
create_bump –cell PAD90UBMBOP –loc_type cell_center –loc $xn $y
deselectAll
select_bump –bum_cell PAD90UBMBOP
assignPGBumps –nets {vss vdd_dig} -selected –checkboard

pma_dig_bumps.drawio

signal or power/ground bump

1
2
3
4
5
set bump_name [create_bump –cell PAD150PITCH –loc $bump_x $bump_y –loc_type cell_center –return_bumps_name] 
# assign power/ground bump
assignPGBumps –bumps $bump_name –nets $pin_name
# or assign to signal bump
assignSigToBump –bumps $bump_name –net $pin_name

Path Based Analysis

aocv : Re-timing the timing critical paths using the LOCV deratingfactors

path_slew_propagation : Re-timing the timing critical paths using the actual slews for thepath

aocv_path_slew_propagation : Combination of re-timing with aocv + path_slew_propagation

waveform_propagation : Re-timing with waveform effect taken into consideration during delayCal

Fix DRC Violation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
clearDrc
set drc_marker_file calibre_drc_markers.err
loadViolationReport -type Calibre -filename $drc_marker_file



foreach marker_id [dbGet -p -e top.markers.userOriginator Calibre] {



editSelect -area [dbget $object.box] -layer M4

dbSet [dbGet -p top.nets.name $net ].wires.status routed

editTrim -selected

check mode variants

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
checkDesign

check_timing

checkPlace



setDesignMode

setFPlanMode

setEcoMode

setPlaceMode

setRouteMode

setExtractRCMode

setOptMode

Common Commands

Analysis Command

Clock Concurrent Optimization Technology (CCOpt)

Database Navigation

Data Exchange

Global Skew and Local Skew

Technology File

timeDesign vs. report_timing

Parasitic Extraction

Floorplan

/2022/02/07/Innovus/Tempus Nonfunctional ECO

Innovus ECO

block level create Power Ground Pin method

1
2
3
4
editStretch x ...
selectWire ... VDD
selectWire ... GND
createPGPin -selected -onDie

createPGPin_onDie.drawio

Add endcap and tapcell

First place place hard macro and add placement halo, then execute the following code to add endcap and tapcell.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
deleteFill -prefix ENDCAP
deleteFill -prefix WELL

setEndCapMode \
-leftEdge BOUNDARY_RIGHTBWP16P90CPD \
-rightEdge BOUNDARY_LEFTBWP16P90CPD \
-leftBottomCorner BOUNDARY_NCORNERBWP16P90CPD \
-leftTopCorner BOUNDARY_PCORNERBWP16P90CPD \
-rightTopEdge FILL3BWP16P90CPD \
-rightBottomEdge FILL3BWP16P90CPD \
-topEdge "BOUNDARY_PROW2BWP16P90CPD BOUNDARY_PROW3BWP16P90CPD"
-bottomEdge "BOUNDARY_NROW2BWP16P90CPD BOUNDARY_NROW3BWP16P90CPD" \
-boundary_tap true

set_well_tap_mode \
-rule 33
-bottom_tap_cell BOUNDARY_NTAPBWP16P90CPD \
-top_tap_cell BOUNDARY_PTAPBWP16P90CPD \
-cell TAPCELLBWP16P90CPD

addEndCap
addWellTap -checkerBoard -cell TAPCELLBWP16P90CPD -cellInterval 160

endcap_tapcell_floorplan.drawio

First place place hard macro and add placement halo, then execute the following code to add endcap and tapcell.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
deleteFill -prefix ENDCAP
deleteFill -prefix WELL

setEndCapMode \
-leftEdge BOUNDARY_RIGHTBWP16P90CPD \
-rightEdge BOUNDARY_LEFTBWP16P90CPD \
-leftBottomCorner BOUNDARY_NCORNERBWP16P90CPD \
-leftTopCorner BOUNDARY_PCORNERBWP16P90CPD \
-rightTopEdge FILL3BWP16P90CPD \
-rightBottomEdge FILL3BWP16P90CPD \
-topEdge "BOUNDARY_PROW2BWP16P90CPD BOUNDARY_PROW3BWP16P90CPD"
-bottomEdge "BOUNDARY_NROW2BWP16P90CPD BOUNDARY_NROW3BWP16P90CPD" \
-boundary_tap true

set_well_tap_mode \
-rule 33
-bottom_tap_cell BOUNDARY_NTAPBWP16P90CPD \
-top_tap_cell BOUNDARY_PTAPBWP16P90CPD \
-cell TAPCELLBWP16P90CPD

addEndCap
addWellTap -checkerBoard -cell TAPCELLBWP16P90CPD -cellInterval 160

[https://github.com/StanfordAHA/PowerDomainDesign/blob/c280a35f01ad0617ff054b925c1d5c2652a0f4e1/scripts/layout_pe_tile_new.power.tcl#L94-L113]

In-design Sign-off Metal Fill Flow

Before inserting sign-off metal fill, stream out a GDSII stream file of the current database. Specify the mapping file and units that match with the rule deck you specify while inserting metal fill. If necessary, include the detailed-cell (-merge option) Graphic Database System (GDS).

PVS:

1
2
streamOut -merge $GDSFile -mode ALL -units $GDSUNITS -mapFile $GDSMAP -outputMacros pvs.fill.gds
run_pvs_metal_fill -ruleFile $DUMMYRULE -defMapFile $DEFMAP -gdsFile pvs.fill.gds -cell [dbgDesignName]

Pegasus:

1
2
streamOut -merge $GDSFile -mode ALL -units $GDSUNITS -mapFile $GDSMAP -outputMacros pegasus.fill.gds
run_pegasus_metal_fill -ruleFile $DUMMYRULE -defMapFile $DEFMAP -gdsFile pegasus.fill.gds -cell [dbgDesignName]

Just replace run_pvs_metal_fill with run_pegasus_metal_fill

Note: Innovus metal fill (e.g. addMetalFill, addViaFill, etc.) does not support 20nm and below node design rules. We strongly recommend the Pegasus/PVS metal fill solution for 20nm and below. If you have sign-off metal fill rule deck for 28nm and above available, we recommend you to move to Pegasus/PVS solution too.

  1. trimMetalFillNearNet does not check DRC rules. It only removes the metal fill with specified spacing

  2. Do not perform ECO operations after dump in sign-off metal fill (by run_pvs_metal_fill or run_pegasus_metal_fill), especially, at 20nm and below nodes.

  3. If you perform an ECO action, the tool cannot get DRC clean because trimMetalFill does not support 20nm and below node design rules.

  4. The sign-off metal fill typically does not cause DRC issues with regular wires.

The run_pvs_metal_fill command does the following:

  • Runs PVS with the fill rules to create a GDSII output file.
  • Converts the GDSII to a DEF format file based on the GDSII to DEF layermap provided.
  • Loads the resulting DEF file into Innovus.

Pegasus is similar to PVS, shown as below,

The run_pegasus_metal_fill command does the following:

  • Runs Pegasus with the fill rules to create a GDSII output file.
  • Converts the GDSII to a DEF format file based on the GDSII to DEF layermap provided.
  • Loads the resulting DEF file into Innovus.

Reference:

Innovus User Guide, Product Version 21.12, Last Updated in November 2021

How does EDI System identify spare cells in a post-mask ECO flow?

How does EDI System identify spare cells in a post-mask ECO flow?

Spare cells should have a unique string in their instance name to identify them. Then the command specifySpareGate or ecoDesign -useSpareCells patternName is run to identify the spare instances. For example, if all spare cells have _spare_ in their name then they are identified using:

1
specifySpareGate -inst *_spare_*

OR

1
ecoDesign -spareCells *_spare_* ...

Note: if you are making manual ECO changes to a netlist and converting a spare cell to a logical instance, it's important to change the instance name. Otherwise, the instance may be identified as a spare cell if a future ECO is performed because it still has the spare cell instance name.

Example

The cell to be swapped is unplaced

image-20220307002842020

pre_buf: unplaced

spare_buf: placed

innovus 49> dbGet top.insts.name

spare_buf pre_buf UDriver USink

innovus 50> dbGet top.insts.

0x7f7b03ef60e0 0x7f7b03ef6150 0x7f7b03ef6000 0x7f7b03ef6070

1
2
specifySpareGate -inst spare_*
ecoSwapSpareCell pre_buf spare_buf

image-20220307003059068

innovus 55> dbGet top.insts.name

pre_buf UDriver USink

innovus 56> dbGet top.insts.

0x7f7b03ef6150 0x7f7b03ef6000 0x7f7b03ef6070

innovus 57> dbGet top.insts.Pstatus

placed fixed fixed

Note: sparecell's pointer and name is swapped with the unplaced cell.

The cell to be swapped is placed

image-20220307004654614

innovus 62> dbGet top.insts.name

spare_buf pre_buf UDriver USink

innovus 63> dbGet top.insts.

0x7f7b03ef60e0 0x7f7b03ef6150 0x7f7b03ef6000 0x7f7b03ef6070

innovus 64> dbGet top.insts.pStatus

placed placed fixed fixed

1
2
3
4
innovus 66> specifySpareGate -inst spare_*
Specifying instance [spare_buf] as spare gate.
Specified 1 instances as spare gate.
innovus 67> ecoSwapSpareCell pre_buf spare_buf

image-20220307005254488

innovus 68> dbGet top.insts.name

spare_buf pre_buf UDriver USink

innovus 69> dbGet top.insts.

0x7f7b03ef60e0 0x7f7b03ef6150 0x7f7b03ef6000 0x7f7b03ef6070

innovus 70> dbGet top.insts.pStatus

placed placed fixed fixed

Note: sparecell's pointer and name is swapped with the placed cell.

Error in "Innovus Text Command Reference 21.12"

ecoSwapSpareCell

If the cell to be swapped is unplaced, it is mapped to the spare cell. *instName* is deleted, and its connection is transferred to the spare cell. If the cell to be swapped is placed, it is swapped with the spare cell and is renamed to *instNameSuffix* if the -suffix option is used. If a suffix is not specified, the *instName* cell is renamed to *spareCellInstName*. The *instName* cell's connections are transferred to *spareCellInstName*. The input of *instName* is tielo, based on the global connection definition.

reference

Answers to Top 10 Questions on Performing ECOs in EDI System

EE 582: Physical Design Automation of VLSI Circuits and Systems

Copy a Visio drawing

Copy a Visio drawing to Word, PowerPoint, or Excel [https://support.microsoft.com/en-us/office/copy-a-visio-drawing-to-word-powerpoint-or-excel-e301fef0-27cf-4540-9f91-901a824074ee]

  1. In Visio, open the file you want to appear in the Word document.

  2. Make sure nothing is selected, and then, on the Home tab, select Copy or press Ctrl+C

  3. paste

In Word, select where you want the Visio drawing to appear and then select Paste or press Ctrl+V

In PowerPoint or Excel, On the Home tab, select Paste > Paste Special, and then select Microsoft Visio Drawing Object

LSF (Platform Load Sharing Facility)

bjobs: Displaying Job Status

bkill: cancels pending batch jobs and sends signals to running jobs

bhosts: displays hosts and their static and dynamic resources

busers: displays information about the user who runs the command

Module System

module avail: Lists the modules currently available to load on the system

module list: Lists the modules currently loaded in the user environment.

module load|add: This loads the requested module into the active environment

module purge: To clear all modules

module rm|unload: To unload a module

module switch|swap: To change the version of a loaded software module

unlock "secured" (read-protected) PDF

1
2
3
4
5
6
7
8
# Source - https://stackoverflow.com/a/63422342
# Posted by Satish Dubey, modified by community. See post 'Timeline' for change history
# Retrieved 2026-01-05, License - CC BY-SA 4.0

import pikepdf

pdf = pikepdf.open('filepath', allow_overwriting_input=True)
pdf.save('filepath')

MATLAB Win10 Unable to open the requested feature. Error code: -202

clear AppData\Roaming\MathWorks\MATLAB

miniconda3/envs/myenv/bin/../lib/libstdc++.so.6: version `GLIBCXX_3.4.32' not found

[https://stackoverflow.com/a/73101774/8037585]

This is because you have some old versions not cleaned up while upgrading gcc.

First of all, make sure that you have the latest lib with conda install -c conda-forge libstdcxx-ng

OBS 无声录制带有声音的视频

录制视频时,想要 obs 能录制声音,但是不想人耳听到。

pactl load-module module-virtual-sink sink_name=VAC

unfortunately, Virtual audio sinks & settings gone after reboot

image-20250726071525899

image-20250726071325895


obs 无声录制带有声音的视频 [https://www.cnblogs.com/wztshine/p/17764073.html]

Virtual Audio Cable For Ubuntu [https://askubuntu.com/a/1268269/845522]

能不能设置个定时器,让录像自动停? [https://www.reddit.com/r/obs/comments/j5j1qa/comment/g7sk588/]

wget fetch a directory

1
$ wget -r -np -R "index.html*" http://example.com/configs/.vim/

Using wget to recursively fetch a directory with arbitrary files in it [https://stackoverflow.com/a/273776/8037585]

mount/umount ISO

1
2
$ sudo mount -t auto -o loop /path/to/matlab.iso /mnt/matlab
$ umount /mnt/iso

Google search tips

[https://static.googleusercontent.com/media/www.google.com/en//educators/downloads/Tips_Tricks_17x22.pdf]

image-20250706091436326

image-20250706091838077

Restart Xfce panel

1
xfce4-panel -r

[https://askubuntu.com/a/891209]

Rocky Linux 8 rpm for cadence binary and cdnshelp

qt5 and openssl

1
sudo yum install openssl compat-openssl10 qca-qt5-ossl.x86_64 openssl-devel

library preparation for EDA installation

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
libc6, gdb
dc
libncurses5-dev
libncurses5-dev:i386
g++
pstack
libelf1:i386
libgcc-4.8-dev:i386
libstdc++6:i386
gcc-multilib
g++-multilib
libc6-dbg
libc6-dbg:i386
libexpat-dev
libexpat-dev:i386libxss-dev
libxpm4
libxpm4:i386
libmng2
libxss-dev:i386
libxft2
libxft2:i386
libxmu6
libxmu6:i386
libjpeg62-dev
libjpeg62-dev:i386
gnome-core
gnome-core:i386
xfce4
libxml2:i386
libxml2, libXft-dev
libXft-dev:i386
libSM
libSM:i386
libpng3
libpng3:i386
libxi6
libxi6:i386
glibc.i686
libX11.i686
libX11-devel.i686
libX11-devel.x86_64
gcc-c++
compat-readline5
libXext.i686
libXtst.i686
redhat-lsb.i686
libXrender.i686
glibc-devel.i686
zlib.i686
ncompress.x86_64
ksh
openmotif22.i686
openmotif22.x86_64
xterm

Rocky Linux 8 extend LVM in VMware

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
1) gparted extend
2)
lvextend -l +100%FREE /dev/rl/root
3)
xfs_growfs /dev/mapper/rl-root

usefull command:
df -h
lvdisplay
fdisk -l

-----------------------------------------------------------
https://pocketadmin.tech/en/centos-8-extend-lvm/

$ sudo lvdisplay
--- Logical volume ---
LV Path /dev/rl/swap
LV Name swap
VG Name rl
LV UUID toZKEu-P5oV-6WOV-026Z-eFnI-xaSP-FgEbz5
LV Write Access read/write
LV Creation host, time myserver, 2021-12-03 21:28:03 +0800
LV Status available
# open 2
LV Size 5.00 GiB
Current LE 1280
Segments 1
Allocation inherit
Read ahead sectors auto
- currently set to 8192
Block device 253:1

--- Logical volume ---
LV Path /dev/rl/root
LV Name root
VG Name rl
LV UUID S2soRE-umc7-Z6b3-i44x-TiBO-ulnk-ETgEoj
LV Write Access read/write
LV Creation host, time myserver, 2021-12-03 21:28:03 +0800
LV Status available
# open 1
LV Size <194.00 GiB
Current LE 49663
Segments 1
Allocation inherit
Read ahead sectors auto
- currently set to 8192
Block device 253:0

compile vim from source with GUI support

1
2
3
4
# gtk3 in Rocky Linux 8.5
./configure --with-features=huge --enable-gui=gtk3 --enable-python3interp --prefix=/usr
make -j`nproc`
sudo make install

binkey

Inserting a new line below: o

above: O

To insert before the cursor: i

After: a

Before the line (home): I

Append at the end of line: A

network connection using nmcli

Problem

There is no network connection and device is not managed

1
2
3
4
$ nmcli device status
DEVICE TYPE STATE CONNECTION
eth0 ethernet unmanaged --
lo loopback unmanaged --

image-20220401011509900

solution

1
sudo nmcli networking on

Then, eth0 is connected

1
2
3
4
$ nmcli device status
DEVICE TYPE STATE CONNECTION
eth0 ethernet connected Ethernet connection 1
lo loopback unmanaged --

image-20220401011543866

What is the proper method to remove old kernels from a Red Hat Enterprise Linux system?

Red Hat Enterprise Linux 8

The YUM version 4 (based on the upstream DNF project) method for removing kernels and keeping only the latest version and running kernel:

1
$ yum remove --oldinstallonly

From the yum man page:

1
2
3
dnf [options] remove --oldinstallonly
Removes old installonly packages, keeping only latest versions and version of running
kernel.

New windows and forms appear behind the Library Manager in background when using GNOME 3

Using Red Hat Enterprise Linux 8, Rocky Linux 8 and the GNOME 3 window manager, the new Virtuoso Schematic/Layout/ADE windows and forms sometimes pop up under or below the Library Manager or on the desktop in the background instead of the foreground and cannot be seen. Sometimes, they are iconized; they do not come on the top in front, though it is the most recent window opened.

solution

Install Focus my window GNOME Shell extension

image-20221022002952578

reference

Article (11612426) Title: New windows and forms appear behind the Library Manager in background or iconized instead of foreground on RHEL and SuSE Linux in GNOME, KDE Desktop, Metacity window manager

VMware Disk Shrink

In Guest OS and run

1
sudo vmware-toolbox-cmd disk shrink /

https://superuser.com/q/211798

GitHub Pages Build and deployment

The repository visibility shall be public

image-20230701142405404

squash all git commits into one?

https://stackoverflow.com/a/9254257/8037585

As of git 1.6.2, you can use git rebase --root -i

For each commit except the first, change pick to squash.

this works find, but I had to do a forced push. be careful! git push -f

Build VMware host modules

1
2
3
4
5
6
7
8
git clone git@github.com:mkubecek/vmware-host-modules.git
cd vmware-host-modules/
git checkout origin/workstation-17.0.2
make -j`nproc`
sudo make install
sudo modprobe -v vmmon
sudo modprobe -v vmnet
sudo vmware-networks --start

AWT-EventQueue-0 Matlab

1
Renaming libstdc++.so.6 to libstdc++.so.6old solved it for me in MATLAB 2021B UBUNTU 20.04. Thanks!

[https://www.mathworks.com/matlabcentral/answers/329796-issue-with-libstdc-so-6#comment_2316900]

0%