image-20230414232256309

image-20230414232439556

Inertial delay

Inertial delay models are simulation delay models that filter pulses that are shorted than the propagation delay of Verilog gate primitives or continuous assignments (assign #5 y = ~a;)

COMBINATIONAL LOGIC ONLY !!!

  • Inertial delays swallow glitches
  • sequential logic implemented with procedure assignments DON'T follow the rule

continuous assignments

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
`timescale 1ns/100ps
module tb;

reg in;
/////////////////////////////////////////////////////
wire out;
assign #2.5 out = in;
/////////////////////////////////////////////////////
initial begin
in = 0;
#16;
in = 1;
#2;
in = 0;
#10;
in = 1;
#4;
in = 0;
end

initial begin
#50;
$finish();
end

endmodule

image-20220317000509716

procedure assignment - combinational logic

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
`timescale 1ns/100ps
module tb;

reg in;
reg out;

//////////// combination logic ////////////////////////
always @(*)
#2.5 out = in;
///////////////////////////////////////////////////////
/* the above code is same with following code
always @(*) begin
#2.5;
out = in;
end
*/
initial begin
in = 0;
#16;
in = 1;
#2;
in = 0;
#10;
in = 1;
#4;
in = 0;
end

initial begin
#50;
$finish();
end

endmodule

image-20220316235257361

procedure assignment - sequential logic

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
`timescale 1ns/100ps
module tb;
reg clk;

reg in;
reg out;

always begin
clk = 0;
#5;
forever begin
clk = ~clk;
#5;
end
end
//////////// sequential logic //////////////////
always @(posedge clk)
#2.5 out <= in;
///////////////////////////////////////////////
initial begin
in = 0;
#16;
in = 1;
#2;
in = 0;
#10;
in = 1;
end

initial begin
#50;
$finish();
end

endmodule

image-20220316235620168

As shown above, sequential logic DON'T follow inertial delay

Transport delay

Transport delay models are simulation delay models that pass all pulses, including pulses that are shorter than the propagation delay of corresponding Verilog procedural assignments

  • Transport delays pass glitches, delayed in time
  • Verilog can model RTL transport delays by adding explicit delays to the right-hand-side (RHS) of a nonblocking assignment
1
2
always @(*)
y <= #5 ~a;

nonblocking assignment

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
`timescale 1ns/100ps
module tb;

reg in;
reg out;
/////////////// nonblocking assignment ///
always @(*) begin
out <= #2.5 in;
end
/////////////////////////////////////////
initial begin
in = 0;
#16;
in = 1;
#2;
in = 0;
#10;
in = 1;
#4;
in = 0;
end

initial begin
#50;
$finish();
end

endmodule

image-20220317003146825

blocking assignment

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
`timescale 1ns/100ps
module tb;

reg in;
reg out;
/////////////// blocking assignment ///
always @(*) begin
out = #2.5 in;
end
/////////////////////////////////////////
initial begin
in = 0;
#16;
in = 1;
#2;
in = 0;
#10;
in = 1;
#4;
in = 0;
end

initial begin
#50;
$finish();
end

endmodule

image-20220317003819457

It seems that new event is discarded before previous event is realized.

reference

Verilog Nonblocking Assignments With Delays, Myths & Mysteries

Correct Methods For Adding Delays To Verilog Behavioral Models

Article (20488135) Title: Selecting Different Delay Modes in GLS (RAK) URL: https://support.cadence.com/apex/ArticleAttachmentPortal?id=a1O3w000009bdLyEAI

Article (20447759) Title: Gate Level Simulation (GLS): A Quick Guide for Beginners URL: https://support.cadence.com/apex/ArticleAttachmentPortal?id=a1Od0000005xEorEAE

What Is Real Number Modeling?

  • model analog blocks operation as signal flow model
  • only the digital solver is used for high-speed simulation
    • Event-driven
    • No convergence issues, because no analog solver is used
  • Five different language standards support real number modeling:
    • wreal (wired-real) ports in Verilog-AMS
    • real data type in VHDL
    • real data type in Verilog
    • real variables and nettypes in SystemVerilog (SV)
    • real types in e

Benefits of RNM

  • Most analog circuits that need to be modeled for MS verification at the SoC level can be described in terms of real-valued voltages or currents
  • RNM is a mixed approach, borrowing concepts from both continuous and discrete domains
    • The values are floating-point (real) number.
    • Time is discrete; the real signals change values based on discrete events
  • Applicability of RNM is bounded primarily by signal-flow model style
  • Migrating analog behavior from the analog domain to the event or pseudo-analog domain can bring huge benefits without sacrificing too much accuracy
  • Simulation is executed by a digital simulation engine without need for the analog solver
  • Hence real-number modeling enables very high performance simulation of mixed-signal systems

Limitations of RNM

  • connecting real or wreal signals to electrical signals requires careful consideration
    • Too conservative an approach can lead to large numbers of timepoints
    • Too liberal an approach can lead to losing signal accuracy
  • Time accuracy limited by the discrete sampling approach and the `timescale setting - no continuous signals anymore
  • Limited capability for combination of signals by wiring outputs together
    • Requires assumptions about impedances to do simple merging

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

EMX ports

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)

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

