Skip to content
Open
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
28 changes: 26 additions & 2 deletions src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,18 @@ pub struct OptArgs {
/// Seed for randomized algorithms
#[arg(long)]
seed: Option<u64>,

/// Balance And/Xor trees to reduce logic depth (delay-oriented)
#[arg(long, default_value_t = false)]
balance: bool,

/// Lower the network to a 2-input And-Inverter Graph (AIG)
#[arg(long, default_value_t = false)]
aig: bool,

/// Lower the network to a Majority-Inverter Graph (MIG)
#[arg(long, default_value_t = false)]
mig: bool,
}

impl OptArgs {
Expand All @@ -170,6 +182,15 @@ impl OptArgs {
optim::infer_dffe(&mut aig);
optim::share_logic(&mut aig, 64);
}
if self.balance {
aig = optim::balance(&aig);
}
if self.aig {
aig = optim::to_aig(&aig);
}
if self.mig {
aig = optim::to_mig(&aig);
}
write_network_file(&self.output, &aig);
}
}
Expand All @@ -183,9 +204,12 @@ pub struct ShowArgs {

impl ShowArgs {
pub fn run(&self) {
use crate::network::stats::stats;
use crate::network::stats::{depth, stats};
use crate::optim::cuts::count_cuts;
let aig = read_network_file(&self.file);
println!("Network stats:\n{}\n\n", stats(&aig));
println!("Network stats:\n{}", stats(&aig));
println!(" Combinational depth: {}", depth(&aig));
println!(" 4-feasible cuts: {}\n\n", count_cuts(&aig, 4));
}
}

Expand Down
37 changes: 35 additions & 2 deletions src/io/blif.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,10 +345,10 @@ pub fn write_blif<W: Write>(w: &mut W, aig: &Network) {
// ABC extension to blif
write!(w, ".flop D={} Q=x{} init=0", sig_to_string(d), i).unwrap();
if *en != Signal::one() {
write!(w, " E={}", en).unwrap();
write!(w, " E={}", sig_to_string(en)).unwrap();
}
if *res != Signal::zero() {
write!(w, " R={}", en).unwrap();
write!(w, " R={}", sig_to_string(res)).unwrap();
}
writeln!(w).unwrap();
} else {
Expand Down Expand Up @@ -495,4 +495,37 @@ mod test {
super::write_blif(&mut buf, &aig);
String::from_utf8(buf.into_inner().unwrap()).unwrap();
}

/// A Dff with both enable and reset must emit distinct E= and R= signals.
/// Regression: the writer used to emit the enable signal for R= as well.
#[test]
fn test_write_flop_enable_reset() {
use std::io::BufWriter;

use crate::Network;

let mut aig = Network::new();
let d = aig.add_input();
let en = aig.add_input();
let res = aig.add_input();
let q = aig.dff(d, en, res);
aig.add_output(q);

let mut buf = BufWriter::new(Vec::new());
super::write_blif(&mut buf, &aig);
let s = String::from_utf8(buf.into_inner().unwrap()).unwrap();

let flop_line = s
.lines()
.find(|l| l.contains(".flop"))
.expect("expected a .flop line for a Dff with enable and reset");
assert!(
flop_line.contains("E=i1"),
"enable should be i1: {flop_line}"
);
assert!(
flop_line.contains("R=i2"),
"reset should be i2, not the enable: {flop_line}"
);
}
}
118 changes: 115 additions & 3 deletions src/network/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,13 @@ pub struct NetworkStats {
impl NetworkStats {
/// Total number of gates, including Dff
pub fn nb_gates(&self) -> usize {
self.nb_and + self.nb_xor + self.nb_mux + self.nb_maj + self.nb_buf + self.nb_dff
self.nb_and
+ self.nb_xor
+ self.nb_lut
+ self.nb_mux
+ self.nb_maj
+ self.nb_buf
+ self.nb_dff
}

/// Record a new and
Expand Down Expand Up @@ -96,10 +102,10 @@ impl fmt::Display for NetworkStats {
if self.nb_dff != 0 {
writeln!(f, " Dff: {}", self.nb_dff)?;
if self.nb_dffe != 0 {
writeln!(f, " enable: {}", self.nb_dff)?;
writeln!(f, " enable: {}", self.nb_dffe)?;
}
if self.nb_dffr != 0 {
writeln!(f, " reset: {}", self.nb_dff)?;
writeln!(f, " reset: {}", self.nb_dffr)?;
}
}
if self.nb_and != 0 {
Expand Down Expand Up @@ -244,3 +250,109 @@ pub fn gate_is_output(aig: &Network) -> Vec<bool> {
}
ret
}

/// Compute the combinational logic level of every node
///
/// Primary inputs, constants and flip-flop outputs are level 0; each combinatorial
/// gate is one level above its highest input. Buffers and inverters do not add a level.
/// The network must be topologically sorted.
pub fn levels(aig: &Network) -> Vec<u32> {
let mut lvl = vec![0u32; aig.nb_nodes()];
for i in 0..aig.nb_nodes() {
let g = aig.gate(i);
if !g.is_comb() {
// A flip-flop output is a sequential source at level 0
continue;
}
let mut m = 0;
for v in g.vars() {
m = m.max(lvl[v as usize]);
}
lvl[i] = if g.is_buf_like() { m } else { m + 1 };
}
lvl
}

/// Compute the combinational depth of the network: the largest logic level over all outputs
///
/// The network must be topologically sorted.
pub fn depth(aig: &Network) -> usize {
let lvl = levels(aig);
let mut d = 0;
for i in 0..aig.nb_outputs() {
let s = aig.output(i);
if s.is_var() {
d = d.max(lvl[s.var() as usize]);
}
}
d as usize
}

#[cfg(test)]
mod tests {
use volute::Lut3;

use super::{depth, stats};
use crate::{Gate, Network, Signal};

#[test]
fn test_dff_enable_reset_counts() {
let mut aig = Network::new();
let d = aig.add_input();
let en = aig.add_input();
let res = aig.add_input();
// plain dff, dff with enable, dff with enable and reset
let q0 = aig.dff(d, Signal::one(), Signal::zero());
let q1 = aig.dff(d, en, Signal::zero());
let q2 = aig.dff(d, en, res);
aig.add_output(q0);
aig.add_output(q1);
aig.add_output(q2);

let st = stats(&aig);
assert_eq!(st.nb_dff, 3);
assert_eq!(st.nb_dffe, 2);
assert_eq!(st.nb_dffr, 1);

// The Display must report the dedicated counts, not nb_dff
let shown = format!("{st}");
assert!(shown.contains("enable: 2"), "{shown}");
assert!(shown.contains("reset: 1"), "{shown}");
}

#[test]
fn test_lut_counts_as_gate() {
let mut aig = Network::new();
let i0 = aig.add_input();
let i1 = aig.add_input();
let i2 = aig.add_input();
let o = aig.add(Gate::lut(&[i0, i1, i2], Lut3::nth_var(0).into()));
aig.add_output(o);

let st = stats(&aig);
assert_eq!(st.nb_lut, 1);
assert_eq!(st.nb_gates(), 1);
}

#[test]
fn test_depth() {
let mut aig = Network::new();
// A right-leaning And chain of 8 inputs has depth 7
let mut sigs = Vec::new();
for _ in 0..8 {
sigs.push(aig.add_input());
}
let mut acc = sigs[0];
for s in &sigs[1..] {
acc = aig.and(acc, *s);
}
aig.add_output(acc);
assert_eq!(depth(&aig), 7);

// A network whose only output is a primary input has depth 0
let mut io = Network::new();
let i = io.add_input();
io.add_output(i);
assert_eq!(depth(&io), 0);
}
}
8 changes: 8 additions & 0 deletions src/optim.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
//! Optimization of logic networks

mod aig;
mod balance;
pub mod cuts;
mod infer_gates;
mod mig;
mod share_logic;

pub use aig::to_aig;
pub use balance::{balance, balance_with};
pub use cuts::{enumerate_cuts, enumerate_cuts_with, is_valid_cut, Cut};
pub use infer_gates::{infer_dffe, infer_xor_mux};
pub use mig::to_mig;
pub use share_logic::share_logic;
Loading