diff --git a/pdks/sky130_common_pdk/src/mos.rs b/pdks/sky130_common_pdk/src/mos.rs index 1668bcb..e322c60 100644 --- a/pdks/sky130_common_pdk/src/mos.rs +++ b/pdks/sky130_common_pdk/src/mos.rs @@ -129,8 +129,6 @@ impl Sky130Pdk { let gate_ct = ctx.instantiate::(&gate_ctp)?; let gate_bbox = gate_ct.layer_bbox(poly); - let mut gate_pins = Vec::with_capacity(nf as usize); - let xpoly = x0 - POLY_DIFF_EXTENSION; let mut ypoly = y0 + DIFF_EDGE_TO_GATE; let wpoly = cx - xpoly + POLY_DIFF_EXTENSION; @@ -141,9 +139,13 @@ impl Sky130Pdk { let poly_fudge_x = 60; let mut poly_rects = Vec::with_capacity(nf as usize); for _ in 0..nf { + let mut p1x = xpoly + wpoly; + if params.contact_strategy == GateContactStrategy::BothSides { + p1x += poly_fudge_x; + }; let rect = Rect { p0: Point::new(xpoly - poly_fudge_x, ypoly), - p1: Point::new(xpoly + wpoly, ypoly + params.length()), + p1: Point::new(p1x, ypoly + params.length()), }; poly_rects.push(rect); ctx.draw(Element { @@ -152,17 +154,16 @@ impl Sky130Pdk { inner: Shape::Rect(rect), })?; - ypoly += params.length(); - ypoly += FINGER_SPACE; + ypoly += params.length() + FINGER_SPACE; } let gate_span = Span::new(poly_rects[0].p0.y, poly_rects.last().unwrap().p1.y); // Place gate contacts and create gate ports match params.contact_strategy { - GateContactStrategy::SingleSide => { + GateContactStrategy::SingleSide | GateContactStrategy::BothSides => { assert!( nf <= 3, - "can only contact nf=3 transistors on a single side" + "can only contact nf>=3 transistors on a single side" ); let line = gate_bbox.height(); let space = POLY_SPACE; @@ -174,6 +175,7 @@ impl Sky130Pdk { ); let mut npc_boxes = Vec::new(); + let mut npc_boxes_x = Vec::new(); for i in 0..nf as i64 { let empty_rect = Rect::new(Point::zero(), Point::zero()); @@ -195,16 +197,33 @@ impl Sky130Pdk { let mut port = CellPort::new(format!("gate_{i}")); port.add(gate_metal, Shape::Rect(ct_box)); ctx.add_port(port).unwrap(); - gate_pins.push(ct_box); + + if params.contact_strategy == GateContactStrategy::BothSides { + let mut gate_ct = ctx.instantiate::(&gate_ctp)?; + let ofsx = rect.p1.x - gate_bbox.p0.x; + let ct_ofs = Point::new(ofsx, ofsy); + gate_ct.translate(ct_ofs); + let ct_box = gate_ct.layer_bbox(gate_metal).into_rect(); + let mut port = CellPort::new(format!("gate_{i}_x")); + port.add(gate_metal, Shape::Rect(ct_box)); + ctx.add_port(port).unwrap(); + let npc_bbox = gate_ct.layer_bbox(npc).into_rect(); + npc_boxes_x.push(npc_bbox); + ctx.draw(gate_ct)?; + } let npc_bbox = gate_ct.layer_bbox(npc).into_rect(); npc_boxes.push(npc_bbox); - ctx.draw(gate_ct)?; + } - let top_npc = npc_boxes.last().unwrap(); + for boxes in [npc_boxes, npc_boxes_x] { + if boxes.is_empty() { + continue; + } + let top_npc = boxes.last().unwrap(); let npc_merge_rect = Rect::new( - Point::new(npc_boxes[0].p0.x, npc_boxes[0].p0.y), + Point::new(boxes[0].p0.x, boxes[0].p0.y), Point::new(top_npc.p1.x, top_npc.p1.y), ); ctx.draw(Element { diff --git a/substrate/src/pdk/mos/mod.rs b/substrate/src/pdk/mos/mod.rs index 2e16e7c..db25bd5 100644 --- a/substrate/src/pdk/mos/mod.rs +++ b/substrate/src/pdk/mos/mod.rs @@ -33,15 +33,17 @@ pub struct LayoutMosParams { /// Specifies the geometric arrangement of contacts for transistor gates. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum GateContactStrategy { - /// Attempt to place all contacts on one side (usually the left) + /// Attempt to place all contacts on one side (usually the left). SingleSide, - /// Alternate contact placement + /// Attempt to place contacts on both sides of the transistor. + BothSides, + /// Alternate contact placement. Alternate, - /// ABBA placement + /// ABBA placement. Abba, - /// Merge all contacts on one side (usually the left) + /// Merge all contacts on one side (usually the left). Merge, - /// Other; effect depends on layout generator + /// Other; effect depends on layout generator. Other(String), } diff --git a/substrate/tests/mos.rs b/substrate/tests/mos.rs index 232b013..8c32d1e 100644 --- a/substrate/tests/mos.rs +++ b/substrate/tests/mos.rs @@ -12,20 +12,20 @@ fn test_sky130_mos_nand2() { &LayoutMosParams { skip_sd_metal: vec![vec![]; 2], deep_nwell: false, - contact_strategy: substrate::pdk::mos::GateContactStrategy::Merge, + contact_strategy: substrate::pdk::mos::GateContactStrategy::BothSides, devices: vec![ MosParams { - w: 200_000, + w: 2000, l: 150, m: 1, - nf: 5, + nf: 2, id: MosId::new(0), }, MosParams { - w: 100_000, + w: 4000, l: 150, m: 1, - nf: 5, + nf: 2, id: MosId::new(1), }, ],