|
| 1 | +use atoll::fold::Foldable; |
| 2 | +use atoll::straps::{GreedyStrapper, LayerStrappingParams, StrappingParams}; |
| 3 | +use atoll::{Tile, TileData, route::GreedyRouter}; |
| 4 | +use sky130::{ |
| 5 | + Sky130, |
| 6 | + atoll::{NmosTile, PtapTile, Sky130ViaMaker}, |
| 7 | + res::PrecisionResistorCell, |
| 8 | +}; |
| 9 | +use substrate::types::codegen::{PortGeometryBundle, View}; |
| 10 | +use substrate::{ |
| 11 | + block::Block, |
| 12 | + geometry::align::AlignMode, |
| 13 | + geometry::bbox::Bbox, |
| 14 | + types::{FlatLen, InOut, Input, Io, Signal, layout::PortGeometryBuilder}, |
| 15 | +}; |
| 16 | + |
| 17 | +#[derive(Debug, Default, Clone, Io)] |
| 18 | +pub struct ResistorBankSliceIo { |
| 19 | + pub din: Input<Signal>, |
| 20 | + pub en: Input<Signal>, |
| 21 | + pub vss: InOut<Signal>, |
| 22 | +} |
| 23 | + |
| 24 | +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Block)] |
| 25 | +#[substrate(io = "ResistorBankSliceIo")] |
| 26 | +pub struct ResistorBankSlice { |
| 27 | + pub n: NmosTile, |
| 28 | + pub res: PrecisionResistorCell, |
| 29 | + pub tap: PtapTile, |
| 30 | +} |
| 31 | + |
| 32 | +impl Foldable for ResistorBankSlice { |
| 33 | + type ViaMaker = Sky130ViaMaker; |
| 34 | + fn via_maker() -> Self::ViaMaker { |
| 35 | + Sky130ViaMaker |
| 36 | + } |
| 37 | +} |
| 38 | + |
| 39 | +impl Tile for ResistorBankSlice { |
| 40 | + type Schema = Sky130; |
| 41 | + type NestedData = (); |
| 42 | + type LayoutBundle = View<ResistorBankSliceIo, PortGeometryBundle<Self::Schema>>; |
| 43 | + type LayoutData = (); |
| 44 | + |
| 45 | + fn tile<'a>( |
| 46 | + &self, |
| 47 | + io: &'a substrate::types::schematic::IoNodeBundle<Self>, |
| 48 | + cell: &mut atoll::TileBuilder<'a, Self::Schema>, |
| 49 | + ) -> substrate::error::Result<atoll::TileData<Self>> { |
| 50 | + let mut din = PortGeometryBuilder::new(); |
| 51 | + let mut en = PortGeometryBuilder::new(); |
| 52 | + let mut vss = PortGeometryBuilder::new(); |
| 53 | + let x = cell.signal("x", Signal); |
| 54 | + let n = cell.generate_primitive_named(self.n, "nmos"); |
| 55 | + for i in 0..n.io().g.len() { |
| 56 | + cell.connect(n.io().g[i], io.en); |
| 57 | + } |
| 58 | + for i in 0..n.io().sd.len() { |
| 59 | + if i % 2 == 0 { |
| 60 | + cell.connect(n.io().sd[i], io.din); |
| 61 | + } else { |
| 62 | + cell.connect(n.io().sd[i], x); |
| 63 | + } |
| 64 | + } |
| 65 | + cell.connect(n.io().b, io.vss); |
| 66 | + let mut tap = cell.generate_primitive_named(self.tap, "tap"); |
| 67 | + cell.connect(tap.io().vnb, io.vss); |
| 68 | + let mut res = cell.generate_primitive_named(self.res, "res"); |
| 69 | + cell.connect(res.io().p, x); |
| 70 | + cell.connect(res.io().n, io.vss); |
| 71 | + let nbbox = n.lcm_bounds(); |
| 72 | + res.align_rect_mut(nbbox, AlignMode::CenterHorizontal, 0); |
| 73 | + res.align_rect_mut(nbbox, AlignMode::Beneath, 0); |
| 74 | + let resbbox = res.lcm_bounds(); |
| 75 | + tap.align_rect_mut(nbbox, AlignMode::Left, 0); |
| 76 | + tap.align_rect_mut(resbbox, AlignMode::Beneath, 0); |
| 77 | + |
| 78 | + let n = cell.draw(n)?; |
| 79 | + let res = cell.draw(res)?; |
| 80 | + let tap = cell.draw(tap)?; |
| 81 | + |
| 82 | + en.merge(n.layout.io().g[0].clone()); |
| 83 | + din.merge(n.layout.io().sd[1].clone()); |
| 84 | + vss.merge(n.layout.io().b); |
| 85 | + vss.merge(res.layout.io().n); |
| 86 | + vss.merge(tap.layout.io().vnb); |
| 87 | + |
| 88 | + cell.set_top_layer(2); |
| 89 | + cell.set_router(GreedyRouter::new()); |
| 90 | + cell.set_via_maker(Sky130ViaMaker); |
| 91 | + cell.set_strapper(GreedyStrapper); |
| 92 | + cell.set_strapping( |
| 93 | + io.vss, |
| 94 | + StrappingParams::new(1, vec![LayerStrappingParams::ViaDown { min_period: 30 }]), |
| 95 | + ); |
| 96 | + |
| 97 | + Ok(TileData { |
| 98 | + nested_data: (), |
| 99 | + layout_bundle: ResistorBankSliceIoView { |
| 100 | + din: din.build()?, |
| 101 | + en: en.build()?, |
| 102 | + vss: vss.build()?, |
| 103 | + }, |
| 104 | + layout_data: (), |
| 105 | + outline: cell.layout.bbox_rect(), |
| 106 | + }) |
| 107 | + } |
| 108 | +} |
| 109 | + |
| 110 | +#[cfg(test)] |
| 111 | +mod tests { |
| 112 | + use super::*; |
| 113 | + |
| 114 | + use atoll::TileWrapper; |
| 115 | + use atoll::fold::{FoldedArray, PinConfig}; |
| 116 | + use scir::netlist::ConvertibleNetlister; |
| 117 | + use sky130::Sky130SrcNdaSchema; |
| 118 | + use sky130::atoll::MosLength; |
| 119 | + use sky130::res::{PrecisionResistor, PrecisionResistorWidth}; |
| 120 | + use sky130::{Sky130, layout::to_gds}; |
| 121 | + use spice::{Spice, netlist::NetlistOptions}; |
| 122 | + use std::path::PathBuf; |
| 123 | + use substrate::context::Context; |
| 124 | + use substrate::geometry::dir::Dir; |
| 125 | + use substrate::geometry::side::Side; |
| 126 | + |
| 127 | + pub fn sky130_src_nda_ctx() -> Context { |
| 128 | + // Open PDK needed for standard cells. |
| 129 | + let open_pdk_root = std::env::var("SKY130_OPEN_PDK_ROOT") |
| 130 | + .expect("the SKY130_OPEN_PDK_ROOT environment variable must be set"); |
| 131 | + let src_nda_pdk_root = std::env::var("SKY130_SRC_NDA_PDK_ROOT") |
| 132 | + .expect("the SKY130_SRC_NDA_PDK_ROOT environment variable must be set"); |
| 133 | + Context::builder() |
| 134 | + .install(Sky130::src_nda(open_pdk_root, src_nda_pdk_root)) |
| 135 | + .build() |
| 136 | + } |
| 137 | + |
| 138 | + #[test] |
| 139 | + fn resistor_bank_slice_lvs() { |
| 140 | + let work_dir = PathBuf::from(concat!( |
| 141 | + env!("CARGO_MANIFEST_DIR"), |
| 142 | + "/build/resistor_bank_slice_lvs" |
| 143 | + )); |
| 144 | + let gds_path = work_dir.join("layout.gds"); |
| 145 | + let netlist_path = work_dir.join("netlist.sp"); |
| 146 | + let ctx = sky130_src_nda_ctx(); |
| 147 | + |
| 148 | + let block = TileWrapper::new(ResistorBankSlice { |
| 149 | + tap: PtapTile::new(7, 4), |
| 150 | + res: PrecisionResistorCell { |
| 151 | + resistor: PrecisionResistor { |
| 152 | + width: PrecisionResistorWidth::W285, |
| 153 | + length: 4_000, |
| 154 | + }, |
| 155 | + dir: Dir::Vert, |
| 156 | + }, |
| 157 | + n: NmosTile::new(2_000, MosLength::L150, 6), |
| 158 | + }); |
| 159 | + |
| 160 | + let scir = ctx |
| 161 | + .export_scir(block) |
| 162 | + .unwrap() |
| 163 | + .scir |
| 164 | + .convert_schema::<Sky130SrcNdaSchema>() |
| 165 | + .unwrap() |
| 166 | + .convert_schema::<Spice>() |
| 167 | + .unwrap() |
| 168 | + .build() |
| 169 | + .unwrap(); |
| 170 | + Spice |
| 171 | + .write_scir_netlist_to_file(&scir, &netlist_path, NetlistOptions::default()) |
| 172 | + .expect("failed to write netlist"); |
| 173 | + |
| 174 | + ctx.write_layout(block, to_gds, &gds_path) |
| 175 | + .expect("failed to write layout"); |
| 176 | + } |
| 177 | + |
| 178 | + #[test] |
| 179 | + fn resistor_bank() { |
| 180 | + let work_dir = PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/build/resistor_bank")); |
| 181 | + let gds_path = work_dir.join("layout.gds"); |
| 182 | + let netlist_path = work_dir.join("netlist.sp"); |
| 183 | + let ctx = sky130_src_nda_ctx(); |
| 184 | + |
| 185 | + let block = TileWrapper::new(FoldedArray { |
| 186 | + rows: 2, |
| 187 | + cols: 20, |
| 188 | + pins: vec![ |
| 189 | + PinConfig::Parallel { layer: 1 }, |
| 190 | + PinConfig::Escape { |
| 191 | + layer: 0, |
| 192 | + side: Side::Top, |
| 193 | + }, |
| 194 | + PinConfig::Parallel { layer: 1 }, |
| 195 | + ], |
| 196 | + tile: ResistorBankSlice { |
| 197 | + tap: PtapTile::new(7, 4), |
| 198 | + res: PrecisionResistorCell { |
| 199 | + resistor: PrecisionResistor { |
| 200 | + width: PrecisionResistorWidth::W141, |
| 201 | + length: 2_000, |
| 202 | + }, |
| 203 | + dir: Dir::Vert, |
| 204 | + }, |
| 205 | + n: NmosTile::new(2_000, MosLength::L150, 6), |
| 206 | + }, |
| 207 | + top_layer: 3, |
| 208 | + dir: Dir::Horiz, |
| 209 | + }); |
| 210 | + |
| 211 | + let scir = ctx |
| 212 | + .export_scir(block.clone()) |
| 213 | + .unwrap() |
| 214 | + .scir |
| 215 | + .convert_schema::<Sky130SrcNdaSchema>() |
| 216 | + .unwrap() |
| 217 | + .convert_schema::<Spice>() |
| 218 | + .unwrap() |
| 219 | + .build() |
| 220 | + .unwrap(); |
| 221 | + Spice |
| 222 | + .write_scir_netlist_to_file(&scir, &netlist_path, NetlistOptions::default()) |
| 223 | + .expect("failed to write netlist"); |
| 224 | + ctx.write_layout(block, to_gds, &gds_path) |
| 225 | + .expect("failed to write layout"); |
| 226 | + } |
| 227 | +} |
0 commit comments