From 562ffa5c541a02036a417dbdc9ebd87e74d26f71 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Dec 2023 17:15:43 -0800 Subject: [PATCH 1/9] feat(atoll): support sky130 atoll-compatible nmos tiles --- libs/atoll/src/grid.rs | 12 +- pdks/sky130pdk/src/atoll.rs | 79 --------- pdks/sky130pdk/src/atoll/mod.rs | 303 ++++++++++++++++++++++++++++++++ pdks/sky130pdk/src/lib.rs | 2 +- tests/src/atoll/mod.rs | 27 ++- 5 files changed, 337 insertions(+), 86 deletions(-) delete mode 100644 pdks/sky130pdk/src/atoll.rs create mode 100644 pdks/sky130pdk/src/atoll/mod.rs diff --git a/libs/atoll/src/grid.rs b/libs/atoll/src/grid.rs index e580d846..48319b51 100644 --- a/libs/atoll/src/grid.rs +++ b/libs/atoll/src/grid.rs @@ -301,12 +301,20 @@ impl RoutingGrid { } } + /// Calculates the span of a particular track on the given layer. + pub fn track_span(&self, layer: usize, track: i64) -> Span { + let slice = self.stack.slice(self.start..self.end); + let tracks = slice.tracks(layer); + + tracks.track(track) + } + /// Calculates the bounds of a particular track on the given layer. /// /// The start and end coordinates are with respect to tracks on the grid defining layer. pub fn track(&self, layer: usize, track: i64, start: i64, end: i64) -> Rect { let slice = self.stack.slice(self.start..self.end); - let tracks = slice.tracks(layer); + // note that the grid defining layer may be outside the slice, // e.g. if the slice contains layers 2 through 5, the grid defining layer of 2 is 1. let adj_tracks = self.stack.tracks(self.grid_defining_layer(layer)); @@ -314,7 +322,7 @@ impl RoutingGrid { // This allows `start` to be larger than `end`. let (start, end) = sorted2(start, end); - let track = tracks.track(track); + let track = self.track_span(layer, track); let endcap = slice.layer(layer).endcap(); let start = adj_tracks.track(start).center() - endcap; let end = adj_tracks.track(end).center() + endcap; diff --git a/pdks/sky130pdk/src/atoll.rs b/pdks/sky130pdk/src/atoll.rs deleted file mode 100644 index ce410f53..00000000 --- a/pdks/sky130pdk/src/atoll.rs +++ /dev/null @@ -1,79 +0,0 @@ -use crate::layers::Sky130Layers; -use atoll::grid::{AbstractLayer, LayerStack, PdkLayer}; -use atoll::RoutingDir; -use substrate::geometry::dir::Dir; -use substrate::pdk::layers::Layer; - -impl Sky130Layers { - /// Returns the ATOLL-compatible routing layer stack. - pub fn atoll_layer_stack(&self) -> LayerStack { - LayerStack { - layers: vec![ - PdkLayer { - id: self.li1.drawing.id(), - inner: AbstractLayer { - dir: RoutingDir::Any { - track_dir: Dir::Vert, - }, - line: 200, - space: 200, - offset: 100, - endcap: 0, - }, - }, - PdkLayer { - id: self.met1.drawing.id(), - inner: AbstractLayer { - dir: RoutingDir::Horiz, - line: 260, - space: 140, - offset: 130, - endcap: 100, - }, - }, - PdkLayer { - id: self.met2.drawing.id(), - inner: AbstractLayer { - dir: RoutingDir::Vert, - line: 300, - space: 300, - offset: 150, - endcap: 130, - }, - }, - PdkLayer { - id: self.met3.drawing.id(), - inner: AbstractLayer { - dir: RoutingDir::Horiz, - line: 400, - space: 400, - offset: 200, - endcap: 150, - }, - }, - PdkLayer { - id: self.met4.drawing.id(), - inner: AbstractLayer { - dir: RoutingDir::Vert, - line: 1_200, - space: 1_200, - offset: 600, - endcap: 200, - }, - }, - PdkLayer { - id: self.met5.drawing.id(), - inner: AbstractLayer { - dir: RoutingDir::Horiz, - line: 1_800, - space: 1_800, - offset: 900, - endcap: 600, - }, - }, - ], - offset_x: 0, - offset_y: 0, - } - } -} diff --git a/pdks/sky130pdk/src/atoll/mod.rs b/pdks/sky130pdk/src/atoll/mod.rs new file mode 100644 index 00000000..b359d3c8 --- /dev/null +++ b/pdks/sky130pdk/src/atoll/mod.rs @@ -0,0 +1,303 @@ +use crate::layers::Sky130Layers; +use crate::Sky130Pdk; +use arcstr::ArcStr; +use atoll::grid::{AbstractLayer, AtollLayer, DebugRoutingGrid, LayerStack, PdkLayer, RoutingGrid}; +use atoll::RoutingDir; +use serde::{Deserialize, Serialize}; +use std::ops::{Deref, DerefMut}; +use substrate::block::Block; +use substrate::geometry::bbox::Bbox; +use substrate::geometry::dir::Dir; +use substrate::geometry::rect::Rect; +use substrate::geometry::span::Span; +use substrate::io::{Array, InOut, Input, Io, IoShape, LayoutType, Signal}; +use substrate::layout::element::Shape; +use substrate::layout::{CellBuilder, ExportsLayoutData, Layout}; +use substrate::pdk::layers::{Layer, LayerId}; + +#[derive(Clone)] +pub struct Sky130AtollLayer(PdkLayer); + +impl AsRef for Sky130AtollLayer { + fn as_ref(&self) -> &LayerId { + self.0.as_ref() + } +} + +impl Deref for Sky130AtollLayer { + type Target = PdkLayer; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Sky130AtollLayer { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl AtollLayer for Sky130AtollLayer { + fn dir(&self) -> RoutingDir { + self.0.dir() + } + + fn line(&self) -> i64 { + self.0.line() + } + + fn space(&self) -> i64 { + self.0.space() + } + + fn offset(&self) -> i64 { + self.0.offset() + } +} + +impl From for Sky130AtollLayer { + fn from(value: PdkLayer) -> Self { + Self(value) + } +} + +impl From for PdkLayer { + fn from(value: Sky130AtollLayer) -> Self { + value.0 + } +} + +impl Sky130Layers { + /// Returns the ATOLL-compatible routing layer stack. + pub fn atoll_layer_stack(&self) -> LayerStack { + LayerStack { + layers: vec![ + PdkLayer { + id: self.li1.drawing.id(), + inner: AbstractLayer { + dir: RoutingDir::Any { + track_dir: Dir::Vert, + }, + line: 170, + space: 260, + offset: 85, + endcap: 0, + }, + } + .into(), + PdkLayer { + id: self.met1.drawing.id(), + inner: AbstractLayer { + dir: RoutingDir::Horiz, + line: 260, + space: 140, + offset: 130, + endcap: 100, + }, + } + .into(), + PdkLayer { + id: self.met2.drawing.id(), + inner: AbstractLayer { + dir: RoutingDir::Vert, + line: 400, + space: 460, + offset: 150, + endcap: 130, + }, + } + .into(), + PdkLayer { + id: self.met3.drawing.id(), + inner: AbstractLayer { + dir: RoutingDir::Horiz, + line: 400, + space: 400, + offset: 200, + endcap: 150, + }, + } + .into(), + PdkLayer { + id: self.met4.drawing.id(), + inner: AbstractLayer { + dir: RoutingDir::Vert, + line: 1_200, + space: 950, + offset: 600, + endcap: 200, + }, + } + .into(), + PdkLayer { + id: self.met5.drawing.id(), + inner: AbstractLayer { + dir: RoutingDir::Horiz, + line: 1_800, + space: 1_800, + offset: 900, + endcap: 600, + }, + } + .into(), + ], + offset_x: 0, + offset_y: 0, + } + } +} + +/// The set of supported gate lengths. +#[derive( + Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default, Serialize, Deserialize, +)] +pub enum MosLength { + /// 150nm. + /// + /// This is the minimum length supported by the SKY130 technology. + #[default] + L150, +} + +impl MosLength { + /// The length in nanometers. + fn nm(&self) -> i64 { + match *self { + Self::L150 => 150, + } + } +} + +/// The IO of a [`MosTile`]. +#[derive(Debug, Clone, Io)] +pub struct MosTileIo { + /// `NF + 1` source/drain contacts on li1, where `NF` is the number of fingers. + pub sd: InOut>, + /// `NF` gate contacts on li1, where `NF` is the number of fingers. + pub g: Input, + /// A body port on either nwell or pwell. + pub b: InOut, +} + +#[derive(Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +pub struct MosTile { + pub w: i64, + pub len: MosLength, + pub nf: i64, +} + +impl Block for MosTile { + type Io = MosTileIo; + fn id() -> ArcStr { + arcstr::literal!("mos_tile") + } + + fn name(&self) -> ArcStr { + arcstr::format!("mos_tile_w{}_l{}_nf{}", self.w, self.len.nm(), self.nf) + } + + fn io(&self) -> Self::Io { + MosTileIo { + sd: InOut(Array::new(self.nf as usize + 1, Signal::new())), + g: Input(Signal::new()), + b: InOut(Signal::new()), + } + } +} + +impl ExportsLayoutData for MosTile { + type LayoutData = (); +} + +impl Layout for MosTile { + fn layout( + &self, + io: &mut <::Io as LayoutType>::Builder, + cell: &mut CellBuilder, + ) -> substrate::error::Result { + let stack = cell + .ctx + .get_installation::>() + .unwrap(); + let grid = RoutingGrid::new((*stack).clone(), 0..2, self.nf + 3, 4); + let debug = DebugRoutingGrid::new(grid.clone()); + // cell.draw(debug)?; + + let tracks = (0..self.nf + 1) + .map(|i| { + let span = grid.track_span(0, i); + Rect::from_spans(span, Span::new(-20, self.w + 20)) + }) + .collect::>(); + + let gates = tracks + .windows(2) + .map(|tracks| { + let (left, right) = (tracks[0], tracks[1]); + Rect::from_spans( + Span::new(left.right(), right.left()), + Span::new(-200, self.w + 130), + ) + .shrink_dir(Dir::Horiz, 55) + .unwrap() + }) + .collect::>(); + + for &rect in gates.iter() { + cell.draw(Shape::new(cell.ctx.layers.poly, rect))?; + } + + for (i, &rect) in tracks.iter().enumerate() { + io.sd[i].push(IoShape::with_layers(cell.ctx.layers.li1, rect)); + cell.draw(Shape::new(cell.ctx.layers.li1, rect))?; + let num_cuts = (self.w + 40 - 160 + 170) / 340; + for j in 0..num_cuts { + let base = rect.bot() + 20 + 80 + 340 * j; + let cut = Rect::from_spans(rect.hspan(), Span::with_start_and_length(base, 170)); + cell.draw(Shape::new(cell.ctx.layers.licon1, cut))?; + } + } + + let diff = Rect::from_sides( + tracks[0].left() - 130, + 0, + tracks.last().unwrap().right() + 130, + self.w, + ); + cell.draw(Shape::new(cell.ctx.layers.diff, diff))?; + let nsdm = diff.expand_all(130); + cell.draw(Shape::new(cell.ctx.layers.nsdm, nsdm))?; + + let poly = Rect::from_sides( + gates[0].left(), + -200 - 350, + gates.last().unwrap().right(), + -200, + ); + cell.draw(Shape::new(cell.ctx.layers.poly, poly))?; + + let poly_li = Rect::from_sides( + tracks[1].left(), + poly.bot(), + tracks[tracks.len() - 2].right(), + poly.top(), + ); + cell.draw(Shape::new(cell.ctx.layers.li1, poly_li))?; + io.g.push(IoShape::with_layers(cell.ctx.layers.li1, poly_li)); + let npc = poly_li + .expand_dir(Dir::Vert, 10) + .expand_dir(Dir::Horiz, 100); + cell.draw(Shape::new(cell.ctx.layers.npc, npc))?; + + for i in 1..self.nf as usize { + let cut = Rect::from_spans(tracks[i].hspan(), poly_li.shrink_all(90).unwrap().vspan()); + cell.draw(Shape::new(cell.ctx.layers.licon1, cut))?; + } + + let pwell = cell.bbox().unwrap(); + cell.draw(Shape::new(cell.ctx.layers.pwell, pwell))?; + io.b.push(IoShape::with_layers(cell.ctx.layers.pwell, pwell)); + + Ok(()) + } +} diff --git a/pdks/sky130pdk/src/lib.rs b/pdks/sky130pdk/src/lib.rs index 18e51c34..956a1bc6 100644 --- a/pdks/sky130pdk/src/lib.rs +++ b/pdks/sky130pdk/src/lib.rs @@ -21,7 +21,7 @@ use scir::{Instance, ParamValue}; use spice::Spice; use substrate::context::{ContextBuilder, Installation}; -mod atoll; +pub mod atoll; pub mod corner; pub mod layers; pub mod mos; diff --git a/tests/src/atoll/mod.rs b/tests/src/atoll/mod.rs index 7036108a..785e5c8b 100644 --- a/tests/src/atoll/mod.rs +++ b/tests/src/atoll/mod.rs @@ -1,21 +1,37 @@ use crate::paths::get_path; use crate::shared::pdk::sky130_open_ctx; use serde::{Deserialize, Serialize}; +use sky130pdk::atoll::{MosLength, Sky130AtollLayer}; use sky130pdk::Sky130Pdk; use substrate::block::Block; use substrate::io::LayoutType; use substrate::layout::{CellBuilder, ExportsLayoutData, Layout}; #[test] -fn atoll_debug_routing_grid() { - let gds_path = get_path("atoll_debug_routing_grid", "layout.gds"); +fn sky130_atoll_debug_routing_grid() { + let gds_path = get_path("sky130_atoll_debug_routing_grid", "layout.gds"); let ctx = sky130_open_ctx(); - // Imports a hard macro from a GDS file. ctx.write_layout(Sky130DebugRoutingGrid, gds_path) .expect("failed to write layout"); } +#[test] +fn sky130_atoll_nmos_tile() { + let gds_path = get_path("sky130_atoll_nmos_tile", "layout.gds"); + let ctx = sky130_open_ctx(); + + ctx.write_layout( + sky130pdk::atoll::MosTile { + w: 1_680, + nf: 3, + len: MosLength::L150, + }, + gds_path, + ) + .expect("failed to write layout"); +} + #[derive(Block, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Debug)] #[substrate(io = "()")] pub struct Sky130DebugRoutingGrid; @@ -31,7 +47,10 @@ impl Layout for Sky130DebugRoutingGrid { cell: &mut CellBuilder, ) -> substrate::error::Result { use atoll::grid::*; - let stack = cell.ctx.get_installation::>().unwrap(); + let stack = cell + .ctx + .get_installation::>() + .unwrap(); let grid = DebugRoutingGrid::new(RoutingGrid::new((*stack).clone(), 0..stack.len(), 10, 2)); cell.draw(grid)?; Ok(()) From d6407a030014e28eaeb022a7b3cef4943ec6fd34 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 20 Dec 2023 23:09:38 -0800 Subject: [PATCH 2/9] export nmos tile schematic in test --- pdks/sky130pdk/src/atoll/mod.rs | 26 ++++++++++++++++++++++++-- tests/src/atoll/mod.rs | 13 +++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/pdks/sky130pdk/src/atoll/mod.rs b/pdks/sky130pdk/src/atoll/mod.rs index b359d3c8..38102b81 100644 --- a/pdks/sky130pdk/src/atoll/mod.rs +++ b/pdks/sky130pdk/src/atoll/mod.rs @@ -10,10 +10,12 @@ use substrate::geometry::bbox::Bbox; use substrate::geometry::dir::Dir; use substrate::geometry::rect::Rect; use substrate::geometry::span::Span; -use substrate::io::{Array, InOut, Input, Io, IoShape, LayoutType, Signal}; +use substrate::io::{Array, InOut, Input, Io, IoShape, LayoutType, MosIoSchematic, SchematicType, Signal}; use substrate::layout::element::Shape; use substrate::layout::{CellBuilder, ExportsLayoutData, Layout}; use substrate::pdk::layers::{Layer, LayerId}; +use substrate::schematic::{ExportsNestedData, Schematic}; +use crate::mos::{MosParams, Nfet01v8}; #[derive(Clone)] pub struct Sky130AtollLayer(PdkLayer); @@ -179,7 +181,7 @@ pub struct MosTileIo { pub b: InOut, } -#[derive(Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] pub struct MosTile { pub w: i64, pub len: MosLength, @@ -301,3 +303,23 @@ impl Layout for MosTile { Ok(()) } } + +impl ExportsNestedData for MosTile { type NestedData = (); } + +impl Schematic for MosTile { + fn schematic(&self, io: &<::Io as SchematicType>::Bundle, cell: &mut substrate::schematic::CellBuilder) -> substrate::error::Result { + for i in 0..self.nf as usize { + cell.instantiate_connected(Nfet01v8::new(MosParams { + w: self.w, + nf: 1, + l: self.len.nm(), + }), MosIoSchematic { + d: io.sd[i], + g: io.g, + s: io.sd[i+1], + b: io.b, + }) + } + Ok(()) + } +} \ No newline at end of file diff --git a/tests/src/atoll/mod.rs b/tests/src/atoll/mod.rs index 785e5c8b..cc6a04fb 100644 --- a/tests/src/atoll/mod.rs +++ b/tests/src/atoll/mod.rs @@ -3,6 +3,8 @@ use crate::shared::pdk::sky130_open_ctx; use serde::{Deserialize, Serialize}; use sky130pdk::atoll::{MosLength, Sky130AtollLayer}; use sky130pdk::Sky130Pdk; +use spice::netlist::NetlistOptions; +use spice::Spice; use substrate::block::Block; use substrate::io::LayoutType; use substrate::layout::{CellBuilder, ExportsLayoutData, Layout}; @@ -19,17 +21,24 @@ fn sky130_atoll_debug_routing_grid() { #[test] fn sky130_atoll_nmos_tile() { let gds_path = get_path("sky130_atoll_nmos_tile", "layout.gds"); + let spice_path = get_path("sky130_atoll_nmos_tile", "schematic.sp"); let ctx = sky130_open_ctx(); - ctx.write_layout( + let block = sky130pdk::atoll::MosTile { w: 1_680, nf: 3, len: MosLength::L150, - }, + }; + + ctx.write_layout( + block, gds_path, ) .expect("failed to write layout"); + + let lib = ctx.export_scir(block).expect("failed to write scir").scir.convert_schema::().unwrap().build().unwrap(); + Spice.write_scir_netlist_to_file(&lib, spice_path, NetlistOptions::default()).expect("failed to write schematic"); } #[derive(Block, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Debug)] From c6d0ed3cd64fdb67f60491c73666c196f31f79c2 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Fri, 22 Dec 2023 13:35:16 -0800 Subject: [PATCH 3/9] fix(docs): add doc comments for NmosTile --- pdks/sky130pdk/src/atoll/mod.rs | 68 +++++++++++++++++++++------------ tests/src/atoll/mod.rs | 25 ++++++------ 2 files changed, 56 insertions(+), 37 deletions(-) diff --git a/pdks/sky130pdk/src/atoll/mod.rs b/pdks/sky130pdk/src/atoll/mod.rs index 38102b81..4b8f3b64 100644 --- a/pdks/sky130pdk/src/atoll/mod.rs +++ b/pdks/sky130pdk/src/atoll/mod.rs @@ -1,4 +1,5 @@ use crate::layers::Sky130Layers; +use crate::mos::{MosParams, Nfet01v8}; use crate::Sky130Pdk; use arcstr::ArcStr; use atoll::grid::{AbstractLayer, AtollLayer, DebugRoutingGrid, LayerStack, PdkLayer, RoutingGrid}; @@ -10,12 +11,12 @@ use substrate::geometry::bbox::Bbox; use substrate::geometry::dir::Dir; use substrate::geometry::rect::Rect; use substrate::geometry::span::Span; -use substrate::io::{Array, InOut, Input, Io, IoShape, LayoutType, MosIoSchematic, SchematicType, Signal}; +use substrate::io::layout::IoShape; +use substrate::io::{Array, InOut, Input, Io, MosIoSchematic, Signal}; use substrate::layout::element::Shape; use substrate::layout::{CellBuilder, ExportsLayoutData, Layout}; use substrate::pdk::layers::{Layer, LayerId}; use substrate::schematic::{ExportsNestedData, Schematic}; -use crate::mos::{MosParams, Nfet01v8}; #[derive(Clone)] pub struct Sky130AtollLayer(PdkLayer); @@ -181,17 +182,29 @@ pub struct MosTileIo { pub b: InOut, } +/// A tile containing a set of NMOS transistors. +/// +/// There are `nf` transistors, each of length `len` and width `w`. +/// The gates of all transistors are connected. +/// The `nf+1` sources and drains are not connected to anything else. +/// +/// This tile does not contain internal taps. #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] -pub struct MosTile { +pub struct NmosTile { + /// Transistor width. pub w: i64, + + /// Transistor length. pub len: MosLength, + + /// Number of fingers. pub nf: i64, } -impl Block for MosTile { +impl Block for NmosTile { type Io = MosTileIo; fn id() -> ArcStr { - arcstr::literal!("mos_tile") + arcstr::literal!("nmos_tile") } fn name(&self) -> ArcStr { @@ -207,14 +220,14 @@ impl Block for MosTile { } } -impl ExportsLayoutData for MosTile { +impl ExportsLayoutData for NmosTile { type LayoutData = (); } -impl Layout for MosTile { +impl Layout for NmosTile { fn layout( &self, - io: &mut <::Io as LayoutType>::Builder, + io: &mut substrate::io::layout::Builder, cell: &mut CellBuilder, ) -> substrate::error::Result { let stack = cell @@ -222,8 +235,6 @@ impl Layout for MosTile { .get_installation::>() .unwrap(); let grid = RoutingGrid::new((*stack).clone(), 0..2, self.nf + 3, 4); - let debug = DebugRoutingGrid::new(grid.clone()); - // cell.draw(debug)?; let tracks = (0..self.nf + 1) .map(|i| { @@ -304,22 +315,31 @@ impl Layout for MosTile { } } -impl ExportsNestedData for MosTile { type NestedData = (); } +impl ExportsNestedData for NmosTile { + type NestedData = (); +} -impl Schematic for MosTile { - fn schematic(&self, io: &<::Io as SchematicType>::Bundle, cell: &mut substrate::schematic::CellBuilder) -> substrate::error::Result { +impl Schematic for NmosTile { + fn schematic( + &self, + io: &substrate::io::schematic::Bundle, + cell: &mut substrate::schematic::CellBuilder, + ) -> substrate::error::Result { for i in 0..self.nf as usize { - cell.instantiate_connected(Nfet01v8::new(MosParams { - w: self.w, - nf: 1, - l: self.len.nm(), - }), MosIoSchematic { - d: io.sd[i], - g: io.g, - s: io.sd[i+1], - b: io.b, - }) + cell.instantiate_connected( + Nfet01v8::new(MosParams { + w: self.w, + nf: 1, + l: self.len.nm(), + }), + MosIoSchematic { + d: io.sd[i], + g: io.g, + s: io.sd[i + 1], + b: io.b, + }, + ) } Ok(()) } -} \ No newline at end of file +} diff --git a/tests/src/atoll/mod.rs b/tests/src/atoll/mod.rs index 93f0bd17..d7a6942e 100644 --- a/tests/src/atoll/mod.rs +++ b/tests/src/atoll/mod.rs @@ -3,11 +3,13 @@ use crate::shared::pdk::sky130_open_ctx; use serde::{Deserialize, Serialize}; use sky130pdk::atoll::{MosLength, Sky130AtollLayer}; use sky130pdk::Sky130Pdk; +use spectre::Spectre; use spice::netlist::NetlistOptions; use spice::Spice; use substrate::block::Block; use substrate::io::layout::HardwareType; use substrate::layout::{CellBuilder, ExportsLayoutData, Layout}; +use substrate::schematic::netlist::ConvertibleNetlister; #[test] fn sky130_atoll_debug_routing_grid() { @@ -24,21 +26,18 @@ fn sky130_atoll_nmos_tile() { let spice_path = get_path("sky130_atoll_nmos_tile", "schematic.sp"); let ctx = sky130_open_ctx(); - let block = - sky130pdk::atoll::MosTile { - w: 1_680, - nf: 3, - len: MosLength::L150, - }; + let block = sky130pdk::atoll::MosTile { + w: 1_680, + nf: 3, + len: MosLength::L150, + }; - ctx.write_layout( - block, - gds_path, - ) - .expect("failed to write layout"); + ctx.write_layout(block, gds_path) + .expect("failed to write layout"); - let lib = ctx.export_scir(block).expect("failed to write scir").scir.convert_schema::().unwrap().build().unwrap(); - Spice.write_scir_netlist_to_file(&lib, spice_path, NetlistOptions::default()).expect("failed to write schematic"); + Spectre::default() + .write_netlist_to_file(&ctx, block, spice_path, NetlistOptions::default()) + .expect("failed to write netlist"); } #[derive(Block, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Debug)] From 3e21a471937f7f8d1a8abd8a7fa186c8c5279965 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Fri, 22 Dec 2023 13:45:53 -0800 Subject: [PATCH 4/9] write out spectre netlist in sky130 atoll nmos tile test --- pdks/sky130pdk/src/atoll/mod.rs | 6 +++++- tests/src/atoll/mod.rs | 13 ++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/pdks/sky130pdk/src/atoll/mod.rs b/pdks/sky130pdk/src/atoll/mod.rs index 4b8f3b64..0697a91e 100644 --- a/pdks/sky130pdk/src/atoll/mod.rs +++ b/pdks/sky130pdk/src/atoll/mod.rs @@ -1,8 +1,10 @@ +//! SKY130 primitives for [ATOLL](atoll). + use crate::layers::Sky130Layers; use crate::mos::{MosParams, Nfet01v8}; use crate::Sky130Pdk; use arcstr::ArcStr; -use atoll::grid::{AbstractLayer, AtollLayer, DebugRoutingGrid, LayerStack, PdkLayer, RoutingGrid}; +use atoll::grid::{AbstractLayer, AtollLayer, LayerStack, PdkLayer, RoutingGrid}; use atoll::RoutingDir; use serde::{Deserialize, Serialize}; use std::ops::{Deref, DerefMut}; @@ -18,6 +20,7 @@ use substrate::layout::{CellBuilder, ExportsLayoutData, Layout}; use substrate::pdk::layers::{Layer, LayerId}; use substrate::schematic::{ExportsNestedData, Schematic}; +/// A SKY130 ATOLL routing layer. #[derive(Clone)] pub struct Sky130AtollLayer(PdkLayer); @@ -302,6 +305,7 @@ impl Layout for NmosTile { .expand_dir(Dir::Horiz, 100); cell.draw(Shape::new(cell.ctx.layers.npc, npc))?; + #[allow(clippy::needless_range_loop)] for i in 1..self.nf as usize { let cut = Rect::from_spans(tracks[i].hspan(), poly_li.shrink_all(90).unwrap().vspan()); cell.draw(Shape::new(cell.ctx.layers.licon1, cut))?; diff --git a/tests/src/atoll/mod.rs b/tests/src/atoll/mod.rs index d7a6942e..d3729b0a 100644 --- a/tests/src/atoll/mod.rs +++ b/tests/src/atoll/mod.rs @@ -5,7 +5,6 @@ use sky130pdk::atoll::{MosLength, Sky130AtollLayer}; use sky130pdk::Sky130Pdk; use spectre::Spectre; use spice::netlist::NetlistOptions; -use spice::Spice; use substrate::block::Block; use substrate::io::layout::HardwareType; use substrate::layout::{CellBuilder, ExportsLayoutData, Layout}; @@ -26,7 +25,7 @@ fn sky130_atoll_nmos_tile() { let spice_path = get_path("sky130_atoll_nmos_tile", "schematic.sp"); let ctx = sky130_open_ctx(); - let block = sky130pdk::atoll::MosTile { + let block = sky130pdk::atoll::NmosTile { w: 1_680, nf: 3, len: MosLength::L150, @@ -35,8 +34,16 @@ fn sky130_atoll_nmos_tile() { ctx.write_layout(block, gds_path) .expect("failed to write layout"); + let scir = ctx + .export_scir(block) + .unwrap() + .scir + .convert_schema::() + .unwrap() + .build() + .unwrap(); Spectre::default() - .write_netlist_to_file(&ctx, block, spice_path, NetlistOptions::default()) + .write_scir_netlist_to_file(&scir, spice_path, NetlistOptions::default()) .expect("failed to write netlist"); } From 16f496d75bf46d1840ec749497829075e4969771 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Fri, 22 Dec 2023 13:50:53 -0800 Subject: [PATCH 5/9] fix(tests): change netlist path to netlist.scs in sky130 atoll NMOS tile test --- tests/src/atoll/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/atoll/mod.rs b/tests/src/atoll/mod.rs index d3729b0a..5f6e3e6c 100644 --- a/tests/src/atoll/mod.rs +++ b/tests/src/atoll/mod.rs @@ -22,7 +22,7 @@ fn sky130_atoll_debug_routing_grid() { #[test] fn sky130_atoll_nmos_tile() { let gds_path = get_path("sky130_atoll_nmos_tile", "layout.gds"); - let spice_path = get_path("sky130_atoll_nmos_tile", "schematic.sp"); + let netlist_path = get_path("sky130_atoll_nmos_tile", "schematic.scs"); let ctx = sky130_open_ctx(); let block = sky130pdk::atoll::NmosTile { @@ -43,7 +43,7 @@ fn sky130_atoll_nmos_tile() { .build() .unwrap(); Spectre::default() - .write_scir_netlist_to_file(&scir, spice_path, NetlistOptions::default()) + .write_scir_netlist_to_file(&scir, netlist_path, NetlistOptions::default()) .expect("failed to write netlist"); } From fd8c8a10b130e2038bc97b69df0b000567c6cea3 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Fri, 22 Dec 2023 19:02:40 -0800 Subject: [PATCH 6/9] fix broken doc link --- pdks/sky130pdk/src/atoll/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdks/sky130pdk/src/atoll/mod.rs b/pdks/sky130pdk/src/atoll/mod.rs index 0697a91e..451280bf 100644 --- a/pdks/sky130pdk/src/atoll/mod.rs +++ b/pdks/sky130pdk/src/atoll/mod.rs @@ -174,7 +174,7 @@ impl MosLength { } } -/// The IO of a [`MosTile`]. +/// The IO of an NMOS or PMOS tile. #[derive(Debug, Clone, Io)] pub struct MosTileIo { /// `NF + 1` source/drain contacts on li1, where `NF` is the number of fingers. From 3f74c97007515dbd1117059f1607edb71ba59b43 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Fri, 22 Dec 2023 19:24:44 -0800 Subject: [PATCH 7/9] use commercial sky130 device names in atoll test for tool compatibility --- tests/src/atoll/mod.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/src/atoll/mod.rs b/tests/src/atoll/mod.rs index 5f6e3e6c..f0636f0f 100644 --- a/tests/src/atoll/mod.rs +++ b/tests/src/atoll/mod.rs @@ -2,9 +2,9 @@ use crate::paths::get_path; use crate::shared::pdk::sky130_open_ctx; use serde::{Deserialize, Serialize}; use sky130pdk::atoll::{MosLength, Sky130AtollLayer}; -use sky130pdk::Sky130Pdk; -use spectre::Spectre; +use sky130pdk::{Sky130CommercialSchema, Sky130Pdk}; use spice::netlist::NetlistOptions; +use spice::Spice; use substrate::block::Block; use substrate::io::layout::HardwareType; use substrate::layout::{CellBuilder, ExportsLayoutData, Layout}; @@ -38,11 +38,15 @@ fn sky130_atoll_nmos_tile() { .export_scir(block) .unwrap() .scir - .convert_schema::() + .convert_schema::() + .unwrap() + .build() + .unwrap() + .convert_schema::() .unwrap() .build() .unwrap(); - Spectre::default() + Spice .write_scir_netlist_to_file(&scir, netlist_path, NetlistOptions::default()) .expect("failed to write netlist"); } From 653183a69bc5cce4a3df78cbafe2a6fc4948b097 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Fri, 22 Dec 2023 19:48:21 -0800 Subject: [PATCH 8/9] fix(schemas): sky 130 commercial schema exports mos primitives --- pdks/sky130pdk/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pdks/sky130pdk/src/lib.rs b/pdks/sky130pdk/src/lib.rs index d17ceffe..18e5aa7f 100644 --- a/pdks/sky130pdk/src/lib.rs +++ b/pdks/sky130pdk/src/lib.rs @@ -280,9 +280,8 @@ impl FromSchema for Spice { .map(|(k, v)| (UniCase::new(k), v)) .collect(), }, - Primitive::Mos { kind, params } => spice::Primitive::RawInstance { - cell: kind.commercial_subckt(), - ports: vec!["D".into(), "G".into(), "S".into(), "B".into()], + Primitive::Mos { kind, params } => spice::Primitive::Mos { + model: kind.commercial_subckt(), params: HashMap::from_iter([ ( UniCase::new(arcstr::literal!("w")), From a0380c93932ebe547ea7a4ad7ce5075670eaac35 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Fri, 22 Dec 2023 20:08:20 -0800 Subject: [PATCH 9/9] fix(netlists): add mult parameter to sky130 commercial spice netlists --- pdks/sky130pdk/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pdks/sky130pdk/src/lib.rs b/pdks/sky130pdk/src/lib.rs index 18e5aa7f..25b574a8 100644 --- a/pdks/sky130pdk/src/lib.rs +++ b/pdks/sky130pdk/src/lib.rs @@ -295,6 +295,7 @@ impl FromSchema for Spice { UniCase::new(arcstr::literal!("nf")), Decimal::from(params.nf).into(), ), + (UniCase::new(arcstr::literal!("mult")), dec!(1).into()), ]), }, })