use rust_decimal::Decimal;
use scir::ParamValue;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use substrate::block::Block;
use substrate::io::schematic::HardwareType;
use substrate::io::{Array, Io, TwoTerminalIo};
use substrate::schematic::primitives::DcVsource;
use substrate::schematic::{CellBuilder, ExportsNestedData, PrimitiveBinding, Schematic};
use substrate::simulation::waveform::{TimeWaveform, Waveform};
use crate::{Primitive, Spectre};
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Pulse {
pub val0: Decimal,
pub val1: Decimal,
pub period: Option<Decimal>,
pub rise: Option<Decimal>,
pub fall: Option<Decimal>,
pub width: Option<Decimal>,
pub delay: Option<Decimal>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
pub enum Vsource {
Dc(Decimal),
Ac(AcSource),
Pulse(Pulse),
Pwl(Waveform<Decimal>),
}
impl Vsource {
#[inline]
pub fn dc(value: Decimal) -> Self {
Self::Dc(value)
}
#[inline]
pub fn pulse(value: Pulse) -> Self {
Self::Pulse(value)
}
#[inline]
pub fn pwl(value: Waveform<Decimal>) -> Self {
Self::Pwl(value)
}
}
impl Block for Vsource {
type Io = TwoTerminalIo;
fn id() -> arcstr::ArcStr {
arcstr::literal!("vsource")
}
fn name(&self) -> arcstr::ArcStr {
arcstr::format!("uservsource")
}
fn io(&self) -> Self::Io {
Default::default()
}
}
impl ExportsNestedData for Vsource {
type NestedData = ();
}
impl Schematic<Spectre> for Vsource {
fn schematic(
&self,
io: &<<Self as Block>::Io as HardwareType>::Bundle,
cell: &mut CellBuilder<Spectre>,
) -> substrate::error::Result<Self::NestedData> {
use arcstr::literal;
let mut params = HashMap::new();
match self {
Vsource::Dc(dc) => {
params.insert(literal!("type"), ParamValue::String(literal!("dc")));
params.insert(literal!("dc"), ParamValue::Numeric(*dc));
}
Vsource::Pulse(pulse) => {
params.insert(literal!("type"), ParamValue::String(literal!("pulse")));
params.insert(literal!("val0"), ParamValue::Numeric(pulse.val0));
params.insert(literal!("val1"), ParamValue::Numeric(pulse.val1));
if let Some(period) = pulse.period {
params.insert(literal!("period"), ParamValue::Numeric(period));
}
if let Some(rise) = pulse.rise {
params.insert(literal!("rise"), ParamValue::Numeric(rise));
}
if let Some(fall) = pulse.fall {
params.insert(literal!("fall"), ParamValue::Numeric(fall));
}
if let Some(width) = pulse.width {
params.insert(literal!("width"), ParamValue::Numeric(width));
}
if let Some(delay) = pulse.delay {
params.insert(literal!("delay"), ParamValue::Numeric(delay));
}
}
Vsource::Pwl(waveform) => {
let mut pwl = String::new();
pwl.push('[');
for (i, pt) in waveform.values().enumerate() {
use std::fmt::Write;
if i != 0 {
pwl.push(' ');
}
write!(&mut pwl, "{} {}", pt.t(), pt.x()).unwrap();
}
pwl.push(']');
params.insert(literal!("type"), ParamValue::String(literal!("pwl")));
params.insert(literal!("wave"), ParamValue::String(pwl.into()));
}
Vsource::Ac(ac) => {
params.insert(literal!("type"), ParamValue::String(literal!("dc")));
params.insert(literal!("dc"), ParamValue::Numeric(ac.dc));
params.insert(literal!("mag"), ParamValue::Numeric(ac.mag));
params.insert(literal!("phase"), ParamValue::Numeric(ac.phase));
}
};
let mut prim = PrimitiveBinding::new(Primitive::RawInstance {
cell: arcstr::literal!("vsource"),
ports: vec!["p".into(), "n".into()],
params,
});
prim.connect("p", io.p);
prim.connect("n", io.n);
cell.set_primitive(prim);
Ok(())
}
}
impl Schematic<Spectre> for DcVsource {
fn schematic(
&self,
io: &<<Self as Block>::Io as HardwareType>::Bundle,
cell: &mut CellBuilder<Spectre>,
) -> substrate::error::Result<Self::NestedData> {
cell.flatten();
cell.instantiate_connected(Vsource::dc(self.value()), io);
Ok(())
}
}
#[derive(Serialize, Deserialize, Default, Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct AcSource {
pub dc: Decimal,
pub mag: Decimal,
pub phase: Decimal,
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum Isource {
Dc(Decimal),
Ac(AcSource),
Pulse(Pulse),
}
impl Isource {
#[inline]
pub fn dc(value: Decimal) -> Self {
Self::Dc(value)
}
#[inline]
pub fn pulse(value: Pulse) -> Self {
Self::Pulse(value)
}
#[inline]
pub fn ac(value: AcSource) -> Self {
Self::Ac(value)
}
}
impl Block for Isource {
type Io = TwoTerminalIo;
fn id() -> arcstr::ArcStr {
arcstr::literal!("isource")
}
fn name(&self) -> arcstr::ArcStr {
arcstr::format!("userisource")
}
fn io(&self) -> Self::Io {
Default::default()
}
}
impl ExportsNestedData for Isource {
type NestedData = ();
}
impl Schematic<Spectre> for Isource {
fn schematic(
&self,
io: &<<Self as Block>::Io as HardwareType>::Bundle,
cell: &mut CellBuilder<Spectre>,
) -> substrate::error::Result<Self::NestedData> {
use arcstr::literal;
let mut params = HashMap::new();
match self {
Isource::Dc(dc) => {
params.insert(literal!("type"), ParamValue::String(literal!("dc")));
params.insert(literal!("dc"), ParamValue::Numeric(*dc));
}
Isource::Pulse(pulse) => {
params.insert(literal!("type"), ParamValue::String(literal!("pulse")));
params.insert(literal!("val0"), ParamValue::Numeric(pulse.val0));
params.insert(literal!("val1"), ParamValue::Numeric(pulse.val1));
if let Some(period) = pulse.period {
params.insert(literal!("period"), ParamValue::Numeric(period));
}
if let Some(rise) = pulse.rise {
params.insert(literal!("rise"), ParamValue::Numeric(rise));
}
if let Some(fall) = pulse.fall {
params.insert(literal!("fall"), ParamValue::Numeric(fall));
}
if let Some(width) = pulse.width {
params.insert(literal!("width"), ParamValue::Numeric(width));
}
if let Some(delay) = pulse.delay {
params.insert(literal!("delay"), ParamValue::Numeric(delay));
}
}
Isource::Ac(ac) => {
params.insert(literal!("type"), ParamValue::String(literal!("dc")));
params.insert(literal!("dc"), ParamValue::Numeric(ac.dc));
params.insert(literal!("mag"), ParamValue::Numeric(ac.mag));
params.insert(literal!("phase"), ParamValue::Numeric(ac.phase));
}
};
let mut prim = PrimitiveBinding::new(Primitive::RawInstance {
cell: arcstr::literal!("isource"),
ports: vec!["p".into(), "n".into()],
params,
});
prim.connect("p", io.p);
prim.connect("n", io.n);
cell.set_primitive(prim);
Ok(())
}
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Iprobe;
impl Block for Iprobe {
type Io = TwoTerminalIo;
fn id() -> arcstr::ArcStr {
arcstr::literal!("iprobe")
}
fn name(&self) -> arcstr::ArcStr {
arcstr::format!("useriprobe")
}
fn io(&self) -> Self::Io {
Default::default()
}
}
impl ExportsNestedData for Iprobe {
type NestedData = ();
}
impl Schematic<Spectre> for Iprobe {
fn schematic(
&self,
io: &<<Self as Block>::Io as HardwareType>::Bundle,
cell: &mut CellBuilder<Spectre>,
) -> substrate::error::Result<Self::NestedData> {
let mut prim = PrimitiveBinding::new(Primitive::RawInstance {
cell: arcstr::literal!("iprobe"),
ports: vec!["in".into(), "out".into()],
params: HashMap::new(),
});
prim.connect("in", io.p);
prim.connect("out", io.n);
cell.set_primitive(prim);
Ok(())
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
pub struct Nport {
parameter_file: PathBuf,
ports: usize,
}
impl Nport {
pub fn new(ports: usize, parameter_file: impl Into<PathBuf>) -> Self {
Self {
parameter_file: parameter_file.into(),
ports,
}
}
}
#[derive(Io, Clone, Debug)]
pub struct NportIo {
pub ports: Array<TwoTerminalIo>,
}
impl Block for Nport {
type Io = NportIo;
fn id() -> arcstr::ArcStr {
arcstr::literal!("nport")
}
fn name(&self) -> arcstr::ArcStr {
arcstr::format!("usernport")
}
fn io(&self) -> Self::Io {
NportIo {
ports: Array::new(self.ports, Default::default()),
}
}
}
impl ExportsNestedData for Nport {
type NestedData = ();
}
impl Schematic<Spectre> for Nport {
fn schematic(
&self,
io: &<<Self as Block>::Io as HardwareType>::Bundle,
cell: &mut CellBuilder<Spectre>,
) -> substrate::error::Result<Self::NestedData> {
let mut prim = PrimitiveBinding::new(Primitive::RawInstance {
cell: arcstr::literal!("nport"),
ports: (1..=self.ports)
.flat_map(|i| [arcstr::format!("t{i}"), arcstr::format!("b{i}")])
.collect(),
params: HashMap::from_iter([(
arcstr::literal!("file"),
ParamValue::String(arcstr::format!("{:?}", self.parameter_file)),
)]),
});
for i in 0..self.ports {
prim.connect(arcstr::format!("t{}", i + 1), io.ports[i].p);
prim.connect(arcstr::format!("b{}", i + 1), io.ports[i].n);
}
cell.set_primitive(prim);
Ok(())
}
}