1
/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} \\ Q1 &= \frac{Im(Z_1)}{Re(Z_1)} \\ L2 &= \frac{Im(Z_2)}{2\pi f} \\ Q2 &= \frac{Im(Z_2)}{Re(Z_2)} \end{align}\]

EMX only plot L1 and Q1

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

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} \]

Differential inductor

Both with and without shield apply

\[\begin{align} L_{diff} &= \frac{Im(Z_{diff})}{2\pi f} \\ 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} \\ 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}}\\ Z_2 &= \frac {1}{Y_{22}} \end{align}\]

Then single-ended cap and Q \[\begin{align} C_1 &= -\frac{1/Im(Z_1)}{2\pi f} \\ Q_1 &= -\frac{Im(Z_1)}{Re(Z_1)} \\ C_2 &= -\frac{1/Im(Z_2)}{2\pi f} \\ Q_2 &= -\frac{Im(Z_2)}{Re(Z_2)} \\ C_{12} &= -\frac{Im(Y_{12})}{2\pi f}\\ 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} \\ Q_{diff} &= -\frac{Im(Z_{diff})}{Re(Z_{diff})} \end{align}\]

image-20220331022224865

Tline

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

\[\begin{align} Z_o &= Z_{11}\\ Z_s &= \frac{1}{Y_{11}}\\ Z_0 &= \sqrt{Z_o*Z_s} \end{align}\]

propagation constant is given as \[\begin{align} \gamma &= \frac{1}{2}\log\left( \frac{Z_0+Z_s}{Z_0-Z_s} \right) \\ &= \alpha + j\beta \end{align}\] where \(\alpha\) is attenuation constant and \(\beta\) is phase constant

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

Note EMX plot the absolute value of \(\alpha\) and \(\beta\)

image-20220630215343377image-20220630215418372

image-20220630215630849

Transformer

image-20231015002344332

image-20231015002403566

[IC Prophet GDSII 文件使用和仿真测试说明]

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

Foundary model

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.

Tips

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).

Self-Resonant Frequency

image-20240802210109935

\[ f_\text{SRF} = \frac{1}{2\pi \sqrt{LC}} \] The SRF of an inductor is the frequency at which the parasitic capacitance of the inductor resonates with the ideal inductance of the inductor, resulting in an extremely high impedance. The inductance only acts like an inductor below its SRF.

image-20240802205835601

