Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
6 changes: 5 additions & 1 deletion src/bin/eqmap_asic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,11 @@ fn main() -> std::io::Result<()> {
PartitionMethod::ArcSet => {
mapper.insert_partitioned().map_err(std::io::Error::other)?;
}
PartitionMethod::DelayPaths => todo!("Implement delay-based partitioning"),
PartitionMethod::DelayPaths => {
mapper
.insert_delay_paths(2)
.map_err(std::io::Error::other)?;
}
}

let mut mapping = mapper.mappings();
Expand Down
6 changes: 5 additions & 1 deletion src/bin/eqmap_fpga.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,11 @@ fn main() -> std::io::Result<()> {
PartitionMethod::ArcSet => {
mapper.insert_partitioned().map_err(std::io::Error::other)?;
}
PartitionMethod::DelayPaths => todo!("Implement delay-based partitioning"),
PartitionMethod::DelayPaths => {
mapper
.insert_delay_paths(2)
.map_err(std::io::Error::other)?;
}
}

let mut mapping = mapper.mappings();
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub mod pass;
pub mod rewrite;
#[cfg(feature = "graph_dumps")]
pub mod serialize;
pub mod timing;
pub mod verilog;

#[cfg(test)]
Expand Down
27 changes: 24 additions & 3 deletions src/netlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
use crate::asic::CellLang;
use crate::driver::CircuitLang;
use crate::lut::LutLang;
use crate::timing::{expand_n_nodes, get_critical_paths};
use bitvec::field::BitField;
use egg::{Id, RecExpr, Symbol};
use nl_compiler::FromId;
use safety_net::graph::MultiDiGraph;
use safety_net::graph::{CombDepthInfo, MultiDiGraph};
use safety_net::{
Analysis, DrivenNet, Error, Identifier, Instantiable, Logic, Net, Netlist, Parameter,
format_id, iter::NetDFSIterator,
Expand Down Expand Up @@ -129,8 +130,7 @@ where
})
}
}

impl<'a, L: CircuitLang, I: Instantiable + LogicFunc<L>> LogicMapper<'a, L, I> {
impl<'a, L: CircuitLang, I: Instantiable + LogicFunc<L> + 'static> LogicMapper<'a, L, I> {
/// Map `nets` to [CircuitLang] nodes. `nets` that do not pass `filter_netref` *and* `filter_inst` become leaves.
fn insert_filtered<F, G>(
&mut self,
Expand Down Expand Up @@ -345,6 +345,27 @@ impl<'a, L: CircuitLang, I: Instantiable + LogicFunc<L>> LogicMapper<'a, L, I> {
pub fn mappings(self) -> Vec<LogicMapping<L, I>> {
self.mappings
}

/// Maps a critical delay path plus a bounded amount of surrounding fan-in logic.
pub fn insert_delay_paths(&mut self, expansion: usize) -> Result<RecExpr<L>, String> {
Comment thread
danielpenas42 marked this conversation as resolved.
Outdated
let analysis = self
._netlist
.get_analysis::<CombDepthInfo<_>>()
.map_err(|e| e.to_string())?;
let critical_path = get_critical_paths(&analysis, 1)
Comment thread
danielpenas42 marked this conversation as resolved.
Outdated
.into_iter()
.next()
.ok_or_else(|| "Critical path is empty".to_string())?;
let endpoint = critical_path.endpoint();
let expanded_nodes = expand_n_nodes(critical_path, expansion);
let roots = vec![DrivenNet::from(&endpoint)];
Comment thread
danielpenas42 marked this conversation as resolved.
Outdated

self.insert_filtered(
roots,
move |d| expanded_nodes.contains(&d.clone().unwrap()),
|_| true,
)
}
}

/// Create an instantiable cell out of the [CellType]
Expand Down
105 changes: 105 additions & 0 deletions src/timing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*!

Timing analysis helpers for timing-aware optimization flows.

*/

use safety_net::Instantiable;
use safety_net::NetRef;
use safety_net::graph::CombDepthInfo;
use std::collections::HashSet;

#[derive(Debug)]
/// A representative critical path ending at a timing endpoint.
pub struct DelayPath<I: Instantiable> {
/// The path from endpoint backward through critical fan-in.
path: Vec<NetRef<I>>,
Comment thread
danielpenas42 marked this conversation as resolved.
Outdated
}

impl<I: Instantiable> DelayPath<I> {
/// Returns the depth/length of the delay path.
pub fn depth(&self) -> usize {
self.path.len()
}

/// The signal being driven by this path
pub fn endpoint(&self) -> NetRef<I> {
self.path[0].clone()
}

/// The nodes along the delay path as a slice
pub fn path(&self) -> &[NetRef<I>] {
&self.path
}
}

impl<I: Instantiable> IntoIterator for DelayPath<I> {
type Item = NetRef<I>;
type IntoIter = std::vec::IntoIter<NetRef<I>>;

fn into_iter(self) -> Self::IntoIter {
self.path.into_iter()
}
}

fn build_path_from_endpoint<I: Instantiable>(
analysis: &CombDepthInfo<'_, I>,
endpoint: NetRef<I>,
Comment thread
danielpenas42 marked this conversation as resolved.
Outdated
) -> Option<DelayPath<I>> {
let mut path = Vec::new();
let mut current = endpoint;

while let Some(crit) = analysis.get_crit_input(&current) {
path.push(current.clone());
if let Some(c) = crit.get_driver() {
current = c.unwrap();
} else {
return None;
}
}

path.push(current);
Some(DelayPath { path })
}

/// Gets up to `n` most critical paths.
pub fn get_critical_paths<I: Instantiable>(
analysis: &CombDepthInfo<'_, I>,
n: usize,
) -> Vec<DelayPath<I>> {

@matth2k matth2k Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this function lazy? Return a Iterator<Item =DelayPath<I>>?
Would be nice because build_critical_path is a heavy function.

Something like

pub fn get_critical_paths<I: Instantiable>(
    analysis: &CombDepthInfo<'_, I>,
    n: usize,
) -> impl Iterator<Item =DelayPath<I>> {
    if analysis.get_max_depth().is_none() {
        return std::iter::empty();
    }

    analysis.get_critical_points().into_iter().take(n).flat_map(|p| build_path_from_endpoint(analysis, p))
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big deal if it doesn't work. Let me know.

if analysis.get_max_depth().is_none() {
return Vec::new();
}

let mut vec = Vec::new();

for p in analysis.get_critical_points().into_iter().take(n) {
if let Some(path) = build_path_from_endpoint(analysis, p.clone()) {
vec.push(path);
}
}

vec
}

/// Expands a critical path backward through fan-in for `n` frontier steps.
Comment thread
danielpenas42 marked this conversation as resolved.
Outdated
pub fn expand_n_nodes<I: Instantiable>(path: DelayPath<I>, n: usize) -> HashSet<NetRef<I>> {
Comment thread
danielpenas42 marked this conversation as resolved.
Outdated
let mut frontier: Vec<NetRef<I>> = path.into_iter().collect();
let mut expanded_nodes: HashSet<NetRef<I>> = frontier.iter().cloned().collect();

for _ in 0..n {
let mut next_frontier = Vec::new();

for node in frontier {
for driver in node.drivers().flatten() {
if expanded_nodes.insert(driver.clone()) {
next_frontier.push(driver);
}
Comment thread
matth2k marked this conversation as resolved.
Outdated
}
}

frontier = next_frontier;
}

expanded_nodes
}
Loading