-
Notifications
You must be signed in to change notification settings - Fork 112
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #359 from Subhampal9/OTA
Files for FVF based OTA, Analog Vibes, Chipathon
- Loading branch information
Showing
12 changed files
with
2,107 additions
and
0 deletions.
There are no files selected for viewing
88 changes: 88 additions & 0 deletions
88
openfasoc/generators/glayout/glayout/flow/blocks/composite/fvf_based_ota/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
# FVF based super class AB OTA | ||
This topology of class AB OTA uses flipped voltage followers as voltage shifters to boost gain and slew rate. It can provide **slew performance independent of bias current**. LCMFB is also used to boost the slew rate even more. | ||
## Pcells used | ||
 | ||
 | ||
### Flipped Voltage Follower | ||
Used as voltage shifters. Also used to crete a low voltage current mirror for biasing. Pcell can be found under ``` glayout/flow/blocks/elementary/FVF/ ``` | ||
### Transmission gate | ||
Due to unavailability of resistors in Glayout, trasmission gates were used as LCMFB resistors. However, this limits the slew performance. Pcell can be found here ``` glayout/flow/blocks/elementary/trasmission_gate/ ``` | ||
### Low Voltage Current Mirror | ||
Low voltage current mirror are used to set a **bias current of 10uA**. The python code can be found in this directory itself. | ||
### Others | ||
Some already existing pcells were used, like current mirrors and four_transistor_interdigitized block. | ||
## Parameterization | ||
``` | ||
def super_class_AB_OTA( | ||
pdk: MappedPDK, | ||
input_pair_params: tuple[float,float]=(4,2), | ||
fvf_shunt_params: tuple[float,float]=(2.75,1), | ||
local_current_bias_params: tuple[float,float]=(3.76,3.0), | ||
diff_pair_load_params: tuple[float,float]=(9,1), | ||
ratio: int=1, | ||
current_mirror_params: tuple[float,float]=(2.25,1), | ||
resistor_params: tuple[float,float,float,float]=(0.5,3,4,4), | ||
global_current_bias_params: tuple[float,float,float]=(8.3,1.42,2) | ||
) -> Component: | ||
""" | ||
creates a super class AB OTA using flipped voltage follower at biasing stage and local common mode feedback to give dynamic current and gain boost much less dependent on biasing current | ||
input_pair_params: differential input pair(N-type) - (width,length), input nmoses of the fvf get the same dimensions | ||
fvf_shunt_params: feedback fet of fvf - (width,length) | ||
local_current_bias_params: local currrent mirror which directly biases each fvf - (width,length) | ||
diff_pair_load_params: creates a p_block consisting of both input stage pmos loads and output stage pmoses - (width,length) | ||
ratio: current mirroring ratio from input stage to output stage, set to 1 by default. | ||
current_mirror_params: output stage N-type currrent mirrors - (width, length) | ||
resistor_params: passgates are used as resistors for LCMFB - (width of nmos, width of pmos,length of nmos, length of pmos) | ||
global_current_bias_params: A low voltage current mirror for biasing - consists of 5 nmoses of (W/L) and one nmos of (W'/L) - (W,W',L) | ||
""" | ||
``` | ||
## Layout generation, PEX and post-layout simulations | ||
### sky130_ota_tapeout.py | ||
This file is used for layout generation, PEX and post-layout simulations. [sky130_nist_tapeout.py](https://github.com/idea-fasoc/OpenFASOC/blob/main/openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130_nist_tapeout.py) was taken as a reference. | ||
Run this command to see various modes in which it can be run | ||
``` python3 sky130_ota_tapeout_py --h ``` | ||
#### gen_ota mode | ||
This generates a complete layout, **with LVT layers and labels added by default**. Custom parameters can be given. Run ``` python3 sky130_ota_tapeout.py gen_ota --h ``` to see the options. | ||
|
||
#### drc report | ||
``` | ||
using provided pdk_root | ||
Defaulting to stale magic_commands.tcl | ||
Magic 8.3 revision 486 - Compiled on Sat Jul 13 11:42:22 AM CEST 2024. | ||
Starting magic under Tcl interpreter | ||
Using the terminal as the console. | ||
Using NULL graphics device. | ||
Processing system .magicrc file | ||
Sourcing design .magicrc for technology sky130A ... | ||
2 Magic internal units = 1 Lambda | ||
Input style sky130(): scaleFactor=2, multiplier=2 | ||
The following types are not handled by extraction and will be treated as non-electrical types: | ||
ubm | ||
Scaled tech values by 2 / 1 to match internal grid scaling | ||
Loading sky130A Device Generator Menu ... | ||
Loading "/tmp/tmpf6p8n7lv/magic_commands.tcl" from command line. | ||
Warning: Calma reading is not undoable! I hope that's OK. | ||
Library written using GDS-II Release 6.0 | ||
Library name: library | ||
Reading "super_class_AB_OTA_01aaa4c6". | ||
Reading "rectangle_82c0dd1f". | ||
Reading "compass_82c0dd1f". | ||
Reading "rectangle_82c0dd1f". | ||
Reading "compass_82c0dd1f". | ||
[INFO]: Loading Super_class_AB_OTA | ||
Creating new cell | ||
Loading DRC CIF style. | ||
No errors found. | ||
[INFO]: DONE with /tmp/tmpf6p8n7lv/Super_class_AB_OTA.rpt | ||
``` | ||
|
||
|
||
**N.B-** | ||
1. The default widths and lengths assume that the lvt layer is added. Different widths and lengths must be given to get an OTA with desired performance if no lvt layer is added. | ||
|
||
2. There is an option to add pads. For now this is set as False by default._ | ||
#### test mode | ||
This mode creates a complete layout with default parameter values, performs PEX (add --noparasitics to do just LVS but PEX is encouraged for better results) and then does some transient, ac, power and noise analysis to get results. The spice testbench ``` ota_perf_eval.sp ``` can be found in this directory itself. | ||
Run python3 ``` sky130_ota_tapeout.py test --output_dir test ```. This puts the simulation results inside test directory. |
56 changes: 56 additions & 0 deletions
56
...asoc/generators/glayout/glayout/flow/blocks/composite/fvf_based_ota/extract.bash.template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
#!/bin/bash | ||
|
||
# Actual | ||
export PDK_ROOT=@@PDK_ROOT | ||
|
||
# args: | ||
# first arg = gds file to read | ||
# second arg = name of top cell in gds file to read | ||
# third arg (optional) = noparasitics (basically an LVS extraction) | ||
|
||
paropt="@@@PAROPT" | ||
|
||
if [ "$paropt" = "noparasitics" ]; then | ||
|
||
magic -rcfile ../../../../../tapeout/tapeout_and_RL/sky130A/sky130A.magicrc -noconsole -dnull << EOF | ||
gds read $1 | ||
flatten $2 | ||
load $2 | ||
select top cell | ||
extract do local | ||
extract all | ||
ext2sim labels on | ||
ext2sim | ||
ext2spice lvs | ||
ext2spice cthresh 0 | ||
ext2spice -o $2_pex.spice | ||
exit | ||
EOF | ||
|
||
else | ||
|
||
magic -rcfile ../../../../../tapeout/tapeout_and_RL/sky130A/sky130A.magicrc -noconsole -dnull << EOF | ||
gds read $1 | ||
flatten $2 | ||
load $2 | ||
select top cell | ||
extract do local | ||
extract all | ||
ext2sim labels on | ||
ext2sim | ||
extresist tolerance 10 | ||
extresist | ||
ext2spice lvs | ||
ext2spice cthresh 0 | ||
ext2spice extresist on | ||
ext2spice -o $2_pex.spice | ||
exit | ||
EOF | ||
|
||
fi | ||
|
||
rm -f $2.nodes | ||
rm -f $2.ext | ||
rm -f $2.res.ext | ||
rm -f $2.sim | ||
|
145 changes: 145 additions & 0 deletions
145
...soc/generators/glayout/glayout/flow/blocks/composite/fvf_based_ota/low_voltage_cmirror.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
from glayout.flow.pdk.mappedpdk import MappedPDK | ||
from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk | ||
from gdsfactory.component import Component | ||
from gdsfactory.component_reference import ComponentReference | ||
from gdsfactory.cell import cell | ||
from gdsfactory import Component | ||
from gdsfactory.components import text_freetype, rectangle | ||
from glayout.flow.primitives.fet import nmos, pmos, multiplier | ||
from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_center, align_comp_to_port, prec_ref_center | ||
from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid | ||
from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation | ||
from glayout.flow.routing.straight_route import straight_route | ||
from glayout.flow.routing.c_route import c_route | ||
from glayout.flow.routing.L_route import L_route | ||
from glayout.flow.primitives.guardring import tapring | ||
from glayout.flow.pdk.util.port_utils import add_ports_perimeter | ||
from glayout.flow.spice.netlist import Netlist | ||
from glayout.flow.blocks.elementary.FVF.fvf import fvf_netlist, flipped_voltage_follower | ||
from glayout.flow.primitives.via_gen import via_stack | ||
from typing import Optional | ||
|
||
|
||
def low_voltage_cmirr_netlist(bias_fvf: Component, cascode_fvf: Component, fet_1_ref: ComponentReference, fet_2_ref: ComponentReference, fet_3_ref: ComponentReference, fet_4_ref: ComponentReference) -> Netlist: | ||
|
||
netlist = Netlist(circuit_name='Low_voltage_current_mirror', nodes=['IBIAS1', 'IBIAS2', 'GND', 'IOUT1', 'IOUT2']) | ||
netlist.connect_netlist(bias_fvf.info['netlist'], [('VIN','IBIAS1'),('VBULK','GND'),('Ib','IBIAS1')]) | ||
netlist.connect_netlist(cascode_fvf.info['netlist'], [('VIN','IBIAS1'),('VBULK','GND'),('Ib', 'IBIAS2')]) | ||
fet_1A_ref=netlist.connect_netlist(fet_2_ref.info['netlist'], [('D', 'IOUT1'),('G','IBIAS1'),('B','GND')]) | ||
fet_2A_ref=netlist.connect_netlist(fet_4_ref.info['netlist'], [('D', 'IOUT2'),('G','IBIAS1'),('B','GND')]) | ||
fet_1B_ref=netlist.connect_netlist(fet_1_ref.info['netlist'], [('G','IBIAS2'),('S', 'GND'),('B','GND')]) | ||
fet_2B_ref=netlist.connect_netlist(fet_3_ref.info['netlist'], [('G','IBIAS2'),('S', 'GND'),('B','GND')]) | ||
netlist.connect_subnets( | ||
fet_1A_ref, | ||
fet_1B_ref, | ||
[('S', 'D')] | ||
) | ||
netlist.connect_subnets( | ||
fet_2A_ref, | ||
fet_2B_ref, | ||
[('S', 'D')] | ||
) | ||
|
||
return netlist | ||
|
||
@cell | ||
def low_voltage_cmirror( | ||
pdk: MappedPDK, | ||
width: tuple[float,float] = (4.15,1.42), | ||
length: float = 2, | ||
fingers: tuple[int,int] = (2,1), | ||
multipliers: tuple[int,int] = (1,1), | ||
) -> Component: | ||
""" | ||
A low voltage N type current mirror. It has two input brnaches and two output branches. It consists of total 8 nfets, 7 of them have the same W/L. One nfet has width of w' = w/3(theoretcially) | ||
The default values are used to mirror 10uA. | ||
""" | ||
#top level component | ||
top_level = Component("Low_voltage_N-type_current_mirror") | ||
|
||
#input branch 2 | ||
cascode_fvf = flipped_voltage_follower(pdk, width=(width[0],width[0]), length=(length,length), fingers=(fingers[0],fingers[0]), multipliers=(multipliers[0],multipliers[0]), with_dnwell=False) | ||
cascode_fvf_ref = prec_ref_center(cascode_fvf) | ||
top_level.add(cascode_fvf_ref) | ||
|
||
#input branch 1 | ||
bias_fvf = flipped_voltage_follower(pdk, width=(width[0],width[1]), length=(length,length), fingers=(fingers[0],fingers[1]), multipliers=(multipliers[0],multipliers[1]), placement="vertical", with_dnwell=False) | ||
bias_fvf_ref = prec_ref_center(bias_fvf) | ||
bias_fvf_ref.movey(cascode_fvf_ref.ymin - 2 - (evaluate_bbox(bias_fvf)[1]/2)) | ||
top_level.add(bias_fvf_ref) | ||
|
||
#creating fets for output branches | ||
fet_1 = nmos(pdk, width=width[0], fingers=fingers[0], multipliers=multipliers[0], with_dummy=True, with_dnwell=False, with_substrate_tap=False, length=length) | ||
fet_1_ref = prec_ref_center(fet_1) | ||
fet_2_ref = prec_ref_center(fet_1) | ||
fet_3_ref = prec_ref_center(fet_1) | ||
fet_4_ref = prec_ref_center(fet_1) | ||
|
||
fet_1_ref.movex(cascode_fvf_ref.xmin - (evaluate_bbox(fet_1)[0]/2) - pdk.util_max_metal_seperation()) | ||
fet_2_ref.movex(cascode_fvf_ref.xmin - (3*evaluate_bbox(fet_1)[0]/2) - 2*pdk.util_max_metal_seperation()) | ||
fet_3_ref.movex(cascode_fvf_ref.xmax + (evaluate_bbox(fet_1)[0]/2) + pdk.util_max_metal_seperation()) | ||
fet_4_ref.movex(cascode_fvf_ref.xmax + (3*evaluate_bbox(fet_1)[0]/2) + 2*pdk.util_max_metal_seperation()) | ||
|
||
top_level.add(fet_1_ref) | ||
top_level.add(fet_2_ref) | ||
top_level.add(fet_3_ref) | ||
top_level.add(fet_4_ref) | ||
|
||
top_level << c_route(pdk, bias_fvf_ref.ports["A_multiplier_0_gate_E"], bias_fvf_ref.ports["B_gate_bottom_met_E"]) | ||
top_level << c_route(pdk, cascode_fvf_ref.ports["A_multiplier_0_gate_W"], bias_fvf_ref.ports["A_multiplier_0_gate_W"]) | ||
top_level << straight_route(pdk, cascode_fvf_ref.ports["B_gate_bottom_met_E"], fet_3_ref.ports["multiplier_0_gate_W"]) | ||
|
||
#creating vias for routing | ||
viam2m3 = via_stack(pdk, "met2", "met3", centered=True) | ||
gate_1_via = top_level << viam2m3 | ||
gate_1_via.move(fet_1_ref.ports["multiplier_0_gate_W"].center).movex(-1) | ||
gate_2_via = top_level << viam2m3 | ||
gate_2_via.move(fet_2_ref.ports["multiplier_0_gate_W"].center).movex(-1) | ||
gate_3_via = top_level << viam2m3 | ||
gate_3_via.move(fet_3_ref.ports["multiplier_0_gate_E"].center).movex(1) | ||
gate_4_via = top_level << viam2m3 | ||
gate_4_via.move(fet_4_ref.ports["multiplier_0_gate_E"].center).movex(1) | ||
|
||
source_2_via = top_level << viam2m3 | ||
drain_1_via = top_level << viam2m3 | ||
source_2_via.move(fet_2_ref.ports["multiplier_0_source_E"].center).movex(1.5) | ||
drain_1_via.move(fet_1_ref.ports["multiplier_0_drain_W"].center).movex(-1) | ||
|
||
source_4_via = top_level << viam2m3 | ||
drain_3_via = top_level << viam2m3 | ||
source_4_via.move(fet_4_ref.ports["multiplier_0_source_W"].center).movex(-1) | ||
drain_3_via.move(fet_3_ref.ports["multiplier_0_drain_E"].center).movex(1.5) | ||
|
||
#routing | ||
top_level << straight_route(pdk, fet_2_ref.ports["multiplier_0_source_E"], source_2_via.ports["bottom_met_W"]) | ||
top_level << straight_route(pdk, fet_1_ref.ports["multiplier_0_drain_W"], drain_1_via.ports["bottom_met_E"]) | ||
top_level << straight_route(pdk, fet_4_ref.ports["multiplier_0_source_W"], source_4_via.ports["bottom_met_E"]) | ||
top_level << straight_route(pdk, fet_3_ref.ports["multiplier_0_drain_E"], drain_3_via.ports["bottom_met_W"]) | ||
top_level << c_route(pdk, source_2_via.ports["top_met_N"], drain_1_via.ports["top_met_N"], extension=0.5*evaluate_bbox(fet_1)[1], width1=0.32, width2=0.32, cwidth=0.32, e1glayer="met3", e2glayer="met3", cglayer="met2") | ||
top_level << c_route(pdk, source_4_via.ports["top_met_N"], drain_3_via.ports["top_met_N"], extension=0.5*evaluate_bbox(fet_1)[1], width1=0.32, width2=0.32, cwidth=0.32, e1glayer="met3", e2glayer="met3", cglayer="met2") | ||
top_level << c_route(pdk, bias_fvf_ref.ports["A_multiplier_0_gate_E"], gate_4_via.ports["bottom_met_E"], width1=0.32, width2=0.32, cwidth=0.32) | ||
|
||
|
||
top_level << straight_route(pdk, fet_1_ref.ports["multiplier_0_gate_W"], gate_1_via.ports["bottom_met_E"]) | ||
top_level << straight_route(pdk, fet_2_ref.ports["multiplier_0_gate_W"], gate_2_via.ports["bottom_met_E"]) | ||
top_level << straight_route(pdk, fet_3_ref.ports["multiplier_0_gate_E"], gate_3_via.ports["bottom_met_W"]) | ||
top_level << straight_route(pdk, fet_4_ref.ports["multiplier_0_gate_E"], gate_4_via.ports["bottom_met_W"]) | ||
|
||
top_level << c_route(pdk, gate_1_via.ports["top_met_S"], gate_3_via.ports["top_met_S"], extension=(1.2*width[0]+0.6), cglayer='met2') | ||
top_level << c_route(pdk, gate_2_via.ports["top_met_S"], gate_4_via.ports["top_met_S"], extension=(1.2*width[0]-0.6), cglayer='met2') | ||
|
||
top_level << straight_route(pdk, fet_1_ref.ports["multiplier_0_source_W"], fet_1_ref.ports["tie_W_top_met_W"], glayer1='met1', width=0.2) | ||
top_level << straight_route(pdk, fet_3_ref.ports["multiplier_0_source_W"], fet_3_ref.ports["tie_W_top_met_W"], glayer1='met1', width=0.2) | ||
|
||
|
||
top_level.add_ports(bias_fvf_ref.get_ports_list(), prefix="M_1_") | ||
top_level.add_ports(cascode_fvf_ref.get_ports_list(), prefix="M_2_") | ||
top_level.add_ports(fet_1_ref.get_ports_list(), prefix="M_3_B_") | ||
top_level.add_ports(fet_2_ref.get_ports_list(), prefix="M_3_A_") | ||
top_level.add_ports(fet_3_ref.get_ports_list(), prefix="M_4_B_") | ||
top_level.add_ports(fet_4_ref.get_ports_list(), prefix="M_4_A_") | ||
|
||
component = component_snap_to_grid(rename_ports_by_orientation(top_level)) | ||
component.info['netlist'] = low_voltage_cmirr_netlist(bias_fvf, cascode_fvf, fet_1_ref, fet_2_ref, fet_3_ref, fet_4_ref) | ||
|
||
return component |
Oops, something went wrong.