Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cadence): add support for Pegasus and Quantus #462

Merged
merged 22 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,7 @@
"substrate": "0.8.1",
"tests": "0.0.0",
"tools/ngspice": "0.3.1",
"tools/pegasus": "0.0.0",
"tools/quantus": "0.0.0",
"tools/spectre": "0.9.1"
}
29 changes: 29 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ members = [
"substrate",
"tests",
"tools/ngspice",
"tools/pegasus",
"tools/quantus",
"tools/spectre",
]

Expand Down
65 changes: 30 additions & 35 deletions codegen/src/block/schematic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,17 @@ impl ToTokens for DataInputReceiver {
ref attrs,
} = *self;

let (generics_imp, generics_ty, generics_wher) = generics.split_for_impl();

let hnv_generic_ty: syn::Ident = parse_quote!(__substrate_T);
let hnv_generic: syn::GenericParam = parse_quote!(#hnv_generic_ty);
let mut hnv_generics = generics.clone();
add_trait_bounds(
&mut hnv_generics,
quote!(#substrate::schematic::HasNestedView),
quote!(#substrate::schematic::HasNestedView<#hnv_generic>),
);
hnv_generics.params.push(hnv_generic.clone());

let (hnv_imp, hnv_ty, hnv_wher) = hnv_generics.split_for_impl();

let view_ident = format_ident!("{}View", ident);
Expand All @@ -172,6 +178,7 @@ impl ToTokens for DataInputReceiver {
let (view_imp, view_ty, view_wher) = view_generics.split_for_impl();

let mut save_generics = generics.clone();
save_generics.params.push(hnv_generic);
save_generics
.params
.push(parse_quote!(__substrate_S: #substrate::simulation::Simulator));
Expand All @@ -187,7 +194,13 @@ impl ToTokens for DataInputReceiver {
},
predicates: Default::default(),
});
let mut save_generics = save_generics.clone();
let mut hnv_where_clause =
hnv_generics.where_clause.clone().unwrap_or(WhereClause {
where_token: Where {
span: Span::call_site(),
},
predicates: Default::default(),
});
let mut save_where_clause =
save_generics.where_clause.clone().unwrap_or(WhereClause {
where_token: Where {
Expand All @@ -200,7 +213,13 @@ impl ToTokens for DataInputReceiver {
view_where_clause.predicates.push(
parse_quote!(#ty: #substrate::types::codegen::HasView<#view_generic_ty>),
);
save_where_clause.predicates.push(parse_quote!(<#ty as #substrate::schematic::HasNestedView>::NestedView: #substrate::simulation::data::Save<__substrate_S, __substrate_A>));
hnv_where_clause.predicates.push(
parse_quote!(#ty: #substrate::schematic::HasNestedView<#hnv_generic_ty>),
);
save_where_clause.predicates.push(
parse_quote!(#ty: #substrate::schematic::HasNestedView<#hnv_generic_ty>),
);
save_where_clause.predicates.push(parse_quote!(<#ty as #substrate::schematic::HasNestedView<#hnv_generic_ty>>::NestedView: #substrate::simulation::data::Save<__substrate_S, __substrate_A>));
}
save_generics.where_clause = Some(save_where_clause.clone());
let (save_imp, save_ty, save_wher) = save_generics.split_for_impl();
Expand All @@ -214,7 +233,7 @@ impl ToTokens for DataInputReceiver {

let nested_fields = fields.clone().map(|mut f| {
let ty = f.ty.clone();
f.ty = parse_quote!(<#ty as #substrate::schematic::HasNestedView>::NestedView);
f.ty = parse_quote!(<#ty as #substrate::schematic::HasNestedView<#hnv_generic_ty>>::NestedView);
f
});

Expand All @@ -227,7 +246,7 @@ impl ToTokens for DataInputReceiver {
Some(&quote!{ self }),
i,
f,
|ty, val| quote! { <#ty as #substrate::schematic::HasNestedView>::nested_view(#val, __substrate_derive_parent) },
|ty, val| quote! { <#ty as #substrate::schematic::HasNestedView<#hnv_generic_ty>>::nested_view(#val, __substrate_derive_parent) },
)
});
let retval = match fields.style {
Expand All @@ -237,20 +256,6 @@ impl ToTokens for DataInputReceiver {
};
let view_body = struct_body(fields.style, true, quote! {#( #view_decls )*});

let nested_assignments = nested_fields.iter().enumerate().map(|(i, f)| {
field_assign(
Some(&quote!{ self }),
i,
f,
|ty, val| quote! { <#ty as #substrate::schematic::HasNestedView>::nested_view(#val, __substrate_derive_parent) },
)
});
let nested_retval = match nested_fields.style {
Style::Unit => quote!(#view_ident),
Style::Tuple => quote!(#view_ident( #(#nested_assignments)* )),
Style::Struct => quote!(#view_ident { #(#nested_assignments)* }),
};

let save_key_assignments = nested_fields.iter().enumerate().map(|(i, f)| {
field_assign(
Some(&quote!{ self }),
Expand Down Expand Up @@ -282,29 +287,19 @@ impl ToTokens for DataInputReceiver {
#(#attrs)*
#vis struct #view_ident #view_generics #view_where_clause #view_body

impl #hnv_imp #substrate::schematic::HasNestedView for #ident #hnv_ty #hnv_wher {
type NestedView = #view_ident<#(#generic_idents,)*#substrate::types::codegen::Nested>;
impl #hnv_imp #substrate::schematic::HasNestedView<#hnv_generic_ty> for #ident #generics_ty #hnv_where_clause {
type NestedView = #view_ident<#(#generic_idents,)*#substrate::types::codegen::Nested<#hnv_generic_ty>>;

fn nested_view(
&self,
__substrate_derive_parent: &#substrate::schematic::InstancePath,
__substrate_derive_parent: &#hnv_generic_ty,
) -> Self::NestedView {
#retval
}
}
impl #hnv_imp #substrate::schematic::HasNestedView for #view_ident<#(#generic_idents,)*#substrate::types::codegen::Nested> #hnv_wher {
type NestedView = #view_ident<#(#generic_idents,)*#substrate::types::codegen::Nested>;

fn nested_view(
&self,
__substrate_derive_parent: &#substrate::schematic::InstancePath,
) -> Self::NestedView {
#nested_retval
}
}
impl #save_imp #substrate::simulation::data::Save<__substrate_S, __substrate_A> for #view_ident<#(#generic_idents,)*#substrate::types::codegen::Nested> #save_wher {
type SaveKey = #view_ident<#(#generic_idents,)*#substrate::types::codegen::NestedSaveKey<__substrate_S, __substrate_A>>;
type Saved = #view_ident<#(#generic_idents,)*#substrate::types::codegen::NestedSaved<__substrate_S, __substrate_A>>;
impl #save_imp #substrate::simulation::data::Save<__substrate_S, __substrate_A> for #view_ident<#(#generic_idents,)*#substrate::types::codegen::Nested<#hnv_generic_ty>> #save_wher {
type SaveKey = #view_ident<#(#generic_idents,)*#substrate::types::codegen::NestedSaveKey<#hnv_generic_ty, __substrate_S, __substrate_A>>;
type Saved = #view_ident<#(#generic_idents,)*#substrate::types::codegen::NestedSaved<#hnv_generic_ty, __substrate_S, __substrate_A>>;

fn save(
&self,
Expand Down
8,104 changes: 7,275 additions & 829 deletions docs/figures/substrate.ai

Large diffs are not rendered by default.

46 changes: 18 additions & 28 deletions 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 @@ -83,16 +83,15 @@ impl Spice {
conv: &NetlistLibConversion,
path: &SliceOnePath,
) -> String {
Self::node_path_with_prefix_and_separator(lib, conv, path, "", ".")
Self::node_path_with_separator(lib, conv, path, ".")
}

/// Converts a [`SliceOnePath`] to a Spice path string corresponding to the associated
/// node voltage, using the given instance prefix hierarchy separator.
pub fn node_path_with_prefix_and_separator(
pub fn node_path_with_separator(
lib: &Library<Spice>,
conv: &NetlistLibConversion,
path: &SliceOnePath,
prefix: &str,
sep: &str,
) -> String {
let path = lib.convert_slice_one_path_with_conv(conv, path.clone(), |name, index| {
Expand All @@ -103,17 +102,7 @@ impl Spice {
}
});
let n = path.len();
path.iter()
.enumerate()
.map(|(i, elt)| {
if i + 1 == n {
// Don't add a prefix to the last element.
elt.clone()
} else {
arcstr::format!("{}{}", prefix, elt)
}
})
.join(sep)
path.iter().join(sep)
}
}

Expand Down Expand Up @@ -223,6 +212,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 Expand Up @@ -297,6 +297,7 @@ impl Primitive {
Primitive::Mos { .. } => vec!["D".into(), "G".into(), "S".into(), "B".into()],
Primitive::RawInstance { ports, .. } => ports.clone(),
Primitive::RawInstanceWithCell { ports, .. } => ports.clone(),
Primitive::RawInstanceWithInclude { ports, .. } => ports.clone(),
Primitive::BlackboxInstance { contents } => contents
.elems
.iter()
Expand All @@ -315,8 +316,8 @@ impl Primitive {
}

/// An ideal 2-terminal resistor.
#[derive(Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(crate = "substrate::serde")]
#[derive(Clone, Copy, Hash, PartialEq, Eq, Block)]
#[substrate(io = "TwoTerminalIo")]
pub struct Resistor {
/// The resistor value.
value: Decimal,
Expand All @@ -336,17 +337,6 @@ impl Resistor {
self.value
}
}
impl Block for Resistor {
type Io = TwoTerminalIo;

fn name(&self) -> ArcStr {
arcstr::format!("ideal_resistor_{}", self.value)
}

fn io(&self) -> Self::Io {
Default::default()
}
}

impl Schematic for Resistor {
type Schema = Spice;
Expand Down
38 changes: 26 additions & 12 deletions 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 @@ -317,6 +317,22 @@ impl HasSpiceLikeNetlist for Spice {
writeln!(out)?;
}
}

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(())
}

Expand Down Expand Up @@ -466,17 +482,15 @@ impl HasSpiceLikeNetlist for Spice {
}
name
}
Primitive::RawInstance {
cell,
ports,
params,
}
| Primitive::RawInstanceWithCell {
cell,
ports,
params,
..
} => {
Primitive::RawInstance { cell, ports, .. }
| Primitive::RawInstanceWithCell { cell, ports, .. }
| Primitive::RawInstanceWithInclude { cell, ports, .. } => {
let default_params = HashMap::new();
let params = match &primitive {
Primitive::RawInstance { params, .. }
| Primitive::RawInstanceWithCell { params, .. } => params,
_ => &default_params,
};
let name = arcstr::format!("X{}", name);
write!(out, "{}", name)?;
for port in ports {
Expand Down
2 changes: 2 additions & 0 deletions release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
"substrate": {},
"tests": {},
"tools/ngspice": {},
"tools/pegasus": {},
"tools/quantus": {},
"tools/spectre": {}
}
}
2 changes: 1 addition & 1 deletion substrate/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use std::any::Any;
use std::hash::Hash;
use std::sync::Arc;

use arcstr::ArcStr;
pub use codegen::Block;

use crate::arcstr::ArcStr;
use crate::types::Io;

/// A block that can be instantiated by Substrate.
Expand Down
Loading
Loading