[Understanding RF Inductor Specifications, https://www.ece.uprm.edu/~rafaelr/inel5325/SupportDocuments/doc671_Selecting_RF_Inductors.pdf]

[RFIC-GPT Wiki, https://wiki.icprophet.net/]

reference

Tips on Specifying Ports in EMX [link]

Using 'Cadence pins' as ports with access direction in EMX simulations [[link](Article (20496398) Title: Using 'Cadence pins' as ports with access direction in EMX simulations URL: https://support.cadence.com/apex/ArticleAttachmentPortal?id=a1O3w00000AH2OfEAL)]

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

Reference

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

User guide for the IEEE 1685 Standard for IP-XACT

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(JIR) & Jitter Impulse Response(JIR)

image-20220313231013645

image-20220313231027512

image-20220313231038542

Example

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

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

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: Regular Papers, vol. 56, no. 1, pp. 17-39, Jan. 2009, doi: 10.1109/TCSI.2008.931647.

Phase-Locked Frequency Generation and Clocking : Architectures and Circuits for Modern Wireless and Wireline Systems by Woogeun Rhee (2020, Hardcover)

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"

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;

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

Parallel Compensation is also known as Lead Compensation, Pole-Zero Compensation

image-20220307234938855

Note: The dominant pole is at output of the first stage, i.e. \(\frac{1}{R_{EQ}C_{EQ}}\).

image-20221006001114562

Pole and Zero in transfer function

Design with operational amplifiers and analog integrated circuits / Sergio Franco, San Francisco State University. – Fourth edition

image-20221005220854411

\[ Y = \frac{1}{R_1} + sC_1+\frac{1}{R_c+1/SC_c} \]

\[\begin{align} Z &= \frac{1}{\frac{1}{R_1} + sC_1+\frac{1}{R_c+1/SC_c}} \\ &= \frac{R_1(1+sR_cC_c)}{s^2R_1C_1R_cC_c+s(R_1C_c+R_1C_1+R_cC_c)+1} \end{align}\] If \(p_{1c} \ll p_{3c}\), two real roots can be found \[\begin{align} p_{1c} &= \frac{1}{R_1C_c+R_1C_1+R_cC_c} \\ p_{3c} &= \frac{R_1C_c+R_1C_1+R_cC_c}{R_1C_1R_cC_c} \end{align}\]

The additional zero is \[ z_c = \frac{1}{R_cC_c} \] Given \(R_c \ll R\) and \(C_c \gg C\) \[\begin{align} p_{1c} &\simeq \frac{1}{R_1(C_c+C_1)} \simeq \frac{1}{R_1C_c}\\ p_{3c} &= \frac{1}{R_cC_1}+\frac{1}{R_cC_c}+\frac{1}{R_1C_1} \simeq \frac{1}{R_cC_1} \end{align}\]

The output pole is unchanged, which is \[ p_2 = \frac{1}{R_LC_L} \] We usually cancel \(p_2\) with \(z_c\), i.e. \[ R_cC_c=R_LC_L \]

Phase margin

unity-gain frequency \(\omega_t\) \[ \omega_t = A_\text{DC}\cdot P_{1c} =\frac{g_{m1}g_{m2}R_L}{C_c} \]

  1. PM=45\(^o\) \[ p_{3c} = \omega_t \] Then, \(C_c\) and \(R_c\) can be obtained

    \[\begin{align} R_c &= \sqrt{\frac{R_1}{C_1\cdot A_{DC}\cdot p_2}}=\sqrt{\frac{R_1\cdot R_LC_L}{C_1\cdot A_{DC}}} \\ C_c &= \sqrt{\frac{A_{DC}\cdot C_1}{R_1\cdot p_2}}=\sqrt{\frac{A_{DC}\cdot C_1 \cdot R_LC_L}{R_1}} \end{align}\]

  2. PM=60\(^o\) \[ p_{3c} = 2\cdot\omega_t \] Then, \(C_c\) and \(R_c\) can be obtained \[\begin{align} R_c &= \sqrt{\frac{R_1}{C_1\cdot 2A_{DC}\cdot p_2}} = \sqrt{\frac{R_1\cdot R_LC_L}{C_1\cdot 2A_{DC}}} \\ &= \sqrt{\frac{C_L}{2g_{m1}g_{m2}C_1}}\\ C_c &= \sqrt{\frac{2A_{DC}\cdot C_1}{R_1\cdot p_2}} = \sqrt{\frac{2A_{DC}\cdot C_1 \cdot R_LC_L}{R_1}} \\ &= R_L\sqrt{2g_{m1}g_{m2}C_1C_L} \end{align}\]

    for the unity-gain frequency \(\omega_t\) we find \[ \omega_t = \sqrt{\frac{1}{2}\cdot \frac{g_{m1}g_{m2}}{C_1C_L}} \] The parallel compensation shows a remarkably good result. The new 0 dB frequency lies only a factor \(\sqrt{2}\) lower than the theoretical maximum

To increase \(\phi_m\), we need to raise \(C_c\) a bit while lowering \(R_c\) in proportion in order to maintain pole-zero cancellation. This causes \(p_{1c}\) and \(p_{3c}\) to split a bit further apart.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
clc;
clear;

fd = 84*1e3; % dominant freq, unit: Hz
fnd = 3.25*1e6; % unit: Hz
C = 478*1e-15;
R = 1/fd/(2*pi)/C;
Adc = 10^(80/20);

ri = 2; % PM=45: 1; PM=60: 2
Rc = (R/C/fnd/2/pi/ri/Adc)^0.5; % compensation resistor
Cc = (ri*Adc*C/fnd/2/pi/R)^0.5; % compensation capacitor

wzc = 1/2/pi/Rc/Cc; % zero frequency

reference

Viola Schäffer, Designing Amplifiers for Stability, ISSCC 2021 Tutorials

R.Eschauzier "Wide Bandwidth Low Power Operational Amplifiers", Delft University Press, 1994.

Gene F. Franklin, J. David Powell, and Abbas Emami-Naeini. 2018. Feedback Control of Dynamic Systems (8th Edition) (8th. ed.). Pearson. 6.7 Compensation

Application Note AN-1286 Compensation for the LM3478 Boost Controller

ECEN 607 Advanced Analog Circuit Design Techniques Spring 2017 URL: Lect 1D Op-Amps Stability and Frequency Compensation Techniques

Sergio Franco, San Francisco State University, Design with Operational Amplifiers and Analog Integrated Circuits, 4/e

J. H. Huijsing, 6.2.2.1 Two-GA-stage Parallel Compensation (PC), "Operational Amplifiers, Theory and Design, 3rd ed. New York: Springer, 2017"

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

maximum frequency

A conventional inverter-based ring oscillator consists of a single loop of an odd number of inverters. While compact, easy to design and tunable over a wide frequency range, this oscillator suffers from several limitations.

  • it is not possible to increase the number of phases while maintaining the same oscillation frequency since the frequency is inversely proportional to the number of inverters in the loop. In other words, the time resolution of the oscillator is limited to one inverter delay and cannot be improved below this limit.
  • the number of phases that can be obtained from this oscillator is limited to odd values. Otherwise, if an even number of inverters is used, the circuit remains in a latched state and does not oscillate.

To overcome the limitations of conventional ring oscillators, multi-paths ring oscillator (MPRO) is proposed. Each phase can be driven by two or more inverters, or multi-paths instead of having each phase in oscillator driven by a single inverter, or single path.

One thing that makes the MPRO design problem even more complicated is its property of having multiple possible oscillation modes. Without a clear understanding of what makes one of these modes dominant, it is very likely that a designer might end-up having an oscillator that can start-up each time in a different oscillation mode depending on the initial state of the oscillator.

image-20220320155440541

In practive, the oscillator starts first from a linear mode of operation where all the buffers are indeed acting as linear transconductors. All oscillation modes that have mode gains, \(a_n\), lower than the actual dc gain of the inverter, \(a_0\), start to grow. As the oscillation amplitude grows, the effective gain of the inverter drops due to nonlinearity. Consequently, modes with higher mode gain die out and only the mode that requires the minimum gain continues to oscillate and hence is the dominant mode.

The dominant mode is dependent only on the relative sizing vector

maximum oscillation frequency

The oscillation frequency of the dominant mode of any MPRO having any arbitrary coupling structure and number of phases is \[ f_{n^*} = \frac {1}{2\pi}\frac {(a_0-1) \cdot \sum_{i=1}^{N}x_isin\left ( \frac {2\pi n^*(i-1)}{N} \right)}{(a_0\tau _p - \tau _o)\cdot \sum_{i=1}^{N}-x_icos\left( \frac{2\pi n^*(i-1)}{N}+(\tau _o - \tau _p) \right)} \] image-20220320172952925

A linear increase in the maximum possible normalized oscillation frequency as the number of stages increases provided that the dc gain of the buffer sufficient to provide the required amplification

image-20220320170324494

image-20220320170523390

assuming unlimited dc gain and zero mode gain margins

mode stability

A common problem in MPRO design is the stability of the dominant oscillation mode. Mode stability refers to whether the MPRO always oscillates at the same mode regardless of the initial conditions of the oscillator. This problem is especially pronounced for MPROs with a large number of phases. This is due to the existence of many modes and the very small differences in the value of the mode gain of adjacent modes if the MPRO is not well designed.

In general, when the mode gain difference between two modes is small, the oscillator can operate in either one depending on initial conditions.

coupling configurations and simulation results

image-20220320165217231

Abou-El-Sonoun, A. A. (2012). High Frequency Multiphase Clock Generation Using Multipath Oscillators and Applications. UCLA. ProQuest ID: AbouElSonoun_ucla_0031D_10684. Merritt ID: ark:/13030/m57p9288. Retrieved from https://escholarship.org/uc/item/75g8j8jt

frequency stability

A problem associated with the design of MPROs is the existence of different possible modes of oscillation. Each of these modes is characterized by a different frequency, phase shift and phase noise.

Linear delay-stage model (mode gain)

mode gain is based on the linear model, independent of process but depends on coupling structure (coupling configuration, size ratio).

The inverting buffer modeled as a linear transconductor. The input-output relationship of a single buffer scaled by \(h_i\) and driving the input capacitance of a similar buffer can be expressed as \[\begin{align} h_ig_mv_{in}(t) + h_ig_oV_{out}(t)+h_iC_g\frac {dV_{out}(t)}{dt} &= 0 \\ a_nV_{in}(t)+V_{out}(t)+\tau \frac {V_{out}(t)}{dt} &= 0 \end{align}\] where \(g_m\) is the transconductance, \(g_o\) is the output conductance, \(C_g\) is the buffer input capacitance which also acts as the load capacitance for the driving buffer, and \(a_n = \frac {g_m}{g_o}\) is the linear dc gain of the buffer, and \(\tau=\frac {C_g}{g_o}\) is a time constant.

Similarly, \(V_1\), the output of the first stage in MPRO can be expressed \[ \sum_{i=1}^{N}h_ig_mV_i(t)+\sum_{i=1}^{N}h_ig_oV_i(t)+\sum_{i=1}^{N}h_iC_g\frac {dV_it(t)}{dt} = 0 \] Defining the fractional sizing factors and the total sizing factor as \(x_i=\frac{h_i}{H}\) and \(H=\sum_{i=1}^{N}h_i\) \[ a_n\sum_{i=1}^{N}x_iV_i(t) + V_1(t)+\tau\frac {dV_i(t)}{dt} = 0 \] where \(a_n = \frac {g_m}{g_o}\) and \(\tau=\frac {C_g}{g_o}\) are same dc gain and time constant defined previously

Since the total phase shift around the loop should be multiples of \(2\pi\), the oscillation waveform at the ith node can be expressed as \[ V_i(t) = V_o \cos(\omega_nt-\Delta \varphi \cdot i) \] where \(\omega_n\) is the oscillation frequency and \(\Delta \varphi = \frac {2\pi n}{N}\), \(N\) is the number of stages in the oscillator and \(n\) can take values between \(0\) and \(N-1\)

Plug \(V_i(t)\) into differential equation, we get \[ a_n\sum_{i=1}^{N}x_i\cos(\omega_n t-\frac{2\pi n}{N}i)+\cos(\omega_n t-\frac{2\pi n}{N}) - \omega_n \tau \sin(\omega_n t-\frac{2\pi n}{N}) = 0 \] By equating the \(cos(\omega_n t)\) and \(sin(\omega_n t)\) terms of the above equation, we get expressions for the oscillation frequency of the nth mode and the minimum dc gain required for this mode to exist. we refer to this gain as the mode gain \[\begin{align} \omega_n\tau &= \frac {\sum_{i=1}^{N}x_i \cdot \sin(\frac{2\pi n}{N}(i-1))}{-\sum_{i=1}^{N}x_i \cdot \cos(\frac{2\pi n}{N}(i-1))} \\ a_n &= \frac {1}{-\sum_{i=1}^{N}x_i \cdot \cos(\frac{2\pi n}{N}(i-1))} \end{align}\] where \(a_n\) should be greater than \(0\) for a existent mode

In practice, the oscillator starts first from a linear mode of operation where all the buffers are indeed acting as linear transconductors. All oscillation modes that have mode gains \(a_n\) lower than the actual dc gain of the inverter \(a_o\) start to grow. As the oscillation amplitude grows, the effective gain of the inverter drops due to nonlinearity. Consequently, modes with higher mode gain die out and only the mode that requires the minimum gain continues to oscillate and hence is the dominant mode

A. A. Hafez and C. K. Yang, "Design and Optimization of Multipath Ring Oscillators," in IEEE Transactions on Circuits and Systems I: Regular Papers, vol. 58, no. 10, pp. 2332-2345, Oct. 2011, doi: 10.1109/TCSI.2011.2142810.

Simulation-based approach

GCHECK is an automated verification tool that validate whether a ring oscillator always converges to the desired mode of operation regardless of the initial conditions and variability conditions. This is the first tool ever reported to address the global convergence failures in presence of variability. It has been shown that the tool can successfully validate a number of coupled ring oscillator circuits with various global convergence failure modes (e.g. no oscillation, false oscillation, and even chaotic oscillation) with reasonable computational costs such as running 1000-point Monte-Carlo simulations for 7~60 initial conditions (maximum 4 hours).

  • The verification is performed using a predictive global optimization algorithm that looks for a problematic initial state from a discretized state space
  • despite the finite number of initial state candidates considered and finite number of Monte-Carlo samples to model variability, the proposed algorithm can verify the oscillator to a prescribed confidence level

image-20220320183344877

  • The observation that the responses of a circuit with nearby initial conditions are strongly correlated with respect to common variability conditions enables us to explore a discretized version of the initial condition space instead of the continuous one.
  • the settling time increases as the initial state gets farther away from the equilibrium state allowed us to use the settling time as a guidance metric to find a problematic initial condition.

Selecting the Next Initial Condition Candidate to Evaluate

To determine whether the algorithm should continue or terminate the search for a new maximum, the algorithm estimates the probability of finding a new initial condition with the longer settling time, based on the information obtained with the previously-evaluated initial conditions.

GCHECK EXAMPLE

1
python gcheck_osc.py input.scs

output log:

1
2
3
4
5
6
7
8
Step 1/4: Simulating the setting-time distribution with the reference initial condition
...
Step 2/4: Simulating the setting-time distribution for randomly-selected initial probes
...
Step 3/4: Searching for Problematic Initial Conditions
...
Step 4/4: Reporting Verification Results and Statistics
...

image-20220320201718018

T. Kim, D. -G. Song, S. Youn, J. Park, H. Park and J. Kim, "Verifying start-up failures in coupled ring oscillators in presence of variability using predictive global optimization," 2013 IEEE/ACM International Conference on Computer-Aided Design (ICCAD), 2013, pp. 486-493, doi: 10.1109/ICCAD.2013.6691161.GCHECK: Global Convergence Checker for Oscillators

0%