Skip to content

Commit

Permalink
work on pex poc
Browse files Browse the repository at this point in the history
  • Loading branch information
rohanku committed Dec 17, 2024
1 parent ce51084 commit 743b0bb
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 20 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion libs/spice/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use scir::schema::{FromSchema, NoSchema, NoSchemaError, Schema};
use scir::{Instance, Library, NetlistLibConversion, ParamValue, SliceOnePath};
use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter};
use std::path::Path;
use std::path::{Path, PathBuf};
use substrate::block::Block;
use substrate::schematic::{CellBuilder, Schematic};
use substrate::serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -223,6 +223,17 @@ pub enum Primitive {
/// The contents of the cell.
contents: BlackboxContents,
},
/// A raw instance with an associated cell in a SPICE netlist.
///
/// Parameters are not supported.
RawInstanceWithInclude {
/// The name of the associated cell.
cell: ArcStr,
/// The path to the included netlist.
netlist: PathBuf,
/// The ordered ports of the instance.
ports: Vec<ArcStr>,
},
}

/// Contents of a blackboxed instance.
Expand Down
17 changes: 16 additions & 1 deletion libs/spice/src/netlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use arcstr::ArcStr;
use itertools::Itertools;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};

use std::io::{Result, Write};
use std::path::PathBuf;
Expand Down Expand Up @@ -53,6 +53,21 @@ pub trait HasSpiceLikeNetlist: Schema {
/// Should include a newline after if needed.
#[allow(unused_variables)]
fn write_prelude<W: Write>(&self, out: &mut W, lib: &Library<Self>) -> Result<()> {
let includes = lib
.primitives()
.filter_map(|p| {
if let Primitive::RawInstanceWithInclude { netlist, .. } = p.1 {
Some(netlist.clone())
} else {
None
}
})
.collect::<HashSet<_>>();
// sort paths before including them to ensure stable output
for include in includes.iter().sorted() {
writeln!(out, ".INCLUDE {:?}", include)?;
}

Ok(())
}
/// Writes an include statement.
Expand Down
2 changes: 1 addition & 1 deletion substrate/src/schematic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ pub trait HasNestedView<T = InstancePath> {
/// Nesting a nested view should return the same type.
type NestedView: HasNestedView<T, NestedView = NestedView<Self, T>> + Send + Sync;
/// Creates a nested view of the object given a parent node.
fn nested_view(&self, parent: &InstancePath) -> NestedView<Self, T>;
fn nested_view(&self, parent: &T) -> NestedView<Self, T>;
}

/// The associated nested view of an object.
Expand Down
3 changes: 2 additions & 1 deletion tools/quantus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ serde = { version = "1", features = ["derive"] }
anyhow = "1"
regex = "1"

substrate = { version = "0.9.1", path = "../../substrate" }
substrate = { version = "0.8.1", path = "../../substrate" }
spice = { version = "0.7.1", path = "../../libs/spice" }
pegasus = { version = "0.0.0", path = "../pegasus" }
200 changes: 184 additions & 16 deletions tools/quantus/src/pex.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
use crate::utils::{aggregate_sources, execute_run_script};
use crate::{error::Error, TEMPLATES};
use pegasus::lvs::{run_lvs, LvsParams, LvsStatus};
use regex::Regex;
use serde::{Deserialize, Serialize};
use spice::netlist::NetlistOptions;
use spice::Spice;
use std::fmt::Display;
use std::fs;
use std::io::{self, BufRead};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use substrate::schematic::conv::RawLib;
use substrate::schematic::netlist::ConvertibleNetlister;
use substrate::schematic::{
Cell, HasNestedView, InstancePath, NestedView, PrimitiveBinding, Schematic,
};
use substrate::types::schematic::NestedNode;
use substrate::types::{Flatten, HasNameTree};
use substrate::{
arcstr::{self, ArcStr},
block::Block,
};
use tera::Context;

pub struct PexParams<'a> {
Expand Down Expand Up @@ -88,6 +103,159 @@ pub fn run_pex(params: &PexParams) -> Result<(), Error> {
Ok(())
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Pex<T> {
pub schematic: Arc<T>,
pub gds_path: PathBuf,
pub layout_cell_name: ArcStr,
pub work_dir: PathBuf,
pub lvs_rules_dir: PathBuf,
pub lvs_rules_path: PathBuf,
pub technology_dir: PathBuf,
}

impl<T: Block> Block for Pex<T> {
type Io = <T as Block>::Io;

fn name(&self) -> ArcStr {
self.schematic.name()
}

fn io(&self) -> Self::Io {
self.schematic.io()
}
}

pub struct PexContext {
/// The source spice file for this DSPF extracted view.
lib: Arc<RawLib<Spice>>,
path: Option<InstancePath>,
}

impl PexContext {
pub fn new(lib: RawLib<Spice>) -> Self {
Self {
lib: Arc::new(lib),
path: None,
}
}
}

impl HasNestedView<PexContext> for NestedNode {
type NestedView = NestedNode;
}

pub struct PexData<T: Schematic> {
cell: Cell<T>,
ctx: PexContext,
}

impl<T: Schematic> PexData<T>
where
NestedView<T::NestedData>: HasNestedView<PexContext>,
{
pub fn data(&self) -> NestedView<NestedView<T::NestedData>, PexContext> {
self.cell.data().nested_view(&self.ctx)
}
}

impl<T: Schematic> HasNestedView for PexData<T> {
type NestedView = Self;
fn nested_view(&self, parent: &InstancePath) -> NestedView<Self, InstancePath> {
Self {
cell: self.cell.clone(),
ctx: PexContext {
lib: self.ctx.lib.clone(),
path: Some(if let Some(path) = self.ctx.path {
path.prepend(parent)
} else {
parent.clone()
}),
},
}
}
}

impl<T: Schematic<Schema = Spice>> Schematic for Pex<T>
where
NestedView<T::NestedData>: HasNestedView<PexContext>,
{
type Schema = Spice;
type NestedData = PexData<T>;

fn schematic(
&self,
io: &substrate::types::schematic::IoNodeBundle<Self>,
cell: &mut substrate::schematic::CellBuilder<<Self as Schematic>::Schema>,
) -> substrate::error::Result<Self::NestedData> {
let source_path = self.work_dir.join("source.sp");
let pex_netlist_path = self
.work_dir
.join(format!("{}.pex.spf", self.schematic.name()));
let rawlib = cell.ctx().export_scir(self.schematic.clone()).unwrap();

Spice.write_scir_netlist_to_file(&rawlib.scir, &source_path, NetlistOptions::default())?;

let lvs_dir = self.work_dir.join("lvs");
let pex_dir = self.work_dir.join("pex");

assert!(
matches!(
run_lvs(&LvsParams {
work_dir: &lvs_dir,
layout_path: &self.gds_path,
layout_cell_name: "test_col_inv_array",
source_paths: &[source_path],
source_cell_name: "col_inv_array",
rules_dir: &self.lvs_rules_dir,
rules_path: &self.lvs_rules_path,
})
.expect("failed to run LVS")
.status,
LvsStatus::Correct
),
"LVS failed"
);

run_pex(&PexParams {
work_dir: &pex_dir,
lvs_work_dir: &lvs_dir,
lvs_run_name: "test_col_inv_array",
technology_dir: &self.technology_dir,
pex_netlist_path: &pex_netlist_path,
})
.expect("failed to run PEX");

let cell_inner = cell
.ctx()
.generate_schematic(self.schematic.clone())
.cell()
.clone();

let ports = self
.io()
.flat_names(None)
.into_iter()
.map(|n| arcstr::format!("{}", n))
.collect::<Vec<ArcStr>>();

let primitive = spice::Primitive::RawInstanceWithInclude {
cell: self.schematic.name(),
netlist: pex_netlist_path,
ports: ports.clone(),
};
let mut binding = PrimitiveBinding::new(primitive);
for (n, name) in io.flatten_vec().iter().zip(ports.iter()) {
binding.connect(name, n);
}
cell.set_primitive(binding);
Ok(PexData {
cell: cell_inner,
ctx: PexContext::new(rawlib),
})
}
}

#[cfg(test)]
mod tests {
use pegasus::lvs::{run_lvs, LvsParams, LvsStatus};
Expand Down Expand Up @@ -124,22 +292,22 @@ mod tests {
let pex_dir = test_dir.join("pex");
let pex_path = pex_dir.join("test_col_inv_array.pex.netlist");

// assert!(
// matches!(
// run_lvs(&LvsParams {
// work_dir: &lvs_dir,
// layout_path: &layout_path,
// layout_cell_name: "test_col_inv_array",
// source_paths: &[source_path],
// source_cell_name: "col_inv_array",
// rules_dir: &PathBuf::from(SKY130_LVS),
// rules_path: &PathBuf::from(SKY130_LVS_RULES_PATH),
// })?
// .status,
// LvsStatus::Correct
// ),
// "LVS failed"
// );
assert!(
matches!(
run_lvs(&LvsParams {
work_dir: &lvs_dir,
layout_path: &layout_path,
layout_cell_name: "test_col_inv_array",
source_paths: &[source_path],
source_cell_name: "col_inv_array",
rules_dir: &PathBuf::from(SKY130_LVS),
rules_path: &PathBuf::from(SKY130_LVS_RULES_PATH),
})?
.status,
LvsStatus::Correct
),
"LVS failed"
);

run_pex(&PexParams {
work_dir: &pex_dir,
Expand Down
19 changes: 19 additions & 0 deletions tools/spectre/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,25 @@ impl HasSpiceLikeNetlist for Spectre {
writeln!(out, "dspf_include {:?}", spf_path)?;
}

// find all unique SPICE netlists and include them
let includes = lib
.primitives()
.filter_map(|p| {
if let Primitive::Spice(spice::Primitive::RawInstanceWithInclude {
netlist, ..
}) = p.1
{
Some(netlist.clone())
} else {
None
}
})
.collect::<HashSet<_>>();
// sort paths before including them to ensure stable output
for include in includes.iter().sorted() {
writeln!(out, "include {:?}", include)?;
}

Ok(())
}

Expand Down

0 comments on commit 743b0bb

Please sign in to comment.