From b1d09963429edd5a9c7653b175d081ba01b2875b Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Thu, 24 Sep 2026 17:30:38 -0700 Subject: [PATCH 1/2] fix(ci): fix CI failures, add dependabot config --- .github/dependabot.yml | 43 +++++++++++++++++++ .github/workflows/ci.yaml | 4 +- codegen/src/hard_macro.rs | 3 -- libs/gds21/Cargo.toml | 2 +- libs/gds21/src/lib.rs | 8 ++-- libs/gds21/src/ser.rs | 2 +- libs/gds21/src/write.rs | 3 +- libs/subgeom/src/lib.rs | 14 +++--- libs/subgeom/src/transform.rs | 1 + libs/sublut/src/lib.rs | 4 +- plugins/spectre/src/lib.rs | 2 +- plugins/spice_rawfile/src/parser/mod.rs | 8 ++-- plugins/subspice/src/lib.rs | 6 +-- plugins/subspice/src/parser/mod.rs | 12 +++--- substrate/Cargo.toml | 2 +- substrate/src/data.rs | 10 +++-- substrate/src/deps.rs | 3 +- substrate/src/digital/wire.rs | 7 ++- substrate/src/fmt/signal.rs | 2 +- substrate/src/layout/cell.rs | 4 +- substrate/src/layout/convert/gds.rs | 6 +-- substrate/src/layout/group/mod.rs | 2 +- substrate/src/layout/placement/grid.rs | 2 +- substrate/src/layout/routing/auto/abs.rs | 11 +++-- substrate/src/layout/routing/auto/straps.rs | 2 +- substrate/src/layout/routing/tracks.rs | 2 +- substrate/src/pdk/corner/mod.rs | 1 - substrate/src/pdk/mos/db.rs | 6 +-- substrate/src/pdk/stdcell/mod.rs | 10 ++--- substrate/src/schematic/netlist/interface.rs | 9 +--- substrate/src/schematic/netlist/preprocess.rs | 2 +- substrate/src/verification/simulation/bits.rs | 2 +- substrate/src/verification/simulation/mod.rs | 2 +- substrate/src/verification/timing/mod.rs | 12 +++--- 34 files changed, 124 insertions(+), 85 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..b54bd7e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,43 @@ +version: 2 +updates: + # Rust dependencies + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + groups: + cargo-updates: + patterns: + - "*" + cargo-security: + applies-to: security-updates + patterns: + - "*" + open-pull-requests-limit: 2 + labels: + - "dependencies" + - "rust" + commit-message: + prefix: "chore" + include: "scope" + + # GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + groups: + github-actions-updates: + patterns: + - "*" + open-pull-requests-limit: 2 + labels: + - "dependencies" + - "github-actions" + commit-message: + prefix: "ci" + include: "scope" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5142c6e..a547c3c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -14,7 +14,7 @@ jobs: name: Build, lint, and test runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: 'recursive' - name: Install native dependencies @@ -22,7 +22,7 @@ jobs: sudo apt-get update sudo apt-get install -y ngspice - name: Clone external dependencies - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: repository: substrate-labs/skywater-pdk path: skywater-pdk diff --git a/codegen/src/hard_macro.rs b/codegen/src/hard_macro.rs index 945bbb6..93949f2 100644 --- a/codegen/src/hard_macro.rs +++ b/codegen/src/hard_macro.rs @@ -11,11 +11,8 @@ struct HardMacroArgs { name: String, pdk: String, path_fn: Ident, - #[darling(default)] spice_subckt_name: Option, - #[darling(default)] gds_cell_name: Option, - #[darling(default)] toml_fn: Option, } diff --git a/libs/gds21/Cargo.toml b/libs/gds21/Cargo.toml index 5d02fd8..1769f12 100644 --- a/libs/gds21/Cargo.toml +++ b/libs/gds21/Cargo.toml @@ -15,7 +15,7 @@ byteorder = "1.4.3" chrono = { version = "0.4.24", features = ["serde"] } derive_builder = "0.9.0" derive_more = "0.99.17" -num-derive = "0.3" +num-derive = "0.4" num-traits = "0.2.15" serde = { version = "1.0.159", features = ["derive"] } serde_derive = "1.0.159" diff --git a/libs/gds21/src/lib.rs b/libs/gds21/src/lib.rs index 432727d..25935a0 100644 --- a/libs/gds21/src/lib.rs +++ b/libs/gds21/src/lib.rs @@ -306,7 +306,7 @@ impl GdsFloat64 { let mantissa: f64 = mantissa as f64 / 2f64.powi(8 * 7); // Combine everything into our overall value if neg { - -1.0 * mantissa * 16f64.powi(exp) + -mantissa * 16f64.powi(exp) } else { mantissa * 16f64.powi(exp) } @@ -438,7 +438,7 @@ impl GdsPoint { pts.iter().map(|pt| Self::new(pt.0, pt.1)).collect() } /// Convert from a two-element vector - fn parse(from: &Vec) -> GdsResult { + fn parse(from: &[i32]) -> GdsResult { if from.len() != 2 { return Err(GdsError::Str( "GdsPoint coordinate vector: Invalid number of elements".into(), @@ -451,7 +451,7 @@ impl GdsPoint { } /// Convert an n-element vector if `i32` into an n/2-element vector of [GdsPoint]s. fn parse_vec(from: &[i32]) -> GdsResult> { - if from.len() % 2 != 0 { + if !from.len().is_multiple_of(2) { return Err(GdsError::Str( "GdsPoint coordinate vector: Invalid number of elements".into(), )); @@ -470,7 +470,7 @@ impl GdsPoint { vec![self.x, self.y] } /// Convert an n-element vector of [GdsPoint]s to a 2n-element i32 vector. - fn flatten_vec(src: &Vec) -> Vec { + fn flatten_vec(src: &[GdsPoint]) -> Vec { let mut rv = Vec::with_capacity(src.len() * 2); for pt in src.iter() { rv.push(pt.x); diff --git a/libs/gds21/src/ser.rs b/libs/gds21/src/ser.rs index f8fd40d..7e8997e 100644 --- a/libs/gds21/src/ser.rs +++ b/libs/gds21/src/ser.rs @@ -86,7 +86,7 @@ pub trait SerdeFile: Serialize + DeserializeOwned { /// Wrapper over other errors #[derive(Debug)] -pub struct Error(Box); +pub struct Error(#[allow(dead_code)] Box); impl std::fmt::Display for Error { /// Delegate [std::fmt::Display] to the (derived) [std::fmt::Debug] implementation. /// Maybe more info that wanted in some cases. But certainly enough. diff --git a/libs/gds21/src/write.rs b/libs/gds21/src/write.rs index a7431d5..ff06f87 100644 --- a/libs/gds21/src/write.rs +++ b/libs/gds21/src/write.rs @@ -455,7 +455,7 @@ trait Encode { } self.encode_record(GdsRecord::Layer(box_.layer))?; self.encode_record(GdsRecord::BoxType(box_.boxtype))?; - self.encode_record(GdsRecord::Xy(GdsPoint::flatten_vec(&box_.xy.to_vec())))?; + self.encode_record(GdsRecord::Xy(GdsPoint::flatten_vec(&box_.xy)))?; for prop in box_.properties.iter() { self.encode_record(GdsRecord::PropAttr(prop.attr))?; self.encode_record(GdsRecord::PropValue(prop.value.clone()))?; @@ -482,6 +482,7 @@ trait Encode { /// # GdsRecordList /// A largely for-testing implementer of the [Encode] trait, /// which collects the generated records into a vector. +#[allow(dead_code)] #[derive(Default, Debug, Deserialize, Serialize)] pub struct GdsRecordList { pub records: Vec, diff --git a/libs/subgeom/src/lib.rs b/libs/subgeom/src/lib.rs index fcdbafc..c1db08a 100644 --- a/libs/subgeom/src/lib.rs +++ b/libs/subgeom/src/lib.rs @@ -1192,17 +1192,17 @@ impl Rect { /// /// The exact behavior depends on the provided [`ExpandMode`]: /// * [`ExpandMode::All`]: expands the top and bottom edges by `dims.h()` - /// and the left and right edges by `dims.w()`. - /// Note that the total horizontal expansion is `2 * dims.w()` and the - /// total vertical expansion is `2 * dims.h()`. + /// and the left and right edges by `dims.w()`. + /// Note that the total horizontal expansion is `2 * dims.w()` and the + /// total vertical expansion is `2 * dims.h()`. /// * [`ExpandMode::LowerLeft`]: expands the lower edge by `dims.h()` and - /// the left edge by `dims.w()`. + /// the left edge by `dims.w()`. /// * [`ExpandMode::LowerRight`]: expands the lower edge by `dims.h()` and - /// the right edge by `dims.w()`. + /// the right edge by `dims.w()`. /// * [`ExpandMode::UpperLeft`]: expands the upper edge by `dims.h()` and - /// the left edge by `dims.w()`. + /// the left edge by `dims.w()`. /// * [`ExpandMode::UpperRight`]: expands the upper edge by `dims.h()` and - /// the right edge by `dims.w()`. + /// the right edge by `dims.w()`. /// /// See [`Dims`] for more information. pub fn expand_dims(self, dims: Dims, mode: ExpandMode) -> Self { diff --git a/libs/subgeom/src/transform.rs b/libs/subgeom/src/transform.rs index a5a9a17..6c808ee 100644 --- a/libs/subgeom/src/transform.rs +++ b/libs/subgeom/src/transform.rs @@ -82,6 +82,7 @@ impl Transformation { /// * (a) Reflect vertically, then /// * (b) Translate by (1,1) /// * (c) Place a point at (local coordinate) (1,1) + /// /// Lands said point at (2,-2) in top-level space, /// whereas reversing the order of (a) and (b) lands it at (2,0). pub fn cascade(parent: Transformation, child: Transformation) -> Transformation { diff --git a/libs/sublut/src/lib.rs b/libs/sublut/src/lib.rs index 4370e9b..399cc74 100644 --- a/libs/sublut/src/lib.rs +++ b/libs/sublut/src/lib.rs @@ -40,7 +40,7 @@ where pub fn get(&self, k1: &K1, k2: &K2) -> Option<&V> { let i1 = self.k1.partition_point(|k| k < k1); let i2 = self.k2.partition_point(|k| k < k2); - if k1 < self.k1.get(0)? || k2 < self.k2.get(0)? { + if k1 < self.k1.first()? || k2 < self.k2.first()? { return None; } self.values.get(i1)?.get(i2) @@ -67,7 +67,7 @@ impl FloatLut2 { self.k1 .iter() .copied() - .zip(interp1.into_iter()) + .zip(interp1) .map(|(k, v)| Key::new(k, v, splines::Interpolation::Linear)) .collect(), ) diff --git a/plugins/spectre/src/lib.rs b/plugins/spectre/src/lib.rs index fe89efc..2b812ac 100644 --- a/plugins/spectre/src/lib.rs +++ b/plugins/spectre/src/lib.rs @@ -71,7 +71,7 @@ fn dc_conv(parsed_data: PsfDcData) -> DcData { PsfDcData::Sweep(data) => HashMap::from_iter( data.signals .into_iter() - .chain([(data.sweep_var, data.sweep_values)].into_iter()) + .chain([(data.sweep_var, data.sweep_values)]) .map(|(k, v)| { ( k, diff --git a/plugins/spice_rawfile/src/parser/mod.rs b/plugins/spice_rawfile/src/parser/mod.rs index 4a089fc..66042a9 100644 --- a/plugins/spice_rawfile/src/parser/mod.rs +++ b/plugins/spice_rawfile/src/parser/mod.rs @@ -122,7 +122,7 @@ fn parse_f64(input: &[u8]) -> Result>> { Ok(value) } -fn variable(input: &[u8]) -> IResult<&[u8], Variable> { +fn variable(input: &[u8]) -> IResult<&[u8], Variable<'_>> { let value = take_till1(is_space_or_line); // In AC analysis, may have a `grid=X` declaration let grid = opt(pair(space1, &value)); @@ -143,7 +143,7 @@ fn variable(input: &[u8]) -> IResult<&[u8], Variable> { Ok((input, Variable { idx, name, unit })) } -fn variables(input: &[u8]) -> IResult<&[u8], Vec> { +fn variables(input: &[u8]) -> IResult<&[u8], Vec>> { let (input, _) = tuple((tag_no_case("Variables:"), space0, line_ending))(input)?; let (input, vars) = many0(variable)(input)?; Ok((input, vars)) @@ -243,7 +243,7 @@ fn complex_data(input: &[u8], vars: usize, points: usize) -> IResult<&[u8], Data ))(input) } -fn analysis(input: &[u8]) -> IResult<&[u8], Analysis> { +fn analysis(input: &[u8]) -> IResult<&[u8], Analysis<'_>> { let (input, title) = header(input, "Title: ")?; let (input, date) = header(input, "Date: ")?; let (input, plotname) = header(input, "Plotname: ")?; @@ -275,6 +275,6 @@ fn analysis(input: &[u8]) -> IResult<&[u8], Analysis> { )) } -pub(crate) fn analyses(input: &[u8]) -> IResult<&[u8], Vec> { +pub(crate) fn analyses(input: &[u8]) -> IResult<&[u8], Vec>> { many0(analysis)(input) } diff --git a/plugins/subspice/src/lib.rs b/plugins/subspice/src/lib.rs index d244200..ae6273c 100644 --- a/plugins/subspice/src/lib.rs +++ b/plugins/subspice/src/lib.rs @@ -23,12 +23,12 @@ where impl<'a> ParsedSpice<'a> { /// Return an iterator over the lines in the parsed SPICE netlist. - pub fn lines(&self) -> impl Iterator { + pub fn lines(&self) -> impl Iterator> { self.lines.iter() } /// Return an iterator over the subcircuit definitions in the netlist. - pub fn subcircuits(&self) -> impl Iterator { + pub fn subcircuits(&self) -> impl Iterator> { self.lines.iter().filter_map(|line| line.subckt()) } @@ -40,7 +40,7 @@ impl<'a> ParsedSpice<'a> { /// If you need to query for multiple subcircuits, you may wish to /// collect the [`subcircuits`](ParsedSpice::subcircuits) into a /// [`HashMap`](std::collections::HashMap), and query the map instead. - pub fn subcircuit_named(&self, name: impl AsRef) -> Option<&SubcktLine> { + pub fn subcircuit_named(&self, name: impl AsRef) -> Option<&SubcktLine<'_>> { let name = name.as_ref(); self.subcircuits().find(|ckt| ckt.name == name) } diff --git a/plugins/subspice/src/parser/mod.rs b/plugins/subspice/src/parser/mod.rs index c2baa46..71b4ee2 100644 --- a/plugins/subspice/src/parser/mod.rs +++ b/plugins/subspice/src/parser/mod.rs @@ -21,7 +21,7 @@ pub enum SpiceLine<'a> { } impl<'a> SpiceLine<'a> { - pub fn subckt(&self) -> Option<&SubcktLine> { + pub fn subckt(&self) -> Option<&SubcktLine<'_>> { match self { SpiceLine::Subckt(line) => Some(line), _ => None, @@ -87,28 +87,28 @@ fn subckt_name(input: &str) -> IResult<&str, &str> { preceded(spice_space1, ident)(input) } -fn subckt_line(input: &str) -> IResult<&str, SpiceLine> { +fn subckt_line(input: &str) -> IResult<&str, SpiceLine<'_>> { let (input, (_, name, ports)) = tuple((tag_no_case(".subckt"), subckt_name, subckt_ports))(input)?; Ok((input, SpiceLine::Subckt(SubcktLine { name, ports }))) } -fn comment_line(input: &str) -> IResult<&str, SpiceLine> { +fn comment_line(input: &str) -> IResult<&str, SpiceLine<'_>> { let (input, (_, _, comment, _)) = tuple((space0, tag_no_case("*"), take_till(is_newline), line_ending))(input)?; Ok((input, SpiceLine::Comment(comment.trim()))) } -fn other_line(input: &str) -> IResult<&str, SpiceLine> { +fn other_line(input: &str) -> IResult<&str, SpiceLine<'_>> { let (input, _) = pair(ident, many0(preceded(ident, spice_space1)))(input)?; Ok((input, SpiceLine::Other)) } -fn spice_line(input: &str) -> IResult<&str, SpiceLine> { +fn spice_line(input: &str) -> IResult<&str, SpiceLine<'_>> { alt((subckt_line, comment_line, other_line))(input) } -pub(crate) fn parse_spice(input: &str) -> IResult<&str, Vec> { +pub(crate) fn parse_spice(input: &str) -> IResult<&str, Vec>> { many0(delimited(multispace0, spice_line, multispace0))(input) } diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index fdc852f..40aa0f1 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Rahul Kumar ", "Rohan Kumar RwLockReadGuard { + pub(crate) fn read(&self) -> RwLockReadGuard<'_, SubstrateData> { self.inner.read().unwrap() } #[inline] - pub(crate) fn write(&self) -> RwLockWriteGuard { + pub(crate) fn write(&self) -> RwLockWriteGuard<'_, SubstrateData> { self.inner.write().unwrap() } #[inline] + // `SubstrateData` is not `Send`/`Sync`; `Arc` is kept so the public type doesn't change. + #[allow(clippy::arc_with_non_send_sync)] pub fn from_config(cfg: SubstrateConfig) -> Result { Ok(Self { inner: Arc::new(RwLock::new(SubstrateData::from_config(cfg)?)), @@ -859,7 +861,7 @@ impl SubstrateCtx { where T: Testbench, { - let work_dir = TempDir::new("subsim")?; + let work_dir = TempDir::with_prefix("subsim")?; let work_dir = work_dir.path(); self.write_simulation::(params, work_dir) } diff --git a/substrate/src/deps.rs b/substrate/src/deps.rs index b6a68c9..cf6003a 100644 --- a/substrate/src/deps.rs +++ b/substrate/src/deps.rs @@ -1 +1,2 @@ -pub use {arcstr, serde}; +pub use arcstr; +pub use serde; diff --git a/substrate/src/digital/wire.rs b/substrate/src/digital/wire.rs index b46ae90..21d0439 100644 --- a/substrate/src/digital/wire.rs +++ b/substrate/src/digital/wire.rs @@ -33,11 +33,11 @@ impl WireDb { } } - pub(crate) fn inner(&self) -> RwLockReadGuard { + pub(crate) fn inner(&self) -> RwLockReadGuard<'_, WireDbInner> { self.inner.read().unwrap() } - pub(crate) fn inner_mut(&self) -> RwLockWriteGuard { + pub(crate) fn inner_mut(&self) -> RwLockWriteGuard<'_, WireDbInner> { self.inner.write().unwrap() } } @@ -96,11 +96,14 @@ pub(crate) enum WireValue { BinOp(Op, WireKey, WireKey), #[allow(unused)] Cast(HardwareType, WireKey), + #[allow(unused)] Port(ArcStr), /// Concatenation of wires in MSB order. Concat(WireKey, WireKey), /// Slice of wire. + #[allow(unused)] Slice(Range, WireKey), + #[allow(unused)] Reg(Reg), /// Output of an instance. InstanceOutput, diff --git a/substrate/src/fmt/signal.rs b/substrate/src/fmt/signal.rs index fe324fc..539eb69 100644 --- a/substrate/src/fmt/signal.rs +++ b/substrate/src/fmt/signal.rs @@ -117,7 +117,7 @@ fn get_regex(format: BusFmt) -> Regex { regex } -pub fn parse_bus(text: &str, format: BusFmt) -> std::result::Result { +pub fn parse_bus(text: &str, format: BusFmt) -> std::result::Result, ParseBusError> { let re = get_regex(format); let caps = re .captures(text.trim()) diff --git a/substrate/src/layout/cell.rs b/substrate/src/layout/cell.rs index 4b7a95f..7b5dfa8 100644 --- a/substrate/src/layout/cell.rs +++ b/substrate/src/layout/cell.rs @@ -1013,7 +1013,7 @@ impl CellPort { shapes: impl IntoIterator, ) -> Self { let mut map = HashMap::with_capacity(1); - let mut entry = map.entry(layer).or_insert_with(Vec::new); + let entry = map.entry(layer).or_insert_with(Vec::new); entry.extend(shapes); Self { id: id.into(), @@ -1446,7 +1446,7 @@ impl Instance { pub fn port( &self, id: impl Into, - ) -> std::result::Result, PortError> { + ) -> std::result::Result, PortError> { let port = self.cell.port(id)?; Ok(TransformedPort { transformation: self.transformation(), diff --git a/substrate/src/layout/convert/gds.rs b/substrate/src/layout/convert/gds.rs index ffe0090..b386c32 100644 --- a/substrate/src/layout/convert/gds.rs +++ b/substrate/src/layout/convert/gds.rs @@ -789,7 +789,7 @@ impl<'a> GdsImporter<'a> { fn import_and_add(&mut self, strukt: &gds21::GdsStruct) -> LayoutResult<()> { let name = &strukt.name; // Check whether we're already defined, and bail if so - if self.cell_map.get(name).is_some() { + if self.cell_map.contains_key(name) { return self.fail(format!("Cell {name} defined multiple times in GDS file")); } @@ -877,7 +877,6 @@ impl<'a> GdsImporter<'a> { if purp == &LayerPurpose::Label || purp == &LayerPurpose::Pin { if let Some(pin_spec) = pin_spec { let mut port = CellPort::new(&net_name); - let mut has_geometry = false; if let Some(layer) = layers.get_mut(&text_spec.layer()) { // Layer exists in geometry; see which elements intersect with this text for ekey in layer.iter() { @@ -898,13 +897,12 @@ impl<'a> GdsImporter<'a> { pname, textelem.string.clone(), textelem.layer, - &strukt.name, + strukt.name, ); } } elem.net = Some(ArcStr::from(&net_name)); port.add(pin_spec.layer(), elem.inner.clone()); - has_geometry = true; // This pin shape is stored in a port. // No need to also include it as a regular element. diff --git a/substrate/src/layout/group/mod.rs b/substrate/src/layout/group/mod.rs index 26bc39c..886426e 100644 --- a/substrate/src/layout/group/mod.rs +++ b/substrate/src/layout/group/mod.rs @@ -197,7 +197,7 @@ impl Group { pub fn port( &self, id: impl Into, - ) -> std::result::Result, PortError> { + ) -> std::result::Result, PortError> { let port = self.ports.port(id)?; Ok(TransformedPort { transformation: self.transformation(), diff --git a/substrate/src/layout/placement/grid.rs b/substrate/src/layout/placement/grid.rs index c0296ae..64a4f0d 100644 --- a/substrate/src/layout/placement/grid.rs +++ b/substrate/src/layout/placement/grid.rs @@ -167,7 +167,7 @@ impl<'a> GridTiler<'a> { /// /// This function panics if `i` or `j` are out of bounds, /// or if no [`Tile`] was specified at the given position. - pub fn tile(&self, i: usize, j: usize) -> &Tile { + pub fn tile(&self, i: usize, j: usize) -> &Tile<'_> { self.tiles[i][j].as_ref().unwrap() } diff --git a/substrate/src/layout/routing/auto/abs.rs b/substrate/src/layout/routing/auto/abs.rs index 9fd1b3f..88d19f9 100644 --- a/substrate/src/layout/routing/auto/abs.rs +++ b/substrate/src/layout/routing/auto/abs.rs @@ -374,10 +374,7 @@ impl AbstractNetInfo { } fn add_to_group(&mut self, pos: Pos, conn_group: ConnectionGroup) { - self.conn_groups - .entry(conn_group) - .or_insert(HashSet::new()) - .insert(pos); + self.conn_groups.entry(conn_group).or_default().insert(pos); } fn pos_in_group(&self, conn_group: ConnectionGroup) -> Option> { @@ -734,7 +731,9 @@ impl GreedyAbstractRouter { // Ensure that next position is on the grid for its corresponding layer. let next_pos = pos.next(action); let next_layer_info = self.layer_info(next_pos.layer); - next_pos.coord(!next_layer_info.dir) % next_layer_info.grid_space == 0 + next_pos + .coord(!next_layer_info.dir) + .is_multiple_of(next_layer_info.grid_space) } fn span_next(&self, span: PosSpan) -> Vec { @@ -792,7 +791,7 @@ fn round_down(x: usize, grid: usize) -> usize { } fn round_up(x: usize, grid: usize) -> usize { - ((x + grid - 1) / grid) * grid + x.div_ceil(grid) * grid } #[cfg(test)] diff --git a/substrate/src/layout/routing/auto/straps.rs b/substrate/src/layout/routing/auto/straps.rs index 7dfe5c7..e9de9df 100644 --- a/substrate/src/layout/routing/auto/straps.rs +++ b/substrate/src/layout/routing/auto/straps.rs @@ -64,7 +64,7 @@ fn index(net: SingleSupplyNet) -> usize { #[inline] fn net_from_idx(idx: usize) -> SingleSupplyNet { - if idx % 2 == 0 { + if idx.is_multiple_of(2) { SingleSupplyNet::Vss } else { SingleSupplyNet::Vdd diff --git a/substrate/src/layout/routing/tracks.rs b/substrate/src/layout/routing/tracks.rs index 1ed0d63..beee6b1 100644 --- a/substrate/src/layout/routing/tracks.rs +++ b/substrate/src/layout/routing/tracks.rs @@ -127,7 +127,7 @@ impl FixedTracks { self.len() == 0 } - pub fn iter(&self) -> FixedTracksIter { + pub fn iter(&self) -> FixedTracksIter<'_> { FixedTracksIter { idx: 0, ptr: self } } } diff --git a/substrate/src/pdk/corner/mod.rs b/substrate/src/pdk/corner/mod.rs index 0beaf1f..2b59104 100644 --- a/substrate/src/pdk/corner/mod.rs +++ b/substrate/src/pdk/corner/mod.rs @@ -14,7 +14,6 @@ new_key_type! { pub struct CornerKey; } -/// #[derive(Debug, Clone, PartialEq, Builder, Serialize, Deserialize)] pub struct Pvt { /// The process corner. diff --git a/substrate/src/pdk/mos/db.rs b/substrate/src/pdk/mos/db.rs index 510e6b8..bd4f0d3 100644 --- a/substrate/src/pdk/mos/db.rs +++ b/substrate/src/pdk/mos/db.rs @@ -17,7 +17,7 @@ impl MosDb { Ok(Self { devices }) } - pub fn query(&self, query: Query) -> Result { + pub fn query(&self, query: Query) -> Result> { self.devices .values() .find(|&m| m.supply == query.supply && m.kind == query.kind && m.flavor == query.flavor) @@ -42,11 +42,11 @@ impl MosDb { .ok_or(ErrorSource::DeviceNotFound.into()) } - pub fn default_nmos(&self) -> Result { + pub fn default_nmos(&self) -> Result> { self.query(Query::builder().kind(MosKind::Nmos).build().unwrap()) } - pub fn default_pmos(&self) -> Result { + pub fn default_pmos(&self) -> Result> { self.query(Query::builder().kind(MosKind::Pmos).build().unwrap()) } } diff --git a/substrate/src/pdk/stdcell/mod.rs b/substrate/src/pdk/stdcell/mod.rs index 84c20fe..869142a 100644 --- a/substrate/src/pdk/stdcell/mod.rs +++ b/substrate/src/pdk/stdcell/mod.rs @@ -209,20 +209,20 @@ impl StdCellLibEntry { } #[inline] - pub fn cells(&self) -> impl Iterator + '_ { + pub fn cells(&self) -> impl Iterator> + '_ { self.data .cells() .map(|cell| StdCellRef::new(self.id(), cell)) } - pub fn try_cell_named(&self, name: &str) -> crate::error::Result { + pub fn try_cell_named(&self, name: &str) -> crate::error::Result> { self.data .try_cell_named(name) .map(|cell| StdCellRef::new(self.id(), cell)) } #[inline] - pub fn try_cell(&self, id: StdCellKey) -> crate::error::Result { + pub fn try_cell(&self, id: StdCellKey) -> crate::error::Result> { self.data .try_cell(id) .map(|cell| StdCellRef::new(self.id(), cell)) @@ -385,7 +385,7 @@ impl StdCellDb { .ok_or_else(|| StdCellError::LibIdNotFound(id).into()) } - pub fn try_cell(&self, id: StdCellId) -> crate::error::Result { + pub fn try_cell(&self, id: StdCellId) -> crate::error::Result> { let lib = self.try_lib(id.lib)?; let cell = lib.try_cell(id.cell)?; Ok(cell) @@ -394,7 +394,7 @@ impl StdCellDb { pub fn try_lib_and_cell( &self, id: StdCellId, - ) -> crate::error::Result<(&StdCellLibEntry, StdCellRef)> { + ) -> crate::error::Result<(&StdCellLibEntry, StdCellRef<'_>)> { let lib = self.try_lib(id.lib)?; let cell = lib.try_cell(id.cell)?; Ok((lib, cell)) diff --git a/substrate/src/schematic/netlist/interface.rs b/substrate/src/schematic/netlist/interface.rs index 43530bf..2cae695 100644 --- a/substrate/src/schematic/netlist/interface.rs +++ b/substrate/src/schematic/netlist/interface.rs @@ -23,13 +23,14 @@ pub struct NetlistOpts { } /// An enumeration of supported netlist formats. -#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Default)] pub enum NetlistFormat { /// Spectre netlist format. Spectre, /// Spectre-compatible SPICE netlist format. SpectreSpice, /// SPICE netlist format. + #[default] Spice, /// NgSpice-compatible SPICE netlist format. NgSpice, @@ -37,12 +38,6 @@ pub enum NetlistFormat { Other(String), } -impl Default for NetlistFormat { - fn default() -> Self { - Self::Spice - } -} - impl Display for NetlistFormat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { diff --git a/substrate/src/schematic/netlist/preprocess.rs b/substrate/src/schematic/netlist/preprocess.rs index 31a3906..09b434b 100644 --- a/substrate/src/schematic/netlist/preprocess.rs +++ b/substrate/src/schematic/netlist/preprocess.rs @@ -70,7 +70,7 @@ impl<'a> NetlistPreprocessor<'a> { /// /// Does the following: /// 1. Makes a list of all modules that are actually used by the top module - /// or its submodules. + /// or its submodules. /// 2. Rewrites duplicate module names. /// 3. Rewrites duplicate instance names within a module. fn bfs(&mut self) -> Result<()> { diff --git a/substrate/src/verification/simulation/bits.rs b/substrate/src/verification/simulation/bits.rs index 3fd9906..24d4358 100644 --- a/substrate/src/verification/simulation/bits.rs +++ b/substrate/src/verification/simulation/bits.rs @@ -50,7 +50,7 @@ impl BitSignal { } #[inline] - pub fn from_u128(mut value: u128, width: usize) -> Self { + pub fn from_u128(value: u128, width: usize) -> Self { assert!(width <= 128); Self::from_u128_padded(value, width) } diff --git a/substrate/src/verification/simulation/mod.rs b/substrate/src/verification/simulation/mod.rs index 2afc0ee..065c03f 100644 --- a/substrate/src/verification/simulation/mod.rs +++ b/substrate/src/verification/simulation/mod.rs @@ -482,7 +482,7 @@ impl TranData { self.data.get(name) } - pub fn waveform(&self, name: &str) -> Option { + pub fn waveform(&self, name: &str) -> Option> { let x = self.data.get(name)?; Some(SharedWaveform::from_signal(&self.time, x)) } diff --git a/substrate/src/verification/timing/mod.rs b/substrate/src/verification/timing/mod.rs index f12a847..08904f7 100644 --- a/substrate/src/verification/timing/mod.rs +++ b/substrate/src/verification/timing/mod.rs @@ -290,12 +290,12 @@ impl TimingReport { pub fn is_failure(&self) -> bool { let setup_fail = self .setup_checks - .get(0) + .first() .map(|c| c.slack < 0.0) .unwrap_or_default(); let hold_fail = self .hold_checks - .get(0) + .first() .map(|c| c.slack < 0.0) .unwrap_or_default(); setup_fail || hold_fail @@ -349,10 +349,10 @@ impl Log for TimingReport { } } else { info!("All timing constraints satisfied"); - if let Some(c) = self.setup_checks.get(0) { + if let Some(c) = self.setup_checks.first() { info!("Minimum setup slack: {:?}", c); } - if let Some(c) = self.hold_checks.get(0) { + if let Some(c) = self.hold_checks.first() { info!("Minimum hold slack: {:?}", c); } } @@ -408,7 +408,7 @@ impl SetupHoldConstraint { impl PreprocessedNetlist { /// Returns a list of the nodes that need to be captured by the simulator. - pub(crate) fn timing_constraint_db(&self, pvt: &Pvt) -> TopConstraintDb { + pub(crate) fn timing_constraint_db(&self, pvt: &Pvt) -> TopConstraintDb<'_> { let mut stack = Vec::new(); let mut out = Vec::new(); self.timing_helper(self.top, pvt, &mut stack, &mut out); @@ -503,7 +503,7 @@ impl<'a> TopConstraintDb<'a> { pub(crate) fn named_constraints( &mut self, netlist: &PreprocessedNetlist, - ) -> impl Iterator { + ) -> impl Iterator> { self.compute_names(netlist); self.named_constraints.as_ref().unwrap().iter() } From bea22348cc5bf1a90ef8f10c79e527ab71d9dd52 Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Thu, 24 Sep 2026 19:05:53 -0700 Subject: [PATCH 2/2] add config.toml and fix logic optimizer algorithm --- .cargo/config.toml | 2 ++ substrate/src/logic/delay.rs | 48 ++++++++++++++++++++++++++++++++---- substrate/src/logic/tests.rs | 38 ++++++++++++++++++++++++---- 3 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..e28cdbc --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[registries] +substrate = { index = "https://github.com/substrate-labs/crates-index" } diff --git a/substrate/src/logic/delay.rs b/substrate/src/logic/delay.rs index 2d5a566..a9b8641 100644 --- a/substrate/src/logic/delay.rs +++ b/substrate/src/logic/delay.rs @@ -167,14 +167,52 @@ impl LogicPath { assert!(opts.max_iter > 0); let mut lr = opts.lr; - for _ in 0..opts.max_iter { + // Backtracking: a step that increased the delay is undone and retried at + // half the size. A fixed step overshoots on the `res / size` terms near + // small sizes. `delay_grad` already returns the delay, so the check is free. + let n = self.variables.len(); + let mut base = vec![0.0; n]; + let mut base_grad = vec![0.0; n]; + let mut base_delay = f64::INFINITY; + let mut step = lr; + let mut iter = 0; + while iter < opts.max_iter { let mut grad = self.zero_grad(); - self.delay_grad(&mut grad); - for (v, s) in self.variables.iter_mut() { - s.value -= lr * grad[v]; + let delay = self.delay_grad(&mut grad); + // The tolerance ignores rounding noise once converged; without it, about + // half of all steps near the optimum are rejected for nothing. + if delay > base_delay * (1.0 + 1e-9) { + step *= 0.5; + if step == 0.0 { + self.set_values(&base); + return; + } + } else { + base_delay = delay; + for (i, (v, s)) in self.variables.iter().enumerate() { + base[i] = s.value; + base_grad[i] = grad[v]; + } + step = lr; + lr *= opts.lr_decay; + iter += 1; + } + for (i, s) in self.variables.values_mut().enumerate() { + // Project back onto the feasible region. Without this, a variable + // pinned at `min_var_value` keeps drifting below it (`value()` hides + // this) and cannot recover if its optimum later moves above the bound. + s.value = f64::max(base[i] - step * base_grad[i], self.min_var_value); } + } + // The last step has not been checked yet. + if self.delay() > base_delay * (1.0 + 1e-9) { + self.set_values(&base); + } + } - lr *= opts.lr_decay; + fn set_values(&mut self, values: &[f64]) { + for (s, &x) in self.variables.values_mut().zip(values) { + s.value = x; } } diff --git a/substrate/src/logic/tests.rs b/substrate/src/logic/tests.rs index 8072abc..73ae58d 100644 --- a/substrate/src/logic/tests.rs +++ b/substrate/src/logic/tests.rs @@ -143,9 +143,13 @@ fn test_inv_chain_4_sizing() { ); } -#[test] -fn test_inv_nand3_nand2() { +/// INV -> NAND3 (size `a`, branching 4 more NAND3 loads) -> NAND2 (size `b`) -> 18 INV loads. +/// +/// Delay is `18 + 25a + 4b/a + 54/b`. The stationary point is `b = 5.4^(2/3)`, +/// `a = 0.4 * sqrt(b)`, i.e. `a = 0.7018`, `b = 3.0780`. +fn inv_nand3_nand2(min_var_value: f64) -> (LogicPath, VarKey, VarKey) { let mut path = LogicPath::new(); + path.set_min_var_value(min_var_value); path.append_sized_gate(INV_MODEL); let cl = 18.0 * INV_MODEL.cin; let a = path.create_variable(); @@ -154,15 +158,39 @@ fn test_inv_nand3_nand2() { path.append_unsized_gate(NAND3_MODEL, a); path.append_unsized_gate(NAND2_MODEL, b); path.append_capacitor(cl); - path.size(); + (path, a, b) +} + +#[test] +fn test_inv_nand3_nand2() { + // The unconstrained optimum has `a < 1`, so `a` sits on the default bound + // and `b` minimizes `4b + 54/b`, giving `b = sqrt(13.5)`. + let (path, a, b) = inv_nand3_nand2(1.0); + assert!( + float_eq!(path.value(a), 1.0, abs <= 0.001), + "incorrect value: {}", + path.value(a) + ); + assert!( + float_eq!(path.value(b), 13.5f64.sqrt(), abs <= 0.001), + "incorrect value: {}", + path.value(b) + ); +} + +#[test] +fn test_inv_nand3_nand2_interior() { + let (path, a, b) = inv_nand3_nand2(0.1); + let b_opt = 5.4f64.powf(2.0 / 3.0); + let a_opt = 0.4 * b_opt.sqrt(); assert!( - float_eq!(path.value(a), 2.052, abs <= 0.001), + float_eq!(path.value(a), a_opt, abs <= 0.001), "incorrect value: {}", path.value(a) ); assert!( - float_eq!(path.value(b), 5.263, abs <= 0.001), + float_eq!(path.value(b), b_opt, abs <= 0.001), "incorrect value: {}", path.value(b) );