From 33c58ee45b85267f450043c1c1b88aa5ec1dcd03 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Sat, 5 Apr 2025 01:36:07 +0000 Subject: [PATCH 01/64] Initial commit of major changes. Using dynamic dispatch for cell functions and petgraph for ordering --- crates/arbolta/Cargo.toml | 4 + crates/arbolta/src/cell.rs | 752 +++++++++++++------ crates/arbolta/src/hardware_module.rs | 359 +++++++++ crates/arbolta/src/lib.rs | 4 +- crates/arbolta/src/module/design.rs | 121 --- crates/arbolta/src/module/hardware_module.rs | 384 ---------- crates/arbolta/src/module/mod.rs | 6 - crates/arbolta/src/{module => }/port.rs | 67 +- crates/arbolta/src/signal.rs | 164 +--- crates/arbolta/src/synth/mod.rs | 5 - crates/arbolta/src/synth/netlist.rs | 228 ------ crates/arbolta/src/synth/yosys.rs | 115 --- crates/arbolta/tests/test_cell.rs | 214 +++--- crates/arbolta/tests/test_module.rs | 245 +++--- crates/arbolta/tests/test_signal.rs | 17 +- crates/arbolta/tests/test_synth.rs | 48 -- crates/python_bindings/src/design.rs | 127 +--- 17 files changed, 1260 insertions(+), 1600 deletions(-) create mode 100644 crates/arbolta/src/hardware_module.rs delete mode 100644 crates/arbolta/src/module/design.rs delete mode 100644 crates/arbolta/src/module/hardware_module.rs delete mode 100644 crates/arbolta/src/module/mod.rs rename crates/arbolta/src/{module => }/port.rs (67%) delete mode 100644 crates/arbolta/src/synth/mod.rs delete mode 100644 crates/arbolta/src/synth/netlist.rs delete mode 100644 crates/arbolta/src/synth/yosys.rs delete mode 100644 crates/arbolta/tests/test_synth.rs diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index 63beab4..84dc01d 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -5,6 +5,10 @@ authors = ["AMD Research & Advanced Development"] edition = "2021" [dependencies] +petgraph = "0.7.1" +indexmap = "2.0.0" +tempfile = "3.19.1" +derive_more = { version = "2", features = ["full"] } serde = { version = "1.0", features = ["derive"] } ndarray = "0.16.1" num-traits = "0.2" diff --git a/crates/arbolta/src/cell.rs b/crates/arbolta/src/cell.rs index 59e0795..7116344 100644 --- a/crates/arbolta/src/cell.rs +++ b/crates/arbolta/src/cell.rs @@ -1,270 +1,562 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. -// SPDX-License-Identifier: MIT - use crate::bit::Bit; -use crate::signal::{AccessSignal, SignalIndex, SignalList}; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use crate::signal::Signal; +use derive_more::Constructor; +use indexmap::IndexMap; +use std::fmt::Debug; use thiserror::Error; +use yosys_netlist_json as yosys; + +/// Proxy for a standard-cell and basic unit of 'compute'. +pub type Cell = Box; -pub const CONNECTION_SIZE: usize = 8; -pub const STATE_SIZE: usize = 2; - -/// Basic logic gate functions. -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -pub enum Function { - Inverter, - And, - Nor, - Nand, - Xor, - Xnor, - Or, - DffPosEdge, - Buf, +pub trait CellFn: Debug + Send + Sync { + fn eval(&mut self, signals: &mut [Signal]); + fn reset(&mut self); + fn input_connections(&self) -> Vec<&usize>; + fn output_connections(&self) -> Vec<&usize>; + fn clone_box(&self) -> Cell; } -/// Proxy for entry in a Liberty Cell Library. -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct CellInfo { - pub name: String, - pub function: Function, - pub area: f64, - pub num_inputs: usize, +#[derive(Debug, Error)] +pub enum CellError { + #[error("unsupported cell `{0}`")] + Unsupported(String), } -/// Proxy for a standard-cell and basic unit of 'compute'. -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -pub struct Cell { - /// Name of cell. - pub name: String, - /// Cell's function. - pub function: Function, - /// For storing cell state (ex, last clock value). - pub state: [Bit; STATE_SIZE], - /// Number of inputs the cell has. - pub num_inputs: usize, - /// Input signal indices. - pub input_connections: [SignalIndex; CONNECTION_SIZE], // Put this on stack - /// Output signal index. - pub output_connection: SignalIndex, +impl Default for Cell { + fn default() -> Self { + Box::new(NoneCell) + } } -#[derive(Debug, Error)] -pub enum CellError { - #[error("couldn't find cell `{0}`")] - NotFound(String), +impl Clone for Cell { + fn clone(&self) -> Self { + self.clone_box() + } } -impl From<&CellInfo> for Cell { - fn from(value: &CellInfo) -> Self { - Self { - name: value.name.clone(), - function: value.function.clone(), - state: [Bit::Zero; STATE_SIZE], - num_inputs: value.num_inputs, - input_connections: [0; CONNECTION_SIZE], - output_connection: 0, +/// Generate a cell given its Yosys netlist description +/// # Arguments +/// * `cell` - Yosys cell +pub fn create_cell(cell: &yosys::Cell) -> Result { + let mut input_connections: IndexMap> = IndexMap::new(); + let mut output_connections: IndexMap> = IndexMap::new(); + + for (port_name, port_bits) in cell.connections.iter() { + // Skip ports with no direction + let Some(direction) = cell.port_directions.get(port_name) else { + continue; + }; + + if *direction == yosys::PortDirection::InOut { + todo!("Inout not supported.") + }; + + for bit in port_bits { + let net = match bit { + yosys::BitVal::N(net) => *net, + yosys::BitVal::S(constant) => match constant { + yosys::SpecialBit::_0 => 0, // Global 0 + yosys::SpecialBit::_1 => 1, // Global 1 + yosys::SpecialBit::X => todo!("X not supported."), + yosys::SpecialBit::Z => todo!("Z not supported."), + }, + }; + + if *direction == yosys::PortDirection::Input { + input_connections + .entry(port_name.to_string()) + .or_default() + .push(net); + } else { + output_connections + .entry(port_name.to_string()) + .or_default() + .push(net); + } } } + + let new_cell: Cell = match cell.cell_type.as_str() { + "BUF" => Box::new(Buf::new( + input_connections["A"][0], + output_connections["Y"][0], + )), + "NOT" => Box::new(Inverter::new( + input_connections["A"][0], + output_connections["Y"][0], + )), + "$_NAND_" | "NAND" => Box::new(Nand::new( + input_connections + .into_values() + .flatten() + .collect::>() + .into_boxed_slice(), + output_connections["Y"][0], + )), + "OR" | "$_OR_" => Box::new(Or::new( + input_connections + .into_values() + .flatten() + .collect::>() + .into_boxed_slice(), + output_connections["Y"][0], + )), + "ORNOT" | "$_ORNOT_" => Box::new(OrNot::new( + input_connections + .into_values() + .flatten() + .collect::>() + .into_boxed_slice(), + output_connections["Y"][0], + )), + "$_NOR_" | "NOR" => Box::new(Nor::new( + input_connections + .into_values() + .flatten() + .collect::>() + .into_boxed_slice(), + output_connections["Y"][0], + )), + "XOR" | "$_XOR_" => Box::new(Xor::new( + input_connections + .into_values() + .flatten() + .collect::>() + .into_boxed_slice(), + output_connections["Y"][0], + )), + "$_XNOR_" | "XNOR" => Box::new(Xnor::new( + input_connections + .into_values() + .flatten() + .collect::>() + .into_boxed_slice(), + output_connections["Y"][0], + )), + "AND" | "$_AND_" => Box::new(And::new( + input_connections + .into_values() + .flatten() + .collect::>() + .into_boxed_slice(), + output_connections["Y"][0], + )), + "ANDNOT" | "$_ANDNOT_" => Box::new(AndNot::new( + input_connections + .into_values() + .flatten() + .collect::>() + .into_boxed_slice(), + output_connections["Y"][0], + )), + "$_SDFF_PP0_" => Box::new(DffPosedgeReset::new( + input_connections["D"][0], + input_connections["C"][0], + input_connections["R"][0], + output_connections["Q"][0], + )), + _ => return Err(CellError::Unsupported(cell.cell_type.to_string())), + }; + + Ok(new_cell) } -impl From for Cell { - fn from(value: CellInfo) -> Self { - Cell::from(&value) +#[derive(Debug, Clone)] +pub struct NoneCell; + +#[allow(unused_variables)] +impl CellFn for NoneCell { + fn eval(&mut self, signals: &mut [Signal]) {} // Do nothing + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + vec![] + } + + fn output_connections(&self) -> Vec<&usize> { + vec![] + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) } } -/// Proxy for a Liberty Cell Library -/// User can define their own cells -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct CellLibrary { - pub cells: HashMap, +#[derive(Debug, Clone, Constructor)] +pub struct Inverter { + input_net: usize, + output_net: usize, } -impl CellLibrary { - /// Generate a cell given its name - /// # Arguments - /// * `cell_name` - Name of cell to generate - pub fn generate_cell(&self, cell_name: &str) -> Result { - match self.cells.get(cell_name) { - Some(cell_info) => Ok(Cell::from(cell_info)), - None => Err(CellError::NotFound(cell_name.to_string())), - } +impl CellFn for Inverter { + fn eval(&mut self, signals: &mut [Signal]) { + signals[self.output_net].set_value(!signals[self.input_net].get_value()); } - pub fn get_cell_area(&self, cell_name: &str) -> Result { - match self.cells.get(cell_name) { - Some(cell_info) => Ok(cell_info.area), - None => Err(CellError::NotFound(cell_name.to_string())), - } + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + vec![&self.input_net] } - pub fn get_cell_breakdown_area( - &self, - breakdown: &HashMap, - ) -> Result { - let mut total_area: f64 = 0.0; - for (cell_name, count) in breakdown { - total_area += (*count as f64) * self.get_cell_area(cell_name)?; - } + fn output_connections(&self) -> Vec<&usize> { + vec![&self.output_net] + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Constructor)] +pub struct Buf { + input_net: usize, + output_net: usize, +} + +impl CellFn for Buf { + fn eval(&mut self, signals: &mut [Signal]) { + signals[self.output_net].set_value(signals[self.input_net].get_value()); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + vec![&self.input_net] + } + + fn output_connections(&self) -> Vec<&usize> { + vec![&self.output_net] + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Constructor)] +pub struct Nand { + input_nets: Box<[usize]>, + output_net: usize, +} + +impl CellFn for Nand { + fn eval(&mut self, signals: &mut [Signal]) { + signals[self.output_net].set_value( + !self + .input_nets + .iter() + .skip(1) + .fold(signals[self.input_nets[0]].get_value(), |acc, net| { + acc & signals[*net].get_value() + }), + ); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.input_nets.as_ref().iter().collect() + } + + fn output_connections(&self) -> Vec<&usize> { + vec![&self.output_net] + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Constructor)] +pub struct And { + input_nets: Box<[usize]>, + output_net: usize, +} + +impl CellFn for And { + fn eval(&mut self, signals: &mut [Signal]) { + signals[self.output_net].set_value( + self + .input_nets + .iter() + .skip(1) + .fold(signals[self.input_nets[0]].get_value(), |acc, net| { + acc & signals[*net].get_value() + }), + ); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.input_nets.as_ref().iter().collect() + } + + fn output_connections(&self) -> Vec<&usize> { + vec![&self.output_net] + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Constructor)] +pub struct AndNot { + input_nets: Box<[usize]>, + output_net: usize, +} + +impl CellFn for AndNot { + fn eval(&mut self, signals: &mut [Signal]) { + signals[self.output_net].set_value(self.input_nets.iter().rev().skip(1).fold( + !signals[*self.input_nets.last().unwrap()].get_value(), + |acc, net| acc & signals[*net].get_value(), + )); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.input_nets.as_ref().iter().collect() + } + + fn output_connections(&self) -> Vec<&usize> { + vec![&self.output_net] + } - Ok(total_area) + fn clone_box(&self) -> Cell { + Box::new(self.clone()) } } -impl Cell { - pub fn empty_from_function(function: Function) -> Self { +#[derive(Debug, Clone, Constructor)] +pub struct Or { + input_nets: Box<[usize]>, + output_net: usize, +} + +impl CellFn for Or { + fn eval(&mut self, signals: &mut [Signal]) { + signals[self.output_net].set_value( + self + .input_nets + .iter() + .skip(1) + .fold(signals[self.input_nets[0]].get_value(), |acc, net| { + acc | signals[*net].get_value() + }), + ); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.input_nets.as_ref().iter().collect() + } + + fn output_connections(&self) -> Vec<&usize> { + vec![&self.output_net] + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Constructor)] +pub struct Nor { + input_nets: Box<[usize]>, + output_net: usize, +} + +impl CellFn for Nor { + fn eval(&mut self, signals: &mut [Signal]) { + signals[self.output_net].set_value( + !self + .input_nets + .iter() + .skip(1) + .fold(signals[self.input_nets[0]].get_value(), |acc, net| { + acc | signals[*net].get_value() + }), + ); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.input_nets.as_ref().iter().collect() + } + + fn output_connections(&self) -> Vec<&usize> { + vec![&self.output_net] + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Constructor)] +pub struct Xor { + input_nets: Box<[usize]>, + output_net: usize, +} + +impl CellFn for Xor { + fn eval(&mut self, signals: &mut [Signal]) { + signals[self.output_net].set_value( + self + .input_nets + .iter() + .skip(1) + .fold(signals[self.input_nets[0]].get_value(), |acc, net| { + acc ^ signals[*net].get_value() + }), + ); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.input_nets.as_ref().iter().collect() + } + + fn output_connections(&self) -> Vec<&usize> { + vec![&self.output_net] + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Constructor)] +pub struct Xnor { + input_nets: Box<[usize]>, + output_net: usize, +} + +impl CellFn for Xnor { + fn eval(&mut self, signals: &mut [Signal]) { + signals[self.output_net].set_value( + !self + .input_nets + .iter() + .skip(1) + .fold(signals[self.input_nets[0]].get_value(), |acc, net| { + acc ^ signals[*net].get_value() + }), + ); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.input_nets.as_ref().iter().collect() + } + + fn output_connections(&self) -> Vec<&usize> { + vec![&self.output_net] + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Constructor)] +pub struct OrNot { + input_nets: Box<[usize]>, + output_net: usize, +} + +impl CellFn for OrNot { + fn eval(&mut self, signals: &mut [Signal]) { + signals[self.output_net].set_value(self.input_nets.iter().rev().skip(1).fold( + !signals[*self.input_nets.last().unwrap()].get_value(), + |acc, net| acc | signals[*net].get_value(), + )); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.input_nets.as_ref().iter().collect() + } + + fn output_connections(&self) -> Vec<&usize> { + vec![&self.output_net] + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone)] +pub struct DffPosedgeReset { + data_in_net: usize, + clock_net: usize, + reset_net: usize, + data_out_net: usize, + last_clock: Bit, + last_data: Bit, +} + +impl DffPosedgeReset { + pub fn new(data_in_net: usize, clock_net: usize, reset_net: usize, data_out_net: usize) -> Self { Self { - name: String::new(), - function, - state: [Bit::Zero; STATE_SIZE], - num_inputs: 0, - input_connections: [0; CONNECTION_SIZE], - output_connection: 0, + data_in_net, + clock_net, + reset_net, + data_out_net, + last_clock: Bit::Zero, + last_data: Bit::Zero, } } +} - pub fn eval(&mut self, signals: &mut SignalList) { - let mut output_bit: Bit; +impl CellFn for DffPosedgeReset { + fn eval(&mut self, signals: &mut [Signal]) { + let (data, clock, reset) = ( + signals[self.data_in_net].get_value(), + signals[self.clock_net].get_value(), + signals[self.reset_net].get_value(), + ); - match &self.function { - Function::Buf => { - output_bit = signals[self.input_connections[0]].get_value(); - } - Function::Inverter => { - output_bit = !signals[self.input_connections[0]].get_value(); - } - Function::And => { - output_bit = signals[self.input_connections[0]].get_value(); - for bit in self.input_connections[1..self.num_inputs] - .iter() - .map(|i| signals[*i].get_value()) - { - output_bit = output_bit & bit; - } - } - Function::Or => { - output_bit = signals[self.input_connections[0]].get_value(); - for bit in self.input_connections[1..self.num_inputs] - .iter() - .map(|i| signals[*i].get_value()) - { - output_bit = output_bit | bit; - } - } - Function::Nor => { - output_bit = signals[self.input_connections[0]].get_value(); - for bit in self.input_connections[1..self.num_inputs] - .iter() - .map(|i| signals[*i].get_value()) - { - output_bit = output_bit | bit; - } - output_bit = !output_bit; - } - Function::Nand => { - output_bit = signals[self.input_connections[0]].get_value(); - for bit in self.input_connections[1..self.num_inputs] - .iter() - .map(|i| signals[*i].get_value()) - { - output_bit = output_bit & bit; - } - output_bit = !output_bit; - } - Function::Xor => { - output_bit = signals[self.input_connections[0]].get_value(); - for bit in self.input_connections[1..self.num_inputs] - .iter() - .map(|i| signals[*i].get_value()) - { - output_bit = output_bit ^ bit; - } - } - Function::Xnor => { - output_bit = signals[self.input_connections[0]].get_value(); - for bit in self.input_connections[1..self.num_inputs] - .iter() - .map(|i| signals[*i].get_value()) - { - output_bit = output_bit ^ bit; - } - output_bit = !output_bit; - } - Function::DffPosEdge => { - let (clock, data) = ( - signals[self.input_connections[0]].get_value(), - signals[self.input_connections[1]].get_value(), - ); - let (last_data, last_clock) = (self.state[0], self.state[1]); - // Detect rising edge, clock new data - output_bit = if clock == Bit::One && last_clock == Bit::Zero { - data - } else { - last_data - }; - self.state = [output_bit, clock]; + // Detect rising edge + let output_bit = if clock == Bit::One && self.last_clock == Bit::Zero { + match reset { + Bit::Zero => data, + Bit::One => Bit::Zero, // Do reset } + } else { + self.last_data }; - signals[self.output_connection].set_value(output_bit); + + signals[self.data_out_net].set_value(output_bit); + (self.last_data, self.last_clock) = (output_bit, clock); } - pub fn reset(&mut self) { - if self.function == Function::DffPosEdge { - self.state = [Bit::Zero; 2] - } + fn reset(&mut self) { + self.last_clock = Bit::Zero; + self.last_data = Bit::Zero; } -} -pub fn default_cell_library() -> CellLibrary { - let cells = HashMap::from([ - ( - "BUF".to_string(), - CellInfo { - name: "BUF".to_string(), - function: Function::Buf, - area: 4.0, - num_inputs: 1, - }, - ), - ( - "NOT".to_string(), - CellInfo { - name: "NOT".to_string(), - function: Function::Inverter, - area: 2.0, - num_inputs: 1, - }, - ), - ( - "NAND".to_string(), - CellInfo { - name: "NAND".to_string(), - function: Function::Nand, - area: 4.0, - num_inputs: 2, - }, - ), - ( - "NOR".to_string(), - CellInfo { - name: "NOR".to_string(), - function: Function::Nor, - area: 4.0, - num_inputs: 2, - }, - ), - ( - "DFF".to_string(), - CellInfo { - name: "DFF".to_string(), - function: Function::DffPosEdge, - area: 8.0, - num_inputs: 2, - }, - ), - ]); - - CellLibrary { cells } + fn input_connections(&self) -> Vec<&usize> { + vec![&self.clock_net, &self.data_in_net, &self.reset_net] + } + + fn output_connections(&self) -> Vec<&usize> { + vec![&self.data_out_net] + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } } diff --git a/crates/arbolta/src/hardware_module.rs b/crates/arbolta/src/hardware_module.rs new file mode 100644 index 0000000..e54bce3 --- /dev/null +++ b/crates/arbolta/src/hardware_module.rs @@ -0,0 +1,359 @@ +// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// SPDX-License-Identifier: MIT + +use super::port::{Port, PortDirection, PortError}; +use crate::bit::{Bit, BitVec}; +use crate::cell::{create_cell, Cell, CellError}; +use crate::signal::Signal; +use indexmap::{IndexMap, IndexSet}; +use ndarray::{Array1, ArrayView1}; +use num_traits::PrimInt; +use petgraph::graph::{Graph, NodeIndex}; +use petgraph::visit::Topo; +use std::collections::HashMap; +use std::fmt::Debug; +use std::process::Command; +use tempfile::NamedTempFile; +use thiserror::Error; +use yosys_netlist_json as yosys; + +pub type CellGraph = Graph; +pub type PortMap = IndexMap; +pub type SignalMap = IndexMap>; + +#[derive(Default, Clone)] +pub struct HardwareModule { + pub name: String, + pub ports: PortMap, + pub signal_map: SignalMap, + pub signals: Box<[Signal]>, + pub cells: Box<[Cell]>, +} + +#[derive(Debug, Error)] +pub enum ModuleError { + #[error("couldn't load netlist `{0}`")] + Netlist(String), + #[error("{0}")] + CellError(#[from] CellError), + #[error("module does not have port `{0}`")] + MissingPort(String), + #[error("error accessing port `{0}`: {1}")] + Port(String, PortError), + #[error("module does not have signal `{0}`")] + MissingSignal(String), + #[error("module does not have net `{0}`")] + MissingNet(usize), + #[error("module `{0}` does not exist")] + MissingModule(String), +} + +impl HardwareModule { + pub fn new_from_path(netlist_path: &str, top_module: &str) -> Result { + let temp_flattened = match NamedTempFile::new() { + Ok(file) => file, + Err(err) => return Err(ModuleError::Netlist(err.to_string())), + }; + + let yosys_output = Command::new("yosys") + .arg(netlist_path) + .arg("-p") + .arg(format!( + "flatten; write_json {}", + temp_flattened.path().display() + )) + .output(); + + if let Err(err) = yosys_output { + return Err(ModuleError::Netlist(err.to_string())); + } + + let netlist = match yosys::Netlist::from_reader(temp_flattened) { + Ok(netlist) => Ok(netlist), + Err(err) => Err(ModuleError::Netlist(err.to_string())), + }?; + + Self::new(netlist, top_module) + } + + pub fn new(netlist: yosys::Netlist, top_module: &str) -> Result { + // Design must be flattened + let Some(synth_module) = netlist.modules.get(top_module) else { + return Err(CellError::Unsupported(top_module.to_string()).into()); + }; + + let mut bit_drivers = IndexMap::>::new(); + let mut bit_users = IndexMap::>::new(); + let mut node_map = IndexMap::<&str, NodeIndex>::new(); + + let mut graph = CellGraph::new(); + let input_node = graph.add_node(Cell::default()); // Start of graph search, module inputs + + for (cell_name, cell) in synth_module.cells.iter() { + if cell.cell_type == "$scopeinfo" { + // Ignore for now + continue; + } + let new_cell = create_cell(cell)?; + + new_cell + .input_connections() + .iter() + .filter(|i| ***i > 1) // Skip constants + .for_each(|i| { + bit_users.entry(**i).or_default().insert(cell_name); + }); + + new_cell + .output_connections() + .iter() + .filter(|i| ***i > 1) // Skip constants + .for_each(|i| { + bit_drivers.entry(**i).or_default().insert(cell_name); + }); + + let cell_node = graph.add_node(new_cell); + node_map.insert(cell_name, cell_node); + } + + for (user_bit, user_cells) in bit_users { + if let Some(drivers) = bit_drivers.get(&user_bit) { + // Existing driver node + for driver_cell in drivers { + for user in &user_cells { + let (driver_node, user_node) = (node_map[driver_cell], node_map[user]); + graph.add_edge(driver_node, user_node, user_bit); + } + } + } else { + // Connect to input cell + for user in user_cells { + let user_node = node_map[user]; + graph.add_edge(input_node, user_node, user_bit); + } + } + } + + let mut signal_map = SignalMap::new(); + let mut max_signal = 0; + for (name, netname) in &synth_module.netnames { + let mut nets = vec![]; + for bit in &netname.bits { + let net = match bit { + yosys::BitVal::N(net) => *net, + yosys::BitVal::S(constant) => match constant { + yosys::SpecialBit::_0 => 0, // Global 0 + yosys::SpecialBit::_1 => 1, // Global 1 + yosys::SpecialBit::X => todo!("X not supported."), + yosys::SpecialBit::Z => todo!("Z not supported."), + }, + }; + max_signal = max_signal.max(net); + nets.push(net); + } + signal_map.insert(name.to_string(), nets.into_boxed_slice()); + } + + let mut ports = PortMap::new(); + for (name, port) in &synth_module.ports { + ports.insert(name.clone(), Port::new(port)); + } + + let mut signals = vec![Signal::default(); max_signal + 1]; + signals[0].set_constant(Bit::Zero); + signals[1].set_constant(Bit::One); + + let mut search = Topo::new(&graph); + let mut cells = vec![]; + while let Some(cell_node) = search.next(&graph) { + cells.push(graph[cell_node].clone()); + } + + Ok(Self { + name: top_module.to_string(), + ports, + signal_map, + signals: signals.into_boxed_slice(), + cells: cells.into_boxed_slice(), // search, + }) + } + + pub fn get_signal_nets(&self, name: &str) -> Result, ModuleError> { + match self.signal_map.get(name) { + Some(net) => Ok(net.clone()), + None => Err(ModuleError::MissingSignal(name.to_string())), + } + } + + pub fn stick_signal(&mut self, net: usize, value: Bit) -> Result<(), ModuleError> { + match self.signals.get_mut(net) { + Some(signal) => { + signal.set_constant(value); + Ok(()) + } + None => Err(ModuleError::MissingNet(net)), + } + } + + pub fn eval(&mut self) { + self + .cells + .iter_mut() + .for_each(|cell| cell.eval(&mut self.signals)); + } + + pub fn reset(&mut self) { + self.cells.iter_mut().for_each(|c| c.reset()); + self.signals.iter_mut().for_each(|s| s.reset()); + self.signals[0].set_constant(Bit::Zero); + self.signals[1].set_constant(Bit::One); + } + + pub fn set_port_shape(&mut self, name: &str, shape: &[usize; 2]) -> Result<(), ModuleError> { + match self.ports.get_mut(name) { + Some(port) => match port.set_shape(shape) { + Ok(()) => Ok(()), + Err(err) => Err(ModuleError::Port(name.to_string(), err)), + }, + None => Err(ModuleError::MissingPort(name.to_string())), + } + } + + pub fn get_port_shape(&self, name: &str) -> Result<[usize; 2], ModuleError> { + match self.ports.get(name) { + Some(port) => Ok(port.get_shape()), + None => Err(ModuleError::MissingPort(name.to_string())), + } + } + + pub fn get_port_direction(&self, name: &str) -> Result { + match self.ports.get(name) { + Some(port) => Ok(port.direction.clone()), + None => Err(ModuleError::MissingPort(name.to_string())), + } + } + + pub fn get_port_bits(&self, name: &str) -> Result { + match self.ports.get(name) { + Some(port) => Ok(port.get_bits(&self.signals)), + None => Err(ModuleError::MissingPort(name.to_string())), + } + } + + pub fn set_port_bits(&mut self, name: &str, vals: &BitVec) -> Result<(), ModuleError> { + match self.ports.get_mut(name) { + Some(port) => match port.set_bits(vals, &mut self.signals) { + Ok(()) => Ok(()), + Err(err) => Err(ModuleError::Port(name.to_string(), err)), + }, + None => Err(ModuleError::MissingPort(name.to_string())), + } + } + + pub fn get_port_int( + &self, + name: &str, + ) -> Result { + match self.ports.get(name) { + Some(port) => Ok(port.get_int(&self.signals)), + None => Err(ModuleError::MissingPort(name.to_string())), + } + } + + pub fn set_port_int( + &mut self, + name: &str, + val: T, + ) -> Result<(), ModuleError> { + match self.ports.get_mut(name) { + Some(port) => match port.set_int(val, &mut self.signals) { + Ok(()) => Ok(()), + Err(err) => Err(ModuleError::Port(name.to_string(), err)), + }, + None => Err(ModuleError::MissingPort(name.to_string())), + } + } + + pub fn get_port_int_vec( + &self, + name: &str, + ) -> Result, ModuleError> { + match self.ports.get(name) { + Some(port) => Ok(port.get_int_vec(&self.signals)), + None => Err(ModuleError::MissingPort(name.to_string())), + } + } + + pub fn set_port_int_vec( + &mut self, + name: &str, + vals: &[T], + ) -> Result<(), ModuleError> { + match self.ports.get_mut(name) { + Some(port) => match port.set_int_vec(vals, &mut self.signals) { + Ok(()) => Ok(()), + Err(err) => Err(ModuleError::Port(name.to_string(), err)), + }, + None => Err(ModuleError::MissingPort(name.to_string())), + } + } + + pub fn get_port_ndarray( + &self, + name: &str, + ) -> Result, ModuleError> { + match self.ports.get(name) { + Some(port) => Ok(port.get_ndarray(&self.signals)), + None => Err(ModuleError::MissingPort(name.to_string())), + } + } + + pub fn set_port_ndarray( + &mut self, + name: &str, + vals: ArrayView1, + ) -> Result<(), ModuleError> { + match self.ports.get(name) { + Some(port) => match port.set_ndarray(vals, &mut self.signals) { + Ok(()) => Ok(()), + Err(err) => Err(ModuleError::Port(name.to_string(), err)), + }, + None => Err(ModuleError::MissingPort(name.to_string())), + } + } + + pub fn get_port_string(&self, name: &str) -> Result { + match self.ports.get(name) { + Some(port) => Ok(port.get_string(&self.signals)), + None => Err(ModuleError::MissingPort(name.to_string())), + } + } + + pub fn get_cell_breakdown(&self) -> HashMap { + todo!() + } + + #[allow(unused_variables)] + pub fn search_module_cell_breakdown( + &self, + name: &str, + ) -> Result, ModuleError> { + todo!() + } + + // TODO: Add tests for these + + pub fn get_total_toggle_count(&self) -> usize { + todo!() + } + + #[allow(unused_variables)] + pub fn search_module_total_toggle_count(&self, name: &str) -> Result { + todo!() + } + + #[allow(unused_variables)] + pub fn get_module_bit_flips(&self, name: &str) -> usize { + todo!() + } +} diff --git a/crates/arbolta/src/lib.rs b/crates/arbolta/src/lib.rs index ecea90c..5d73f3a 100644 --- a/crates/arbolta/src/lib.rs +++ b/crates/arbolta/src/lib.rs @@ -3,6 +3,6 @@ pub mod bit; pub mod cell; -pub mod module; +pub mod hardware_module; +pub mod port; pub mod signal; -pub mod synth; diff --git a/crates/arbolta/src/module/design.rs b/crates/arbolta/src/module/design.rs deleted file mode 100644 index 1fcfc87..0000000 --- a/crates/arbolta/src/module/design.rs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. -// SPDX-License-Identifier: MIT - -use crate::bit::Bit; -use crate::cell::{CellError, CellLibrary}; -use crate::module::hardware_module::{HardwareModule, ModuleError}; -use crate::signal::SignalIndex; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::io; -use std::io::Write; -use thiserror::Error; - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct Design { - pub module: HardwareModule, - pub clock: Option, - pub reset: Option, - pub cell_library: CellLibrary, -} - -#[derive(Debug, Error)] -pub enum DesignError { - #[error("{0}")] - ModuleError(#[from] ModuleError), - #[error("{0}")] - CellError(#[from] CellError), - #[error("{0}")] - IoError(#[from] io::Error), - #[error("{0}")] - FlexReaderError(#[from] flexbuffers::ReaderError), - #[error("{0}")] - DeserializeError(#[from] flexbuffers::DeserializationError), - #[error("{0}")] - SerializeError(#[from] flexbuffers::SerializationError), -} - -impl Design { - pub fn load(path: &str) -> Result { - let serialized = std::fs::read(path)?; - let reader = flexbuffers::Reader::get_root(serialized.as_slice())?; - Ok(Self::deserialize(reader)?) - } - - pub fn save(&self, path: &str) -> Result<(), DesignError> { - let mut serializer = flexbuffers::FlexbufferSerializer::new(); - self.serialize(&mut serializer)?; - let mut file_output = std::fs::File::create(path)?; - _ = file_output.write(serializer.view())?; - Ok(()) - } - - pub fn from_module(module: HardwareModule, cell_library: CellLibrary) -> Self { - Self { - module, - clock: None, - reset: None, - cell_library, - } - } - - pub fn set_clock(&mut self, name: &str) -> Result<(), DesignError> { - self.clock = Some(self.module.get_signal_idx(name)?); - Ok(()) - } - - pub fn set_reset(&mut self, name: &str) -> Result<(), DesignError> { - self.reset = Some(self.module.get_signal_idx(name)?); - Ok(()) - } - - pub fn eval(&mut self) { - self.module.eval(); - } - - pub fn eval_clocked(&mut self) -> Result<(), DesignError> { - let Some(clock) = self.clock else { - return Err(DesignError::ModuleError(ModuleError::MissingSignal( - "clock".to_string(), - ))); - }; - - // Can we do this deterministically? - self.module.eval(); - self.module.eval(); - self.module.eval(); - self.module.set_signal(clock, Bit::One)?; - self.module.eval(); - self.module.set_signal(clock, Bit::Zero)?; - self.module.eval(); - Ok(()) - } - - pub fn reset_clocked(&mut self) -> Result<(), DesignError> { - let Some(reset) = self.reset else { - return Err(DesignError::ModuleError(ModuleError::MissingSignal( - "reset".to_string(), - ))); - }; - - self.module.set_signal(reset, Bit::One)?; - self.eval_clocked()?; - self.module.set_signal(reset, Bit::Zero)?; - self.module.eval(); - - Ok(()) - } - - pub fn get_module_area(&self, name: &str) -> Result { - let breakdown = self.get_module_breakdown(name)?; - Ok(self.cell_library.get_cell_breakdown_area(&breakdown)?) - } - - pub fn get_module_breakdown(&self, name: &str) -> Result, DesignError> { - Ok(self.module.search_module_cell_breakdown(name)?) - } - - pub fn get_module_total_toggle_count(&self, name: &str) -> Result { - Ok(self.module.search_module_total_toggle_count(name)?) - } -} diff --git a/crates/arbolta/src/module/hardware_module.rs b/crates/arbolta/src/module/hardware_module.rs deleted file mode 100644 index 24cef76..0000000 --- a/crates/arbolta/src/module/hardware_module.rs +++ /dev/null @@ -1,384 +0,0 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. -// SPDX-License-Identifier: MIT - -use super::port::{Port, PortDirection, PortError}; -use crate::bit::{Bit, BitVec}; -use crate::cell::Cell; -use crate::signal::{AccessSignal, SignalIndex, SignalIndexMap, SignalList}; -use ndarray::{Array1, ArrayView1}; -use num_traits::PrimInt; -use serde::{Deserialize, Serialize}; -use std::collections::{BTreeMap, HashMap, HashSet}; -use std::fmt::Debug; -use thiserror::Error; - -pub type PortMap = BTreeMap; - -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -pub enum Component { - Cell(Cell), - Module(HardwareModule), -} - -pub type ComponentIndex = usize; -pub type ComponentIndexMap = BTreeMap; - -#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq)] -pub struct HardwareModule { - pub name: String, - pub ports: PortMap, - pub signals: SignalList, - pub signal_map: SignalIndexMap, - pub components: Vec, - pub component_map: ComponentIndexMap, - pub input_connections: Vec<(SignalIndex, SignalIndex)>, - pub output_connections: Vec<(SignalIndex, SignalIndex)>, -} - -#[derive(Debug, Error)] -pub enum ModuleError { - #[error("module does not have port `{0}`")] - MissingPort(String), - #[error("error accessing port `{0}`: {1}")] - Port(String, PortError), - #[error("module does not have signal `{0}`")] - MissingSignal(String), - #[error("module does not have signal index `{0}`")] - MissingSignalIndex(SignalIndex), - #[error("module `{0}` does not exist")] - MissingModule(String), -} - -impl HardwareModule { - pub fn get_signal_idx(&self, name: &str) -> Result { - match self.signal_map.get(name) { - Some(idx) => Ok(*idx), - None => Err(ModuleError::MissingSignal(name.to_string())), - } - } - - pub fn set_signal(&mut self, idx: SignalIndex, val: Bit) -> Result<(), ModuleError> { - if idx > self.signals.len() { - Err(ModuleError::MissingSignalIndex(idx)) - } else { - self.signals[idx].set_value(val); - Ok(()) - } - } - - pub fn get_module_port_int( - &self, - path: Vec<&str>, - name: &str, - ) -> Result { - if path.is_empty() { - return self.get_port_int(name); - } - - for component in &self.components { - match component { - Component::Cell(_) => (), - Component::Module(module) => { - if path[0] == module.name { - return module.get_module_port_int(path[1..].to_vec(), name); - } - } - } - } - Err(ModuleError::MissingPort(name.to_string())) - } - - pub fn search_signal(&mut self, name: &str) -> Option { - for signal in &self.signals { - if signal.get_name() == name { - return Some(signal.get_value()); - } - } - - for component in &mut self.components { - match component { - Component::Cell(_) => (), - Component::Module(module) => match module.search_signal(name) { - Some(val) => return Some(val), - None => continue, - }, - } - } - None - } - - pub fn eval(&mut self) { - for component in &mut self.components { - match component { - Component::Cell(cell) => { - cell.eval(&mut self.signals); - } - Component::Module(module) => { - // Propagate input connections - for (external_idx, internal_idx) in &module.input_connections { - let bit = self.signals[*external_idx].get_value(); - module.signals[*internal_idx].set_value(bit); - } - module.eval(); - // Propagate output connections - for (external_idx, internal_idx) in &module.output_connections { - let bit = module.signals[*internal_idx].get_value(); - self.signals[*external_idx].set_value(bit); - } - } - } - } - } - - pub fn reset(&mut self) { - // Reset signals - self.signals.iter_mut().for_each(|signal| signal.reset()); - - // Reset components - self - .components - .iter_mut() - .for_each(|component| match component { - Component::Cell(cell) => cell.reset(), - Component::Module(module) => module.reset(), - }); - } - - pub fn set_port_shape(&mut self, name: &str, shape: &[usize; 2]) -> Result<(), ModuleError> { - match self.ports.get_mut(name) { - Some(port) => match port.set_shape(shape) { - Ok(()) => Ok(()), - Err(err) => Err(ModuleError::Port(name.to_string(), err)), - }, - None => Err(ModuleError::MissingPort(name.to_string())), - } - } - - pub fn get_port_shape(&self, name: &str) -> Result<[usize; 2], ModuleError> { - match self.ports.get(name) { - Some(port) => Ok(port.get_shape()), - None => Err(ModuleError::MissingPort(name.to_string())), - } - } - - pub fn get_port_direction(&self, name: &str) -> Result { - match self.ports.get(name) { - Some(port) => Ok(port.direction.clone()), - None => Err(ModuleError::MissingPort(name.to_string())), - } - } - - pub fn get_port_bits(&self, name: &str) -> Result { - match self.ports.get(name) { - Some(port) => Ok(port.get_bits(&self.signals)), - None => Err(ModuleError::MissingPort(name.to_string())), - } - } - - pub fn set_port_bits(&mut self, name: &str, vals: &BitVec) -> Result<(), ModuleError> { - match self.ports.get_mut(name) { - Some(port) => match port.set_bits(vals, &mut self.signals) { - Ok(()) => Ok(()), - Err(err) => Err(ModuleError::Port(name.to_string(), err)), - }, - None => Err(ModuleError::MissingPort(name.to_string())), - } - } - - pub fn get_port_int( - &self, - name: &str, - ) -> Result { - match self.ports.get(name) { - Some(port) => Ok(port.get_int(&self.signals)), - None => Err(ModuleError::MissingPort(name.to_string())), - } - } - - pub fn set_port_int( - &mut self, - name: &str, - val: T, - ) -> Result<(), ModuleError> { - match self.ports.get_mut(name) { - Some(port) => match port.set_int(val, &mut self.signals) { - Ok(()) => Ok(()), - Err(err) => Err(ModuleError::Port(name.to_string(), err)), - }, - None => Err(ModuleError::MissingPort(name.to_string())), - } - } - - pub fn get_port_int_vec( - &self, - name: &str, - ) -> Result, ModuleError> { - match self.ports.get(name) { - Some(port) => Ok(port.get_int_vec(&self.signals)), - None => Err(ModuleError::MissingPort(name.to_string())), - } - } - - pub fn set_port_int_vec( - &mut self, - name: &str, - vals: &[T], - ) -> Result<(), ModuleError> { - match self.ports.get_mut(name) { - Some(port) => match port.set_int_vec(vals, &mut self.signals) { - Ok(()) => Ok(()), - Err(err) => Err(ModuleError::Port(name.to_string(), err)), - }, - None => Err(ModuleError::MissingPort(name.to_string())), - } - } - - pub fn get_port_ndarray( - &self, - name: &str, - ) -> Result, ModuleError> { - match self.ports.get(name) { - Some(port) => Ok(port.get_ndarray(&self.signals)), - None => Err(ModuleError::MissingPort(name.to_string())), - } - } - - pub fn set_port_ndarray( - &mut self, - name: &str, - vals: ArrayView1, - ) -> Result<(), ModuleError> { - match self.ports.get(name) { - Some(port) => match port.set_ndarray(vals, &mut self.signals) { - Ok(()) => Ok(()), - Err(err) => Err(ModuleError::Port(name.to_string(), err)), - }, - None => Err(ModuleError::MissingPort(name.to_string())), - } - } - - pub fn get_port_string(&self, name: &str) -> Result { - match self.ports.get(name) { - Some(port) => Ok(port.get_string(&self.signals)), - None => Err(ModuleError::MissingPort(name.to_string())), - } - } - - pub fn get_cell_breakdown(&self) -> HashMap { - let mut breakdown = HashMap::::new(); - for component in &self.components { - match component { - Component::Cell(cell) => { - if !breakdown.contains_key(&cell.name) { - breakdown.insert(cell.name.clone(), 0); - } - - *breakdown.get_mut(&cell.name).unwrap() += 1; - } - Component::Module(module) => { - for (cell_name, count) in module.get_cell_breakdown() { - if !breakdown.contains_key(&cell_name) { - breakdown.insert(cell_name.clone(), 0); - } - *breakdown.get_mut(&cell_name).unwrap() += count; - } - } - } - } - breakdown - } - - pub fn search_module_cell_breakdown( - &self, - name: &str, - ) -> Result, ModuleError> { - if name == self.name { - Ok(self.get_cell_breakdown()) - } else { - for component in &self.components { - match component { - Component::Cell(_) => continue, - Component::Module(sub_module) => match sub_module.search_module_cell_breakdown(name) { - Ok(breakdown) => return Ok(breakdown), - Err(_) => continue, - }, - } - } - Err(ModuleError::MissingModule(name.to_string())) - } - } - - // TODO: Add tests for these - - pub fn get_total_toggle_count(&self) -> usize { - let mut total_toggles: usize = 0; - let input_connections: HashSet = self - .input_connections - .iter() - .map(|(_, internal_idx)| *internal_idx) - .collect(); - self.signals.iter().for_each(|signal| { - if !input_connections.contains(&signal.get_index()) { - total_toggles += signal.get_total_toggle_count(); - } - }); - self - .components - .iter() - .for_each(|component| match component { - Component::Cell(_) => (), - Component::Module(module) => total_toggles += module.get_total_toggle_count(), - }); - - total_toggles - } - - pub fn search_module_total_toggle_count(&self, name: &str) -> Result { - if name == self.name { - Ok(self.get_total_toggle_count()) - } else { - for component in &self.components { - match component { - Component::Cell(_) => continue, - Component::Module(sub_module) => { - match sub_module.search_module_total_toggle_count(name) { - Ok(count) => return Ok(count), - Err(_) => continue, - } - } - } - } - Err(ModuleError::MissingModule(name.to_string())) - } - } - - // need wrapper function to get input ports and not use connections - pub fn get_module_bit_flips(&self, name: &str) -> usize { - if self.name == name { - let mut total_toggles = 0; - - let input_connections: HashSet = self - .input_connections - .iter() - .map(|(_, internal_idx)| *internal_idx) - .collect(); - self.signals.iter().for_each(|signal| { - if !input_connections.contains(&signal.get_index()) { - total_toggles += signal.get_total_toggle_count(); - } - }); - - return total_toggles; - } else { - for component in &self.components { - match component { - Component::Module(module) if module.name == name => { - return module.get_module_bit_flips(name) - } - _ => continue, - } - } - } - 0 - } -} diff --git a/crates/arbolta/src/module/mod.rs b/crates/arbolta/src/module/mod.rs deleted file mode 100644 index af45b56..0000000 --- a/crates/arbolta/src/module/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. -// SPDX-License-Identifier: MIT - -pub mod design; -pub mod hardware_module; -pub mod port; diff --git a/crates/arbolta/src/module/port.rs b/crates/arbolta/src/port.rs similarity index 67% rename from crates/arbolta/src/module/port.rs rename to crates/arbolta/src/port.rs index 73787fc..207da0c 100644 --- a/crates/arbolta/src/module/port.rs +++ b/crates/arbolta/src/port.rs @@ -2,12 +2,13 @@ // SPDX-License-Identifier: MIT use crate::bit::{Bit, BitVec}; -use crate::signal::{AccessSignal, SignalIndexList, SignalList}; +use crate::signal::Signal; use ndarray::{Array1, ArrayView1}; use num_traits::PrimInt; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use thiserror::Error; +use yosys_netlist_json as yosys; #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] pub enum PortDirection { @@ -17,10 +18,9 @@ pub enum PortDirection { #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] pub struct Port { - pub signal_idx_list: SignalIndexList, - pub shape: [usize; 2], pub direction: PortDirection, - pub signed: bool, + pub nets: Box<[usize]>, + pub shape: [usize; 2], } #[derive(Debug, Error)] @@ -37,8 +37,38 @@ pub enum PortError { } impl Port { + pub fn new(port: &yosys::Port) -> Self { + let direction = match port.direction { + yosys::PortDirection::InOut => todo!("Inout not supported"), + yosys::PortDirection::Input => PortDirection::Input, + yosys::PortDirection::Output => PortDirection::Output, + }; + + let nets: Vec = port + .bits + .iter() + .map(|bit| match bit { + yosys::BitVal::N(net) => *net, + yosys::BitVal::S(constant) => match constant { + yosys::SpecialBit::_0 => 0, // Global 0 + yosys::SpecialBit::_1 => 1, // Global 1 + yosys::SpecialBit::X => todo!("X not supported."), + yosys::SpecialBit::Z => todo!("Z not supported."), + }, + }) + .collect(); + + let shape = [1, nets.len()]; + + Self { + direction, + nets: nets.into_boxed_slice(), + shape, + } + } + pub fn set_shape(&mut self, shape: &[usize; 2]) -> Result<(), PortError> { - if shape[0] * shape[1] != self.signal_idx_list.len() { + if shape[0] * shape[1] != self.nets.len() { return Err(PortError::Shape { requested: *shape, actual: self.shape, @@ -54,17 +84,17 @@ impl Port { self.shape } - pub fn get_bits(&self, signals: &SignalList) -> BitVec { + pub fn get_bits(&self, signals: &[Signal]) -> BitVec { BitVec::from( self - .signal_idx_list + .nets .iter() .map(|idx| signals[*idx].get_value()) .collect::>(), ) } - pub fn set_bits(&self, vals: &BitVec, signals: &mut SignalList) -> Result<(), PortError> { + pub fn set_bits(&self, vals: &BitVec, signals: &mut [Signal]) -> Result<(), PortError> { if self.direction == PortDirection::Output { return Err(PortError::Direction); } @@ -75,22 +105,22 @@ impl Port { .bits .iter() .enumerate() - .take(stop_idx.clamp(0, self.signal_idx_list.len())) + .take(stop_idx.clamp(0, self.nets.len())) { - signals[self.signal_idx_list[i]].set_value(*val); + signals[self.nets[i]].set_value(*val); } Ok(()) } - pub fn get_int(&self, signals: &SignalList) -> T { + pub fn get_int(&self, signals: &[Signal]) -> T { self.get_bits(signals).to_int() } pub fn set_int( &self, val: T, - signals: &mut SignalList, + signals: &mut [Signal], ) -> Result<(), PortError> { if self.direction == PortDirection::Output { return Err(PortError::Direction); @@ -103,7 +133,7 @@ impl Port { self.set_bits(&bits, signals) } - pub fn get_int_vec(&self, signals: &SignalList) -> Vec { + pub fn get_int_vec(&self, signals: &[Signal]) -> Vec { let elem_size = self.shape[1]; self.get_bits(signals).to_ints_sized(elem_size) } @@ -111,7 +141,7 @@ impl Port { pub fn set_int_vec( &self, vals: &[T], - signals: &mut SignalList, + signals: &mut [Signal], ) -> Result<(), PortError> { if vals.len() != self.shape[0] { return Err(PortError::Shape { @@ -128,10 +158,7 @@ impl Port { } } - pub fn get_ndarray( - &self, - signals: &SignalList, - ) -> Array1 { + pub fn get_ndarray(&self, signals: &[Signal]) -> Array1 { let elem_size = self.shape[1]; self.get_bits(signals).to_int_ndarray_sized(elem_size) } @@ -139,7 +166,7 @@ impl Port { pub fn set_ndarray( &self, vals: ArrayView1, - signals: &mut SignalList, + signals: &mut [Signal], ) -> Result<(), PortError> { if vals.len() != self.shape[0] { return Err(PortError::Shape { @@ -156,7 +183,7 @@ impl Port { } } - pub fn get_string(&self, signals: &SignalList) -> String { + pub fn get_string(&self, signals: &[Signal]) -> String { self.get_bits(signals).to_string() } } diff --git a/crates/arbolta/src/signal.rs b/crates/arbolta/src/signal.rs index 170589f..db7b3f4 100644 --- a/crates/arbolta/src/signal.rs +++ b/crates/arbolta/src/signal.rs @@ -3,176 +3,82 @@ use crate::bit::Bit; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; - -pub type SignalIndex = usize; -pub type SignalList = Vec; -pub type SignalIndexList = Vec; -pub type SignalIndexMap = BTreeMap; /// Connection between cells/modules. #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)] -pub struct Net { - /// Name of net - pub name: String, - /// Index in netlist connections (proxy to Yosys bit) - pub index: usize, +pub struct Signal { /// Value of net pub value: Bit, + // Is signal constant + pub constant: bool, /// Number of times net has transitioned from 0 -> 1 pub toggle_count_rising: usize, /// Number of times net has transitioned from 1 -> 0 pub toggle_count_falling: usize, } -/// Connection between cells/modules or constant. -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -pub enum Signal { - Constant(Bit), - Net(Net), -} - impl Signal { /// Create a new constant. /// /// # Arguments /// * `value` - Constant `Bit` value. pub fn new_constant(value: Bit) -> Self { - Self::Constant(value) - } - - /// Create a new net. - /// - /// # Arguments - /// * `index` - Net index. - pub fn new_net(index: usize) -> Self { - Self::Net(Net { - index, - ..Default::default() - }) - } - - /// Create a new net. - /// - /// # Arguments - /// * `index` - Net index. - /// * `value` - Value to initialize net with. - pub fn new_net_from(index: usize, value: Bit) -> Self { - Self::Net(Net { - index, + Self { value, + constant: true, ..Default::default() - }) - } - - /// Create a new list of `Signal`s. - /// List initialized with zero `Constant`s. - /// - /// # Arguments - /// * `size` - Size of list. - pub fn new_list(size: usize) -> SignalList { - let mut signal_list = SignalList::with_capacity(size); - for _ in 0..size { - signal_list.push(Self::new_constant(Bit::Zero)); } - signal_list } -} -pub trait AccessSignal { - fn reset(&mut self); - fn set_name(&mut self, name: String); - fn get_name(&self) -> &str; - fn get_index(&self) -> usize; - fn get_value(&self) -> Bit; - fn set_value(&mut self, val: Bit); - fn get_total_toggle_count(&self) -> usize; - fn get_toggle_count_rising(&self) -> usize; - fn get_toggle_count_falling(&self) -> usize; -} + pub fn set_constant(&mut self, value: Bit) { + self.constant = true; + self.value = value; + self.toggle_count_falling = 0; + self.toggle_count_rising = 0; + } -impl AccessSignal for Signal { /// Reset signal value to zero. /// Clear all signal statistics. - fn reset(&mut self) { - match self { - Signal::Constant(_) => (), // Do nothing - Signal::Net(net) => { - net.toggle_count_rising = 0; - net.toggle_count_falling = 0; - net.value = Bit::Zero; - } - } - } - - /// Set name of signal. - fn set_name(&mut self, name: String) { - match self { - Signal::Constant(_) => (), // Do nothing - Signal::Net(net) => net.name = name, - } - } - - /// Get name of signal. - fn get_name(&self) -> &str { - match self { - Signal::Constant(_) => "const", // Do nothing - Signal::Net(net) => &net.name, - } - } - - /// Get netlist index of signal. - fn get_index(&self) -> usize { - match self { - Signal::Constant(_) => 0, // TODO: Improve constant handling - Signal::Net(net) => net.index, - } + pub fn reset(&mut self) { + // Ignore constants + // if !self.constant { + // self.value = Bit::Zero + // } + self.constant = false; + self.value = Bit::Zero; + self.toggle_count_falling = 0; + self.toggle_count_rising = 0; } /// Get value of signal. - fn get_value(&self) -> Bit { - match self { - Signal::Constant(bit) => *bit, - Signal::Net(net) => net.value, - } + pub fn get_value(&self) -> Bit { + self.value } /// Set value of signal. Updates toggle statistics. - fn set_value(&mut self, val: Bit) { - match self { - Signal::Constant(_) => (), // Do nothing - Signal::Net(net) => { - match &[net.value, val] { - [Bit::Zero, Bit::One] => net.toggle_count_rising += 1, - [Bit::One, Bit::Zero] => net.toggle_count_falling += 1, - [Bit::Zero, Bit::Zero] | [Bit::One, Bit::One] => return, - } - net.value = val; + pub fn set_value(&mut self, val: Bit) { + if !self.constant { + match &[self.value, val] { + [Bit::Zero, Bit::One] => self.toggle_count_rising += 1, + [Bit::One, Bit::Zero] => self.toggle_count_falling += 1, + [Bit::Zero, Bit::Zero] | [Bit::One, Bit::One] => return, } + self.value = val; } } /// Get total signal toggle count (rising + falling). - fn get_total_toggle_count(&self) -> usize { - match self { - Signal::Constant(_) => 0, - Signal::Net(net) => net.toggle_count_falling + net.toggle_count_rising, - } + pub fn get_total_toggle_count(&self) -> usize { + self.toggle_count_falling + self.toggle_count_rising } /// Get total rising toggle count of signal. - fn get_toggle_count_rising(&self) -> usize { - match self { - Signal::Constant(_) => 0, - Signal::Net(net) => net.toggle_count_rising, - } + pub fn get_toggle_count_rising(&self) -> usize { + self.toggle_count_rising } /// Get total falling toggle count of signal. - fn get_toggle_count_falling(&self) -> usize { - match self { - Signal::Constant(_) => 0, - Signal::Net(net) => net.toggle_count_falling, - } + pub fn get_toggle_count_falling(&self) -> usize { + self.toggle_count_falling } } diff --git a/crates/arbolta/src/synth/mod.rs b/crates/arbolta/src/synth/mod.rs deleted file mode 100644 index 9ebf465..0000000 --- a/crates/arbolta/src/synth/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. -// SPDX-License-Identifier: MIT - -pub mod netlist; -pub mod yosys; diff --git a/crates/arbolta/src/synth/netlist.rs b/crates/arbolta/src/synth/netlist.rs deleted file mode 100644 index f6a6b72..0000000 --- a/crates/arbolta/src/synth/netlist.rs +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. -// SPDX-License-Identifier: MIT - -use crate::bit::Bit; -use crate::cell::{Cell, CellLibrary}; -use crate::module::hardware_module::{Component, ComponentIndexMap, HardwareModule, PortMap}; -use crate::module::port::{Port, PortDirection}; -use crate::signal::{AccessSignal, Signal, SignalIndexMap, SignalList}; -use std::collections::BTreeMap; -use std::io; -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum SynthError { - #[error("Module `{0}` does not exist")] - MissingModule(String), - #[error("Error opening netlist: {0}")] - Netlist(String), - #[error("{0}")] - IoError(#[from] io::Error), -} - -#[derive(Debug)] -pub enum SynthBit { - Constant(Bit), - NetIndex(usize), -} - -#[derive(Debug)] -pub struct SynthPort { - pub direction: PortDirection, - pub bits: Vec, // context of local module - pub signed: bool, -} - -#[derive(Debug)] -pub struct SynthCell { - pub cell_type: String, - pub connections: BTreeMap>, -} - -#[derive(Debug)] -pub struct SynthModule { - // Preserve topological order of cells - pub ports: BTreeMap, - pub cells: BTreeMap, - pub nets: BTreeMap>, // context of local module -} - -impl SynthModule { - pub fn max_net_idx(&self) -> usize { - self - .nets - .values() - .map(|bits| { - bits - .iter() - .map(|x| match x { - SynthBit::Constant(_) => 0, - SynthBit::NetIndex(idx) => *idx, - }) - .max() - .unwrap() - }) - .max() - .unwrap() - } -} - -impl From<&SynthBit> for Signal { - fn from(value: &SynthBit) -> Self { - match value { - SynthBit::Constant(x) => Signal::new_constant(*x), - SynthBit::NetIndex(x) => Signal::new_net(*x), - } - } -} - -impl From for Signal { - fn from(value: SynthBit) -> Self { - Self::from(&value) - } -} - -impl From<&SynthPort> for Port { - fn from(value: &SynthPort) -> Self { - let signal_idx_list: Vec = value - .bits - .iter() - .map(|x| match x { - SynthBit::Constant(bit) => match bit { - Bit::Zero => 0, - Bit::One => 1, - }, - SynthBit::NetIndex(idx) => *idx, - }) - .collect(); - - let shape = [1, signal_idx_list.len()]; - - Self { - signal_idx_list, - shape, - direction: value.direction.clone(), - signed: value.signed, - } - } -} - -impl From for Port { - fn from(value: SynthPort) -> Self { - Self::from(&value) - } -} - -#[derive(Debug)] -pub struct Netlist { - pub modules: BTreeMap, -} - -impl Netlist { - pub fn generate_module( - &self, - name: &str, - cell_library: &CellLibrary, - ) -> Result { - let top_module: &SynthModule = match self.modules.get(name) { - Some(x) => x, - None => return Err(SynthError::MissingModule(name.to_string())), - }; - - let ports: PortMap = top_module - .ports - .iter() - .map(|(port_name, port_synth)| (port_name.clone(), Port::from(port_synth))) - .collect(); - - let mut signals: SignalList = (0..(top_module.max_net_idx() + 1)) - .map(|_| Signal::new_constant(Bit::Zero)) - .collect(); - // Bits 0 and 1 are unused by Yosys so we keep them as constant 0 and 1 respectively - signals[1] = Signal::new_constant(Bit::One); - - let mut signal_map = SignalIndexMap::new(); - for (net_name, bits) in &top_module.nets { - for (i, bit) in bits.iter().enumerate() { - let signal_name = if bits.len() > 1 { - format!("{net_name}[{i}]") - } else { - net_name.clone() - }; - - match bit { - SynthBit::Constant(_) => (), // Do nothing - SynthBit::NetIndex(idx) => { - let mut signal = Signal::new_net(*idx); - signal_map.insert(signal_name.clone(), *idx); - signal.set_name(signal_name); - signals[*idx] = signal; - } - } - } - } - - let mut components: Vec = vec![]; - let mut component_map = ComponentIndexMap::new(); - - for (instance_name, synth_cell) in &top_module.cells { - let new_component = match cell_library.cells.get(&synth_cell.cell_type) { - Some(cell_info) => { - let mut cell = Cell::from(cell_info); - // flatten this for now, should only be 1 bit - for (i, bits) in synth_cell.connections.values().enumerate() { - let idx = match &bits[0] { - SynthBit::Constant(bit) => match bit { - Bit::Zero => 0, - Bit::One => 1, - }, - SynthBit::NetIndex(idx) => *idx, - }; - cell.input_connections[i] = idx; - } - // this sets last input as output but, fix later - cell.output_connection = cell.input_connections[cell.num_inputs]; - Component::Cell(cell) - } - None => { - let mut submodule = self.generate_module(&synth_cell.cell_type, cell_library)?; - for (port_name, bits) in &synth_cell.connections { - let port = submodule.ports.get(port_name).unwrap(); - for (i, bit) in bits.iter().enumerate() { - let idx = match bit { - SynthBit::Constant(bit) => match bit { - Bit::Zero => 0, - Bit::One => 1, - }, - SynthBit::NetIndex(idx) => *idx, - }; - - match port.direction { - PortDirection::Input => submodule - .input_connections - .push((idx, port.signal_idx_list[i])), - PortDirection::Output => submodule - .output_connections - .push((idx, port.signal_idx_list[i])), - } - } - } - Component::Module(submodule) - } - }; - component_map.insert(instance_name.clone(), components.len()); - components.push(new_component); - } - - Ok(HardwareModule { - name: name.to_string(), - ports, - signals, - signal_map, - components, - component_map, - input_connections: vec![], - output_connections: vec![], - }) - } -} diff --git a/crates/arbolta/src/synth/yosys.rs b/crates/arbolta/src/synth/yosys.rs deleted file mode 100644 index 5335fbf..0000000 --- a/crates/arbolta/src/synth/yosys.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. -// SPDX-License-Identifier: MIT - -use super::netlist::{Netlist, SynthBit, SynthCell, SynthError, SynthModule, SynthPort}; -use crate::bit::Bit; -use crate::module::port::PortDirection; -use std::collections::BTreeMap; - -impl From for PortDirection { - fn from(value: yosys_netlist_json::PortDirection) -> Self { - match value { - yosys_netlist_json::PortDirection::InOut => todo!("Inout not supported"), - yosys_netlist_json::PortDirection::Input => Self::Input, - yosys_netlist_json::PortDirection::Output => Self::Output, - } - } -} - -impl From for SynthBit { - fn from(value: yosys_netlist_json::BitVal) -> Self { - match value { - yosys_netlist_json::BitVal::N(idx) => Self::NetIndex(idx), - yosys_netlist_json::BitVal::S(constant) => Self::Constant(match constant { - yosys_netlist_json::SpecialBit::_0 => Bit::Zero, - yosys_netlist_json::SpecialBit::_1 => Bit::One, - yosys_netlist_json::SpecialBit::X => todo!("X not supported"), - yosys_netlist_json::SpecialBit::Z => todo!("Z not supported"), - }), - } - } -} - -impl From for SynthPort { - fn from(value: yosys_netlist_json::Port) -> Self { - Self { - direction: PortDirection::from(value.direction), - bits: value.bits.iter().map(|x| SynthBit::from(*x)).collect(), - signed: value.signed > 0, - } - } -} - -impl From for SynthCell { - fn from(value: yosys_netlist_json::Cell) -> Self { - let mut connections: BTreeMap> = BTreeMap::new(); - for (key, vals) in value.connections { - let bits: Vec = vals.iter().map(|x| SynthBit::from(*x)).collect(); - connections.insert(key, bits); - } - - Self { - cell_type: value.cell_type, - connections, - } - } -} - -impl From for SynthModule { - fn from(value: yosys_netlist_json::Module) -> Self { - let ports: BTreeMap = value - .ports - .into_iter() - .map(|(key, val)| (key.clone(), SynthPort::from(val))) - .collect(); - - let cells: BTreeMap = value - .cells - .into_iter() - .map(|(key, val)| (key.clone(), SynthCell::from(val))) - .collect(); - - let nets: BTreeMap> = value - .netnames - .into_iter() - .map(|(key, vals)| { - ( - key.clone(), - vals.bits.iter().map(|x| SynthBit::from(*x)).collect(), - ) - }) - .collect(); - - Self { ports, cells, nets } - } -} - -impl From for Netlist { - fn from(value: yosys_netlist_json::Netlist) -> Self { - let mut modules: BTreeMap = BTreeMap::new(); - for (key, val) in value.modules { - modules.insert(key, SynthModule::from(val)); - } - - Self { modules } - } -} - -impl Netlist { - pub fn from_yosys(json_path: &str) -> Result { - let raw_json = std::fs::read(json_path)?; - let Ok(raw_netlist) = yosys_netlist_json::Netlist::from_slice(&raw_json) else { - return Err(SynthError::Netlist(json_path.to_string())); - }; - - Ok(Netlist::from(raw_netlist)) - } - - pub fn from_yosys_raw(raw_json: &[u8]) -> Result { - let Ok(raw_netlist) = yosys_netlist_json::Netlist::from_slice(raw_json) else { - return Err(SynthError::Netlist("raw_json".to_string())); - }; - - Ok(Netlist::from(raw_netlist)) - } -} diff --git a/crates/arbolta/tests/test_cell.rs b/crates/arbolta/tests/test_cell.rs index d7cd855..e101f93 100644 --- a/crates/arbolta/tests/test_cell.rs +++ b/crates/arbolta/tests/test_cell.rs @@ -2,140 +2,162 @@ // SPDX-License-Identifier: MIT use arbolta::bit::Bit; -use arbolta::cell::{Cell, Function}; -use arbolta::signal::{AccessSignal, Signal, SignalList}; +use arbolta::cell::{create_cell, Cell, CellFn, DffPosedgeReset}; +use arbolta::signal::Signal; +use once_cell::sync::Lazy; use rstest::rstest; +use yosys_netlist_json as yosys; + +static VARIABLE_ALPHABET: Lazy> = Lazy::new(|| { + (b'A'..=b'Z') + .map(|x| String::from_utf8(vec![x]).unwrap()) + .collect() +}); + +fn generate_cell(cell_type: &str, num_inputs: usize) -> Cell { + let mut cell = yosys::Cell::default(); + cell.cell_type = cell_type.to_string(); + + for net in 0..num_inputs { + let name = &VARIABLE_ALPHABET[net]; + cell + .connections + .insert(name.to_string(), vec![yosys::BitVal::N(net)]); + cell + .port_directions + .insert(name.to_string(), yosys::PortDirection::Input); + } + + cell + .connections + .insert("Y".to_string(), vec![yosys::BitVal::N(num_inputs)]); + cell + .port_directions + .insert("Y".to_string(), yosys::PortDirection::Output); + + create_cell(&cell).unwrap() +} #[rstest] -#[case(Function::Inverter, Bit::Zero, Bit::One)] -#[case(Function::Inverter, Bit::One, Bit::Zero)] -#[case(Function::Buf, Bit::Zero, Bit::Zero)] -#[case(Function::Buf, Bit::One, Bit::One)] -fn test_cell_1_input(#[case] function: Function, #[case] a: Bit, #[case] expected: Bit) { - let mut cell = Cell::empty_from_function(function); - let mut signals: SignalList = vec![Signal::new_constant(a), Signal::new_net(1)]; - cell.num_inputs = 1; - cell.input_connections[0] = 0; - cell.output_connection = 1; - +#[case("NOT", Bit::Zero, Bit::One)] +#[case("NOT", Bit::One, Bit::Zero)] +#[case("BUF", Bit::Zero, Bit::Zero)] +#[case("BUF", Bit::One, Bit::One)] +fn test_cell_1_input(#[case] cell_type: &str, #[case] a: Bit, #[case] expected: Bit) { + let mut cell = generate_cell(cell_type, 1); + let mut signals = vec![Signal::new_constant(a), Signal::default()].into_boxed_slice(); cell.eval(&mut signals); - - let actual = signals[1].get_value(); - assert_eq!(actual, expected); + assert_eq!(signals[1].get_value(), expected) } #[rstest] -#[case(Function::And, Bit::Zero, Bit::Zero, Bit::Zero)] -#[case(Function::And, Bit::Zero, Bit::One, Bit::Zero)] -#[case(Function::And, Bit::One, Bit::Zero, Bit::Zero)] -#[case(Function::And, Bit::One, Bit::One, Bit::One)] -#[case(Function::Nor, Bit::Zero, Bit::Zero, Bit::One)] -#[case(Function::Nor, Bit::Zero, Bit::One, Bit::Zero)] -#[case(Function::Nor, Bit::One, Bit::Zero, Bit::Zero)] -#[case(Function::Nor, Bit::One, Bit::One, Bit::Zero)] -#[case(Function::Nand, Bit::Zero, Bit::Zero, Bit::One)] -#[case(Function::Nand, Bit::Zero, Bit::One, Bit::One)] -#[case(Function::Nand, Bit::One, Bit::Zero, Bit::One)] -#[case(Function::Nand, Bit::One, Bit::One, Bit::Zero)] -#[case(Function::Or, Bit::Zero, Bit::Zero, Bit::Zero)] -#[case(Function::Or, Bit::Zero, Bit::One, Bit::One)] -#[case(Function::Or, Bit::One, Bit::Zero, Bit::One)] -#[case(Function::Or, Bit::One, Bit::One, Bit::One)] -#[case(Function::Xor, Bit::Zero, Bit::Zero, Bit::Zero)] -#[case(Function::Xor, Bit::Zero, Bit::One, Bit::One)] -#[case(Function::Xor, Bit::One, Bit::Zero, Bit::One)] -#[case(Function::Xor, Bit::One, Bit::One, Bit::Zero)] -#[case(Function::Xnor, Bit::Zero, Bit::Zero, Bit::One)] -#[case(Function::Xnor, Bit::Zero, Bit::One, Bit::Zero)] -#[case(Function::Xnor, Bit::One, Bit::Zero, Bit::Zero)] -#[case(Function::Xnor, Bit::One, Bit::One, Bit::One)] +#[case("AND", Bit::Zero, Bit::Zero, Bit::Zero)] +#[case("AND", Bit::Zero, Bit::One, Bit::Zero)] +#[case("AND", Bit::One, Bit::Zero, Bit::Zero)] +#[case("AND", Bit::One, Bit::One, Bit::One)] +#[case("ANDNOT", Bit::Zero, Bit::Zero, Bit::Zero)] +#[case("ANDNOT", Bit::Zero, Bit::One, Bit::Zero)] +#[case("ANDNOT", Bit::One, Bit::Zero, Bit::One)] +#[case("ANDNOT", Bit::One, Bit::One, Bit::Zero)] +#[case("NOR", Bit::Zero, Bit::Zero, Bit::One)] +#[case("NOR", Bit::Zero, Bit::One, Bit::Zero)] +#[case("NOR", Bit::One, Bit::Zero, Bit::Zero)] +#[case("NOR", Bit::One, Bit::One, Bit::Zero)] +#[case("NAND", Bit::Zero, Bit::Zero, Bit::One)] +#[case("NAND", Bit::Zero, Bit::One, Bit::One)] +#[case("NAND", Bit::One, Bit::Zero, Bit::One)] +#[case("NAND", Bit::One, Bit::One, Bit::Zero)] +#[case("OR", Bit::Zero, Bit::Zero, Bit::Zero)] +#[case("OR", Bit::Zero, Bit::One, Bit::One)] +#[case("OR", Bit::One, Bit::Zero, Bit::One)] +#[case("OR", Bit::One, Bit::One, Bit::One)] +#[case("XOR", Bit::Zero, Bit::Zero, Bit::Zero)] +#[case("XOR", Bit::Zero, Bit::One, Bit::One)] +#[case("XOR", Bit::One, Bit::Zero, Bit::One)] +#[case("XOR", Bit::One, Bit::One, Bit::Zero)] +#[case("XNOR", Bit::Zero, Bit::Zero, Bit::One)] +#[case("XNOR", Bit::Zero, Bit::One, Bit::Zero)] +#[case("XNOR", Bit::One, Bit::Zero, Bit::Zero)] +#[case("XNOR", Bit::One, Bit::One, Bit::One)] +#[case("ORNOT", Bit::Zero, Bit::Zero, Bit::One)] +#[case("ORNOT", Bit::Zero, Bit::One, Bit::Zero)] +#[case("ORNOT", Bit::One, Bit::Zero, Bit::One)] +#[case("ORNOT", Bit::One, Bit::One, Bit::One)] fn test_cell_2_input( - #[case] function: Function, + #[case] cell_type: &str, #[case] a: Bit, #[case] b: Bit, #[case] expected: Bit, ) { - let mut cell = Cell::empty_from_function(function); - let mut signals: SignalList = vec![ + let mut cell = generate_cell(cell_type, 2); + let mut signals = vec![ Signal::new_constant(a), Signal::new_constant(b), - Signal::new_net(2), - ]; - - cell.num_inputs = 2; - cell.input_connections[0] = 0; - cell.input_connections[1] = 1; - cell.output_connection = 2; - + Signal::default(), + ] + .into_boxed_slice(); cell.eval(&mut signals); - let actual = signals[2].get_value(); - assert_eq!(actual, expected); + assert_eq!(signals[2].get_value(), expected); } #[rstest] -#[case(Function::Or, Bit::Zero, Bit::Zero, Bit::Zero, Bit::Zero)] -#[case(Function::Or, Bit::Zero, Bit::Zero, Bit::One, Bit::One)] -#[case(Function::Or, Bit::Zero, Bit::One, Bit::Zero, Bit::One)] -#[case(Function::Or, Bit::Zero, Bit::One, Bit::One, Bit::One)] -#[case(Function::Or, Bit::One, Bit::Zero, Bit::Zero, Bit::One)] -#[case(Function::Or, Bit::One, Bit::Zero, Bit::One, Bit::One)] -#[case(Function::Or, Bit::One, Bit::One, Bit::Zero, Bit::One)] -#[case(Function::Or, Bit::One, Bit::One, Bit::One, Bit::One)] +#[case("OR", Bit::Zero, Bit::Zero, Bit::Zero, Bit::Zero)] +#[case("OR", Bit::Zero, Bit::Zero, Bit::One, Bit::One)] +#[case("OR", Bit::Zero, Bit::One, Bit::Zero, Bit::One)] +#[case("OR", Bit::Zero, Bit::One, Bit::One, Bit::One)] +#[case("OR", Bit::One, Bit::Zero, Bit::Zero, Bit::One)] +#[case("OR", Bit::One, Bit::Zero, Bit::One, Bit::One)] +#[case("OR", Bit::One, Bit::One, Bit::Zero, Bit::One)] +#[case("OR", Bit::One, Bit::One, Bit::One, Bit::One)] fn test_cell_3_input( - #[case] function: Function, + #[case] cell_type: &str, #[case] a: Bit, #[case] b: Bit, #[case] c: Bit, #[case] expected: Bit, ) { - let mut cell = Cell::empty_from_function(function); - let mut signals: SignalList = vec![ + let mut cell = generate_cell(cell_type, 3); + let mut signals = vec![ Signal::new_constant(a), Signal::new_constant(b), Signal::new_constant(c), - Signal::new_net(3), - ]; - - cell.num_inputs = 3; - cell.input_connections[0] = 0; - cell.input_connections[1] = 1; - cell.input_connections[2] = 2; - cell.output_connection = 3; - + Signal::default(), + ] + .into_boxed_slice(); cell.eval(&mut signals); - let actual = signals[3].get_value(); - assert_eq!(actual, expected); + assert_eq!(signals[3].get_value(), expected); } #[rstest] -#[case(Function::DffPosEdge, Bit::Zero, Bit::Zero)] -#[case(Function::DffPosEdge, Bit::One, Bit::One)] -fn test_cell_1_input_clocked(#[case] function: Function, #[case] a: Bit, #[case] expected: Bit) { - let mut cell = Cell::empty_from_function(function); - let mut signals: SignalList = vec![ - Signal::new_net(0), - Signal::new_constant(a), - Signal::new_net(2), - ]; +fn test_cell_sdff_pp() { + // D, C, R, Q + let (data_in, clock, reset, data_out) = (0, 1, 2, 3); + let mut cell = DffPosedgeReset::new(data_in, clock, reset, data_out); + let mut signals = vec![Signal::default(); 4].into_boxed_slice(); - cell.num_inputs = 2; - cell.input_connections[0] = 0; - cell.input_connections[1] = 1; - cell.output_connection = 2; + cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::Zero); - signals[0].set_value(Bit::Zero); + signals[data_in].set_value(Bit::One); cell.eval(&mut signals); - signals[0].set_value(Bit::One); + assert_eq!(signals[data_out].get_value(), Bit::Zero); + + signals[clock].set_value(Bit::One); // Rising edge cell.eval(&mut signals); - signals[0].set_value(Bit::Zero); + assert_eq!(signals[data_out].get_value(), Bit::One); + + signals[clock].set_value(Bit::Zero); // Falling edge cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::One); - let actual = signals[2].get_value(); - assert_eq!(actual, expected); -} + signals[reset].set_value(Bit::One); + cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::One); -// TODO: Randomize input testing -// TODO: N-input gate tests + signals[clock].set_value(Bit::One); // Rising edge + cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::Zero); +} diff --git a/crates/arbolta/tests/test_module.rs b/crates/arbolta/tests/test_module.rs index 1b683a8..6d69a6a 100644 --- a/crates/arbolta/tests/test_module.rs +++ b/crates/arbolta/tests/test_module.rs @@ -1,161 +1,172 @@ // Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. // SPDX-License-Identifier: MIT -use arbolta::bit::Bit; -use arbolta::cell::{Cell, Function}; -use arbolta::module::hardware_module::{Component, HardwareModule}; -use arbolta::module::port::{Port, PortDirection}; +use arbolta::hardware_module::HardwareModule; use arbolta::signal::Signal; + use once_cell::sync::Lazy; use rstest::rstest; +use yosys_netlist_json as yosys; static VARIABLE_ALPHABET: Lazy> = Lazy::new(|| { - (b'a'..=b'z') + (b'A'..=b'Z') .map(|x| String::from_utf8(vec![x]).unwrap()) .collect() }); -fn cell_module_from_function(function: Function, num_inputs: usize) -> HardwareModule { - let mut cell_inputs_connections = [0; 8]; - let mut module = HardwareModule::default(); - - for i in 0..num_inputs { - module.signals.push(Signal::new_net(i)); - module.ports.insert( - VARIABLE_ALPHABET[i].clone(), - Port { - signal_idx_list: vec![i], - shape: [1, 1], - direction: PortDirection::Input, - signed: false, - }, - ); - - cell_inputs_connections[i] = i; +fn generate_module(cell_type: &str, num_inputs: usize) -> HardwareModule { + let mut module = yosys::Module::default(); + let mut cell = yosys::Cell::default(); + cell.cell_type = cell_type.to_string(); + + let net_offset = 2; // Offset constants + for net in 0..num_inputs { + let name = &VARIABLE_ALPHABET[net]; + let bitval = yosys::BitVal::N(net + net_offset); + let mut netname = yosys::Netname::default(); + netname.bits.push(bitval); + + let port = yosys::Port { + direction: yosys::PortDirection::Input, + bits: vec![bitval], + offset: Default::default(), + upto: Default::default(), + signed: Default::default(), + }; + + cell.connections.insert(name.to_string(), port.bits.clone()); + cell + .port_directions + .insert(name.to_string(), port.direction); + module.ports.insert(name.to_string(), port); + module.netnames.insert(name.to_string(), netname); } - module.signals.push(Signal::new_net(num_inputs)); - module.ports.insert( - VARIABLE_ALPHABET[num_inputs].clone(), - Port { - signal_idx_list: vec![num_inputs], - shape: [1, 1], - direction: PortDirection::Output, - signed: false, - }, - ); - - module.components.push(Component::Cell(Cell { - name: String::new(), - function, - state: [Bit::Zero; 2], - input_connections: cell_inputs_connections, - output_connection: num_inputs, - num_inputs, - })); - - module + + let bitval = yosys::BitVal::N(num_inputs + net_offset); + let mut netname = yosys::Netname::default(); + netname.bits.push(bitval); + + let port = yosys::Port { + direction: yosys::PortDirection::Output, + bits: vec![bitval], + offset: Default::default(), + upto: Default::default(), + signed: Default::default(), + }; + + cell.connections.insert("Y".to_string(), port.bits.clone()); + cell.port_directions.insert("Y".to_string(), port.direction); + module.ports.insert("Y".to_string(), port); + module.netnames.insert("Y".to_string(), netname); + + module.cells.insert(cell_type.to_string(), cell); + + let mut netlist = yosys::Netlist::default(); + netlist.modules.insert(cell_type.to_string(), module); + + HardwareModule::new(netlist, cell_type).unwrap() } #[rstest] -#[case(Function::Inverter, 0, 1)] -#[case(Function::Inverter, 1, 0)] -#[case(Function::Buf, 0, 0)] -#[case(Function::Buf, 1, 1)] -fn test_module_1_input_cell(#[case] function: Function, #[case] a: u8, #[case] expected: u8) { - let mut cell_module = cell_module_from_function(function, 1); - cell_module.set_port_int("a", a).unwrap(); +#[case("NOT", 0, 1)] +#[case("NOT", 1, 0)] +#[case("BUF", 0, 0)] +#[case("BUF", 1, 1)] +fn test_module_1_input_cell(#[case] cell_type: &str, #[case] a: u8, #[case] expected: u8) { + let mut cell_module = generate_module(cell_type, 1); + cell_module.set_port_int("A", a).unwrap(); cell_module.eval(); - let actual: u8 = cell_module.get_port_int("b").unwrap(); + let actual: u8 = cell_module.get_port_int("Y").unwrap(); assert_eq!(actual, expected); } #[rstest] -#[case(Function::And, 0, 0, 0)] -#[case(Function::And, 0, 1, 0)] -#[case(Function::And, 1, 0, 0)] -#[case(Function::And, 1, 1, 1)] -#[case(Function::Nor, 0, 0, 1)] -#[case(Function::Nor, 0, 1, 0)] -#[case(Function::Nor, 1, 0, 0)] -#[case(Function::Nor, 1, 1, 0)] -#[case(Function::Nand, 0, 0, 1)] -#[case(Function::Nand, 0, 1, 1)] -#[case(Function::Nand, 1, 0, 1)] -#[case(Function::Nand, 1, 1, 0)] -#[case(Function::Or, 0, 0, 0)] -#[case(Function::Or, 0, 1, 1)] -#[case(Function::Or, 1, 0, 1)] -#[case(Function::Or, 1, 1, 1)] -#[case(Function::Xor, 0, 0, 0)] -#[case(Function::Xor, 0, 1, 1)] -#[case(Function::Xor, 1, 0, 1)] -#[case(Function::Xor, 1, 1, 0)] -#[case(Function::Xnor, 0, 0, 1)] -#[case(Function::Xnor, 0, 1, 0)] -#[case(Function::Xnor, 1, 0, 0)] -#[case(Function::Xnor, 1, 1, 1)] +#[case("AND", 0, 0, 0)] +#[case("AND", 0, 1, 0)] +#[case("AND", 1, 0, 0)] +#[case("AND", 1, 1, 1)] +#[case("NOR", 0, 0, 1)] +#[case("NOR", 0, 1, 0)] +#[case("NOR", 1, 0, 0)] +#[case("NOR", 1, 1, 0)] +#[case("NAND", 0, 0, 1)] +#[case("NAND", 0, 1, 1)] +#[case("NAND", 1, 0, 1)] +#[case("NAND", 1, 1, 0)] +#[case("OR", 0, 0, 0)] +#[case("OR", 0, 1, 1)] +#[case("OR", 1, 0, 1)] +#[case("OR", 1, 1, 1)] +#[case("XOR", 0, 0, 0)] +#[case("XOR", 0, 1, 1)] +#[case("XOR", 1, 0, 1)] +#[case("XOR", 1, 1, 0)] +#[case("XNOR", 0, 0, 1)] +#[case("XNOR", 0, 1, 0)] +#[case("XNOR", 1, 0, 0)] +#[case("XNOR", 1, 1, 1)] fn test_module_2_input_cell( - #[case] function: Function, + #[case] cell_type: &str, #[case] a: u8, #[case] b: u8, #[case] expected: u8, ) { - let mut cell_module = cell_module_from_function(function, 2); - cell_module.set_port_int("a", a).unwrap(); - cell_module.set_port_int("b", b).unwrap(); + let mut cell_module = generate_module(cell_type, 2); + cell_module.set_port_int("A", a).unwrap(); + cell_module.set_port_int("B", b).unwrap(); cell_module.eval(); - let actual: u8 = cell_module.get_port_int("c").unwrap(); + let actual: u8 = cell_module.get_port_int("Y").unwrap(); assert_eq!(actual, expected); } #[rstest] -#[case(Function::Or, 0, 0, 0, 0)] -#[case(Function::Or, 0, 0, 1, 1)] -#[case(Function::Or, 0, 1, 0, 1)] -#[case(Function::Or, 0, 1, 1, 1)] -#[case(Function::Or, 1, 0, 0, 1)] -#[case(Function::Or, 1, 0, 1, 1)] -#[case(Function::Or, 1, 1, 0, 1)] -#[case(Function::Or, 1, 1, 1, 1)] +#[case("OR", 0, 0, 0, 0)] +#[case("OR", 0, 0, 1, 1)] +#[case("OR", 0, 1, 0, 1)] +#[case("OR", 0, 1, 1, 1)] +#[case("OR", 1, 0, 0, 1)] +#[case("OR", 1, 0, 1, 1)] +#[case("OR", 1, 1, 0, 1)] +#[case("OR", 1, 1, 1, 1)] fn test_module_3_input_cell( - #[case] function: Function, + #[case] cell_type: &str, #[case] a: u8, #[case] b: u8, #[case] c: u8, #[case] expected: u8, ) { - let mut cell_module = cell_module_from_function(function, 3); - cell_module.set_port_int("a", a).unwrap(); - cell_module.set_port_int("b", b).unwrap(); - cell_module.set_port_int("c", c).unwrap(); + let mut cell_module = generate_module(cell_type, 3); + cell_module.set_port_int("A", a).unwrap(); + cell_module.set_port_int("B", b).unwrap(); + cell_module.set_port_int("C", c).unwrap(); cell_module.eval(); - let actual: u8 = cell_module.get_port_int("d").unwrap(); + let actual: u8 = cell_module.get_port_int("Y").unwrap(); assert_eq!(actual, expected); } -#[rstest] -#[case(Function::DffPosEdge, 0, 0)] -#[case(Function::DffPosEdge, 1, 1)] -fn test_module_1_input_cell_clocked( - #[case] function: Function, - #[case] a: u8, - #[case] expected: u8, -) { - let mut cell_module = cell_module_from_function(function, 2); - - cell_module.set_port_int("a", 0).unwrap(); // clock - cell_module.set_port_int("b", a).unwrap(); - cell_module.eval(); - - cell_module.set_port_int("a", 1).unwrap(); - cell_module.eval(); - - cell_module.set_port_int("a", 0).unwrap(); - cell_module.eval(); - - let actual: u8 = cell_module.get_port_int("c").unwrap(); - assert_eq!(actual, expected); -} +// #[rstest] +// #[case(Function::DffPosEdge, 0, 0)] +// #[case(Function::DffPosEdge, 1, 1)] +// fn test_module_1_input_cell_clocked( +// #[case] function: Function, +// #[case] a: u8, +// #[case] expected: u8, +// ) { +// let mut cell_module = cell_module_from_function(function, 2); + +// cell_module.set_port_int("a", 0).unwrap(); // clock +// cell_module.set_port_int("b", a).unwrap(); +// cell_module.eval(); + +// cell_module.set_port_int("a", 1).unwrap(); +// cell_module.eval(); + +// cell_module.set_port_int("a", 0).unwrap(); +// cell_module.eval(); + +// let actual: u8 = cell_module.get_port_int("Y").unwrap(); +// assert_eq!(actual, expected); +// } diff --git a/crates/arbolta/tests/test_signal.rs b/crates/arbolta/tests/test_signal.rs index 4f149e0..617778f 100644 --- a/crates/arbolta/tests/test_signal.rs +++ b/crates/arbolta/tests/test_signal.rs @@ -2,22 +2,21 @@ // SPDX-License-Identifier: MIT use arbolta::bit::Bit; -use arbolta::signal::{AccessSignal, Signal}; +use arbolta::signal::Signal; #[test] fn test_signal_net_init() { - let x = Signal::new_net(0); + let x = Signal::default(); assert_eq!(x.get_value(), Bit::Zero); assert_eq!(x.get_toggle_count_falling(), 0); assert_eq!(x.get_toggle_count_rising(), 0); assert_eq!(x.get_total_toggle_count(), 0); - assert_eq!(x.get_index(), 0); } #[test] fn test_signal_net_set_value() { - let mut x = Signal::new_net(0); + let mut x = Signal::default(); assert_eq!(x.get_value(), Bit::Zero); x.set_value(Bit::One); @@ -26,7 +25,7 @@ fn test_signal_net_set_value() { #[test] fn test_signal_net_toggle_rising() { - let mut x = Signal::new_net(0); + let mut x = Signal::default(); assert_eq!(x.get_total_toggle_count(), 0); assert_eq!(x.get_toggle_count_falling(), 0); @@ -41,7 +40,8 @@ fn test_signal_net_toggle_rising() { #[test] fn test_signal_net_toggle_falling() { - let mut x = Signal::new_net_from(0, Bit::One); + let mut x = Signal::default(); + x.value = Bit::One; assert_eq!(x.get_total_toggle_count(), 0); assert_eq!(x.get_toggle_count_falling(), 0); @@ -56,7 +56,7 @@ fn test_signal_net_toggle_falling() { #[test] fn test_signal_net_toggle_same_zero() { - let mut x = Signal::new_net(0); + let mut x = Signal::default(); assert_eq!(x.get_total_toggle_count(), 0); assert_eq!(x.get_toggle_count_falling(), 0); @@ -71,7 +71,8 @@ fn test_signal_net_toggle_same_zero() { #[test] fn test_signal_net_toggle_same_one() { - let mut x = Signal::new_net_from(0, Bit::One); + let mut x = Signal::default(); + x.value = Bit::One; assert_eq!(x.get_total_toggle_count(), 0); assert_eq!(x.get_toggle_count_falling(), 0); diff --git a/crates/arbolta/tests/test_synth.rs b/crates/arbolta/tests/test_synth.rs deleted file mode 100644 index 863d249..0000000 --- a/crates/arbolta/tests/test_synth.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. -// SPDX-License-Identifier: MIT - -use arbolta::cell::default_cell_library; -use arbolta::synth::netlist::Netlist; - -static ADDER_RAW: &str = include_str!("test_netlists/4b_adder_netlist.json"); -static NESTED_ADDER_RAW: &str = include_str!("test_netlists/4b_nested_adder_netlist.json"); - -#[test] -fn test_synth_4b_adder() { - let netlist = Netlist::from_yosys_raw(ADDER_RAW.as_bytes()).unwrap(); - let mut adder_module = netlist - .generate_module("adder", &default_cell_library()) - .unwrap(); - - for a in 0..16_u8 { - adder_module.set_port_int("op0_i", a).unwrap(); - for b in 0..16_u8 { - adder_module.set_port_int("op1_i", b).unwrap(); - adder_module.eval(); - let actual_sum = adder_module.get_port_int::("sum_o").unwrap(); - let expected_sum = a + b; - - assert_eq!(actual_sum, expected_sum) - } - } -} - -#[test] -fn test_synth_4b_nested_adder() { - let netlist = Netlist::from_yosys_raw(NESTED_ADDER_RAW.as_bytes()).unwrap(); - let mut adder_module = netlist - .generate_module("adder", &default_cell_library()) - .unwrap(); - - for a in 0..16_u8 { - adder_module.set_port_int("op0_i", a).unwrap(); - for b in 0..16_u8 { - adder_module.set_port_int("op1_i", b).unwrap(); - adder_module.eval(); - let actual_sum = adder_module.get_port_int::("sum_o").unwrap(); - let expected_sum = a + b; - - assert_eq!(actual_sum, expected_sum) - } - } -} diff --git a/crates/python_bindings/src/design.rs b/crates/python_bindings/src/design.rs index f8949bd..9a67a88 100644 --- a/crates/python_bindings/src/design.rs +++ b/crates/python_bindings/src/design.rs @@ -4,88 +4,63 @@ use crate::conversion::{ bits_to_bool_numpy, bits_to_int_numpy, bool_numpy_to_bits, int_numpy_to_bits, }; -use arbol::cell::default_cell_library; -use arbol::module::{design::Design, port::PortDirection}; -use arbol::synth::netlist::Netlist; -use bincode; +use arbol::hardware_module::HardwareModule; +use arbol::port::PortDirection; use pyo3::exceptions::{PyAttributeError, PyException, PyValueError}; use pyo3::prelude::*; use pyo3::types::PyBytes; -use serde::{Deserialize, Serialize}; use std::collections::HashMap; #[pyclass(dict, module = "arbolta", name = "Design")] -#[derive(Deserialize, Serialize)] pub struct PyDesign { #[pyo3(get)] pub top_module: String, pub netlist_path: String, - design: Design, + module: HardwareModule, } #[pymethods] impl PyDesign { #[new] fn __new__(top_module: &str, netlist_path: &str) -> PyResult { - let cell_library = default_cell_library(); - let netlist = match Netlist::from_yosys(netlist_path) { - Ok(netlist) => netlist, - Err(err) => return Err(PyException::new_err(format!("{err}"))), - }; - - let module = match netlist.generate_module(top_module, &cell_library) { + let module = match HardwareModule::new_from_path(netlist_path, top_module) { Ok(module) => module, Err(err) => return Err(PyException::new_err(format!("{err}"))), }; - let design = Design::from_module(module, cell_library); - Ok(Self { top_module: top_module.to_string(), netlist_path: netlist_path.to_string(), - design, + module, }) } + #[allow(unused_variables)] fn __setstate__(&mut self, state: &Bound<'_, PyBytes>) { - *self = bincode::deserialize(state.as_bytes()).unwrap(); + todo!() } + #[allow(unused_variables)] fn __getstate__<'py>(&self, py: Python<'py>) -> PyResult> { - match bincode::serialize(&self) { - Ok(bytes) => Ok(PyBytes::new(py, &bytes)), - Err(err) => Err(PyValueError::new_err(format!("{err}"))), - } + todo!() } fn __getnewargs__(&self) -> (String, String) { (self.top_module.clone(), self.netlist_path.clone()) } + #[allow(unused_variables)] fn save(&self, path: &str) -> PyResult<()> { - match self.design.save(path) { - Ok(()) => Ok(()), - Err(err) => Err(PyValueError::new_err(format!("{err}"))), - } + todo!() } + #[allow(unused_variables)] fn load(&self, path: &str) -> PyResult { - let design = match Design::load(path) { - Ok(design) => design, - Err(err) => return Err(PyValueError::new_err(format!("{err}"))), - }; - - let top_module = design.module.name.clone(); - - Ok(Self { - top_module, - netlist_path: String::new(), // leave this empty for now, - design, - }) + todo!() } fn get_port_shape(&self, name: &str) -> PyResult<[usize; 2]> { - match self.design.module.get_port_shape(name) { + match self.module.get_port_shape(name) { Ok(shape) => Ok(shape), Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), } @@ -100,98 +75,68 @@ impl PyDesign { let internal_shape = self.get_port_shape(name)?; let (num_elems, elem_size) = (shape[1], internal_shape[1] / shape[1]); - match self - .design - .module - .set_port_shape(name, &[num_elems, elem_size]) - { + match self.module.set_port_shape(name, &[num_elems, elem_size]) { Ok(()) => Ok(()), Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), } } fn get_module_names(&self) -> Vec { - let mut names: Vec = vec![]; - self - .design - .module - .components - .iter() - .for_each(|component| match component { - arbol::module::hardware_module::Component::Cell(_) => (), - arbol::module::hardware_module::Component::Module(module) => { - names.push(module.name.clone()) - } - }); - names + todo!() } + #[allow(unused_variables)] fn set_clock(&mut self, name: &str) -> PyResult<()> { - match self.design.set_clock(name) { - Ok(()) => Ok(()), - Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), - } + todo!() } + #[allow(unused_variables)] fn set_reset(&mut self, name: &str) -> PyResult<()> { - match self.design.set_reset(name) { - Ok(()) => Ok(()), - Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), - } + todo!() } fn reset(&mut self) { - self.design.module.reset(); + self.module.reset(); } + #[allow(unused_variables)] fn reset_clocked(&mut self) -> PyResult<()> { - match self.design.reset_clocked() { - Ok(()) => Ok(()), - Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), - } + todo!() } fn eval(&mut self) { - self.design.eval(); + self.module.eval(); } + #[allow(unused_variables)] fn eval_clocked(&mut self) -> PyResult<()> { - match self.design.eval_clocked() { - Ok(()) => Ok(()), - Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), - } + todo!() } + #[allow(unused_variables)] fn get_module_breakdown(&self, name: &str) -> PyResult> { - match self.design.get_module_breakdown(name) { - Ok(breakdown) => Ok(breakdown), - Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), - } + todo!() } + #[allow(unused_variables)] fn get_module_area(&self, name: &str) -> PyResult { - match self.design.get_module_area(name) { - Ok(area) => Ok(area), - Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), - } + todo!() } + #[allow(unused_variables)] fn get_module_total_toggle_count(&self, name: &str) -> PyResult { - match self.design.get_module_total_toggle_count(name) { - Ok(count) => Ok(count), - Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), - } + todo!() } fn get_port_string(&self, name: &str) -> PyResult { - match self.design.module.get_port_string(name) { + match self.module.get_port_string(name) { Ok(bit_string) => Ok(bit_string), Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), } } fn is_port_input(&self, name: &str) -> PyResult { - let direction = match self.design.module.get_port_direction(name) { + let direction = match self.module.get_port_direction(name) { Ok(direction) => direction, Err(err) => return Err(PyAttributeError::new_err(format!("{err}"))), }; @@ -203,7 +148,7 @@ impl PyDesign { let item_type = numpy_array.getattr("dtype")?.getattr("str")?.to_string(); let shape = self.get_port_shape(name)?; let elem_size = shape[1]; - let bits = match self.design.module.get_port_bits(name) { + let bits = match self.module.get_port_bits(name) { Ok(bits) => bits, Err(err) => return Err(PyAttributeError::new_err(format!("{err}"))), }; @@ -266,7 +211,7 @@ impl PyDesign { ))) } }; - match self.design.module.set_port_bits(name, &bits) { + match self.module.set_port_bits(name, &bits) { Ok(()) => Ok(()), Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), } From 728c425c6f63095d22fac33d30fa86e8c14f354d Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Mon, 7 Apr 2025 23:19:05 +0000 Subject: [PATCH 02/64] Adding more process cells --- crates/arbolta/Cargo.toml | 2 +- crates/arbolta/src/cell.rs | 558 +++++++++++++++++- crates/arbolta/src/hardware_module.rs | 64 +- crates/arbolta/tests/test_cell.rs | 92 ++- crates/arbolta/tests/test_module.rs | 214 +++++-- .../python_bindings/py_src/arbolta/design.py | 8 +- crates/python_bindings/src/design.rs | 16 + 7 files changed, 851 insertions(+), 103 deletions(-) diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index 84dc01d..59243af 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -10,9 +10,9 @@ indexmap = "2.0.0" tempfile = "3.19.1" derive_more = { version = "2", features = ["full"] } serde = { version = "1.0", features = ["derive"] } +erased-serde = "0.4.6" ndarray = "0.16.1" num-traits = "0.2" -once_cell = "1.19.0" rstest = "0.23.0" flexbuffers = "2.0.0" yosys-netlist-json = { git = "https://github.com/alexredd99/yosys-netlist-json.git" } diff --git a/crates/arbolta/src/cell.rs b/crates/arbolta/src/cell.rs index 7116344..3839eac 100644 --- a/crates/arbolta/src/cell.rs +++ b/crates/arbolta/src/cell.rs @@ -1,7 +1,8 @@ -use crate::bit::Bit; +use crate::bit::{Bit, BitVec}; use crate::signal::Signal; use derive_more::Constructor; use indexmap::IndexMap; +use serde::{Deserialize, Serialize}; use std::fmt::Debug; use thiserror::Error; use yosys_netlist_json as yosys; @@ -9,7 +10,7 @@ use yosys_netlist_json as yosys; /// Proxy for a standard-cell and basic unit of 'compute'. pub type Cell = Box; -pub trait CellFn: Debug + Send + Sync { +pub trait CellFn: Debug + Send + Sync + erased_serde::Serialize { fn eval(&mut self, signals: &mut [Signal]); fn reset(&mut self); fn input_connections(&self) -> Vec<&usize>; @@ -82,7 +83,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { input_connections["A"][0], output_connections["Y"][0], )), - "NOT" => Box::new(Inverter::new( + "NOT" | "$_NOT_" => Box::new(Inverter::new( input_connections["A"][0], output_connections["Y"][0], )), @@ -94,7 +95,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_boxed_slice(), output_connections["Y"][0], )), - "OR" | "$_OR_" => Box::new(Or::new( + "OR" | "$_OR_" | "$reduce_or" => Box::new(Or::new( input_connections .into_values() .flatten() @@ -150,19 +151,77 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_boxed_slice(), output_connections["Y"][0], )), + "DFF" | "$_DFF_P_" => Box::new(Dff::new( + Bit::One, + input_connections["C"][0], + input_connections["D"][0], + output_connections["Q"][0], + )), "$_SDFF_PP0_" => Box::new(DffPosedgeReset::new( input_connections["D"][0], input_connections["C"][0], input_connections["R"][0], output_connections["Q"][0], )), + "$pos" => Box::new(Pos::new( + cell.parameters["A_SIGNED"].to_number().unwrap() == 1, + input_connections["A"].clone().into_boxed_slice(), + output_connections["Y"].clone().into_boxed_slice(), + )), + "$neg" => Box::new(Neg::new( + cell.parameters["A_SIGNED"].to_number().unwrap() == 1, + input_connections["A"].clone().into_boxed_slice(), + output_connections["Y"].clone().into_boxed_slice(), + )), + // Proc Cells + "$mux" | "$_MUX_" => Box::new(Mux::new( + input_connections["S"][0], + input_connections["A"].clone().into_boxed_slice(), + input_connections["B"].clone().into_boxed_slice(), + output_connections["Y"].clone().into_boxed_slice(), + )), + "$dff" => Box::new(Reg::new( + Bit::One, + input_connections["CLK"][0], + input_connections["D"].clone().into_boxed_slice(), + output_connections["Q"].clone().into_boxed_slice(), + )), + "$add" => Box::new(Add::new( + // Hacky, fix later + cell.parameters["A_SIGNED"].to_number().unwrap() == 1, + input_connections["A"].clone().into_boxed_slice(), + input_connections["B"].clone().into_boxed_slice(), + output_connections["Y"].clone().into_boxed_slice(), + )), + "$sub" => Box::new(Sub::new( + // Hacky, fix later + cell.parameters["A_SIGNED"].to_number().unwrap() == 1, + input_connections["A"].clone().into_boxed_slice(), + input_connections["B"].clone().into_boxed_slice(), + output_connections["Y"].clone().into_boxed_slice(), + )), + "$mul" => Box::new(Mul::new( + // Hacky, fix later + cell.parameters["A_SIGNED"].to_number().unwrap() + == cell.parameters["B_SIGNED"].to_number().unwrap(), + input_connections["A"].clone().into_boxed_slice(), + input_connections["B"].clone().into_boxed_slice(), + output_connections["Y"].clone().into_boxed_slice(), + )), + "$shl" => Box::new(Shl::new( + // Hacky, fix later + cell.parameters["A_SIGNED"].to_number().unwrap() == 1, + input_connections["A"].clone().into_boxed_slice(), + input_connections["B"].clone().into_boxed_slice(), + output_connections["Y"].clone().into_boxed_slice(), + )), _ => return Err(CellError::Unsupported(cell.cell_type.to_string())), }; Ok(new_cell) } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct NoneCell; #[allow(unused_variables)] @@ -184,7 +243,7 @@ impl CellFn for NoneCell { } } -#[derive(Debug, Clone, Constructor)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct Inverter { input_net: usize, output_net: usize, @@ -210,7 +269,7 @@ impl CellFn for Inverter { } } -#[derive(Debug, Clone, Constructor)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct Buf { input_net: usize, output_net: usize, @@ -236,7 +295,7 @@ impl CellFn for Buf { } } -#[derive(Debug, Clone, Constructor)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct Nand { input_nets: Box<[usize]>, output_net: usize, @@ -270,7 +329,7 @@ impl CellFn for Nand { } } -#[derive(Debug, Clone, Constructor)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct And { input_nets: Box<[usize]>, output_net: usize, @@ -304,7 +363,7 @@ impl CellFn for And { } } -#[derive(Debug, Clone, Constructor)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct AndNot { input_nets: Box<[usize]>, output_net: usize, @@ -333,7 +392,7 @@ impl CellFn for AndNot { } } -#[derive(Debug, Clone, Constructor)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct Or { input_nets: Box<[usize]>, output_net: usize, @@ -367,7 +426,7 @@ impl CellFn for Or { } } -#[derive(Debug, Clone, Constructor)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct Nor { input_nets: Box<[usize]>, output_net: usize, @@ -401,7 +460,7 @@ impl CellFn for Nor { } } -#[derive(Debug, Clone, Constructor)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct Xor { input_nets: Box<[usize]>, output_net: usize, @@ -435,7 +494,7 @@ impl CellFn for Xor { } } -#[derive(Debug, Clone, Constructor)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct Xnor { input_nets: Box<[usize]>, output_net: usize, @@ -469,7 +528,7 @@ impl CellFn for Xnor { } } -#[derive(Debug, Clone, Constructor)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct OrNot { input_nets: Box<[usize]>, output_net: usize, @@ -498,7 +557,7 @@ impl CellFn for OrNot { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct DffPosedgeReset { data_in_net: usize, clock_net: usize, @@ -560,3 +619,470 @@ impl CellFn for DffPosedgeReset { Box::new(self.clone()) } } + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Dff { + polarity: Bit, + data_in_net: usize, + clock_net: usize, + data_out_net: usize, + last_clock: Bit, +} + +impl Dff { + pub fn new(polarity: Bit, clock_net: usize, data_in_net: usize, data_out_net: usize) -> Self { + Self { + polarity, + data_in_net, + clock_net, + data_out_net, + last_clock: Bit::Zero, + } + } +} + +impl CellFn for Dff { + fn eval(&mut self, signals: &mut [Signal]) { + let clock = !(signals[self.clock_net].get_value() ^ self.polarity); + + if clock == Bit::One && self.last_clock == Bit::Zero { + signals[self.data_out_net].set_value(signals[self.data_in_net].get_value()); + } + + self.last_clock = clock; + } + + fn reset(&mut self) { + self.last_clock = Bit::Zero; + } + + fn input_connections(&self) -> Vec<&usize> { + vec![&self.clock_net, &self.data_in_net] + } + + fn output_connections(&self) -> Vec<&usize> { + vec![&self.data_out_net] + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Reg { + polarity: Bit, + data_in_nets: Box<[usize]>, + clock_net: usize, + data_out_nets: Box<[usize]>, + last_clock: Bit, +} + +impl Reg { + pub fn new( + polarity: Bit, + clock_net: usize, + data_in_nets: Box<[usize]>, + data_out_nets: Box<[usize]>, + ) -> Self { + Self { + polarity, + data_in_nets, + clock_net, + data_out_nets, + last_clock: Bit::Zero, + } + } +} + +impl CellFn for Reg { + fn eval(&mut self, signals: &mut [Signal]) { + let clock = !(signals[self.clock_net].get_value() ^ self.polarity); + + if clock == Bit::One && self.last_clock == Bit::Zero { + self + .data_out_nets + .iter() + .zip(self.data_in_nets.iter()) + .for_each(|(out_net, in_net)| signals[*out_net].set_value(signals[*in_net].get_value())); + } + + self.last_clock = clock; + } + + fn reset(&mut self) { + self.last_clock = Bit::Zero; + } + + fn input_connections(&self) -> Vec<&usize> { + self.data_in_nets.iter().chain([&self.clock_net]).collect() + } + + fn output_connections(&self) -> Vec<&usize> { + self.data_out_nets.as_ref().iter().collect() + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +// +++ Proc Cells +++ +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +pub struct Mux { + select_net: usize, + a_nets: Box<[usize]>, + b_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for Mux { + fn eval(&mut self, signals: &mut [Signal]) { + if signals[self.select_net].get_value() == Bit::One { + // Select B + self + .y_nets + .iter() + .enumerate() + .for_each(|(i, net)| signals[*net].set_value(signals[self.b_nets[i]].get_value())); + } else { + // Select A + self + .y_nets + .iter() + .enumerate() + .for_each(|(i, net)| signals[*net].set_value(signals[self.a_nets[i]].get_value())); + } + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self + .a_nets + .iter() + .chain(self.b_nets.iter().chain([&self.select_net])) + .collect() + } + + fn output_connections(&self) -> Vec<&usize> { + self.y_nets.as_ref().iter().collect() + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +pub struct Add { + signed: bool, + a_nets: Box<[usize]>, + b_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for Add { + fn eval(&mut self, signals: &mut [Signal]) { + let a = BitVec::from( + self + .a_nets + .iter() + .map(|net| signals[*net].get_value()) + .collect::>(), + ); + + let b = BitVec::from( + self + .b_nets + .iter() + .map(|net| signals[*net].get_value()) + .collect::>(), + ); + + // Hard code as i64 add, fix later + let y = if self.signed { + BitVec::from_int_sized(a.to_int::() + b.to_int::(), self.y_nets.len()).unwrap() + } else { + BitVec::from_int_sized(a.to_int::() + b.to_int::(), self.y_nets.len()).unwrap() + }; + + self + .y_nets + .iter() + .enumerate() + .for_each(|(i, net)| signals[*net].set_value(y.bits[i])); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.a_nets.iter().chain(self.b_nets.iter()).collect() + } + + fn output_connections(&self) -> Vec<&usize> { + self.y_nets.as_ref().iter().collect() + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +pub struct Sub { + signed: bool, + a_nets: Box<[usize]>, + b_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for Sub { + fn eval(&mut self, signals: &mut [Signal]) { + let a = BitVec::from( + self + .a_nets + .iter() + .map(|net| signals[*net].get_value()) + .collect::>(), + ); + + let b = BitVec::from( + self + .b_nets + .iter() + .map(|net| signals[*net].get_value()) + .collect::>(), + ); + + // Hard code as i64 add, fix later + let y = if self.signed { + BitVec::from_int_sized(a.to_int::() - b.to_int::(), self.y_nets.len()).unwrap() + } else { + BitVec::from_int_sized(a.to_int::() - b.to_int::(), self.y_nets.len()).unwrap() + }; + + self + .y_nets + .iter() + .enumerate() + .for_each(|(i, net)| signals[*net].set_value(y.bits[i])); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.a_nets.iter().chain(self.b_nets.iter()).collect() + } + + fn output_connections(&self) -> Vec<&usize> { + self.y_nets.as_ref().iter().collect() + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +pub struct Mul { + signed: bool, + a_nets: Box<[usize]>, + b_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for Mul { + fn eval(&mut self, signals: &mut [Signal]) { + let a = BitVec::from( + self + .a_nets + .iter() + .map(|net| signals[*net].get_value()) + .collect::>(), + ); + + let b = BitVec::from( + self + .b_nets + .iter() + .map(|net| signals[*net].get_value()) + .collect::>(), + ); + + // Hard code as i64 add, fix later + let y = if self.signed { + BitVec::from_int_sized(a.to_int::() * b.to_int::(), self.y_nets.len()).unwrap() + } else { + BitVec::from_int_sized(a.to_int::() * b.to_int::(), self.y_nets.len()).unwrap() + }; + + self + .y_nets + .iter() + .enumerate() + .for_each(|(i, net)| signals[*net].set_value(y.bits[i])); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.a_nets.iter().chain(self.b_nets.iter()).collect() + } + + fn output_connections(&self) -> Vec<&usize> { + self.y_nets.as_ref().iter().collect() + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +pub struct Pos { + signed: bool, + a_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for Pos { + fn eval(&mut self, signals: &mut [Signal]) { + let a = BitVec::from( + self + .a_nets + .iter() + .map(|net| signals[*net].get_value()) + .collect::>(), + ); + + // Hard code as i64 add, fix later + let y = if self.signed { + BitVec::from_int_sized(a.to_int::(), self.y_nets.len()).unwrap() + } else { + BitVec::from_int_sized(a.to_int::(), self.y_nets.len()).unwrap() + }; + + self + .y_nets + .iter() + .enumerate() + .for_each(|(i, net)| signals[*net].set_value(y.bits[i])); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.a_nets.iter().collect() + } + + fn output_connections(&self) -> Vec<&usize> { + self.y_nets.iter().collect() + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +pub struct Neg { + signed: bool, + a_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for Neg { + fn eval(&mut self, signals: &mut [Signal]) { + let a = BitVec::from( + self + .a_nets + .iter() + .map(|net| signals[*net].get_value()) + .collect::>(), + ); + + // Hard code as i64 add, fix later + let y = if self.signed { + BitVec::from_int_sized(-a.to_int::(), self.y_nets.len()).unwrap() + } else { + BitVec::from_int_sized(!(a.to_int::()) + 1, self.y_nets.len()).unwrap() + }; + + self + .y_nets + .iter() + .enumerate() + .for_each(|(i, net)| signals[*net].set_value(y.bits[i])); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.a_nets.iter().collect() + } + + fn output_connections(&self) -> Vec<&usize> { + self.y_nets.iter().collect() + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +pub struct Shl { + signed: bool, + a_nets: Box<[usize]>, + b_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for Shl { + fn eval(&mut self, signals: &mut [Signal]) { + let a = BitVec::from( + self + .a_nets + .iter() + .map(|net| signals[*net].get_value()) + .collect::>(), + ); + + let b = BitVec::from( + self + .b_nets + .iter() + .map(|net| signals[*net].get_value()) + .collect::>(), + ); + + let shift = b.to_int::(); + // Hard code as i64 add, fix later + let y = if self.signed { + BitVec::from_int_sized(a.to_int::() << shift, self.y_nets.len()).unwrap() + } else { + BitVec::from_int_sized(a.to_int::() << shift, self.y_nets.len()).unwrap() + }; + + self + .y_nets + .iter() + .enumerate() + .for_each(|(i, net)| signals[*net].set_value(y.bits[i])); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.a_nets.iter().chain(self.b_nets.iter()).collect() + } + + fn output_connections(&self) -> Vec<&usize> { + self.y_nets.iter().collect() + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} diff --git a/crates/arbolta/src/hardware_module.rs b/crates/arbolta/src/hardware_module.rs index e54bce3..2ca2d80 100644 --- a/crates/arbolta/src/hardware_module.rs +++ b/crates/arbolta/src/hardware_module.rs @@ -9,7 +9,6 @@ use indexmap::{IndexMap, IndexSet}; use ndarray::{Array1, ArrayView1}; use num_traits::PrimInt; use petgraph::graph::{Graph, NodeIndex}; -use petgraph::visit::Topo; use std::collections::HashMap; use std::fmt::Debug; use std::process::Command; @@ -18,16 +17,18 @@ use thiserror::Error; use yosys_netlist_json as yosys; pub type CellGraph = Graph; -pub type PortMap = IndexMap; -pub type SignalMap = IndexMap>; +pub type PortMap = HashMap; +pub type SignalMap = HashMap>; -#[derive(Default, Clone)] +#[derive(Default, Clone, Debug)] pub struct HardwareModule { pub name: String, pub ports: PortMap, pub signal_map: SignalMap, pub signals: Box<[Signal]>, pub cells: Box<[Cell]>, + pub clock_net: Option, // TODO: Add polarity + pub reset_net: Option, // TODO: Add polarity } #[derive(Debug, Error)] @@ -163,18 +164,22 @@ impl HardwareModule { signals[0].set_constant(Bit::Zero); signals[1].set_constant(Bit::One); - let mut search = Topo::new(&graph); + // let mut search = Topo::new(&graph); + let mut search = petgraph::visit::DfsPostOrder::new(&graph, input_node); let mut cells = vec![]; while let Some(cell_node) = search.next(&graph) { cells.push(graph[cell_node].clone()); } + cells.reverse(); Ok(Self { name: top_module.to_string(), ports, signal_map, signals: signals.into_boxed_slice(), - cells: cells.into_boxed_slice(), // search, + cells: cells.into_boxed_slice(), + clock_net: None, + reset_net: None, }) } @@ -195,6 +200,26 @@ impl HardwareModule { } } + pub fn set_clock(&mut self, net: usize) -> Result<(), ModuleError> { + match self.signals.get(net) { + Some(_) => { + self.clock_net = Some(net); + Ok(()) + } + None => Err(ModuleError::MissingNet(net)), + } + } + + pub fn set_reset(&mut self, net: usize) -> Result<(), ModuleError> { + match self.signals.get(net) { + Some(_) => { + self.reset_net = Some(net); + Ok(()) + } + None => Err(ModuleError::MissingNet(net)), + } + } + pub fn eval(&mut self) { self .cells @@ -202,6 +227,33 @@ impl HardwareModule { .for_each(|cell| cell.eval(&mut self.signals)); } + pub fn eval_clocked(&mut self) -> Result<(), ModuleError> { + let Some(clock_net) = self.clock_net else { + return Err(ModuleError::MissingSignal("clock".to_string())); + }; + + self.eval(); + self.signals[clock_net].set_value(Bit::One); + self.eval(); + self.signals[clock_net].set_value(Bit::Zero); + self.eval(); + + Ok(()) + } + + pub fn eval_reset_clocked(&mut self) -> Result<(), ModuleError> { + let Some(reset_net) = self.reset_net else { + return Err(ModuleError::MissingSignal("reset".to_string())); + }; + + self.signals[reset_net].set_value(Bit::One); + self.eval_clocked()?; + self.signals[reset_net].set_value(Bit::Zero); + self.eval(); + + Ok(()) + } + pub fn reset(&mut self) { self.cells.iter_mut().for_each(|c| c.reset()); self.signals.iter_mut().for_each(|s| s.reset()); diff --git a/crates/arbolta/tests/test_cell.rs b/crates/arbolta/tests/test_cell.rs index e101f93..d4f5cf2 100644 --- a/crates/arbolta/tests/test_cell.rs +++ b/crates/arbolta/tests/test_cell.rs @@ -2,39 +2,48 @@ // SPDX-License-Identifier: MIT use arbolta::bit::Bit; -use arbolta::cell::{create_cell, Cell, CellFn, DffPosedgeReset}; +use arbolta::cell::{create_cell, Cell, CellFn, Dff, DffPosedgeReset}; use arbolta::signal::Signal; -use once_cell::sync::Lazy; use rstest::rstest; +use std::collections::HashMap; use yosys_netlist_json as yosys; -static VARIABLE_ALPHABET: Lazy> = Lazy::new(|| { - (b'A'..=b'Z') - .map(|x| String::from_utf8(vec![x]).unwrap()) - .collect() -}); - -fn generate_cell(cell_type: &str, num_inputs: usize) -> Cell { +fn generate_cell( + cell_type: &str, + inputs: HashMap<&str, usize>, + outputs: HashMap<&str, usize>, + parameters: Option>, +) -> Cell { let mut cell = yosys::Cell::default(); cell.cell_type = cell_type.to_string(); - for net in 0..num_inputs { - let name = &VARIABLE_ALPHABET[net]; - cell - .connections - .insert(name.to_string(), vec![yosys::BitVal::N(net)]); + let mut num_nets = 0; + for (name, size) in inputs.iter() { + let bits: Vec = (0..*size).map(|i| yosys::BitVal::N(num_nets + i)).collect(); + num_nets += size; + cell.connections.insert(name.to_string(), bits); cell .port_directions .insert(name.to_string(), yosys::PortDirection::Input); } - cell - .connections - .insert("Y".to_string(), vec![yosys::BitVal::N(num_inputs)]); - cell - .port_directions - .insert("Y".to_string(), yosys::PortDirection::Output); + for (name, size) in outputs.iter() { + let bits: Vec = (0..*size).map(|i| yosys::BitVal::N(num_nets + i)).collect(); + num_nets += size; + cell.connections.insert(name.to_string(), bits); + cell + .port_directions + .insert(name.to_string(), yosys::PortDirection::Output); + } + + if let Some(parameters) = parameters { + for (param, val) in parameters.iter() { + cell + .parameters + .insert(param.to_string(), yosys::AttributeVal::S(val.to_string())); + } + } create_cell(&cell).unwrap() } @@ -45,7 +54,9 @@ fn generate_cell(cell_type: &str, num_inputs: usize) -> Cell { #[case("BUF", Bit::Zero, Bit::Zero)] #[case("BUF", Bit::One, Bit::One)] fn test_cell_1_input(#[case] cell_type: &str, #[case] a: Bit, #[case] expected: Bit) { - let mut cell = generate_cell(cell_type, 1); + let inputs = HashMap::from([("A", 1)]); + let outputs = HashMap::from([("Y", 1)]); + let mut cell = generate_cell(cell_type, inputs, outputs, None); let mut signals = vec![Signal::new_constant(a), Signal::default()].into_boxed_slice(); cell.eval(&mut signals); assert_eq!(signals[1].get_value(), expected) @@ -90,7 +101,9 @@ fn test_cell_2_input( #[case] b: Bit, #[case] expected: Bit, ) { - let mut cell = generate_cell(cell_type, 2); + let inputs = HashMap::from([("A", 1), ("B", 1)]); + let outputs = HashMap::from([("Y", 1)]); + let mut cell = generate_cell(cell_type, inputs, outputs, None); let mut signals = vec![ Signal::new_constant(a), Signal::new_constant(b), @@ -118,7 +131,9 @@ fn test_cell_3_input( #[case] c: Bit, #[case] expected: Bit, ) { - let mut cell = generate_cell(cell_type, 3); + let inputs = HashMap::from([("A", 1), ("B", 1), ("C", 1)]); + let outputs = HashMap::from([("Y", 1)]); + let mut cell = generate_cell(cell_type, inputs, outputs, None); let mut signals = vec![ Signal::new_constant(a), Signal::new_constant(b), @@ -131,6 +146,37 @@ fn test_cell_3_input( assert_eq!(signals[3].get_value(), expected); } +#[rstest] +fn test_cell_dff_p() { + // D, C, Q + let (data_in, clock, data_out) = (0, 1, 2); + let mut cell = Dff::new(Bit::One, clock, data_in, data_out); + let mut signals = vec![Signal::default(); 3].into_boxed_slice(); + + cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::Zero); + + signals[data_in].set_value(Bit::One); + cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::Zero); + + signals[clock].set_value(Bit::One); // Rising edge + cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::One); + + signals[clock].set_value(Bit::Zero); // Falling edge + cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::One); + + signals[data_in].set_value(Bit::Zero); + cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::One); + + signals[clock].set_value(Bit::One); // Rising edge + cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::Zero); +} + #[rstest] fn test_cell_sdff_pp() { // D, C, R, Q diff --git a/crates/arbolta/tests/test_module.rs b/crates/arbolta/tests/test_module.rs index 6d69a6a..f615e69 100644 --- a/crates/arbolta/tests/test_module.rs +++ b/crates/arbolta/tests/test_module.rs @@ -2,39 +2,38 @@ // SPDX-License-Identifier: MIT use arbolta::hardware_module::HardwareModule; -use arbolta::signal::Signal; -use once_cell::sync::Lazy; use rstest::rstest; +use std::collections::HashMap; use yosys_netlist_json as yosys; -static VARIABLE_ALPHABET: Lazy> = Lazy::new(|| { - (b'A'..=b'Z') - .map(|x| String::from_utf8(vec![x]).unwrap()) - .collect() -}); - -fn generate_module(cell_type: &str, num_inputs: usize) -> HardwareModule { +fn generate_module( + cell_type: &str, + inputs: HashMap<&str, usize>, + outputs: HashMap<&str, usize>, + parameters: Option>, +) -> HardwareModule { let mut module = yosys::Module::default(); let mut cell = yosys::Cell::default(); cell.cell_type = cell_type.to_string(); - let net_offset = 2; // Offset constants - for net in 0..num_inputs { - let name = &VARIABLE_ALPHABET[net]; - let bitval = yosys::BitVal::N(net + net_offset); + let mut num_nets = 2; + for (name, size) in inputs.iter() { + let bits: Vec = (0..*size).map(|i| yosys::BitVal::N(num_nets + i)).collect(); + num_nets += size; + let mut netname = yosys::Netname::default(); - netname.bits.push(bitval); + netname.bits = bits.clone(); let port = yosys::Port { direction: yosys::PortDirection::Input, - bits: vec![bitval], + bits: bits.clone(), offset: Default::default(), upto: Default::default(), signed: Default::default(), }; - cell.connections.insert(name.to_string(), port.bits.clone()); + cell.connections.insert(name.to_string(), bits); cell .port_directions .insert(name.to_string(), port.direction); @@ -42,23 +41,36 @@ fn generate_module(cell_type: &str, num_inputs: usize) -> HardwareModule { module.netnames.insert(name.to_string(), netname); } - let bitval = yosys::BitVal::N(num_inputs + net_offset); - let mut netname = yosys::Netname::default(); - netname.bits.push(bitval); + for (name, size) in outputs.iter() { + let bits: Vec = (0..*size).map(|i| yosys::BitVal::N(num_nets + i)).collect(); + num_nets += size; - let port = yosys::Port { - direction: yosys::PortDirection::Output, - bits: vec![bitval], - offset: Default::default(), - upto: Default::default(), - signed: Default::default(), - }; + let mut netname = yosys::Netname::default(); + netname.bits = bits.clone(); - cell.connections.insert("Y".to_string(), port.bits.clone()); - cell.port_directions.insert("Y".to_string(), port.direction); - module.ports.insert("Y".to_string(), port); - module.netnames.insert("Y".to_string(), netname); + let port = yosys::Port { + direction: yosys::PortDirection::Output, + bits: bits.clone(), + offset: Default::default(), + upto: Default::default(), + signed: Default::default(), + }; + + cell.connections.insert(name.to_string(), bits); + cell + .port_directions + .insert(name.to_string(), port.direction); + module.ports.insert(name.to_string(), port); + module.netnames.insert(name.to_string(), netname); + } + if let Some(parameters) = parameters { + for (param, val) in parameters.iter() { + cell + .parameters + .insert(param.to_string(), yosys::AttributeVal::S(val.to_string())); + } + } module.cells.insert(cell_type.to_string(), cell); let mut netlist = yosys::Netlist::default(); @@ -73,7 +85,9 @@ fn generate_module(cell_type: &str, num_inputs: usize) -> HardwareModule { #[case("BUF", 0, 0)] #[case("BUF", 1, 1)] fn test_module_1_input_cell(#[case] cell_type: &str, #[case] a: u8, #[case] expected: u8) { - let mut cell_module = generate_module(cell_type, 1); + let inputs = HashMap::from([("A", 1)]); + let outputs = HashMap::from([("Y", 1)]); + let mut cell_module = generate_module(cell_type, inputs, outputs, None); cell_module.set_port_int("A", a).unwrap(); cell_module.eval(); @@ -112,7 +126,9 @@ fn test_module_2_input_cell( #[case] b: u8, #[case] expected: u8, ) { - let mut cell_module = generate_module(cell_type, 2); + let inputs = HashMap::from([("A", 1), ("B", 1)]); + let outputs = HashMap::from([("Y", 1)]); + let mut cell_module = generate_module(cell_type, inputs, outputs, None); cell_module.set_port_int("A", a).unwrap(); cell_module.set_port_int("B", b).unwrap(); cell_module.eval(); @@ -137,7 +153,9 @@ fn test_module_3_input_cell( #[case] c: u8, #[case] expected: u8, ) { - let mut cell_module = generate_module(cell_type, 3); + let inputs = HashMap::from([("A", 1), ("B", 1), ("C", 1)]); + let outputs = HashMap::from([("Y", 1)]); + let mut cell_module = generate_module(cell_type, inputs, outputs, None); cell_module.set_port_int("A", a).unwrap(); cell_module.set_port_int("B", b).unwrap(); cell_module.set_port_int("C", c).unwrap(); @@ -147,26 +165,110 @@ fn test_module_3_input_cell( assert_eq!(actual, expected); } -// #[rstest] -// #[case(Function::DffPosEdge, 0, 0)] -// #[case(Function::DffPosEdge, 1, 1)] -// fn test_module_1_input_cell_clocked( -// #[case] function: Function, -// #[case] a: u8, -// #[case] expected: u8, -// ) { -// let mut cell_module = cell_module_from_function(function, 2); - -// cell_module.set_port_int("a", 0).unwrap(); // clock -// cell_module.set_port_int("b", a).unwrap(); -// cell_module.eval(); - -// cell_module.set_port_int("a", 1).unwrap(); -// cell_module.eval(); - -// cell_module.set_port_int("a", 0).unwrap(); -// cell_module.eval(); - -// let actual: u8 = cell_module.get_port_int("Y").unwrap(); -// assert_eq!(actual, expected); -// } +#[rstest] +#[case(false, 2, 3)] +#[case(true, -45, 20)] +#[case(false, 0, 0)] +#[case(true, 0, 0)] +#[case(true, -1, 1)] +fn test_module_add(#[case] signed: bool, #[case] a: i32, #[case] b: i32) { + let signed_param = if signed { "1" } else { "0" }; + let params = HashMap::from([("A_SIGNED", signed_param)]); + let inputs = HashMap::from([("A", 8), ("B", 8)]); + let outputs = HashMap::from([("Y", 8)]); + + let mut cell_module = generate_module("$add", inputs, outputs, Some(params)); + cell_module.set_port_int("A", a).unwrap(); + cell_module.set_port_int("B", b).unwrap(); + cell_module.eval(); + + let actual: i32 = cell_module.get_port_int("Y").unwrap(); + let expected = a + b; + assert_eq!(actual, expected); +} + +#[rstest] +#[case(true, 2, 3)] +#[case(true, -45, 20)] +#[case(false, 0, 0)] +#[case(true, 0, 0)] +#[case(true, -1, 1)] +fn test_module_sub(#[case] signed: bool, #[case] a: i32, #[case] b: i32) { + let signed_param = if signed { "1" } else { "0" }; + let params = HashMap::from([("A_SIGNED", signed_param)]); + let inputs = HashMap::from([("A", 8), ("B", 8)]); + let outputs = HashMap::from([("Y", 8)]); + + let mut cell_module = generate_module("$sub", inputs, outputs, Some(params)); + cell_module.set_port_int("A", a).unwrap(); + cell_module.set_port_int("B", b).unwrap(); + cell_module.eval(); + + let actual: i32 = cell_module.get_port_int("Y").unwrap(); + let expected = a - b; + assert_eq!(actual, expected); +} + +#[rstest] +#[case(true, 2, 3)] +#[case(true, -45, 20)] +#[case(false, 0, 0)] +#[case(true, 0, 0)] +#[case(true, -1, 1)] +fn test_module_mul(#[case] signed: bool, #[case] a: i32, #[case] b: i32) { + let signed_param = if signed { "1" } else { "0" }; + let params = HashMap::from([("A_SIGNED", signed_param), ("B_SIGNED", signed_param)]); + let inputs = HashMap::from([("A", 16), ("B", 16)]); + let outputs = HashMap::from([("Y", 16)]); + + let mut cell_module = generate_module("$mul", inputs, outputs, Some(params)); + cell_module.set_port_int("A", a).unwrap(); + cell_module.set_port_int("B", b).unwrap(); + cell_module.eval(); + + let actual: i32 = cell_module.get_port_int("Y").unwrap(); + let expected = a * b; + assert_eq!(actual, expected); +} + +#[rstest] +#[case(true, 2, 3)] +#[case(true, -45, 1)] +#[case(false, 0, 0)] +#[case(true, 0, 0)] +#[case(true, -1, 1)] +fn test_module_shl(#[case] signed: bool, #[case] a: i16, #[case] b: i16) { + let signed_param = if signed { "1" } else { "0" }; + let params = HashMap::from([("A_SIGNED", signed_param), ("B_SIGNED", signed_param)]); + let inputs = HashMap::from([("A", 16), ("B", 16)]); + let outputs = HashMap::from([("Y", 16)]); + + let mut cell_module = generate_module("$shl", inputs, outputs, Some(params)); + cell_module.set_port_int("A", a).unwrap(); + cell_module.set_port_int("B", b).unwrap(); + cell_module.eval(); + + let actual: i16 = cell_module.get_port_int("Y").unwrap(); + let expected = a << b; + assert_eq!(actual, expected); +} + +#[rstest] +#[case(true, 2)] +#[case(true, -45)] +#[case(true, 0)] +#[case(true, -1)] +fn test_module_neg(#[case] signed: bool, #[case] a: i16) { + let signed_param = if signed { "1" } else { "0" }; + let params = HashMap::from([("A_SIGNED", signed_param)]); + let inputs = HashMap::from([("A", 16)]); + let outputs = HashMap::from([("Y", 16)]); + + let mut cell_module = generate_module("$neg", inputs, outputs, Some(params)); + cell_module.set_port_int("A", a).unwrap(); + cell_module.eval(); + + let actual: i16 = cell_module.get_port_int("Y").unwrap(); + let expected = -a; + assert_eq!(actual, expected); +} diff --git a/crates/python_bindings/py_src/arbolta/design.py b/crates/python_bindings/py_src/arbolta/design.py index df79b14..21b9324 100644 --- a/crates/python_bindings/py_src/arbolta/design.py +++ b/crates/python_bindings/py_src/arbolta/design.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: MIT from dataclasses import dataclass -from typing import Any, Dict, List, Optional, Tuple, TypedDict +from typing import Any, Dict, List, Optional, Tuple, TypedDict, Union import numpy as np @@ -244,6 +244,12 @@ def module_names(self) -> List[str]: """ return self.design.get_module_names() + def signal_map(self) -> Dict[str, List[int]]: + return self.design.get_signal_map() + + def stick_signal(self, net: int, val: Union[int, bool]) -> None: + return self.design.stick_signal(net, bool(val)) + def save(file: str, design: HardwareDesign) -> None: assert RuntimeError("Unimplemented") diff --git a/crates/python_bindings/src/design.rs b/crates/python_bindings/src/design.rs index 9a67a88..36a4b80 100644 --- a/crates/python_bindings/src/design.rs +++ b/crates/python_bindings/src/design.rs @@ -85,6 +85,22 @@ impl PyDesign { todo!() } + fn get_signal_map(&self) -> HashMap> { + self + .module + .signal_map + .iter() + .map(|(name, nets)| (name.to_string(), nets.to_vec())) + .collect() + } + + pub fn stick_signal(&mut self, net: usize, val: bool) -> PyResult<()> { + match self.module.stick_signal(net, val.into()) { + Ok(()) => Ok(()), + Err(err) => Err(PyException::new_err(format!("{err}"))), + } + } + #[allow(unused_variables)] fn set_clock(&mut self, name: &str) -> PyResult<()> { todo!() From 751e12154e16c5461defc9dddf4c09dd00680189 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Fri, 11 Apr 2025 21:49:09 +0000 Subject: [PATCH 03/64] Adding more cells. Need to do significant cleaning --- crates/arbolta/src/cell.rs | 99 +++++++++++- crates/arbolta/src/hardware_module.rs | 151 ++++++++++++++---- crates/arbolta/tests/test_module.rs | 125 +++++++++------ .../python_bindings/py_src/arbolta/design.py | 59 ++++--- crates/python_bindings/src/design.rs | 96 +++++++++-- 5 files changed, 414 insertions(+), 116 deletions(-) diff --git a/crates/arbolta/src/cell.rs b/crates/arbolta/src/cell.rs index 3839eac..c01c1ab 100644 --- a/crates/arbolta/src/cell.rs +++ b/crates/arbolta/src/cell.rs @@ -79,11 +79,11 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { } let new_cell: Cell = match cell.cell_type.as_str() { - "BUF" => Box::new(Buf::new( + "BUF" | "$_BUF_" => Box::new(Buf::new( input_connections["A"][0], output_connections["Y"][0], )), - "NOT" | "$_NOT_" => Box::new(Inverter::new( + "NOT" | "$_NOT_" | "$not" => Box::new(Inverter::new( input_connections["A"][0], output_connections["Y"][0], )), @@ -215,6 +215,17 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { input_connections["B"].clone().into_boxed_slice(), output_connections["Y"].clone().into_boxed_slice(), )), + "$logic_and" => Box::new(LogicAnd::new( + // Hacky, fix later + input_connections["A"].clone().into_boxed_slice(), + input_connections["B"].clone().into_boxed_slice(), + output_connections["Y"].clone().into_boxed_slice(), + )), + "$logic_not" => Box::new(LogicNot::new( + // Hacky, fix later + input_connections["A"].clone().into_boxed_slice(), + output_connections["Y"].clone().into_boxed_slice(), + )), _ => return Err(CellError::Unsupported(cell.cell_type.to_string())), }; @@ -1086,3 +1097,87 @@ impl CellFn for Shl { Box::new(self.clone()) } } + +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +pub struct LogicAnd { + a_nets: Box<[usize]>, + b_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for LogicAnd { + fn eval(&mut self, signals: &mut [Signal]) { + let a = BitVec::from( + self + .a_nets + .iter() + .map(|net| signals[*net].get_value()) + .collect::>(), + ); + + let b = BitVec::from( + self + .b_nets + .iter() + .map(|net| signals[*net].get_value()) + .collect::>(), + ); + + // Hard code as u64 add, fix later + // Assume only need to set LSB + let a = Bit::from(a.to_int::() != 0); + let b = Bit::from(b.to_int::() != 0); + signals[self.y_nets[0]].set_value(a & b); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.a_nets.iter().chain(self.b_nets.iter()).collect() + } + + fn output_connections(&self) -> Vec<&usize> { + self.y_nets.iter().collect() + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} + +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +pub struct LogicNot { + a_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for LogicNot { + fn eval(&mut self, signals: &mut [Signal]) { + let a = BitVec::from( + self + .a_nets + .iter() + .map(|net| signals[*net].get_value()) + .collect::>(), + ); + + // Hard code as u64 add, fix later + // Assume only need to set LSB + let a = Bit::from(a.to_int::() != 0); + signals[self.y_nets[0]].set_value(!a); + } + + fn reset(&mut self) {} + + fn input_connections(&self) -> Vec<&usize> { + self.a_nets.iter().collect() + } + + fn output_connections(&self) -> Vec<&usize> { + self.y_nets.iter().collect() + } + + fn clone_box(&self) -> Cell { + Box::new(self.clone()) + } +} diff --git a/crates/arbolta/src/hardware_module.rs b/crates/arbolta/src/hardware_module.rs index 2ca2d80..04f0364 100644 --- a/crates/arbolta/src/hardware_module.rs +++ b/crates/arbolta/src/hardware_module.rs @@ -11,6 +11,7 @@ use num_traits::PrimInt; use petgraph::graph::{Graph, NodeIndex}; use std::collections::HashMap; use std::fmt::Debug; +use std::io::{Read, Write}; use std::process::Command; use tempfile::NamedTempFile; use thiserror::Error; @@ -27,8 +28,8 @@ pub struct HardwareModule { pub signal_map: SignalMap, pub signals: Box<[Signal]>, pub cells: Box<[Cell]>, - pub clock_net: Option, // TODO: Add polarity - pub reset_net: Option, // TODO: Add polarity + pub clock_net: Option<(usize, Bit)>, // TODO: Add polarity + pub reset_net: Option<(usize, Bit)>, // TODO: Add polarity } #[derive(Debug, Error)] @@ -50,18 +51,38 @@ pub enum ModuleError { } impl HardwareModule { + pub fn new_from_str(raw_netlist: &str, top_module: &str) -> Result { + let mut temp_netlist = match NamedTempFile::new() { + Ok(file) => file, + Err(err) => return Err(ModuleError::Netlist(err.to_string())), + }; + + // TODO: Better error handling + let _ = temp_netlist.write(raw_netlist.as_bytes()).unwrap(); + + Self::new_from_path(temp_netlist.path().to_str().unwrap(), top_module) + } + pub fn new_from_path(netlist_path: &str, top_module: &str) -> Result { let temp_flattened = match NamedTempFile::new() { Ok(file) => file, Err(err) => return Err(ModuleError::Netlist(err.to_string())), }; + let mut temp_torder = match NamedTempFile::new() { + Ok(file) => file, + Err(err) => return Err(ModuleError::Netlist(err.to_string())), + }; + let yosys_output = Command::new("yosys") + .arg("-f") + .arg("json") .arg(netlist_path) .arg("-p") .arg(format!( - "flatten; write_json {}", - temp_flattened.path().display() + "flatten; write_json {}; tee -o {} torder", + temp_flattened.path().display(), + temp_torder.path().display() )) .output(); @@ -74,10 +95,29 @@ impl HardwareModule { Err(err) => Err(ModuleError::Netlist(err.to_string())), }?; - Self::new(netlist, top_module) + let mut topo_order = String::new(); + if let Err(err) = temp_torder.read_to_string(&mut topo_order) { + return Err(ModuleError::Netlist(err.to_string())); + } + + let mut clean_topo_order = vec![]; + + // Don't need these lines + for line in topo_order.lines().skip(3) { + let split: Vec<&str> = line.split_whitespace().collect(); + if split[0] == "cell" { + clean_topo_order.push(split[1]); + } + } + + Self::new(netlist, &clean_topo_order, top_module) } - pub fn new(netlist: yosys::Netlist, top_module: &str) -> Result { + pub fn new( + netlist: yosys::Netlist, + topo_order: &[&str], + top_module: &str, + ) -> Result { // Design must be flattened let Some(synth_module) = netlist.modules.get(top_module) else { return Err(CellError::Unsupported(top_module.to_string()).into()); @@ -145,8 +185,8 @@ impl HardwareModule { yosys::BitVal::S(constant) => match constant { yosys::SpecialBit::_0 => 0, // Global 0 yosys::SpecialBit::_1 => 1, // Global 1 - yosys::SpecialBit::X => todo!("X not supported."), - yosys::SpecialBit::Z => todo!("Z not supported."), + yosys::SpecialBit::X => todo!("X bit not supported."), + yosys::SpecialBit::Z => todo!("Z bit not supported."), }, }; max_signal = max_signal.max(net); @@ -159,17 +199,40 @@ impl HardwareModule { for (name, port) in &synth_module.ports { ports.insert(name.clone(), Port::new(port)); } + // Temp? + for (name, netname) in &synth_module.netnames { + if ports.contains_key(name) { + continue; + } + + let port = yosys::Port { + direction: yosys::PortDirection::Output, + bits: netname.bits.clone(), + offset: Default::default(), + upto: Default::default(), + signed: Default::default(), + }; + + ports.insert(name.clone(), Port::new(&port)); + } let mut signals = vec![Signal::default(); max_signal + 1]; signals[0].set_constant(Bit::Zero); signals[1].set_constant(Bit::One); - // let mut search = Topo::new(&graph); - let mut search = petgraph::visit::DfsPostOrder::new(&graph, input_node); + // TODO: Remove? + // let mut search = petgraph::visit::DfsPostOrder::new(&graph, input_node); let mut cells = vec![]; - while let Some(cell_node) = search.next(&graph) { - cells.push(graph[cell_node].clone()); + // while let Some(cell_node) = search.next(&graph) { + // cells.push(graph[cell_node].clone()); + // } + + for cell_name in topo_order { + let idx = node_map[cell_name]; + let node = graph[idx].clone(); + cells.push(node); } + cells.reverse(); Ok(Self { @@ -200,55 +263,79 @@ impl HardwareModule { } } - pub fn set_clock(&mut self, net: usize) -> Result<(), ModuleError> { + pub fn set_clock(&mut self, net: usize, polarity: Bit) -> Result<(), ModuleError> { match self.signals.get(net) { Some(_) => { - self.clock_net = Some(net); + self.clock_net = Some((net, polarity)); Ok(()) } None => Err(ModuleError::MissingNet(net)), } } - pub fn set_reset(&mut self, net: usize) -> Result<(), ModuleError> { + pub fn set_reset(&mut self, net: usize, polarity: Bit) -> Result<(), ModuleError> { match self.signals.get(net) { Some(_) => { - self.reset_net = Some(net); + self.reset_net = Some((net, polarity)); Ok(()) } None => Err(ModuleError::MissingNet(net)), } } + // Eval until all signals have settled + // TODO: Make this more efficient pub fn eval(&mut self) { - self - .cells - .iter_mut() - .for_each(|cell| cell.eval(&mut self.signals)); + loop { + let before = self + .signals + .iter() + .map(|s| s.get_value()) + .collect::>(); + + self + .cells + .iter_mut() + .for_each(|cell| cell.eval(&mut self.signals)); + + let after = self + .signals + .iter() + .map(|s| s.get_value()) + .collect::>(); + + if before == after { + break; + } + } } - pub fn eval_clocked(&mut self) -> Result<(), ModuleError> { - let Some(clock_net) = self.clock_net else { + pub fn eval_clocked(&mut self, cycles: Option) -> Result<(), ModuleError> { + let Some((clock_net, polarity)) = self.clock_net else { return Err(ModuleError::MissingSignal("clock".to_string())); }; - self.eval(); - self.signals[clock_net].set_value(Bit::One); - self.eval(); - self.signals[clock_net].set_value(Bit::Zero); - self.eval(); + let cycles = cycles.unwrap_or(1); + + for _ in 0..cycles { + self.eval(); + self.signals[clock_net].set_value(polarity); + self.eval(); + self.signals[clock_net].set_value(!polarity); + self.eval(); + } Ok(()) } - pub fn eval_reset_clocked(&mut self) -> Result<(), ModuleError> { - let Some(reset_net) = self.reset_net else { + pub fn eval_reset_clocked(&mut self, cycles: Option) -> Result<(), ModuleError> { + let Some((reset_net, polarity)) = self.reset_net else { return Err(ModuleError::MissingSignal("reset".to_string())); }; - self.signals[reset_net].set_value(Bit::One); - self.eval_clocked()?; - self.signals[reset_net].set_value(Bit::Zero); + self.signals[reset_net].set_value(polarity); + self.eval_clocked(cycles)?; + self.signals[reset_net].set_value(!polarity); self.eval(); Ok(()) diff --git a/crates/arbolta/tests/test_module.rs b/crates/arbolta/tests/test_module.rs index f615e69..fe2651a 100644 --- a/crates/arbolta/tests/test_module.rs +++ b/crates/arbolta/tests/test_module.rs @@ -74,16 +74,17 @@ fn generate_module( module.cells.insert(cell_type.to_string(), cell); let mut netlist = yosys::Netlist::default(); - netlist.modules.insert(cell_type.to_string(), module); + netlist.modules.insert("test".to_string(), module); + let raw_netlist = netlist.to_string().unwrap(); - HardwareModule::new(netlist, cell_type).unwrap() + HardwareModule::new_from_str(&raw_netlist, "test").unwrap() } #[rstest] -#[case("NOT", 0, 1)] -#[case("NOT", 1, 0)] -#[case("BUF", 0, 0)] -#[case("BUF", 1, 1)] +#[case("$_NOT_", 0, 1)] +#[case("$_NOT_", 1, 0)] +#[case("$_BUF_", 0, 0)] +#[case("$_BUF_", 1, 1)] fn test_module_1_input_cell(#[case] cell_type: &str, #[case] a: u8, #[case] expected: u8) { let inputs = HashMap::from([("A", 1)]); let outputs = HashMap::from([("Y", 1)]); @@ -96,30 +97,30 @@ fn test_module_1_input_cell(#[case] cell_type: &str, #[case] a: u8, #[case] expe } #[rstest] -#[case("AND", 0, 0, 0)] -#[case("AND", 0, 1, 0)] -#[case("AND", 1, 0, 0)] -#[case("AND", 1, 1, 1)] -#[case("NOR", 0, 0, 1)] -#[case("NOR", 0, 1, 0)] -#[case("NOR", 1, 0, 0)] -#[case("NOR", 1, 1, 0)] -#[case("NAND", 0, 0, 1)] -#[case("NAND", 0, 1, 1)] -#[case("NAND", 1, 0, 1)] -#[case("NAND", 1, 1, 0)] -#[case("OR", 0, 0, 0)] -#[case("OR", 0, 1, 1)] -#[case("OR", 1, 0, 1)] -#[case("OR", 1, 1, 1)] -#[case("XOR", 0, 0, 0)] -#[case("XOR", 0, 1, 1)] -#[case("XOR", 1, 0, 1)] -#[case("XOR", 1, 1, 0)] -#[case("XNOR", 0, 0, 1)] -#[case("XNOR", 0, 1, 0)] -#[case("XNOR", 1, 0, 0)] -#[case("XNOR", 1, 1, 1)] +#[case("$_AND_", 0, 0, 0)] +#[case("$_AND_", 0, 1, 0)] +#[case("$_AND_", 1, 0, 0)] +#[case("$_AND_", 1, 1, 1)] +#[case("$_NOR_", 0, 0, 1)] +#[case("$_NOR_", 0, 1, 0)] +#[case("$_NOR_", 1, 0, 0)] +#[case("$_NOR_", 1, 1, 0)] +#[case("$_NAND_", 0, 0, 1)] +#[case("$_NAND_", 0, 1, 1)] +#[case("$_NAND_", 1, 0, 1)] +#[case("$_NAND_", 1, 1, 0)] +#[case("$_OR_", 0, 0, 0)] +#[case("$_OR_", 0, 1, 1)] +#[case("$_OR_", 1, 0, 1)] +#[case("$_OR_", 1, 1, 1)] +#[case("$_XOR_", 0, 0, 0)] +#[case("$_XOR_", 0, 1, 1)] +#[case("$_XOR_", 1, 0, 1)] +#[case("$_XOR_", 1, 1, 0)] +#[case("$_XNOR_", 0, 0, 1)] +#[case("$_XNOR_", 0, 1, 0)] +#[case("$_XNOR_", 1, 0, 0)] +#[case("$_XNOR_", 1, 1, 1)] fn test_module_2_input_cell( #[case] cell_type: &str, #[case] a: u8, @@ -138,14 +139,14 @@ fn test_module_2_input_cell( } #[rstest] -#[case("OR", 0, 0, 0, 0)] -#[case("OR", 0, 0, 1, 1)] -#[case("OR", 0, 1, 0, 1)] -#[case("OR", 0, 1, 1, 1)] -#[case("OR", 1, 0, 0, 1)] -#[case("OR", 1, 0, 1, 1)] -#[case("OR", 1, 1, 0, 1)] -#[case("OR", 1, 1, 1, 1)] +#[case("$reduce_or", 0, 0, 0, 0)] +#[case("$reduce_or", 0, 0, 1, 1)] +#[case("$reduce_or", 0, 1, 0, 1)] +#[case("$reduce_or", 0, 1, 1, 1)] +#[case("$reduce_or", 1, 0, 0, 1)] +#[case("$reduce_or", 1, 0, 1, 1)] +#[case("$reduce_or", 1, 1, 0, 1)] +#[case("$reduce_or", 1, 1, 1, 1)] fn test_module_3_input_cell( #[case] cell_type: &str, #[case] a: u8, @@ -153,12 +154,12 @@ fn test_module_3_input_cell( #[case] c: u8, #[case] expected: u8, ) { - let inputs = HashMap::from([("A", 1), ("B", 1), ("C", 1)]); + let inputs = HashMap::from([("A", 3)]); let outputs = HashMap::from([("Y", 1)]); - let mut cell_module = generate_module(cell_type, inputs, outputs, None); - cell_module.set_port_int("A", a).unwrap(); - cell_module.set_port_int("B", b).unwrap(); - cell_module.set_port_int("C", c).unwrap(); + let params = HashMap::from([("A_SIGNED", "0"), ("A_WIDTH", "11"), ("Y_WIDTH", "1")]); + let mut cell_module = generate_module(cell_type, inputs, outputs, Some(params)); + cell_module.set_port_shape("A", &[3, 1]).unwrap(); + cell_module.set_port_int_vec("A", &[a, b, c]).unwrap(); cell_module.eval(); let actual: u8 = cell_module.get_port_int("Y").unwrap(); @@ -173,9 +174,15 @@ fn test_module_3_input_cell( #[case(true, -1, 1)] fn test_module_add(#[case] signed: bool, #[case] a: i32, #[case] b: i32) { let signed_param = if signed { "1" } else { "0" }; - let params = HashMap::from([("A_SIGNED", signed_param)]); let inputs = HashMap::from([("A", 8), ("B", 8)]); let outputs = HashMap::from([("Y", 8)]); + let params = HashMap::from([ + ("A_SIGNED", signed_param), + ("B_SIGNED", signed_param), + ("A_WIDTH", "1000"), + ("B_WIDTH", "1000"), + ("Y_WIDTH", "1000"), + ]); let mut cell_module = generate_module("$add", inputs, outputs, Some(params)); cell_module.set_port_int("A", a).unwrap(); @@ -195,7 +202,13 @@ fn test_module_add(#[case] signed: bool, #[case] a: i32, #[case] b: i32) { #[case(true, -1, 1)] fn test_module_sub(#[case] signed: bool, #[case] a: i32, #[case] b: i32) { let signed_param = if signed { "1" } else { "0" }; - let params = HashMap::from([("A_SIGNED", signed_param)]); + let params = HashMap::from([ + ("A_SIGNED", signed_param), + ("B_SIGNED", signed_param), + ("A_WIDTH", "1000"), + ("B_WIDTH", "1000"), + ("Y_WIDTH", "1000"), + ]); let inputs = HashMap::from([("A", 8), ("B", 8)]); let outputs = HashMap::from([("Y", 8)]); @@ -217,7 +230,13 @@ fn test_module_sub(#[case] signed: bool, #[case] a: i32, #[case] b: i32) { #[case(true, -1, 1)] fn test_module_mul(#[case] signed: bool, #[case] a: i32, #[case] b: i32) { let signed_param = if signed { "1" } else { "0" }; - let params = HashMap::from([("A_SIGNED", signed_param), ("B_SIGNED", signed_param)]); + let params = HashMap::from([ + ("A_SIGNED", signed_param), + ("B_SIGNED", signed_param), + ("A_WIDTH", "10000"), + ("B_WIDTH", "10000"), + ("Y_WIDTH", "10000"), + ]); let inputs = HashMap::from([("A", 16), ("B", 16)]); let outputs = HashMap::from([("Y", 16)]); @@ -239,7 +258,13 @@ fn test_module_mul(#[case] signed: bool, #[case] a: i32, #[case] b: i32) { #[case(true, -1, 1)] fn test_module_shl(#[case] signed: bool, #[case] a: i16, #[case] b: i16) { let signed_param = if signed { "1" } else { "0" }; - let params = HashMap::from([("A_SIGNED", signed_param), ("B_SIGNED", signed_param)]); + let params = HashMap::from([ + ("A_SIGNED", signed_param), + ("B_SIGNED", "0"), + ("A_WIDTH", "10000"), + ("B_WIDTH", "10000"), + ("Y_WIDTH", "10000"), + ]); let inputs = HashMap::from([("A", 16), ("B", 16)]); let outputs = HashMap::from([("Y", 16)]); @@ -260,7 +285,11 @@ fn test_module_shl(#[case] signed: bool, #[case] a: i16, #[case] b: i16) { #[case(true, -1)] fn test_module_neg(#[case] signed: bool, #[case] a: i16) { let signed_param = if signed { "1" } else { "0" }; - let params = HashMap::from([("A_SIGNED", signed_param)]); + let params = HashMap::from([ + ("A_SIGNED", signed_param), + ("A_WIDTH", "10000"), + ("Y_WIDTH", "10000"), + ]); let inputs = HashMap::from([("A", 16)]); let outputs = HashMap::from([("Y", 16)]); diff --git a/crates/python_bindings/py_src/arbolta/design.py b/crates/python_bindings/py_src/arbolta/design.py index 21b9324..e8e64b8 100644 --- a/crates/python_bindings/py_src/arbolta/design.py +++ b/crates/python_bindings/py_src/arbolta/design.py @@ -26,11 +26,14 @@ class PortConfig: Port is a clock signal. reset : bool, optional Port is a reset signal. + polarity: bool, optional + Clock polarity of port. """ shape: Tuple[int, int] = (1, 1) dtype: np.dtype = np.uint32 clock: bool = False reset: bool = False + polarity: int = 1 class DesignConfig(TypedDict): @@ -48,11 +51,17 @@ class DesignConfig(TypedDict): config: PortConfig +@dataclass +class Port: + data: np.ndarray + updated: bool = False + + class HardwarePorts: def __init__(self, config: DesignConfig, design: Design): - _ports: Dict[str, np.ndarray] = {} + _ports: Dict[str, Port] = {} port_name: str port_config: PortConfig @@ -61,20 +70,20 @@ def __init__(self, config: DesignConfig, design: Design): raise AttributeError( f"Port `{port_name}` cannot be a reset and clock") if port_config.reset: - design.set_reset(port_name) + design.set_reset(port_name, bool(port_config.polarity)) if port_config.clock: - design.set_clock(port_name) + design.set_clock(port_name, bool(port_config.polarity)) design.set_port_shape(port_name, port_config.shape) - _ports[port_name] = np.zeros(port_config.shape[1], - dtype=port_config.dtype) + _ports[port_name] = Port( + np.zeros(port_config.shape[1], dtype=port_config.dtype)) super().__setattr__('_ports', _ports) super().__setattr__('_design', design) def __getattr__(self, name: str) -> Any: if '_ports' in self.__dict__ and '_design' in self.__dict__: - _ports = self.__dict__['_ports'] + _ports: dict[str, Port] = self.__dict__['_ports'] if name not in _ports: raise AttributeError(f"Port `{name}` does not exist") @@ -82,21 +91,22 @@ def __getattr__(self, name: str) -> Any: _design = self.__dict__['_design'] if not _design.is_port_input(name): - _design.get_port_numpy(name, _ports[name]) + _design.get_port_numpy(name, _ports[name].data) - return _ports[name] + return _ports[name].data else: raise AttributeError("Ports not initialized") def __setattr__(self, name: str, value: Any) -> None: if '_ports' in self.__dict__ and '_design' in self.__dict__: - _ports = self.__dict__['_ports'] + _ports: dict[str, Port] = self.__dict__['_ports'] if name not in _ports: raise AttributeError(f"Port `{name}` does not exist") - np.copyto(_ports[name], value) + np.copyto(_ports[name].data, value) + _ports[name].updated = True else: raise AttributeError("Ports not initialized") @@ -127,7 +137,7 @@ def reset(self): """ self.design.reset() - def reset_clocked(self): + def eval_reset_clocked(self, cycles: Optional[int] = 1): """ Asserts reset signal and clocks design for 1 cycle. @@ -135,19 +145,22 @@ def reset_clocked(self): ------ AttributeError: No reset and/or clock signal configured. """ - self.design.reset_clocked() + self.design.eval_reset_clocked(cycles) def eval(self): """ Evaluates all cells in design. """ - for port_name, port_array in self.ports._ports.items(): - if self.design.is_port_input(port_name): - self.design.set_port_numpy(port_name, port_array) + port: Port + for port_name, port in self.ports._ports.items(): + if port.updated: + self.design.set_port_numpy(port_name, port.data) + port.updated = False + # if self.design.is_port_input(port_name): self.design.eval() - def eval_clocked(self): + def eval_clocked(self, cycles: Optional[int] = 1): """ Clocks and evaluates design for 1 cycle. @@ -155,11 +168,15 @@ def eval_clocked(self): ------ AttributeError: No clock signal configured. """ - for port_name, port_array in self.ports._ports.items(): - if self.design.is_port_input(port_name): - self.design.set_port_numpy(port_name, port_array) - - self.design.eval_clocked() + port: Port + for port_name, port in self.ports._ports.items(): + # if self.design.is_port_input(port_name): + if port.updated: + self.design.set_port_numpy(port_name, port.data) + port.updated = False + # self.design.set_port_numpy(port_name, port_array) + + self.design.eval_clocked(cycles) def cell_breakdown(self, module_name: Optional[str] = None) -> Dict[str, int]: diff --git a/crates/python_bindings/src/design.rs b/crates/python_bindings/src/design.rs index 36a4b80..8814ac7 100644 --- a/crates/python_bindings/src/design.rs +++ b/crates/python_bindings/src/design.rs @@ -102,31 +102,101 @@ impl PyDesign { } #[allow(unused_variables)] - fn set_clock(&mut self, name: &str) -> PyResult<()> { - todo!() + fn set_clock(&mut self, name: &str, polarity: bool) -> PyResult<()> { + let Some(nets) = self.module.signal_map.get(name) else { + return Err(PyException::new_err(format!("No signal `{name}`"))); + }; + + if nets.len() != 1 { + return Err(PyException::new_err("Clock net ambiguous".to_string())); + } + + self.module.set_clock(nets[0], polarity.into()).unwrap(); + + Ok(()) } - #[allow(unused_variables)] - fn set_reset(&mut self, name: &str) -> PyResult<()> { - todo!() + fn set_reset(&mut self, name: &str, polarity: bool) -> PyResult<()> { + let Some(nets) = self.module.signal_map.get(name) else { + return Err(PyException::new_err(format!("No signal `{name}`"))); + }; + + if nets.len() != 1 { + return Err(PyException::new_err("Reset net ambiguous".to_string())); + } + + self.module.set_reset(nets[0], polarity.into()).unwrap(); + + Ok(()) } fn reset(&mut self) { self.module.reset(); } - #[allow(unused_variables)] - fn reset_clocked(&mut self) -> PyResult<()> { - todo!() - } - fn eval(&mut self) { self.module.eval(); + // println!("{:?}", self.module.clock_net); + // println!("{:?}", self.module.reset_net); + // println!( + // "clk: {:#>08x}", + // self.module.get_port_int::("clk").unwrap() + // ); + // println!( + // "rstn: {:#>08x}", + // self.module.get_port_int::("rstn").unwrap() + // ); + // println!( + // "s_valid: {:#>08x}", + // self.module.get_port_int::("s_valid").unwrap() + // ); + // println!( + // "s_last: {:#>08x}", + // self.module.get_port_int::("s_last").unwrap() + // ); + // println!( + // "m_ready: {:#>08x}", + // self.module.get_port_int::("m_ready").unwrap() + // ); + // println!( + // "s_ready: {:#>08x}", + // self.module.get_port_int::("s_ready").unwrap() + // ); + // println!( + // "m_valid: {:#>08x}", + // self.module.get_port_int::("m_valid").unwrap() + // ); + // println!( + // "m_last: {:#>08x}", + // self.module.get_port_int::("m_last").unwrap() + // ); + // println!( + // "sx_data: {:#>08x}", + // self.module.get_port_int::("sx_data").unwrap() + // ); + // println!( + // "sk_data: {:#>08x}", + // self.module.get_port_int::("sk_data").unwrap() + // ); + // println!( + // "m_data: {:#>08x}", + // self.module.get_port_int::("m_data").unwrap() + // ); } - #[allow(unused_variables)] - fn eval_clocked(&mut self) -> PyResult<()> { - todo!() + // TODO: Fix this + fn eval_clocked(&mut self, cycles: u32) -> PyResult<()> { + match self.module.eval_clocked(Some(cycles)) { + Ok(()) => Ok(()), + Err(err) => Err(PyException::new_err(format!("{err}"))), + } + } + + fn eval_reset_clocked(&mut self, cycles: u32) -> PyResult<()> { + match self.module.eval_reset_clocked(Some(cycles)) { + Ok(()) => Ok(()), + Err(err) => Err(PyException::new_err(format!("{err}"))), + } } #[allow(unused_variables)] From c8d0fec2537aaaaa5974efc3facd62063911df72 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Tue, 15 Apr 2025 01:55:33 +0000 Subject: [PATCH 04/64] Using dynamic enum dispatch and fixed pickling for multiprocessing --- crates/arbolta/Cargo.toml | 4 +- crates/arbolta/src/bit.rs | 3 +- crates/arbolta/src/cell.rs | 473 ++++-------------- crates/arbolta/src/hardware_module.rs | 123 ++--- crates/arbolta/src/port.rs | 5 +- crates/arbolta/src/signal.rs | 7 +- crates/arbolta/tests/test_cell.rs | 4 +- crates/python_bindings/Cargo.toml | 2 +- .../python_bindings/py_src/arbolta/design.py | 6 + crates/python_bindings/src/design.rs | 108 ++-- 10 files changed, 226 insertions(+), 509 deletions(-) diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index 59243af..884f74d 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -5,12 +5,12 @@ authors = ["AMD Research & Advanced Development"] edition = "2021" [dependencies] -petgraph = "0.7.1" indexmap = "2.0.0" tempfile = "3.19.1" derive_more = { version = "2", features = ["full"] } serde = { version = "1.0", features = ["derive"] } -erased-serde = "0.4.6" +bincode = {version = "2.0.1", features = ["serde"] } +enum_dispatch = "0.3" ndarray = "0.16.1" num-traits = "0.2" rstest = "0.23.0" diff --git a/crates/arbolta/src/bit.rs b/crates/arbolta/src/bit.rs index 9439d51..1ca7081 100644 --- a/crates/arbolta/src/bit.rs +++ b/crates/arbolta/src/bit.rs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. // SPDX-License-Identifier: MIT +use bincode::{Decode, Encode}; use core::fmt; use ndarray::{Array1, ArrayView1, ArrayViewMut1}; use num_traits::PrimInt; @@ -12,7 +13,7 @@ use std::str::FromStr; use thiserror::Error; /// Primitive signal value -#[derive(Debug, Clone, Eq, Copy, PartialEq, Deserialize, Serialize, Default)] +#[derive(Debug, Clone, Eq, Copy, PartialEq, Deserialize, Serialize, Default, Encode, Decode)] pub enum Bit { #[default] Zero, diff --git a/crates/arbolta/src/cell.rs b/crates/arbolta/src/cell.rs index c01c1ab..8eca176 100644 --- a/crates/arbolta/src/cell.rs +++ b/crates/arbolta/src/cell.rs @@ -1,21 +1,46 @@ use crate::bit::{Bit, BitVec}; use crate::signal::Signal; +use bincode::{Decode, Encode}; use derive_more::Constructor; +use enum_dispatch::enum_dispatch; use indexmap::IndexMap; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use thiserror::Error; use yosys_netlist_json as yosys; -/// Proxy for a standard-cell and basic unit of 'compute'. -pub type Cell = Box; - -pub trait CellFn: Debug + Send + Sync + erased_serde::Serialize { +#[enum_dispatch] +pub trait CellFn { fn eval(&mut self, signals: &mut [Signal]); fn reset(&mut self); - fn input_connections(&self) -> Vec<&usize>; - fn output_connections(&self) -> Vec<&usize>; - fn clone_box(&self) -> Cell; +} + +#[enum_dispatch(CellFn)] +#[derive(Debug, Serialize, Deserialize, Clone, Encode, Decode)] +/// Proxy for a standard-cell and basic unit of 'compute'. +pub enum Cell { + Inverter, + Buf, + Nand, + And, + AndNot, + Or, + Nor, + Xor, + Xnor, + OrNot, + Dff, + DffReset, + Reg, + Mux, + Add, + Sub, + Mul, + Pos, + Neg, + Shl, + LogicAnd, + LogicNot, } #[derive(Debug, Error)] @@ -24,18 +49,6 @@ pub enum CellError { Unsupported(String), } -impl Default for Cell { - fn default() -> Self { - Box::new(NoneCell) - } -} - -impl Clone for Cell { - fn clone(&self) -> Self { - self.clone_box() - } -} - /// Generate a cell given its Yosys netlist description /// # Arguments /// * `cell` - Yosys cell @@ -79,15 +92,15 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { } let new_cell: Cell = match cell.cell_type.as_str() { - "BUF" | "$_BUF_" => Box::new(Buf::new( + "BUF" | "$_BUF_" => Cell::Buf(Buf::new( input_connections["A"][0], output_connections["Y"][0], )), - "NOT" | "$_NOT_" | "$not" => Box::new(Inverter::new( + "NOT" | "$_NOT_" | "$not" => Cell::Inverter(Inverter::new( input_connections["A"][0], output_connections["Y"][0], )), - "$_NAND_" | "NAND" => Box::new(Nand::new( + "$_NAND_" | "NAND" => Cell::Nand(Nand::new( input_connections .into_values() .flatten() @@ -95,7 +108,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_boxed_slice(), output_connections["Y"][0], )), - "OR" | "$_OR_" | "$reduce_or" => Box::new(Or::new( + "OR" | "$_OR_" | "$reduce_or" => Cell::Or(Or::new( input_connections .into_values() .flatten() @@ -103,7 +116,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_boxed_slice(), output_connections["Y"][0], )), - "ORNOT" | "$_ORNOT_" => Box::new(OrNot::new( + "ORNOT" | "$_ORNOT_" => Cell::OrNot(OrNot::new( input_connections .into_values() .flatten() @@ -111,7 +124,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_boxed_slice(), output_connections["Y"][0], )), - "$_NOR_" | "NOR" => Box::new(Nor::new( + "$_NOR_" | "NOR" => Cell::Nor(Nor::new( input_connections .into_values() .flatten() @@ -119,7 +132,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_boxed_slice(), output_connections["Y"][0], )), - "XOR" | "$_XOR_" => Box::new(Xor::new( + "XOR" | "$_XOR_" => Cell::Xor(Xor::new( input_connections .into_values() .flatten() @@ -127,7 +140,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_boxed_slice(), output_connections["Y"][0], )), - "$_XNOR_" | "XNOR" => Box::new(Xnor::new( + "$_XNOR_" | "XNOR" => Cell::Xnor(Xnor::new( input_connections .into_values() .flatten() @@ -135,7 +148,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_boxed_slice(), output_connections["Y"][0], )), - "AND" | "$_AND_" => Box::new(And::new( + "AND" | "$_AND_" => Cell::And(And::new( input_connections .into_values() .flatten() @@ -143,7 +156,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_boxed_slice(), output_connections["Y"][0], )), - "ANDNOT" | "$_ANDNOT_" => Box::new(AndNot::new( + "ANDNOT" | "$_ANDNOT_" => Cell::AndNot(AndNot::new( input_connections .into_values() .flatten() @@ -151,56 +164,57 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_boxed_slice(), output_connections["Y"][0], )), - "DFF" | "$_DFF_P_" => Box::new(Dff::new( + "DFF" | "$_DFF_P_" => Cell::Dff(Dff::new( Bit::One, input_connections["C"][0], input_connections["D"][0], output_connections["Q"][0], )), - "$_SDFF_PP0_" => Box::new(DffPosedgeReset::new( + "$_SDFF_PP0_" => Cell::DffReset(DffReset::new( + Bit::One, input_connections["D"][0], input_connections["C"][0], input_connections["R"][0], output_connections["Q"][0], )), - "$pos" => Box::new(Pos::new( + "$pos" => Cell::Pos(Pos::new( cell.parameters["A_SIGNED"].to_number().unwrap() == 1, input_connections["A"].clone().into_boxed_slice(), output_connections["Y"].clone().into_boxed_slice(), )), - "$neg" => Box::new(Neg::new( + "$neg" => Cell::Neg(Neg::new( cell.parameters["A_SIGNED"].to_number().unwrap() == 1, input_connections["A"].clone().into_boxed_slice(), output_connections["Y"].clone().into_boxed_slice(), )), // Proc Cells - "$mux" | "$_MUX_" => Box::new(Mux::new( + "$mux" | "$_MUX_" => Cell::Mux(Mux::new( input_connections["S"][0], input_connections["A"].clone().into_boxed_slice(), input_connections["B"].clone().into_boxed_slice(), output_connections["Y"].clone().into_boxed_slice(), )), - "$dff" => Box::new(Reg::new( + "$dff" => Cell::Reg(Reg::new( Bit::One, input_connections["CLK"][0], input_connections["D"].clone().into_boxed_slice(), output_connections["Q"].clone().into_boxed_slice(), )), - "$add" => Box::new(Add::new( + "$add" => Cell::Add(Add::new( // Hacky, fix later cell.parameters["A_SIGNED"].to_number().unwrap() == 1, input_connections["A"].clone().into_boxed_slice(), input_connections["B"].clone().into_boxed_slice(), output_connections["Y"].clone().into_boxed_slice(), )), - "$sub" => Box::new(Sub::new( + "$sub" => Cell::Sub(Sub::new( // Hacky, fix later cell.parameters["A_SIGNED"].to_number().unwrap() == 1, input_connections["A"].clone().into_boxed_slice(), input_connections["B"].clone().into_boxed_slice(), output_connections["Y"].clone().into_boxed_slice(), )), - "$mul" => Box::new(Mul::new( + "$mul" => Cell::Mul(Mul::new( // Hacky, fix later cell.parameters["A_SIGNED"].to_number().unwrap() == cell.parameters["B_SIGNED"].to_number().unwrap(), @@ -208,20 +222,20 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { input_connections["B"].clone().into_boxed_slice(), output_connections["Y"].clone().into_boxed_slice(), )), - "$shl" => Box::new(Shl::new( + "$shl" => Cell::Shl(Shl::new( // Hacky, fix later cell.parameters["A_SIGNED"].to_number().unwrap() == 1, input_connections["A"].clone().into_boxed_slice(), input_connections["B"].clone().into_boxed_slice(), output_connections["Y"].clone().into_boxed_slice(), )), - "$logic_and" => Box::new(LogicAnd::new( + "$logic_and" => Cell::LogicAnd(LogicAnd::new( // Hacky, fix later input_connections["A"].clone().into_boxed_slice(), input_connections["B"].clone().into_boxed_slice(), output_connections["Y"].clone().into_boxed_slice(), )), - "$logic_not" => Box::new(LogicNot::new( + "$logic_not" => Cell::LogicNot(LogicNot::new( // Hacky, fix later input_connections["A"].clone().into_boxed_slice(), output_connections["Y"].clone().into_boxed_slice(), @@ -232,29 +246,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { Ok(new_cell) } -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct NoneCell; - -#[allow(unused_variables)] -impl CellFn for NoneCell { - fn eval(&mut self, signals: &mut [Signal]) {} // Do nothing - - fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - vec![] - } - - fn output_connections(&self) -> Vec<&usize> { - vec![] - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Inverter { input_net: usize, output_net: usize, @@ -266,21 +258,9 @@ impl CellFn for Inverter { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - vec![&self.input_net] - } - - fn output_connections(&self) -> Vec<&usize> { - vec![&self.output_net] - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Buf { input_net: usize, output_net: usize, @@ -292,21 +272,9 @@ impl CellFn for Buf { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - vec![&self.input_net] - } - - fn output_connections(&self) -> Vec<&usize> { - vec![&self.output_net] - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Nand { input_nets: Box<[usize]>, output_net: usize, @@ -326,21 +294,9 @@ impl CellFn for Nand { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.input_nets.as_ref().iter().collect() - } - - fn output_connections(&self) -> Vec<&usize> { - vec![&self.output_net] - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct And { input_nets: Box<[usize]>, output_net: usize, @@ -360,21 +316,9 @@ impl CellFn for And { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.input_nets.as_ref().iter().collect() - } - - fn output_connections(&self) -> Vec<&usize> { - vec![&self.output_net] - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct AndNot { input_nets: Box<[usize]>, output_net: usize, @@ -389,21 +333,9 @@ impl CellFn for AndNot { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.input_nets.as_ref().iter().collect() - } - - fn output_connections(&self) -> Vec<&usize> { - vec![&self.output_net] - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Or { input_nets: Box<[usize]>, output_net: usize, @@ -423,21 +355,9 @@ impl CellFn for Or { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.input_nets.as_ref().iter().collect() - } - - fn output_connections(&self) -> Vec<&usize> { - vec![&self.output_net] - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Nor { input_nets: Box<[usize]>, output_net: usize, @@ -457,21 +377,9 @@ impl CellFn for Nor { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.input_nets.as_ref().iter().collect() - } - - fn output_connections(&self) -> Vec<&usize> { - vec![&self.output_net] - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Xor { input_nets: Box<[usize]>, output_net: usize, @@ -491,21 +399,9 @@ impl CellFn for Xor { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.input_nets.as_ref().iter().collect() - } - - fn output_connections(&self) -> Vec<&usize> { - vec![&self.output_net] - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Xnor { input_nets: Box<[usize]>, output_net: usize, @@ -525,21 +421,9 @@ impl CellFn for Xnor { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.input_nets.as_ref().iter().collect() - } - - fn output_connections(&self) -> Vec<&usize> { - vec![&self.output_net] - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct OrNot { input_nets: Box<[usize]>, output_net: usize, @@ -554,84 +438,59 @@ impl CellFn for OrNot { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.input_nets.as_ref().iter().collect() - } - - fn output_connections(&self) -> Vec<&usize> { - vec![&self.output_net] - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DffPosedgeReset { +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] +pub struct DffReset { + polarity: Bit, data_in_net: usize, clock_net: usize, reset_net: usize, data_out_net: usize, last_clock: Bit, - last_data: Bit, } -impl DffPosedgeReset { - pub fn new(data_in_net: usize, clock_net: usize, reset_net: usize, data_out_net: usize) -> Self { +impl DffReset { + pub fn new( + polarity: Bit, + data_in_net: usize, + clock_net: usize, + reset_net: usize, + data_out_net: usize, + ) -> Self { Self { + polarity, data_in_net, clock_net, reset_net, data_out_net, last_clock: Bit::Zero, - last_data: Bit::Zero, } } } -impl CellFn for DffPosedgeReset { +impl CellFn for DffReset { fn eval(&mut self, signals: &mut [Signal]) { - let (data, clock, reset) = ( - signals[self.data_in_net].get_value(), - signals[self.clock_net].get_value(), - signals[self.reset_net].get_value(), - ); + let clock = !(signals[self.clock_net].get_value() ^ self.polarity); - // Detect rising edge - let output_bit = if clock == Bit::One && self.last_clock == Bit::Zero { - match reset { - Bit::Zero => data, - Bit::One => Bit::Zero, // Do reset + if clock == Bit::One && self.last_clock == Bit::Zero { + if signals[self.reset_net].get_value() == Bit::One { + // Reset + signals[self.data_out_net].set_value(Bit::Zero); + } else { + signals[self.data_out_net].set_value(signals[self.data_in_net].get_value()); } - } else { - self.last_data - }; + } - signals[self.data_out_net].set_value(output_bit); - (self.last_data, self.last_clock) = (output_bit, clock); + self.last_clock = clock; } fn reset(&mut self) { self.last_clock = Bit::Zero; - self.last_data = Bit::Zero; - } - - fn input_connections(&self) -> Vec<&usize> { - vec![&self.clock_net, &self.data_in_net, &self.reset_net] - } - - fn output_connections(&self) -> Vec<&usize> { - vec![&self.data_out_net] - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) } } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] pub struct Dff { polarity: Bit, data_in_net: usize, @@ -666,21 +525,9 @@ impl CellFn for Dff { fn reset(&mut self) { self.last_clock = Bit::Zero; } - - fn input_connections(&self) -> Vec<&usize> { - vec![&self.clock_net, &self.data_in_net] - } - - fn output_connections(&self) -> Vec<&usize> { - vec![&self.data_out_net] - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] pub struct Reg { polarity: Bit, data_in_nets: Box<[usize]>, @@ -724,22 +571,10 @@ impl CellFn for Reg { fn reset(&mut self) { self.last_clock = Bit::Zero; } - - fn input_connections(&self) -> Vec<&usize> { - self.data_in_nets.iter().chain([&self.clock_net]).collect() - } - - fn output_connections(&self) -> Vec<&usize> { - self.data_out_nets.as_ref().iter().collect() - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } // +++ Proc Cells +++ -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Mux { select_net: usize, a_nets: Box<[usize]>, @@ -767,25 +602,9 @@ impl CellFn for Mux { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self - .a_nets - .iter() - .chain(self.b_nets.iter().chain([&self.select_net])) - .collect() - } - - fn output_connections(&self) -> Vec<&usize> { - self.y_nets.as_ref().iter().collect() - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Add { signed: bool, a_nets: Box<[usize]>, @@ -826,21 +645,9 @@ impl CellFn for Add { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.a_nets.iter().chain(self.b_nets.iter()).collect() - } - - fn output_connections(&self) -> Vec<&usize> { - self.y_nets.as_ref().iter().collect() - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Sub { signed: bool, a_nets: Box<[usize]>, @@ -881,21 +688,9 @@ impl CellFn for Sub { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.a_nets.iter().chain(self.b_nets.iter()).collect() - } - - fn output_connections(&self) -> Vec<&usize> { - self.y_nets.as_ref().iter().collect() - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Mul { signed: bool, a_nets: Box<[usize]>, @@ -936,21 +731,9 @@ impl CellFn for Mul { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.a_nets.iter().chain(self.b_nets.iter()).collect() - } - - fn output_connections(&self) -> Vec<&usize> { - self.y_nets.as_ref().iter().collect() - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Pos { signed: bool, a_nets: Box<[usize]>, @@ -982,21 +765,9 @@ impl CellFn for Pos { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.a_nets.iter().collect() - } - - fn output_connections(&self) -> Vec<&usize> { - self.y_nets.iter().collect() - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Neg { signed: bool, a_nets: Box<[usize]>, @@ -1028,21 +799,9 @@ impl CellFn for Neg { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.a_nets.iter().collect() - } - - fn output_connections(&self) -> Vec<&usize> { - self.y_nets.iter().collect() - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Shl { signed: bool, a_nets: Box<[usize]>, @@ -1084,21 +843,9 @@ impl CellFn for Shl { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.a_nets.iter().chain(self.b_nets.iter()).collect() - } - - fn output_connections(&self) -> Vec<&usize> { - self.y_nets.iter().collect() - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct LogicAnd { a_nets: Box<[usize]>, b_nets: Box<[usize]>, @@ -1131,21 +878,9 @@ impl CellFn for LogicAnd { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.a_nets.iter().chain(self.b_nets.iter()).collect() - } - - fn output_connections(&self) -> Vec<&usize> { - self.y_nets.iter().collect() - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct LogicNot { a_nets: Box<[usize]>, y_nets: Box<[usize]>, @@ -1168,16 +903,4 @@ impl CellFn for LogicNot { } fn reset(&mut self) {} - - fn input_connections(&self) -> Vec<&usize> { - self.a_nets.iter().collect() - } - - fn output_connections(&self) -> Vec<&usize> { - self.y_nets.iter().collect() - } - - fn clone_box(&self) -> Cell { - Box::new(self.clone()) - } } diff --git a/crates/arbolta/src/hardware_module.rs b/crates/arbolta/src/hardware_module.rs index 04f0364..e7f38b0 100644 --- a/crates/arbolta/src/hardware_module.rs +++ b/crates/arbolta/src/hardware_module.rs @@ -3,12 +3,12 @@ use super::port::{Port, PortDirection, PortError}; use crate::bit::{Bit, BitVec}; -use crate::cell::{create_cell, Cell, CellError}; +use crate::cell::{create_cell, Cell, CellError, CellFn}; use crate::signal::Signal; -use indexmap::{IndexMap, IndexSet}; +use bincode::{Decode, Encode}; use ndarray::{Array1, ArrayView1}; use num_traits::PrimInt; -use petgraph::graph::{Graph, NodeIndex}; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fmt::Debug; use std::io::{Read, Write}; @@ -17,16 +17,16 @@ use tempfile::NamedTempFile; use thiserror::Error; use yosys_netlist_json as yosys; -pub type CellGraph = Graph; pub type PortMap = HashMap; pub type SignalMap = HashMap>; -#[derive(Default, Clone, Debug)] +#[derive(Default, Clone, Debug, Deserialize, Serialize, Encode, Decode)] pub struct HardwareModule { pub name: String, pub ports: PortMap, pub signal_map: SignalMap, pub signals: Box<[Signal]>, + pub cell_info: HashMap, pub cells: Box<[Cell]>, pub clock_net: Option<(usize, Bit)>, // TODO: Add polarity pub reset_net: Option<(usize, Bit)>, // TODO: Add polarity @@ -48,9 +48,31 @@ pub enum ModuleError { MissingNet(usize), #[error("module `{0}` does not exist")] MissingModule(String), + #[error("{0}")] + FlexReaderError(#[from] flexbuffers::ReaderError), + #[error("{0}")] + DeserializeError(#[from] flexbuffers::DeserializationError), + #[error("{0}")] + SerializeError(#[from] flexbuffers::SerializationError), + #[error("{0}")] + IoError(#[from] std::io::Error), } impl HardwareModule { + pub fn load(path: &str) -> Result { + let serialized = std::fs::read(path)?; + let reader = flexbuffers::Reader::get_root(serialized.as_slice())?; + Ok(Self::deserialize(reader)?) + } + + pub fn save(&self, path: &str) -> Result<(), ModuleError> { + let mut serializer = flexbuffers::FlexbufferSerializer::new(); + self.serialize(&mut serializer)?; + let mut file_output = std::fs::File::create(path)?; + _ = file_output.write(serializer.view())?; + Ok(()) + } + pub fn new_from_str(raw_netlist: &str, top_module: &str) -> Result { let mut temp_netlist = match NamedTempFile::new() { Ok(file) => file, @@ -123,56 +145,23 @@ impl HardwareModule { return Err(CellError::Unsupported(top_module.to_string()).into()); }; - let mut bit_drivers = IndexMap::>::new(); - let mut bit_users = IndexMap::>::new(); - let mut node_map = IndexMap::<&str, NodeIndex>::new(); - - let mut graph = CellGraph::new(); - let input_node = graph.add_node(Cell::default()); // Start of graph search, module inputs + let mut cells = vec![]; + // let mut cell_map = CellMap::new(); + let mut cell_info: HashMap = HashMap::new(); - for (cell_name, cell) in synth_module.cells.iter() { - if cell.cell_type == "$scopeinfo" { + for cell_name in topo_order.iter().rev() { + if *cell_name == "$scopeinfo" { // Ignore for now continue; } - let new_cell = create_cell(cell)?; - - new_cell - .input_connections() - .iter() - .filter(|i| ***i > 1) // Skip constants - .for_each(|i| { - bit_users.entry(**i).or_default().insert(cell_name); - }); - new_cell - .output_connections() - .iter() - .filter(|i| ***i > 1) // Skip constants - .for_each(|i| { - bit_drivers.entry(**i).or_default().insert(cell_name); - }); + let synth_cell = synth_module.cells.get(*cell_name).unwrap(); + let cell = create_cell(synth_cell)?; - let cell_node = graph.add_node(new_cell); - node_map.insert(cell_name, cell_node); - } - - for (user_bit, user_cells) in bit_users { - if let Some(drivers) = bit_drivers.get(&user_bit) { - // Existing driver node - for driver_cell in drivers { - for user in &user_cells { - let (driver_node, user_node) = (node_map[driver_cell], node_map[user]); - graph.add_edge(driver_node, user_node, user_bit); - } - } - } else { - // Connect to input cell - for user in user_cells { - let user_node = node_map[user]; - graph.add_edge(input_node, user_node, user_bit); - } - } + cells.push(cell); + // cell_map.insert(cell_name.to_string(), cells.len() - 1); + // cell_info.insert(cell_name.to_string(), synth_cell.clone()); + cell_info.insert(cell_name.to_string(), synth_cell.cell_type.to_string()); } let mut signal_map = SignalMap::new(); @@ -220,26 +209,12 @@ impl HardwareModule { signals[0].set_constant(Bit::Zero); signals[1].set_constant(Bit::One); - // TODO: Remove? - // let mut search = petgraph::visit::DfsPostOrder::new(&graph, input_node); - let mut cells = vec![]; - // while let Some(cell_node) = search.next(&graph) { - // cells.push(graph[cell_node].clone()); - // } - - for cell_name in topo_order { - let idx = node_map[cell_name]; - let node = graph[idx].clone(); - cells.push(node); - } - - cells.reverse(); - Ok(Self { name: top_module.to_string(), ports, signal_map, signals: signals.into_boxed_slice(), + cell_info, cells: cells.into_boxed_slice(), clock_net: None, reset_net: None, @@ -254,13 +229,19 @@ impl HardwareModule { } pub fn stick_signal(&mut self, net: usize, value: Bit) -> Result<(), ModuleError> { - match self.signals.get_mut(net) { - Some(signal) => { - signal.set_constant(value); - Ok(()) - } - None => Err(ModuleError::MissingNet(net)), - } + let Some(signal) = self.signals.get_mut(net) else { + return Err(ModuleError::MissingNet(net)); + }; + signal.set_constant(value); + Ok(()) + } + + pub fn unstick_signal(&mut self, net: usize) -> Result<(), ModuleError> { + let Some(signal) = self.signals.get_mut(net) else { + return Err(ModuleError::MissingNet(net)); + }; + signal.unset_constant(); + Ok(()) } pub fn set_clock(&mut self, net: usize, polarity: Bit) -> Result<(), ModuleError> { diff --git a/crates/arbolta/src/port.rs b/crates/arbolta/src/port.rs index 207da0c..0f6a9a9 100644 --- a/crates/arbolta/src/port.rs +++ b/crates/arbolta/src/port.rs @@ -3,6 +3,7 @@ use crate::bit::{Bit, BitVec}; use crate::signal::Signal; +use bincode::{Decode, Encode}; use ndarray::{Array1, ArrayView1}; use num_traits::PrimInt; use serde::{Deserialize, Serialize}; @@ -10,13 +11,13 @@ use std::fmt::Debug; use thiserror::Error; use yosys_netlist_json as yosys; -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Encode, Decode)] pub enum PortDirection { Input, Output, } -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Encode, Decode)] pub struct Port { pub direction: PortDirection, pub nets: Box<[usize]>, diff --git a/crates/arbolta/src/signal.rs b/crates/arbolta/src/signal.rs index db7b3f4..956ef52 100644 --- a/crates/arbolta/src/signal.rs +++ b/crates/arbolta/src/signal.rs @@ -2,10 +2,11 @@ // SPDX-License-Identifier: MIT use crate::bit::Bit; +use bincode::{Decode, Encode}; use serde::{Deserialize, Serialize}; /// Connection between cells/modules. -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)] +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default, Encode, Decode)] pub struct Signal { /// Value of net pub value: Bit, @@ -37,6 +38,10 @@ impl Signal { self.toggle_count_rising = 0; } + pub fn unset_constant(&mut self) { + self.constant = false; + } + /// Reset signal value to zero. /// Clear all signal statistics. pub fn reset(&mut self) { diff --git a/crates/arbolta/tests/test_cell.rs b/crates/arbolta/tests/test_cell.rs index d4f5cf2..1408e1f 100644 --- a/crates/arbolta/tests/test_cell.rs +++ b/crates/arbolta/tests/test_cell.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: MIT use arbolta::bit::Bit; -use arbolta::cell::{create_cell, Cell, CellFn, Dff, DffPosedgeReset}; +use arbolta::cell::{create_cell, Cell, CellFn, Dff, DffReset}; use arbolta::signal::Signal; use rstest::rstest; @@ -181,7 +181,7 @@ fn test_cell_dff_p() { fn test_cell_sdff_pp() { // D, C, R, Q let (data_in, clock, reset, data_out) = (0, 1, 2, 3); - let mut cell = DffPosedgeReset::new(data_in, clock, reset, data_out); + let mut cell = DffReset::new(Bit::One, data_in, clock, reset, data_out); let mut signals = vec![Signal::default(); 4].into_boxed_slice(); cell.eval(&mut signals); diff --git a/crates/python_bindings/Cargo.toml b/crates/python_bindings/Cargo.toml index fe879ed..27f1dbb 100644 --- a/crates/python_bindings/Cargo.toml +++ b/crates/python_bindings/Cargo.toml @@ -13,7 +13,7 @@ pyo3 = "0.23.3" numpy = "0.23.0" num-traits = "0.2" serde = { version = "1.0", features = ["derive"] } -bincode = "1.3.3" +bincode = {version = "2.0.1", features = ["serde"] } [dependencies.arbolta] path = "../arbolta" diff --git a/crates/python_bindings/py_src/arbolta/design.py b/crates/python_bindings/py_src/arbolta/design.py index e8e64b8..b0f1100 100644 --- a/crates/python_bindings/py_src/arbolta/design.py +++ b/crates/python_bindings/py_src/arbolta/design.py @@ -267,6 +267,12 @@ def signal_map(self) -> Dict[str, List[int]]: def stick_signal(self, net: int, val: Union[int, bool]) -> None: return self.design.stick_signal(net, bool(val)) + def unstick_signal(self, net: int) -> None: + return self.design.unstick_signal(net) + + def cell_info(self) -> Dict[str, Dict]: + return self.design.get_cell_info() + def save(file: str, design: HardwareDesign) -> None: assert RuntimeError("Unimplemented") diff --git a/crates/python_bindings/src/design.rs b/crates/python_bindings/src/design.rs index 8814ac7..d60cc45 100644 --- a/crates/python_bindings/src/design.rs +++ b/crates/python_bindings/src/design.rs @@ -4,14 +4,18 @@ use crate::conversion::{ bits_to_bool_numpy, bits_to_int_numpy, bool_numpy_to_bits, int_numpy_to_bits, }; + use arbol::hardware_module::HardwareModule; use arbol::port::PortDirection; +use bincode::{Decode, Encode}; use pyo3::exceptions::{PyAttributeError, PyException, PyValueError}; use pyo3::prelude::*; -use pyo3::types::PyBytes; +use pyo3::types::{PyBytes, PyDict}; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; #[pyclass(dict, module = "arbolta", name = "Design")] +#[derive(Deserialize, Serialize, Decode, Encode)] pub struct PyDesign { #[pyo3(get)] pub top_module: String, @@ -37,26 +41,49 @@ impl PyDesign { #[allow(unused_variables)] fn __setstate__(&mut self, state: &Bound<'_, PyBytes>) { - todo!() + // state.as_bytes() + // *self = bincode::deserialize(state.as_bytes()).unwrap(); + let config = bincode::config::standard(); + (*self, _) = bincode::decode_from_slice(state.as_bytes(), config).unwrap(); } #[allow(unused_variables)] fn __getstate__<'py>(&self, py: Python<'py>) -> PyResult> { - todo!() + let config = bincode::config::standard(); + match bincode::encode_to_vec(self, config) { + Ok(bytes) => Ok(PyBytes::new(py, &bytes)), + Err(err) => Err(PyValueError::new_err(format!("{err}"))), + } + // match bincode::serialize(&self) { + // Ok(bytes) => Ok(PyBytes::new(py, &bytes)), + // Err(err) => Err(PyValueError::new_err(format!("{err}"))), + // } } fn __getnewargs__(&self) -> (String, String) { (self.top_module.clone(), self.netlist_path.clone()) } - #[allow(unused_variables)] fn save(&self, path: &str) -> PyResult<()> { - todo!() + match self.module.save(path) { + Ok(()) => Ok(()), + Err(err) => Err(PyValueError::new_err(format!("{err}"))), + } } - #[allow(unused_variables)] fn load(&self, path: &str) -> PyResult { - todo!() + let module = match HardwareModule::load(path) { + Ok(module) => module, + Err(err) => return Err(PyValueError::new_err(format!("{err}"))), + }; + + let top_module = module.name.clone(); + + Ok(Self { + top_module, + netlist_path: String::new(), // TODO: Fix + module, + }) } fn get_port_shape(&self, name: &str) -> PyResult<[usize; 2]> { @@ -94,6 +121,19 @@ impl PyDesign { .collect() } + fn get_cell_info(&self, py: Python<'_>) -> PyResult { + let all_cell_info = PyDict::new(py); + for (name, cell_type) in self.module.cell_info.iter() { + let cell_info = PyDict::new(py); + cell_info.set_item("type", cell_type)?; + + // TODO: Add other fields + all_cell_info.set_item(name.clone(), cell_info)?; + } + + Ok(all_cell_info.into()) + } + pub fn stick_signal(&mut self, net: usize, val: bool) -> PyResult<()> { match self.module.stick_signal(net, val.into()) { Ok(()) => Ok(()), @@ -101,7 +141,13 @@ impl PyDesign { } } - #[allow(unused_variables)] + pub fn unstick_signal(&mut self, net: usize) -> PyResult<()> { + match self.module.unstick_signal(net) { + Ok(()) => Ok(()), + Err(err) => Err(PyException::new_err(format!("{err}"))), + } + } + fn set_clock(&mut self, name: &str, polarity: bool) -> PyResult<()> { let Some(nets) = self.module.signal_map.get(name) else { return Err(PyException::new_err(format!("No signal `{name}`"))); @@ -136,52 +182,6 @@ impl PyDesign { fn eval(&mut self) { self.module.eval(); - // println!("{:?}", self.module.clock_net); - // println!("{:?}", self.module.reset_net); - // println!( - // "clk: {:#>08x}", - // self.module.get_port_int::("clk").unwrap() - // ); - // println!( - // "rstn: {:#>08x}", - // self.module.get_port_int::("rstn").unwrap() - // ); - // println!( - // "s_valid: {:#>08x}", - // self.module.get_port_int::("s_valid").unwrap() - // ); - // println!( - // "s_last: {:#>08x}", - // self.module.get_port_int::("s_last").unwrap() - // ); - // println!( - // "m_ready: {:#>08x}", - // self.module.get_port_int::("m_ready").unwrap() - // ); - // println!( - // "s_ready: {:#>08x}", - // self.module.get_port_int::("s_ready").unwrap() - // ); - // println!( - // "m_valid: {:#>08x}", - // self.module.get_port_int::("m_valid").unwrap() - // ); - // println!( - // "m_last: {:#>08x}", - // self.module.get_port_int::("m_last").unwrap() - // ); - // println!( - // "sx_data: {:#>08x}", - // self.module.get_port_int::("sx_data").unwrap() - // ); - // println!( - // "sk_data: {:#>08x}", - // self.module.get_port_int::("sk_data").unwrap() - // ); - // println!( - // "m_data: {:#>08x}", - // self.module.get_port_int::("m_data").unwrap() - // ); } // TODO: Fix this From c9df1b50315b5934c15ada0d14b2467f6c5f1883 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Tue, 15 Apr 2025 02:05:49 +0000 Subject: [PATCH 05/64] Fixed saving and loading from Python --- crates/python_bindings/py_src/arbolta/design.py | 11 ++++++----- crates/python_bindings/src/design.rs | 11 ++--------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/crates/python_bindings/py_src/arbolta/design.py b/crates/python_bindings/py_src/arbolta/design.py index b0f1100..519c969 100644 --- a/crates/python_bindings/py_src/arbolta/design.py +++ b/crates/python_bindings/py_src/arbolta/design.py @@ -8,7 +8,7 @@ from .arbolta import Design -__all__ = ["PortConfig", "DesignConfig", "HardwareDesign"] +__all__ = ["PortConfig", "DesignConfig", "HardwareDesign", "save", "load"] @dataclass @@ -274,9 +274,10 @@ def cell_info(self) -> Dict[str, Dict]: return self.design.get_cell_info() -def save(file: str, design: HardwareDesign) -> None: - assert RuntimeError("Unimplemented") +def save(path: str, design: HardwareDesign) -> None: + design.design.save(path) -def load(file: str) -> HardwareDesign: - assert RuntimeError("Unimplemented") +def load(path: str) -> HardwareDesign: + return Design.load(path) + # assert RuntimeError("Unimplemented") diff --git a/crates/python_bindings/src/design.rs b/crates/python_bindings/src/design.rs index d60cc45..7ba399f 100644 --- a/crates/python_bindings/src/design.rs +++ b/crates/python_bindings/src/design.rs @@ -39,25 +39,17 @@ impl PyDesign { }) } - #[allow(unused_variables)] fn __setstate__(&mut self, state: &Bound<'_, PyBytes>) { - // state.as_bytes() - // *self = bincode::deserialize(state.as_bytes()).unwrap(); let config = bincode::config::standard(); (*self, _) = bincode::decode_from_slice(state.as_bytes(), config).unwrap(); } - #[allow(unused_variables)] fn __getstate__<'py>(&self, py: Python<'py>) -> PyResult> { let config = bincode::config::standard(); match bincode::encode_to_vec(self, config) { Ok(bytes) => Ok(PyBytes::new(py, &bytes)), Err(err) => Err(PyValueError::new_err(format!("{err}"))), } - // match bincode::serialize(&self) { - // Ok(bytes) => Ok(PyBytes::new(py, &bytes)), - // Err(err) => Err(PyValueError::new_err(format!("{err}"))), - // } } fn __getnewargs__(&self) -> (String, String) { @@ -71,7 +63,8 @@ impl PyDesign { } } - fn load(&self, path: &str) -> PyResult { + #[staticmethod] + fn load(path: &str) -> PyResult { let module = match HardwareModule::load(path) { Ok(module) => module, Err(err) => return Err(PyValueError::new_err(format!("{err}"))), From 7f236033525186376a3352f48479b229dbaf963e Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Tue, 15 Apr 2025 17:16:02 +0000 Subject: [PATCH 06/64] Updated dependencies --- crates/arbolta/Cargo.toml | 8 ++++---- crates/python_bindings/Cargo.toml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index 884f74d..bf68a72 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -5,7 +5,7 @@ authors = ["AMD Research & Advanced Development"] edition = "2021" [dependencies] -indexmap = "2.0.0" +indexmap = "2.9.0" tempfile = "3.19.1" derive_more = { version = "2", features = ["full"] } serde = { version = "1.0", features = ["derive"] } @@ -13,7 +13,7 @@ bincode = {version = "2.0.1", features = ["serde"] } enum_dispatch = "0.3" ndarray = "0.16.1" num-traits = "0.2" -rstest = "0.23.0" -flexbuffers = "2.0.0" +rstest = "0.25.0" +flexbuffers = "25.2.10" yosys-netlist-json = { git = "https://github.com/alexredd99/yosys-netlist-json.git" } -thiserror = "2.0.3" +thiserror = "2.0.12" diff --git a/crates/python_bindings/Cargo.toml b/crates/python_bindings/Cargo.toml index 27f1dbb..4bad0a2 100644 --- a/crates/python_bindings/Cargo.toml +++ b/crates/python_bindings/Cargo.toml @@ -9,8 +9,8 @@ name = "arbolta" crate-type = ["cdylib"] [dependencies] -pyo3 = "0.23.3" -numpy = "0.23.0" +pyo3 = "0.24.1" +numpy = "0.24.0" num-traits = "0.2" serde = { version = "1.0", features = ["derive"] } bincode = {version = "2.0.1", features = ["serde"] } From f677b94ab5367092e8028ba04acbcd88adc48c07 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 16 Apr 2025 23:36:17 +0000 Subject: [PATCH 07/64] Added temp systolic array experiment submodule --- .gitmodules | 3 ++ experiments/systolic_array/README.md | 5 ++ .../systolic_array/axis-systolic-array | 1 + experiments/systolic_array/synth.tcl | 49 +++++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 .gitmodules create mode 100644 experiments/systolic_array/README.md create mode 160000 experiments/systolic_array/axis-systolic-array create mode 100644 experiments/systolic_array/synth.tcl diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..95e7f51 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "experiments/systolic_array/axis-systolic-array"] + path = experiments/systolic_array/axis-systolic-array + url = git@github.com:alexredd99/axis-systolic-array.git diff --git a/experiments/systolic_array/README.md b/experiments/systolic_array/README.md new file mode 100644 index 0000000..40f42a2 --- /dev/null +++ b/experiments/systolic_array/README.md @@ -0,0 +1,5 @@ +Synthesize systolic array: + +```shell +yosys -c synth.tcl -- R= C= +``` diff --git a/experiments/systolic_array/axis-systolic-array b/experiments/systolic_array/axis-systolic-array new file mode 160000 index 0000000..e90c3e0 --- /dev/null +++ b/experiments/systolic_array/axis-systolic-array @@ -0,0 +1 @@ +Subproject commit e90c3e076e765ee9c27d9cb0095f1bff44a7fb6c diff --git a/experiments/systolic_array/synth.tcl b/experiments/systolic_array/synth.tcl new file mode 100644 index 0000000..b564797 --- /dev/null +++ b/experiments/systolic_array/synth.tcl @@ -0,0 +1,49 @@ +yosys -import + +set top_module axis_sa + +set rtl_path axis-systolic-array/rtl/sa +set rtl_files [glob -directory $rtl_path -- "*.sv" "**/*.sv"] + +foreach verilog_source $rtl_files { + read_verilog -defer -sv $verilog_source +} + +# Overwrite module parameters +foreach param [lrange $argv 0 end] { + set param [split $param "="] + set param_name [lindex $param 0] + set param_val [lindex $param 1] + chparam -set $param_name $param_val $top_module +} + +hierarchy -check -top $top_module + +procs;; + +proc export_synth {output_path} { + file mkdir $output_path + write_json $output_path/synth.json + # write_verilog $output_path/synth.v + # tee -o $output_path/torder.txt torder + # show -viewer none -format dot -prefix $output_path/schematic +} + +flatten + +puts [export_synth {output/0_proc}] + +# Synthesize design +synth -top $top_module +clean -purge +autoname + +setundef -zero +puts [export_synth {output/1_synth}] + +# read_verilog -lib cells/cells.v +# dfflibmap -liberty cells/cells.lib +# abc -liberty cells/cells.lib +# opt_clean + +# puts [export_synth {output/2_cell_synth}] From 7319e9b224ce4208a7831ca4e17794bc9b3fd303 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 16 Apr 2025 23:42:25 +0000 Subject: [PATCH 08/64] Changed submodule URL from SSH to HTTPS --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 95e7f51..4397b2e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "experiments/systolic_array/axis-systolic-array"] path = experiments/systolic_array/axis-systolic-array - url = git@github.com:alexredd99/axis-systolic-array.git + url = https://github.com/alexredd99/axis-systolic-array.git From f425279d6480db6aed66430113eabb00a998c292 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Fri, 18 Apr 2025 21:20:28 +0000 Subject: [PATCH 09/64] Initial MNIST experiment code --- experiments/mnist/train.ipynb | 136 ++++++++++++++++++++++++++++ experiments/mnist/utils/__init__.py | 4 + experiments/mnist/utils/dataset.py | 43 +++++++++ experiments/mnist/utils/hardware.py | 46 ++++++++++ experiments/mnist/utils/model.py | 31 +++++++ experiments/mnist/utils/train.py | 71 +++++++++++++++ 6 files changed, 331 insertions(+) create mode 100644 experiments/mnist/train.ipynb create mode 100644 experiments/mnist/utils/__init__.py create mode 100644 experiments/mnist/utils/dataset.py create mode 100644 experiments/mnist/utils/hardware.py create mode 100644 experiments/mnist/utils/model.py create mode 100644 experiments/mnist/utils/train.py diff --git a/experiments/mnist/train.ipynb b/experiments/mnist/train.ipynb new file mode 100644 index 0000000..2d269d7 --- /dev/null +++ b/experiments/mnist/train.ipynb @@ -0,0 +1,136 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "586d6d25", + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "from brevitas.quant_tensor.int_quant_tensor import IntQuantTensor\n", + "from torch import Tensor\n", + "from utils import QuantModel, get_mnist_dataloaders, test_model, train_for_epoch\n", + "\n", + "DATASET_PATH = \"/home/a.redding/workspace/datasets\"\n", + "EXPORT_PATH = \"quant.pt\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f14bb52f", + "metadata": {}, + "outputs": [], + "source": [ + "learning_rate = 0.001\n", + "momentum = 0.5\n", + "device = \"cuda:0\"\n", + "model = QuantModel().to(device)\n", + "criterion = nn.CrossEntropyLoss()\n", + "optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc062a9f", + "metadata": {}, + "outputs": [], + "source": [ + "train_loader, test_loader = get_mnist_dataloaders(DATASET_PATH)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ae77e888", + "metadata": {}, + "outputs": [], + "source": [ + "for epoch in range(10):\n", + " train_for_epoch(model, device, train_loader, criterion, optimizer)\n", + " test_model(model, device, test_loader, F.cross_entropy)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35ee2d90", + "metadata": {}, + "outputs": [], + "source": [ + "model_weights: Tensor\n", + "model_scale: Tensor\n", + "correct_inputs: list[Tensor] = []\n", + "input_scale: Tensor = None\n", + "correct_targets: list[Tensor] = []\n", + "\n", + "with torch.no_grad():\n", + " for images, targets in test_loader:\n", + " images, targets = images.to(device), targets.to(device)\n", + "\n", + " logits = model(images)\n", + " preds = logits.argmax(dim=1, keepdim=True).squeeze()\n", + "\n", + " for i, correct in enumerate(preds == targets):\n", + " if correct: # Get quantized image\n", + " x_quant: IntQuantTensor = model.quant_inp(torch.flatten(images[i]))\n", + " if input_scale is None:\n", + " input_scale = x_quant.scale.cpu()\n", + "\n", + " correct_inputs.append(x_quant.int().cpu())\n", + " correct_targets.append(targets[i].cpu())\n", + "\n", + " # Get weights last for faster inference\n", + " quant_weight = model.cpu().fc1.quant_weight()\n", + " model_weights = quant_weight.int()\n", + " model_scale = quant_weight.scale\n", + "\n", + "correct_inputs = torch.vstack(correct_inputs)\n", + "correct_targets = torch.vstack(correct_targets)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ffd16f06", + "metadata": {}, + "outputs": [], + "source": [ + "torch.save(\n", + " {\n", + " \"model_weights\": model_weights,\n", + " \"model_scale\": model_scale,\n", + " \"correct_inputs\": correct_inputs,\n", + " \"input_scale\": input_scale,\n", + " \"correct_targets\": correct_targets,\n", + " },\n", + " EXPORT_PATH,\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/experiments/mnist/utils/__init__.py b/experiments/mnist/utils/__init__.py new file mode 100644 index 0000000..227ed33 --- /dev/null +++ b/experiments/mnist/utils/__init__.py @@ -0,0 +1,4 @@ +from .dataset import * +from .hardware import * +from .model import * +from .train import * diff --git a/experiments/mnist/utils/dataset.py b/experiments/mnist/utils/dataset.py new file mode 100644 index 0000000..cd9325e --- /dev/null +++ b/experiments/mnist/utils/dataset.py @@ -0,0 +1,43 @@ +import torchvision +from torch.utils.data import DataLoader + + +def get_mnist_dataloaders( + data_root: str, + num_workers: int = 0, + batch_size_train: int = 64, + batch_size_test: int = 1024, + pin_memory: bool = True, + download: bool = False, +): + image_transform = torchvision.transforms.Compose([ + torchvision.transforms.ToTensor(), + torchvision.transforms.Normalize((0.1307, ), (0.3081, )), + ]) + + train_dataset = torchvision.datasets.MNIST(data_root, + train=True, + download=download, + transform=image_transform) + test_dataset = torchvision.datasets.MNIST(data_root, + train=False, + download=download, + transform=image_transform) + + train_loader = DataLoader( + train_dataset, + batch_size=batch_size_train, + shuffle=True, + num_workers=num_workers, + pin_memory=pin_memory, + ) + + test_loader = DataLoader( + test_dataset, + batch_size=batch_size_test, + shuffle=True, + num_workers=num_workers, + pin_memory=pin_memory, + ) + + return train_loader, test_loader diff --git a/experiments/mnist/utils/hardware.py b/experiments/mnist/utils/hardware.py new file mode 100644 index 0000000..962d7da --- /dev/null +++ b/experiments/mnist/utils/hardware.py @@ -0,0 +1,46 @@ +import numpy as np +import torch +from arbolta import HardwareDesign + + +def run_systolic_array(design: HardwareDesign, x: torch.Tensor, + k: torch.Tensor) -> torch.Tensor: + """ + Run inputs through systolic array. + Expects x: (K,R), k: (K,C) -> y: (C,R) + """ + K, R, C = x.shape[0], x.shape[1], k.shape[1] + actual = np.zeros((C, R), dtype=np.int32) + + # Start simulation + design.eval_reset_clocked() + + for i in range(K): + while design.ports.s_ready == 0: + design.eval_clocked() + + design.ports.s_valid = 1 + design.ports.sx_data = x[i] + design.ports.sk_data = k[i] + + if i == K - 1: + design.ports.s_last = 1 + + design.eval_clocked() + + design.ports.m_ready = 1 + design.ports.s_valid = 0 + design.ports.s_last = 0 + + idx = 0 + while True: + if design.ports.m_valid.item() == 1: + actual[idx] = design.ports.m_data + idx += 1 + + if design.ports.m_last.item() == 1: + break + + design.eval_clocked() + + return torch.from_numpy(actual) diff --git a/experiments/mnist/utils/model.py b/experiments/mnist/utils/model.py new file mode 100644 index 0000000..a2bc88d --- /dev/null +++ b/experiments/mnist/utils/model.py @@ -0,0 +1,31 @@ +import brevitas.nn as qnn +import torch.nn as nn +import torch.nn.functional as F +from brevitas.quant.scaled_int import (Int8ActPerTensorFloat, + Int8WeightPerTensorFloat) + + +class QuantModel(nn.Module): + + def __init__(self, input_bit_width: int = 8, weight_bit_width: int = 8): + super(QuantModel, self).__init__() + self.flatten_inp = nn.Flatten() + self.quant_inp = qnn.QuantIdentity( + act_quant=Int8ActPerTensorFloat, + bit_width=input_bit_width, + return_quant_tensor=True, + ) + self.fc1 = qnn.QuantLinear( + in_features=28 * 28, + out_features=10, + bias=False, + weight_quant=Int8WeightPerTensorFloat, + weight_bit_width=weight_bit_width, + ) + + def forward(self, x): + out = self.flatten_inp(x) + out = self.quant_inp(out) + out = self.fc1(out) + out = F.log_softmax(out, dim=-1) + return out diff --git a/experiments/mnist/utils/train.py b/experiments/mnist/utils/train.py new file mode 100644 index 0000000..eed23b2 --- /dev/null +++ b/experiments/mnist/utils/train.py @@ -0,0 +1,71 @@ +import torch +import torch.nn as nn +from torch import Tensor +from torch.nn.modules.loss import _Loss +from torch.optim.optimizer import Optimizer +from torch.utils.data import DataLoader +from tqdm.notebook import tqdm + + +def train_for_epoch( + model: nn.Module, + device: str, + train_loader: DataLoader, + criterion: _Loss, + optimizer: Optimizer, +) -> None: + model.train() + + correct_total = 0 + size_total = 0 + with tqdm(train_loader) as tepoch: + tepoch.set_description("Train") + for images, targets in tepoch: + images, targets = images.to(device), targets.to(device) + + optimizer.zero_grad() + logits: Tensor = model(images) + loss: Tensor = criterion(logits, targets) + loss.backward() + optimizer.step() + + # Compute metrics + preds = logits.argmax(dim=1, keepdim=True).squeeze() + num_correct = (preds == targets).sum().item() + correct_total += num_correct + size_total += len(targets) + accuracy = correct_total / size_total + + tepoch.set_postfix(loss=loss.item(), acc=format(accuracy, "3.2%")) + + +def test_model(model: nn.Module, device: str, test_loader: DataLoader, + criterion: _Loss) -> None: + model.eval() + + test_loss = 0 + correct_total = 0 + size_total = 0 + with torch.no_grad(): + with tqdm(test_loader) as tepoch: + tepoch.set_description("Test") + for images, targets in tepoch: + images, targets = images.to(device), targets.to(device) + + logits: Tensor = model(images) + loss: Tensor = criterion(logits, targets, reduction="sum") + + preds = logits.argmax(dim=1, keepdim=True).squeeze() + num_correct = (preds == targets).sum().item() + correct_total += num_correct + size_total += len(targets) + accuracy = correct_total / size_total + + test_loss += loss.item() + avg_loss = test_loss / size_total + + tepoch.set_postfix( + loss=loss.item(), + acc=format(accuracy, "3.2%"), + avg_loss=avg_loss, + ) From bec2c3eb912f12a94948c8a9a4edc07e972153e9 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Sat, 19 Apr 2025 21:53:40 +0000 Subject: [PATCH 10/64] Runtime performance improvements --- crates/arbolta/Cargo.toml | 1 + crates/arbolta/src/bit.rs | 61 +++----- crates/arbolta/src/cell.rs | 211 ++++++++++++-------------- crates/arbolta/src/hardware_module.rs | 8 +- crates/arbolta/src/port.rs | 16 +- crates/arbolta/src/signal.rs | 22 ++- crates/arbolta/tests/test_bit.rs | 52 +++---- crates/arbolta/tests/test_bitvec.rs | 48 +++--- crates/arbolta/tests/test_cell.rs | 136 ++++++++--------- crates/arbolta/tests/test_signal.rs | 20 +-- 10 files changed, 273 insertions(+), 302 deletions(-) diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index bf68a72..e4f85dc 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -17,3 +17,4 @@ rstest = "0.25.0" flexbuffers = "25.2.10" yosys-netlist-json = { git = "https://github.com/alexredd99/yosys-netlist-json.git" } thiserror = "2.0.12" +smallvec = {version = "1.15.0", features = ["serde", "impl_bincode"] } diff --git a/crates/arbolta/src/bit.rs b/crates/arbolta/src/bit.rs index 1ca7081..c67187e 100644 --- a/crates/arbolta/src/bit.rs +++ b/crates/arbolta/src/bit.rs @@ -14,11 +14,7 @@ use thiserror::Error; /// Primitive signal value #[derive(Debug, Clone, Eq, Copy, PartialEq, Deserialize, Serialize, Default, Encode, Decode)] -pub enum Bit { - #[default] - Zero, - One, -} +pub struct Bit(pub bool); #[derive(Debug, PartialEq, Eq, Error)] #[error("error converting bits")] @@ -27,9 +23,9 @@ pub struct ParseBitError; impl From for Bit { fn from(val: bool) -> Self { if val { - Self::One + Self::ONE } else { - Self::Zero + Self::ZERO } } } @@ -37,8 +33,8 @@ impl From for Bit { impl From for bool { fn from(val: Bit) -> Self { match val { - Bit::Zero => false, - Bit::One => true, + Bit::ZERO => false, + Bit::ONE => true, } } } @@ -47,8 +43,8 @@ impl TryFrom for Bit { type Error = ParseBitError; fn try_from(val: char) -> Result { match val { - '0' => Ok(Self::Zero), - '1' => Ok(Self::One), + '0' => Ok(Self(false)), + '1' => Ok(Self(true)), _ => Err(ParseBitError), } } @@ -57,18 +53,21 @@ impl TryFrom for Bit { impl From for char { fn from(bit: Bit) -> Self { match bit { - Bit::Zero => '0', - Bit::One => '1', + Bit(false) => '0', + Bit(true) => '1', } } } impl Bit { + pub const ZERO: Bit = Bit(false); + pub const ONE: Bit = Bit(true); + pub fn from_int(val: T) -> Result { if val == T::zero() { - Ok(Self::Zero) + Ok(Self(false)) } else if val == T::one() { - Ok(Self::One) + Ok(Self(true)) } else { Err(ParseBitError) } @@ -76,8 +75,8 @@ impl Bit { pub fn to_int(self) -> T { match self { - Self::Zero => T::zero(), - Self::One => T::one(), + Self(false) => T::zero(), + Self(true) => T::one(), } } } @@ -95,10 +94,7 @@ impl Not for Bit { type Output = Self; fn not(self) -> Self::Output { - match self { - Bit::Zero => Bit::One, - Bit::One => Bit::Zero, - } + Bit(!self.0) } } @@ -106,10 +102,7 @@ impl BitAnd for Bit { type Output = Self; fn bitand(self, rhs: Self) -> Self::Output { - match &[self, rhs] { - [Bit::Zero, Bit::Zero] | [Bit::Zero, Bit::One] | [Bit::One, Bit::Zero] => Bit::Zero, - [Bit::One, Bit::One] => Bit::One, - } + Bit(self.0 & rhs.0) } } @@ -117,10 +110,7 @@ impl BitOr for Bit { type Output = Self; fn bitor(self, rhs: Self) -> Self::Output { - match &[self, rhs] { - [Bit::Zero, Bit::Zero] => Bit::Zero, - [Bit::Zero, Bit::One] | [Bit::One, Bit::Zero] | [Bit::One, Bit::One] => Bit::One, - } + Bit(self.0 | rhs.0) } } @@ -128,10 +118,7 @@ impl BitXor for Bit { type Output = Self; fn bitxor(self, rhs: Self) -> Self::Output { - match &[self, rhs] { - [Bit::Zero, Bit::Zero] | [Bit::One, Bit::One] => Bit::Zero, - [Bit::Zero, Bit::One] | [Bit::One, Bit::Zero] => Bit::One, - } + Bit(self.0 ^ rhs.0) } } @@ -185,19 +172,19 @@ impl From for Vec { impl From<&BitVec> for Vec { fn from(val: &BitVec) -> Self { - val.bits.iter().rev().map(|b| (*b).into()).collect() + val.bits.iter().rev().map(|b| b.0).collect() } } impl From for Vec { fn from(val: BitVec) -> Self { - val.bits.iter().rev().map(|b| (*b).into()).collect() + val.bits.iter().rev().map(|b| b.0).collect() } } impl From<&[bool]> for BitVec { fn from(vals: &[bool]) -> Self { - let bits: Vec = vals.iter().rev().map(|b| (*b).into()).collect(); + let bits: Vec = vals.iter().rev().map(|b| Bit(*b)).collect(); Self::from(bits) } } @@ -497,7 +484,7 @@ impl BitVec { .bits .iter() .enumerate() - .for_each(|(i, b)| buffer_slice[i] = (*b).into()); + .for_each(|(i, b)| buffer_slice[i] = b.0); Ok(()) } } diff --git a/crates/arbolta/src/cell.rs b/crates/arbolta/src/cell.rs index 8eca176..a74720f 100644 --- a/crates/arbolta/src/cell.rs +++ b/crates/arbolta/src/cell.rs @@ -5,6 +5,7 @@ use derive_more::Constructor; use enum_dispatch::enum_dispatch; use indexmap::IndexMap; use serde::{Deserialize, Serialize}; +use smallvec::SmallVec; use std::fmt::Debug; use thiserror::Error; use yosys_netlist_json as yosys; @@ -105,7 +106,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_values() .flatten() .collect::>() - .into_boxed_slice(), + .into(), output_connections["Y"][0], )), "OR" | "$_OR_" | "$reduce_or" => Cell::Or(Or::new( @@ -113,7 +114,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_values() .flatten() .collect::>() - .into_boxed_slice(), + .into(), output_connections["Y"][0], )), "ORNOT" | "$_ORNOT_" => Cell::OrNot(OrNot::new( @@ -121,7 +122,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_values() .flatten() .collect::>() - .into_boxed_slice(), + .into(), output_connections["Y"][0], )), "$_NOR_" | "NOR" => Cell::Nor(Nor::new( @@ -129,7 +130,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_values() .flatten() .collect::>() - .into_boxed_slice(), + .into(), output_connections["Y"][0], )), "XOR" | "$_XOR_" => Cell::Xor(Xor::new( @@ -137,7 +138,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_values() .flatten() .collect::>() - .into_boxed_slice(), + .into(), output_connections["Y"][0], )), "$_XNOR_" | "XNOR" => Cell::Xnor(Xnor::new( @@ -145,7 +146,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_values() .flatten() .collect::>() - .into_boxed_slice(), + .into(), output_connections["Y"][0], )), "AND" | "$_AND_" => Cell::And(And::new( @@ -153,7 +154,7 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_values() .flatten() .collect::>() - .into_boxed_slice(), + .into(), output_connections["Y"][0], )), "ANDNOT" | "$_ANDNOT_" => Cell::AndNot(AndNot::new( @@ -161,17 +162,17 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { .into_values() .flatten() .collect::>() - .into_boxed_slice(), + .into(), output_connections["Y"][0], )), "DFF" | "$_DFF_P_" => Cell::Dff(Dff::new( - Bit::One, + Bit::ONE, input_connections["C"][0], input_connections["D"][0], output_connections["Q"][0], )), "$_SDFF_PP0_" => Cell::DffReset(DffReset::new( - Bit::One, + Bit::ONE, input_connections["D"][0], input_connections["C"][0], input_connections["R"][0], @@ -179,66 +180,66 @@ pub fn create_cell(cell: &yosys::Cell) -> Result { )), "$pos" => Cell::Pos(Pos::new( cell.parameters["A_SIGNED"].to_number().unwrap() == 1, - input_connections["A"].clone().into_boxed_slice(), - output_connections["Y"].clone().into_boxed_slice(), + input_connections["A"].clone().into(), + output_connections["Y"].clone().into(), )), "$neg" => Cell::Neg(Neg::new( cell.parameters["A_SIGNED"].to_number().unwrap() == 1, - input_connections["A"].clone().into_boxed_slice(), - output_connections["Y"].clone().into_boxed_slice(), + input_connections["A"].clone().into(), + output_connections["Y"].clone().into(), )), // Proc Cells "$mux" | "$_MUX_" => Cell::Mux(Mux::new( input_connections["S"][0], - input_connections["A"].clone().into_boxed_slice(), - input_connections["B"].clone().into_boxed_slice(), - output_connections["Y"].clone().into_boxed_slice(), + input_connections["A"].clone().into(), + input_connections["B"].clone().into(), + output_connections["Y"].clone().into(), )), "$dff" => Cell::Reg(Reg::new( - Bit::One, + Bit::ONE, input_connections["CLK"][0], - input_connections["D"].clone().into_boxed_slice(), - output_connections["Q"].clone().into_boxed_slice(), + input_connections["D"].clone().into(), + output_connections["Q"].clone().into(), )), "$add" => Cell::Add(Add::new( // Hacky, fix later cell.parameters["A_SIGNED"].to_number().unwrap() == 1, - input_connections["A"].clone().into_boxed_slice(), - input_connections["B"].clone().into_boxed_slice(), - output_connections["Y"].clone().into_boxed_slice(), + input_connections["A"].clone().into(), + input_connections["B"].clone().into(), + output_connections["Y"].clone().into(), )), "$sub" => Cell::Sub(Sub::new( // Hacky, fix later cell.parameters["A_SIGNED"].to_number().unwrap() == 1, - input_connections["A"].clone().into_boxed_slice(), - input_connections["B"].clone().into_boxed_slice(), - output_connections["Y"].clone().into_boxed_slice(), + input_connections["A"].clone().into(), + input_connections["B"].clone().into(), + output_connections["Y"].clone().into(), )), "$mul" => Cell::Mul(Mul::new( // Hacky, fix later cell.parameters["A_SIGNED"].to_number().unwrap() == cell.parameters["B_SIGNED"].to_number().unwrap(), - input_connections["A"].clone().into_boxed_slice(), - input_connections["B"].clone().into_boxed_slice(), - output_connections["Y"].clone().into_boxed_slice(), + input_connections["A"].clone().into(), + input_connections["B"].clone().into(), + output_connections["Y"].clone().into(), )), "$shl" => Cell::Shl(Shl::new( // Hacky, fix later cell.parameters["A_SIGNED"].to_number().unwrap() == 1, - input_connections["A"].clone().into_boxed_slice(), - input_connections["B"].clone().into_boxed_slice(), - output_connections["Y"].clone().into_boxed_slice(), + input_connections["A"].clone().into(), + input_connections["B"].clone().into(), + output_connections["Y"].clone().into(), )), "$logic_and" => Cell::LogicAnd(LogicAnd::new( // Hacky, fix later - input_connections["A"].clone().into_boxed_slice(), - input_connections["B"].clone().into_boxed_slice(), - output_connections["Y"].clone().into_boxed_slice(), + input_connections["A"].clone().into(), + input_connections["B"].clone().into(), + output_connections["Y"].clone().into(), )), "$logic_not" => Cell::LogicNot(LogicNot::new( // Hacky, fix later - input_connections["A"].clone().into_boxed_slice(), - output_connections["Y"].clone().into_boxed_slice(), + input_connections["A"].clone().into(), + output_connections["Y"].clone().into(), )), _ => return Err(CellError::Unsupported(cell.cell_type.to_string())), }; @@ -276,7 +277,7 @@ impl CellFn for Buf { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Nand { - input_nets: Box<[usize]>, + input_nets: SmallVec<[usize; 64]>, output_net: usize, } @@ -298,7 +299,7 @@ impl CellFn for Nand { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct And { - input_nets: Box<[usize]>, + input_nets: SmallVec<[usize; 64]>, output_net: usize, } @@ -320,7 +321,7 @@ impl CellFn for And { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct AndNot { - input_nets: Box<[usize]>, + input_nets: SmallVec<[usize; 64]>, output_net: usize, } @@ -337,7 +338,7 @@ impl CellFn for AndNot { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Or { - input_nets: Box<[usize]>, + input_nets: SmallVec<[usize; 64]>, output_net: usize, } @@ -359,7 +360,7 @@ impl CellFn for Or { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Nor { - input_nets: Box<[usize]>, + input_nets: SmallVec<[usize; 64]>, output_net: usize, } @@ -381,7 +382,7 @@ impl CellFn for Nor { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Xor { - input_nets: Box<[usize]>, + input_nets: SmallVec<[usize; 64]>, output_net: usize, } @@ -403,7 +404,7 @@ impl CellFn for Xor { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Xnor { - input_nets: Box<[usize]>, + input_nets: SmallVec<[usize; 64]>, output_net: usize, } @@ -425,7 +426,7 @@ impl CellFn for Xnor { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct OrNot { - input_nets: Box<[usize]>, + input_nets: SmallVec<[usize; 64]>, output_net: usize, } @@ -464,7 +465,7 @@ impl DffReset { clock_net, reset_net, data_out_net, - last_clock: Bit::Zero, + last_clock: Bit::ZERO, } } } @@ -473,10 +474,10 @@ impl CellFn for DffReset { fn eval(&mut self, signals: &mut [Signal]) { let clock = !(signals[self.clock_net].get_value() ^ self.polarity); - if clock == Bit::One && self.last_clock == Bit::Zero { - if signals[self.reset_net].get_value() == Bit::One { + if clock == Bit::ONE && self.last_clock == Bit::ZERO { + if signals[self.reset_net].get_value() == Bit::ONE { // Reset - signals[self.data_out_net].set_value(Bit::Zero); + signals[self.data_out_net].set_value(Bit::ZERO); } else { signals[self.data_out_net].set_value(signals[self.data_in_net].get_value()); } @@ -486,7 +487,7 @@ impl CellFn for DffReset { } fn reset(&mut self) { - self.last_clock = Bit::Zero; + self.last_clock = Bit::ZERO; } } @@ -506,7 +507,7 @@ impl Dff { data_in_net, clock_net, data_out_net, - last_clock: Bit::Zero, + last_clock: Bit::ZERO, } } } @@ -515,7 +516,7 @@ impl CellFn for Dff { fn eval(&mut self, signals: &mut [Signal]) { let clock = !(signals[self.clock_net].get_value() ^ self.polarity); - if clock == Bit::One && self.last_clock == Bit::Zero { + if clock == Bit::ONE && self.last_clock == Bit::ZERO { signals[self.data_out_net].set_value(signals[self.data_in_net].get_value()); } @@ -523,16 +524,16 @@ impl CellFn for Dff { } fn reset(&mut self) { - self.last_clock = Bit::Zero; + self.last_clock = Bit::ZERO; } } #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] pub struct Reg { polarity: Bit, - data_in_nets: Box<[usize]>, + data_in_nets: SmallVec<[usize; 64]>, clock_net: usize, - data_out_nets: Box<[usize]>, + data_out_nets: SmallVec<[usize; 64]>, last_clock: Bit, } @@ -540,15 +541,15 @@ impl Reg { pub fn new( polarity: Bit, clock_net: usize, - data_in_nets: Box<[usize]>, - data_out_nets: Box<[usize]>, + data_in_nets: SmallVec<[usize; 64]>, + data_out_nets: SmallVec<[usize; 64]>, ) -> Self { Self { polarity, data_in_nets, clock_net, data_out_nets, - last_clock: Bit::Zero, + last_clock: Bit::ZERO, } } } @@ -557,7 +558,7 @@ impl CellFn for Reg { fn eval(&mut self, signals: &mut [Signal]) { let clock = !(signals[self.clock_net].get_value() ^ self.polarity); - if clock == Bit::One && self.last_clock == Bit::Zero { + if clock == Bit::ONE && self.last_clock == Bit::ZERO { self .data_out_nets .iter() @@ -569,7 +570,7 @@ impl CellFn for Reg { } fn reset(&mut self) { - self.last_clock = Bit::Zero; + self.last_clock = Bit::ZERO; } } @@ -577,27 +578,22 @@ impl CellFn for Reg { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Mux { select_net: usize, - a_nets: Box<[usize]>, - b_nets: Box<[usize]>, - y_nets: Box<[usize]>, + a_nets: SmallVec<[usize; 64]>, + b_nets: SmallVec<[usize; 64]>, + y_nets: SmallVec<[usize; 64]>, } impl CellFn for Mux { fn eval(&mut self, signals: &mut [Signal]) { - if signals[self.select_net].get_value() == Bit::One { - // Select B - self - .y_nets - .iter() - .enumerate() - .for_each(|(i, net)| signals[*net].set_value(signals[self.b_nets[i]].get_value())); + let sel = signals[self.select_net].get_value(); + let src_nets = if sel == Bit::ONE { + &self.b_nets } else { - // Select A - self - .y_nets - .iter() - .enumerate() - .for_each(|(i, net)| signals[*net].set_value(signals[self.a_nets[i]].get_value())); + &self.a_nets + }; + + for (src, dst) in src_nets.iter().zip(self.y_nets.iter()) { + signals[*dst].set_value(signals[*src].get_value()); } } @@ -607,9 +603,9 @@ impl CellFn for Mux { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Add { signed: bool, - a_nets: Box<[usize]>, - b_nets: Box<[usize]>, - y_nets: Box<[usize]>, + a_nets: SmallVec<[usize; 64]>, + b_nets: SmallVec<[usize; 64]>, + y_nets: SmallVec<[usize; 64]>, } impl CellFn for Add { @@ -650,9 +646,9 @@ impl CellFn for Add { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Sub { signed: bool, - a_nets: Box<[usize]>, - b_nets: Box<[usize]>, - y_nets: Box<[usize]>, + a_nets: SmallVec<[usize; 64]>, + b_nets: SmallVec<[usize; 64]>, + y_nets: SmallVec<[usize; 64]>, } impl CellFn for Sub { @@ -693,9 +689,9 @@ impl CellFn for Sub { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Mul { signed: bool, - a_nets: Box<[usize]>, - b_nets: Box<[usize]>, - y_nets: Box<[usize]>, + a_nets: SmallVec<[usize; 64]>, + b_nets: SmallVec<[usize; 64]>, + y_nets: SmallVec<[usize; 64]>, } impl CellFn for Mul { @@ -736,8 +732,8 @@ impl CellFn for Mul { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Pos { signed: bool, - a_nets: Box<[usize]>, - y_nets: Box<[usize]>, + a_nets: SmallVec<[usize; 64]>, + y_nets: SmallVec<[usize; 64]>, } impl CellFn for Pos { @@ -770,8 +766,8 @@ impl CellFn for Pos { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Neg { signed: bool, - a_nets: Box<[usize]>, - y_nets: Box<[usize]>, + a_nets: SmallVec<[usize; 64]>, + y_nets: SmallVec<[usize; 64]>, } impl CellFn for Neg { @@ -804,9 +800,9 @@ impl CellFn for Neg { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Shl { signed: bool, - a_nets: Box<[usize]>, - b_nets: Box<[usize]>, - y_nets: Box<[usize]>, + a_nets: SmallVec<[usize; 64]>, + b_nets: SmallVec<[usize; 64]>, + y_nets: SmallVec<[usize; 64]>, } impl CellFn for Shl { @@ -847,9 +843,9 @@ impl CellFn for Shl { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct LogicAnd { - a_nets: Box<[usize]>, - b_nets: Box<[usize]>, - y_nets: Box<[usize]>, + a_nets: SmallVec<[usize; 64]>, + b_nets: SmallVec<[usize; 64]>, + y_nets: SmallVec<[usize; 64]>, } impl CellFn for LogicAnd { @@ -872,8 +868,8 @@ impl CellFn for LogicAnd { // Hard code as u64 add, fix later // Assume only need to set LSB - let a = Bit::from(a.to_int::() != 0); - let b = Bit::from(b.to_int::() != 0); + let a = Bit(a.to_int::() != 0); + let b = Bit(b.to_int::() != 0); signals[self.y_nets[0]].set_value(a & b); } @@ -882,24 +878,19 @@ impl CellFn for LogicAnd { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct LogicNot { - a_nets: Box<[usize]>, - y_nets: Box<[usize]>, + a_nets: SmallVec<[usize; 64]>, + y_nets: SmallVec<[usize; 64]>, } impl CellFn for LogicNot { fn eval(&mut self, signals: &mut [Signal]) { - let a = BitVec::from( - self - .a_nets - .iter() - .map(|net| signals[*net].get_value()) - .collect::>(), - ); + let mut val = Bit::ZERO; + self + .a_nets + .iter() + .for_each(|net| val = val | signals[*net].get_value()); - // Hard code as u64 add, fix later - // Assume only need to set LSB - let a = Bit::from(a.to_int::() != 0); - signals[self.y_nets[0]].set_value(!a); + signals[self.y_nets[0]].set_value(!val); } fn reset(&mut self) {} diff --git a/crates/arbolta/src/hardware_module.rs b/crates/arbolta/src/hardware_module.rs index e7f38b0..713e3a6 100644 --- a/crates/arbolta/src/hardware_module.rs +++ b/crates/arbolta/src/hardware_module.rs @@ -206,8 +206,8 @@ impl HardwareModule { } let mut signals = vec![Signal::default(); max_signal + 1]; - signals[0].set_constant(Bit::Zero); - signals[1].set_constant(Bit::One); + signals[0].set_constant(Bit::ZERO); + signals[1].set_constant(Bit::ONE); Ok(Self { name: top_module.to_string(), @@ -325,8 +325,8 @@ impl HardwareModule { pub fn reset(&mut self) { self.cells.iter_mut().for_each(|c| c.reset()); self.signals.iter_mut().for_each(|s| s.reset()); - self.signals[0].set_constant(Bit::Zero); - self.signals[1].set_constant(Bit::One); + self.signals[0].set_constant(Bit::ZERO); + self.signals[1].set_constant(Bit::ONE); } pub fn set_port_shape(&mut self, name: &str, shape: &[usize; 2]) -> Result<(), ModuleError> { diff --git a/crates/arbolta/src/port.rs b/crates/arbolta/src/port.rs index 0f6a9a9..a7e7a54 100644 --- a/crates/arbolta/src/port.rs +++ b/crates/arbolta/src/port.rs @@ -7,6 +7,7 @@ use bincode::{Decode, Encode}; use ndarray::{Array1, ArrayView1}; use num_traits::PrimInt; use serde::{Deserialize, Serialize}; +use smallvec::SmallVec; use std::fmt::Debug; use thiserror::Error; use yosys_netlist_json as yosys; @@ -20,7 +21,7 @@ pub enum PortDirection { #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Encode, Decode)] pub struct Port { pub direction: PortDirection, - pub nets: Box<[usize]>, + pub nets: SmallVec<[usize; 512]>, pub shape: [usize; 2], } @@ -63,7 +64,7 @@ impl Port { Self { direction, - nets: nets.into_boxed_slice(), + nets: nets.into(), shape, } } @@ -100,15 +101,8 @@ impl Port { return Err(PortError::Direction); } - let stop_idx = vals.bits.len(); - - for (i, val) in vals - .bits - .iter() - .enumerate() - .take(stop_idx.clamp(0, self.nets.len())) - { - signals[self.nets[i]].set_value(*val); + for (dst, val) in self.nets.iter().zip(vals.bits.iter()) { + signals[*dst].set_value(*val); } Ok(()) diff --git a/crates/arbolta/src/signal.rs b/crates/arbolta/src/signal.rs index 956ef52..b79ce6a 100644 --- a/crates/arbolta/src/signal.rs +++ b/crates/arbolta/src/signal.rs @@ -45,12 +45,8 @@ impl Signal { /// Reset signal value to zero. /// Clear all signal statistics. pub fn reset(&mut self) { - // Ignore constants - // if !self.constant { - // self.value = Bit::Zero - // } self.constant = false; - self.value = Bit::Zero; + self.value = Bit::ZERO; self.toggle_count_falling = 0; self.toggle_count_rising = 0; } @@ -62,14 +58,16 @@ impl Signal { /// Set value of signal. Updates toggle statistics. pub fn set_value(&mut self, val: Bit) { - if !self.constant { - match &[self.value, val] { - [Bit::Zero, Bit::One] => self.toggle_count_rising += 1, - [Bit::One, Bit::Zero] => self.toggle_count_falling += 1, - [Bit::Zero, Bit::Zero] | [Bit::One, Bit::One] => return, - } - self.value = val; + if self.constant || self.value == val { + return; } + + match (self.value, val) { + (Bit::ZERO, Bit::ONE) => self.toggle_count_rising += 1, + (Bit::ONE, Bit::ZERO) => self.toggle_count_falling += 1, + _ => {} + } + self.value = val; } /// Get total signal toggle count (rising + falling). diff --git a/crates/arbolta/tests/test_bit.rs b/crates/arbolta/tests/test_bit.rs index 121fa3f..4462c4c 100644 --- a/crates/arbolta/tests/test_bit.rs +++ b/crates/arbolta/tests/test_bit.rs @@ -7,66 +7,66 @@ use std::str::FromStr; use rstest::rstest; #[rstest] -#[case("0", Bit::Zero)] -#[case("1", Bit::One)] +#[case("0", Bit::ZERO)] +#[case("1", Bit::ONE)] fn test_bit_from_str(#[case] val: String, #[case] expected: Bit) { assert_eq!(Bit::from_str(&val).unwrap(), expected); } #[rstest] -#[case(Bit::Zero, '0')] -#[case(Bit::One, '1')] +#[case(Bit::ZERO, '0')] +#[case(Bit::ONE, '1')] fn test_bit_to_char(#[case] bit: Bit, #[case] expected: char) { assert_eq!(>::into(bit), expected); } #[rstest] -#[case(false, Bit::Zero)] -#[case(true, Bit::One)] +#[case(false, Bit::ZERO)] +#[case(true, Bit::ONE)] fn test_bit_from_bool(#[case] val: bool, #[case] expected: Bit) { - assert_eq!(Bit::from(val), expected); + assert_eq!(Bit(val), expected); } #[rstest] -#[case(Bit::Zero, false)] -#[case(Bit::One, true)] +#[case(Bit::ZERO, false)] +#[case(Bit::ONE, true)] fn test_bit_to_bool(#[case] bit: Bit, #[case] expected: bool) { - assert_eq!(>::into(bit), expected); + assert_eq!(bit.0, expected); } #[rstest] -#[case(0, Bit::Zero)] -#[case(1, Bit::One)] +#[case(0, Bit::ZERO)] +#[case(1, Bit::ONE)] fn test_bit_from_int(#[case] val: usize, #[case] expected: Bit) { assert_eq!(Bit::from_int(val).unwrap(), expected); } #[test] fn test_bit_not() { - assert_eq!(!Bit::Zero, Bit::One); - assert_eq!(!Bit::One, Bit::Zero); + assert_eq!(!Bit::ZERO, Bit::ONE); + assert_eq!(!Bit::ONE, Bit::ZERO); } #[test] fn test_bit_and() { - assert_eq!(Bit::Zero & Bit::Zero, Bit::Zero); - assert_eq!(Bit::Zero & Bit::One, Bit::Zero); - assert_eq!(Bit::One & Bit::Zero, Bit::Zero); - assert_eq!(Bit::One & Bit::One, Bit::One); + assert_eq!(Bit::ZERO & Bit::ZERO, Bit::ZERO); + assert_eq!(Bit::ZERO & Bit::ONE, Bit::ZERO); + assert_eq!(Bit::ONE & Bit::ZERO, Bit::ZERO); + assert_eq!(Bit::ONE & Bit::ONE, Bit::ONE); } #[test] fn test_bit_or() { - assert_eq!(Bit::Zero | Bit::Zero, Bit::Zero); - assert_eq!(Bit::Zero | Bit::One, Bit::One); - assert_eq!(Bit::One | Bit::Zero, Bit::One); - assert_eq!(Bit::One | Bit::One, Bit::One); + assert_eq!(Bit::ZERO | Bit::ZERO, Bit::ZERO); + assert_eq!(Bit::ZERO | Bit::ONE, Bit::ONE); + assert_eq!(Bit::ONE | Bit::ZERO, Bit::ONE); + assert_eq!(Bit::ONE | Bit::ONE, Bit::ONE); } #[test] fn test_bit_xor() { - assert_eq!(Bit::Zero ^ Bit::Zero, Bit::Zero); - assert_eq!(Bit::Zero ^ Bit::One, Bit::One); - assert_eq!(Bit::One ^ Bit::Zero, Bit::One); - assert_eq!(Bit::One ^ Bit::One, Bit::Zero); + assert_eq!(Bit::ZERO ^ Bit::ZERO, Bit::ZERO); + assert_eq!(Bit::ZERO ^ Bit::ONE, Bit::ONE); + assert_eq!(Bit::ONE ^ Bit::ZERO, Bit::ONE); + assert_eq!(Bit::ONE ^ Bit::ONE, Bit::ZERO); } diff --git a/crates/arbolta/tests/test_bitvec.rs b/crates/arbolta/tests/test_bitvec.rs index 8f51268..8bce240 100644 --- a/crates/arbolta/tests/test_bitvec.rs +++ b/crates/arbolta/tests/test_bitvec.rs @@ -8,14 +8,14 @@ use rstest::rstest; #[rstest] // TODO: Generate random bit patterns and check #[case(vec![ - Bit::One, - Bit::Zero, - Bit::One, - Bit::Zero, - Bit::Zero, - Bit::One, - Bit::Zero, - Bit::Zero, + Bit::ONE, + Bit::ZERO, + Bit::ONE, + Bit::ZERO, + Bit::ZERO, + Bit::ONE, + Bit::ZERO, + Bit::ZERO, ], "00100101")] fn test_bits_to_str(#[case] bits: Vec, #[case] expected: String) { let bits = BitVec { bits: bits }; @@ -24,14 +24,14 @@ fn test_bits_to_str(#[case] bits: Vec, #[case] expected: String) { #[rstest] #[case("00100101", vec![ - Bit::One, - Bit::Zero, - Bit::One, - Bit::Zero, - Bit::Zero, - Bit::One, - Bit::Zero, - Bit::Zero, + Bit::ONE, + Bit::ZERO, + Bit::ONE, + Bit::ZERO, + Bit::ZERO, + Bit::ONE, + Bit::ZERO, + Bit::ZERO, ] )] fn test_str_to_bits(#[case] val: String, #[case] expected: Vec) { @@ -49,14 +49,14 @@ fn test_str_to_bits(#[case] val: String, #[case] expected: Vec) { false, true, ], vec![ - Bit::One, - Bit::Zero, - Bit::One, - Bit::Zero, - Bit::Zero, - Bit::One, - Bit::Zero, - Bit::Zero, + Bit::ONE, + Bit::ZERO, + Bit::ONE, + Bit::ZERO, + Bit::ZERO, + Bit::ONE, + Bit::ZERO, + Bit::ZERO, ] )] fn test_bools_to_bits(#[case] vals: Vec, #[case] expected: Vec) { diff --git a/crates/arbolta/tests/test_cell.rs b/crates/arbolta/tests/test_cell.rs index 1408e1f..4a92691 100644 --- a/crates/arbolta/tests/test_cell.rs +++ b/crates/arbolta/tests/test_cell.rs @@ -49,10 +49,10 @@ fn generate_cell( } #[rstest] -#[case("NOT", Bit::Zero, Bit::One)] -#[case("NOT", Bit::One, Bit::Zero)] -#[case("BUF", Bit::Zero, Bit::Zero)] -#[case("BUF", Bit::One, Bit::One)] +#[case("NOT", Bit::ZERO, Bit::ONE)] +#[case("NOT", Bit::ONE, Bit::ZERO)] +#[case("BUF", Bit::ZERO, Bit::ZERO)] +#[case("BUF", Bit::ONE, Bit::ONE)] fn test_cell_1_input(#[case] cell_type: &str, #[case] a: Bit, #[case] expected: Bit) { let inputs = HashMap::from([("A", 1)]); let outputs = HashMap::from([("Y", 1)]); @@ -63,38 +63,38 @@ fn test_cell_1_input(#[case] cell_type: &str, #[case] a: Bit, #[case] expected: } #[rstest] -#[case("AND", Bit::Zero, Bit::Zero, Bit::Zero)] -#[case("AND", Bit::Zero, Bit::One, Bit::Zero)] -#[case("AND", Bit::One, Bit::Zero, Bit::Zero)] -#[case("AND", Bit::One, Bit::One, Bit::One)] -#[case("ANDNOT", Bit::Zero, Bit::Zero, Bit::Zero)] -#[case("ANDNOT", Bit::Zero, Bit::One, Bit::Zero)] -#[case("ANDNOT", Bit::One, Bit::Zero, Bit::One)] -#[case("ANDNOT", Bit::One, Bit::One, Bit::Zero)] -#[case("NOR", Bit::Zero, Bit::Zero, Bit::One)] -#[case("NOR", Bit::Zero, Bit::One, Bit::Zero)] -#[case("NOR", Bit::One, Bit::Zero, Bit::Zero)] -#[case("NOR", Bit::One, Bit::One, Bit::Zero)] -#[case("NAND", Bit::Zero, Bit::Zero, Bit::One)] -#[case("NAND", Bit::Zero, Bit::One, Bit::One)] -#[case("NAND", Bit::One, Bit::Zero, Bit::One)] -#[case("NAND", Bit::One, Bit::One, Bit::Zero)] -#[case("OR", Bit::Zero, Bit::Zero, Bit::Zero)] -#[case("OR", Bit::Zero, Bit::One, Bit::One)] -#[case("OR", Bit::One, Bit::Zero, Bit::One)] -#[case("OR", Bit::One, Bit::One, Bit::One)] -#[case("XOR", Bit::Zero, Bit::Zero, Bit::Zero)] -#[case("XOR", Bit::Zero, Bit::One, Bit::One)] -#[case("XOR", Bit::One, Bit::Zero, Bit::One)] -#[case("XOR", Bit::One, Bit::One, Bit::Zero)] -#[case("XNOR", Bit::Zero, Bit::Zero, Bit::One)] -#[case("XNOR", Bit::Zero, Bit::One, Bit::Zero)] -#[case("XNOR", Bit::One, Bit::Zero, Bit::Zero)] -#[case("XNOR", Bit::One, Bit::One, Bit::One)] -#[case("ORNOT", Bit::Zero, Bit::Zero, Bit::One)] -#[case("ORNOT", Bit::Zero, Bit::One, Bit::Zero)] -#[case("ORNOT", Bit::One, Bit::Zero, Bit::One)] -#[case("ORNOT", Bit::One, Bit::One, Bit::One)] +#[case("AND", Bit::ZERO, Bit::ZERO, Bit::ZERO)] +#[case("AND", Bit::ZERO, Bit::ONE, Bit::ZERO)] +#[case("AND", Bit::ONE, Bit::ZERO, Bit::ZERO)] +#[case("AND", Bit::ONE, Bit::ONE, Bit::ONE)] +#[case("ANDNOT", Bit::ZERO, Bit::ZERO, Bit::ZERO)] +#[case("ANDNOT", Bit::ZERO, Bit::ONE, Bit::ZERO)] +#[case("ANDNOT", Bit::ONE, Bit::ZERO, Bit::ONE)] +#[case("ANDNOT", Bit::ONE, Bit::ONE, Bit::ZERO)] +#[case("NOR", Bit::ZERO, Bit::ZERO, Bit::ONE)] +#[case("NOR", Bit::ZERO, Bit::ONE, Bit::ZERO)] +#[case("NOR", Bit::ONE, Bit::ZERO, Bit::ZERO)] +#[case("NOR", Bit::ONE, Bit::ONE, Bit::ZERO)] +#[case("NAND", Bit::ZERO, Bit::ZERO, Bit::ONE)] +#[case("NAND", Bit::ZERO, Bit::ONE, Bit::ONE)] +#[case("NAND", Bit::ONE, Bit::ZERO, Bit::ONE)] +#[case("NAND", Bit::ONE, Bit::ONE, Bit::ZERO)] +#[case("OR", Bit::ZERO, Bit::ZERO, Bit::ZERO)] +#[case("OR", Bit::ZERO, Bit::ONE, Bit::ONE)] +#[case("OR", Bit::ONE, Bit::ZERO, Bit::ONE)] +#[case("OR", Bit::ONE, Bit::ONE, Bit::ONE)] +#[case("XOR", Bit::ZERO, Bit::ZERO, Bit::ZERO)] +#[case("XOR", Bit::ZERO, Bit::ONE, Bit::ONE)] +#[case("XOR", Bit::ONE, Bit::ZERO, Bit::ONE)] +#[case("XOR", Bit::ONE, Bit::ONE, Bit::ZERO)] +#[case("XNOR", Bit::ZERO, Bit::ZERO, Bit::ONE)] +#[case("XNOR", Bit::ZERO, Bit::ONE, Bit::ZERO)] +#[case("XNOR", Bit::ONE, Bit::ZERO, Bit::ZERO)] +#[case("XNOR", Bit::ONE, Bit::ONE, Bit::ONE)] +#[case("ORNOT", Bit::ZERO, Bit::ZERO, Bit::ONE)] +#[case("ORNOT", Bit::ZERO, Bit::ONE, Bit::ZERO)] +#[case("ORNOT", Bit::ONE, Bit::ZERO, Bit::ONE)] +#[case("ORNOT", Bit::ONE, Bit::ONE, Bit::ONE)] fn test_cell_2_input( #[case] cell_type: &str, #[case] a: Bit, @@ -116,14 +116,14 @@ fn test_cell_2_input( } #[rstest] -#[case("OR", Bit::Zero, Bit::Zero, Bit::Zero, Bit::Zero)] -#[case("OR", Bit::Zero, Bit::Zero, Bit::One, Bit::One)] -#[case("OR", Bit::Zero, Bit::One, Bit::Zero, Bit::One)] -#[case("OR", Bit::Zero, Bit::One, Bit::One, Bit::One)] -#[case("OR", Bit::One, Bit::Zero, Bit::Zero, Bit::One)] -#[case("OR", Bit::One, Bit::Zero, Bit::One, Bit::One)] -#[case("OR", Bit::One, Bit::One, Bit::Zero, Bit::One)] -#[case("OR", Bit::One, Bit::One, Bit::One, Bit::One)] +#[case("OR", Bit::ZERO, Bit::ZERO, Bit::ZERO, Bit::ZERO)] +#[case("OR", Bit::ZERO, Bit::ZERO, Bit::ONE, Bit::ONE)] +#[case("OR", Bit::ZERO, Bit::ONE, Bit::ZERO, Bit::ONE)] +#[case("OR", Bit::ZERO, Bit::ONE, Bit::ONE, Bit::ONE)] +#[case("OR", Bit::ONE, Bit::ZERO, Bit::ZERO, Bit::ONE)] +#[case("OR", Bit::ONE, Bit::ZERO, Bit::ONE, Bit::ONE)] +#[case("OR", Bit::ONE, Bit::ONE, Bit::ZERO, Bit::ONE)] +#[case("OR", Bit::ONE, Bit::ONE, Bit::ONE, Bit::ONE)] fn test_cell_3_input( #[case] cell_type: &str, #[case] a: Bit, @@ -150,60 +150,60 @@ fn test_cell_3_input( fn test_cell_dff_p() { // D, C, Q let (data_in, clock, data_out) = (0, 1, 2); - let mut cell = Dff::new(Bit::One, clock, data_in, data_out); + let mut cell = Dff::new(Bit::ONE, clock, data_in, data_out); let mut signals = vec![Signal::default(); 3].into_boxed_slice(); cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::Zero); + assert_eq!(signals[data_out].get_value(), Bit::ZERO); - signals[data_in].set_value(Bit::One); + signals[data_in].set_value(Bit::ONE); cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::Zero); + assert_eq!(signals[data_out].get_value(), Bit::ZERO); - signals[clock].set_value(Bit::One); // Rising edge + signals[clock].set_value(Bit::ONE); // Rising edge cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::One); + assert_eq!(signals[data_out].get_value(), Bit::ONE); - signals[clock].set_value(Bit::Zero); // Falling edge + signals[clock].set_value(Bit::ZERO); // Falling edge cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::One); + assert_eq!(signals[data_out].get_value(), Bit::ONE); - signals[data_in].set_value(Bit::Zero); + signals[data_in].set_value(Bit::ZERO); cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::One); + assert_eq!(signals[data_out].get_value(), Bit::ONE); - signals[clock].set_value(Bit::One); // Rising edge + signals[clock].set_value(Bit::ONE); // Rising edge cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::Zero); + assert_eq!(signals[data_out].get_value(), Bit::ZERO); } #[rstest] fn test_cell_sdff_pp() { // D, C, R, Q let (data_in, clock, reset, data_out) = (0, 1, 2, 3); - let mut cell = DffReset::new(Bit::One, data_in, clock, reset, data_out); + let mut cell = DffReset::new(Bit::ONE, data_in, clock, reset, data_out); let mut signals = vec![Signal::default(); 4].into_boxed_slice(); cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::Zero); + assert_eq!(signals[data_out].get_value(), Bit::ZERO); - signals[data_in].set_value(Bit::One); + signals[data_in].set_value(Bit::ONE); cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::Zero); + assert_eq!(signals[data_out].get_value(), Bit::ZERO); - signals[clock].set_value(Bit::One); // Rising edge + signals[clock].set_value(Bit::ONE); // Rising edge cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::One); + assert_eq!(signals[data_out].get_value(), Bit::ONE); - signals[clock].set_value(Bit::Zero); // Falling edge + signals[clock].set_value(Bit::ZERO); // Falling edge cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::One); + assert_eq!(signals[data_out].get_value(), Bit::ONE); - signals[reset].set_value(Bit::One); + signals[reset].set_value(Bit::ONE); cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::One); + assert_eq!(signals[data_out].get_value(), Bit::ONE); - signals[clock].set_value(Bit::One); // Rising edge + signals[clock].set_value(Bit::ONE); // Rising edge cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::Zero); + assert_eq!(signals[data_out].get_value(), Bit::ZERO); } diff --git a/crates/arbolta/tests/test_signal.rs b/crates/arbolta/tests/test_signal.rs index 617778f..7df9363 100644 --- a/crates/arbolta/tests/test_signal.rs +++ b/crates/arbolta/tests/test_signal.rs @@ -8,7 +8,7 @@ use arbolta::signal::Signal; fn test_signal_net_init() { let x = Signal::default(); - assert_eq!(x.get_value(), Bit::Zero); + assert_eq!(x.get_value(), Bit::ZERO); assert_eq!(x.get_toggle_count_falling(), 0); assert_eq!(x.get_toggle_count_rising(), 0); assert_eq!(x.get_total_toggle_count(), 0); @@ -18,9 +18,9 @@ fn test_signal_net_init() { fn test_signal_net_set_value() { let mut x = Signal::default(); - assert_eq!(x.get_value(), Bit::Zero); - x.set_value(Bit::One); - assert_eq!(x.get_value(), Bit::One); + assert_eq!(x.get_value(), Bit::ZERO); + x.set_value(Bit::ONE); + assert_eq!(x.get_value(), Bit::ONE); } #[test] @@ -31,7 +31,7 @@ fn test_signal_net_toggle_rising() { assert_eq!(x.get_toggle_count_falling(), 0); assert_eq!(x.get_toggle_count_rising(), 0); - x.set_value(Bit::One); + x.set_value(Bit::ONE); assert_eq!(x.get_total_toggle_count(), 1); assert_eq!(x.get_toggle_count_falling(), 0); @@ -41,13 +41,13 @@ fn test_signal_net_toggle_rising() { #[test] fn test_signal_net_toggle_falling() { let mut x = Signal::default(); - x.value = Bit::One; + x.value = Bit::ONE; assert_eq!(x.get_total_toggle_count(), 0); assert_eq!(x.get_toggle_count_falling(), 0); assert_eq!(x.get_toggle_count_rising(), 0); - x.set_value(Bit::Zero); + x.set_value(Bit::ZERO); assert_eq!(x.get_total_toggle_count(), 1); assert_eq!(x.get_toggle_count_falling(), 1); @@ -62,7 +62,7 @@ fn test_signal_net_toggle_same_zero() { assert_eq!(x.get_toggle_count_falling(), 0); assert_eq!(x.get_toggle_count_rising(), 0); - x.set_value(Bit::Zero); + x.set_value(Bit::ZERO); assert_eq!(x.get_total_toggle_count(), 0); assert_eq!(x.get_toggle_count_falling(), 0); @@ -72,13 +72,13 @@ fn test_signal_net_toggle_same_zero() { #[test] fn test_signal_net_toggle_same_one() { let mut x = Signal::default(); - x.value = Bit::One; + x.value = Bit::ONE; assert_eq!(x.get_total_toggle_count(), 0); assert_eq!(x.get_toggle_count_falling(), 0); assert_eq!(x.get_toggle_count_rising(), 0); - x.set_value(Bit::One); + x.set_value(Bit::ONE); assert_eq!(x.get_total_toggle_count(), 0); assert_eq!(x.get_toggle_count_falling(), 0); From 11d56d1d7fca0146d0c81d0fe9b4dbc70c0c164e Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Sat, 19 Apr 2025 21:55:09 +0000 Subject: [PATCH 11/64] Fixed input quantization for MNIST model --- experiments/mnist/utils/model.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/experiments/mnist/utils/model.py b/experiments/mnist/utils/model.py index a2bc88d..a01338d 100644 --- a/experiments/mnist/utils/model.py +++ b/experiments/mnist/utils/model.py @@ -10,22 +10,18 @@ class QuantModel(nn.Module): def __init__(self, input_bit_width: int = 8, weight_bit_width: int = 8): super(QuantModel, self).__init__() self.flatten_inp = nn.Flatten() - self.quant_inp = qnn.QuantIdentity( - act_quant=Int8ActPerTensorFloat, - bit_width=input_bit_width, - return_quant_tensor=True, - ) self.fc1 = qnn.QuantLinear( in_features=28 * 28, out_features=10, bias=False, + input_quant=Int8ActPerTensorFloat, + input_bit_width=input_bit_width, weight_quant=Int8WeightPerTensorFloat, weight_bit_width=weight_bit_width, ) def forward(self, x): out = self.flatten_inp(x) - out = self.quant_inp(out) out = self.fc1(out) out = F.log_softmax(out, dim=-1) return out From 529914c0ae48fabd8d3debd1d54efc59a52cf87d Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Sat, 19 Apr 2025 22:02:28 +0000 Subject: [PATCH 12/64] Updated training notebook --- experiments/mnist/train.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/experiments/mnist/train.ipynb b/experiments/mnist/train.ipynb index 2d269d7..224e46f 100644 --- a/experiments/mnist/train.ipynb +++ b/experiments/mnist/train.ipynb @@ -25,10 +25,10 @@ "metadata": {}, "outputs": [], "source": [ - "learning_rate = 0.001\n", + "learning_rate = 0.01\n", "momentum = 0.5\n", "device = \"cuda:0\"\n", - "model = QuantModel().to(device)\n", + "model = QuantModel(input_bit_width=4, weight_bit_width=4).to(device)\n", "criterion = nn.CrossEntropyLoss()\n", "optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum)" ] @@ -77,7 +77,7 @@ "\n", " for i, correct in enumerate(preds == targets):\n", " if correct: # Get quantized image\n", - " x_quant: IntQuantTensor = model.quant_inp(torch.flatten(images[i]))\n", + " x_quant: IntQuantTensor = model.fc1.input_quant(torch.flatten(images[i]))\n", " if input_scale is None:\n", " input_scale = x_quant.scale.cpu()\n", "\n", From 484fefb27de105e48b6c3a14b58f2cb2e0ea5cdf Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 7 May 2025 22:51:53 +0000 Subject: [PATCH 13/64] Added minifloat support to MNIST example --- .pre-commit-config.yaml | 29 +++-- experiments/mnist/train.ipynb | 110 +++++++++------- experiments/mnist/utils/__init__.py | 19 ++- experiments/mnist/utils/dataset.py | 27 ++-- experiments/mnist/utils/hardware.py | 5 +- experiments/mnist/utils/model.py | 194 ++++++++++++++++++++++++++-- experiments/mnist/utils/train.py | 32 ++++- 7 files changed, 322 insertions(+), 94 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d942220..eb0745c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,17 +23,26 @@ repos: name: clippy (rust) - id: cargo-check name: cargo-check (rust) - - repo: https://github.com/pycqa/isort - rev: 5.13.2 + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.11.8 hooks: - - id: isort - name: isort (python) - - repo: https://github.com/google/yapf - rev: v0.43.0 - hooks: - - id: yapf - name: yapf (python) - args: ["-i"] + # Run the linter. + - id: ruff + # Run the formatter. + - id: ruff-format + + # - repo: https://github.com/pycqa/isort + # rev: 5.13.2 + # hooks: + # - id: isort + # name: isort (python) + # - repo: https://github.com/google/yapf + # rev: v0.43.0 + # hooks: + # - id: yapf + # name: yapf (python) + # args: ["-i"] - repo: https://github.com/srstevenson/nb-clean rev: 4.0.1 hooks: diff --git a/experiments/mnist/train.ipynb b/experiments/mnist/train.ipynb index 224e46f..b903de0 100644 --- a/experiments/mnist/train.ipynb +++ b/experiments/mnist/train.ipynb @@ -7,15 +7,22 @@ "metadata": {}, "outputs": [], "source": [ + "import os\n", + "\n", + "import numpy as np\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", - "from brevitas.quant_tensor.int_quant_tensor import IntQuantTensor\n", - "from torch import Tensor\n", - "from utils import QuantModel, get_mnist_dataloaders, test_model, train_for_epoch\n", + "from utils import (\n", + " FloatQuantLinearModel,\n", + " IntQuantLinearModel,\n", + " get_mnist_dataloaders,\n", + " test_model,\n", + " train_for_epoch,\n", + ")\n", "\n", - "DATASET_PATH = \"/home/a.redding/workspace/datasets\"\n", - "EXPORT_PATH = \"quant.pt\"" + "DATASET_PATH = \"./data\" # Change me\n", + "EXPORT_PATH = \"./model_outputs\" # Change me" ] }, { @@ -25,10 +32,33 @@ "metadata": {}, "outputs": [], "source": [ + "# Training parameters\n", "learning_rate = 0.01\n", "momentum = 0.5\n", - "device = \"cuda:0\"\n", - "model = QuantModel(input_bit_width=4, weight_bit_width=4).to(device)\n", + "\n", + "# ++++ Quantization method (comment/uncomment) ++++\n", + "# Int quant\n", + "model = IntQuantLinearModel(input_bit_width=4, weight_bit_width=4)\n", + "\n", + "# Float quant\n", + "model = FloatQuantLinearModel(\n", + " input_exponent_bit_width=2,\n", + " input_mantissa_bit_width=1,\n", + " weight_exponent_bit_width=4,\n", + " weight_mantissa_bit_width=3,\n", + ")\n", + "\n", + "# ---- Quantization method ----\n", + "\n", + "device = (\n", + " \"xpu\"\n", + " if torch.xpu.is_available() # For the only person who has an Intel GPU\n", + " else \"cuda\"\n", + " if torch.cuda.is_available()\n", + " else \"cpu\"\n", + ")\n", + "\n", + "model = model.to(device)\n", "criterion = nn.CrossEntropyLoss()\n", "optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum)" ] @@ -40,7 +70,9 @@ "metadata": {}, "outputs": [], "source": [ - "train_loader, test_loader = get_mnist_dataloaders(DATASET_PATH)" + "train_loader, test_loader = get_mnist_dataloaders(\n", + " DATASET_PATH, pin_memory=True, pin_memory_device=device\n", + ")" ] }, { @@ -51,6 +83,7 @@ "outputs": [], "source": [ "for epoch in range(10):\n", + " print(f\"Epoch: {epoch}\")\n", " train_for_epoch(model, device, train_loader, criterion, optimizer)\n", " test_model(model, device, test_loader, F.cross_entropy)" ] @@ -58,64 +91,43 @@ { "cell_type": "code", "execution_count": null, - "id": "35ee2d90", + "id": "979531b2", "metadata": {}, "outputs": [], "source": [ - "model_weights: Tensor\n", - "model_scale: Tensor\n", - "correct_inputs: list[Tensor] = []\n", - "input_scale: Tensor = None\n", - "correct_targets: list[Tensor] = []\n", + "# Get all correctly classified images/inputs from test dataset\n", + "correct_inputs, correct_targets = test_model(\n", + " model, device, test_loader, F.cross_entropy, return_correct=True\n", + ")\n", "\n", - "with torch.no_grad():\n", - " for images, targets in test_loader:\n", - " images, targets = images.to(device), targets.to(device)\n", - "\n", - " logits = model(images)\n", - " preds = logits.argmax(dim=1, keepdim=True).squeeze()\n", - "\n", - " for i, correct in enumerate(preds == targets):\n", - " if correct: # Get quantized image\n", - " x_quant: IntQuantTensor = model.fc1.input_quant(torch.flatten(images[i]))\n", - " if input_scale is None:\n", - " input_scale = x_quant.scale.cpu()\n", - "\n", - " correct_inputs.append(x_quant.int().cpu())\n", - " correct_targets.append(targets[i].cpu())\n", - "\n", - " # Get weights last for faster inference\n", - " quant_weight = model.cpu().fc1.quant_weight()\n", - " model_weights = quant_weight.int()\n", - " model_scale = quant_weight.scale\n", - "\n", - "correct_inputs = torch.vstack(correct_inputs)\n", - "correct_targets = torch.vstack(correct_targets)" + "# Get quantized weights and inputs\n", + "model_weights, model_scale = model.quant_weight()\n", + "correct_inputs, input_scale = model.quant_input(correct_inputs)" ] }, { "cell_type": "code", "execution_count": null, - "id": "ffd16f06", + "id": "52d7e089", "metadata": {}, "outputs": [], "source": [ - "torch.save(\n", - " {\n", - " \"model_weights\": model_weights,\n", - " \"model_scale\": model_scale,\n", - " \"correct_inputs\": correct_inputs,\n", - " \"input_scale\": input_scale,\n", - " \"correct_targets\": correct_targets,\n", - " },\n", - " EXPORT_PATH,\n", - ")" + "# Create output directory if it doesn't exist\n", + "if not os.path.exists(EXPORT_PATH):\n", + " os.makedirs(EXPORT_PATH)\n", + "\n", + "# Export NumPy for compatibility with Rust\n", + "np.save(EXPORT_PATH + \"/model_weights.npy\", model_weights.cpu().numpy())\n", + "np.save(EXPORT_PATH + \"/model_scale.npy\", model_scale.cpu().numpy())\n", + "np.save(EXPORT_PATH + \"/correct_inputs.npy\", correct_inputs.cpu().numpy())\n", + "np.save(EXPORT_PATH + \"/input_scale.npy\", input_scale.cpu().numpy())\n", + "np.save(EXPORT_PATH + \"/correct_targets.npy\", correct_targets.cpu().numpy())" ] } ], "metadata": { "kernelspec": { - "display_name": "base", + "display_name": "arbolta", "language": "python", "name": "python3" }, diff --git a/experiments/mnist/utils/__init__.py b/experiments/mnist/utils/__init__.py index 227ed33..e57e7cb 100644 --- a/experiments/mnist/utils/__init__.py +++ b/experiments/mnist/utils/__init__.py @@ -1,4 +1,15 @@ -from .dataset import * -from .hardware import * -from .model import * -from .train import * +from .dataset import get_mnist_dataloaders +from .hardware import run_systolic_array +from .model import IntQuantLinearModel, FloatQuantLinearModel, mf_to_raw, raw_to_mf +from .train import train_for_epoch, test_model + +__all__ = [ + "get_mnist_dataloaders", + "run_systolic_array", + "IntQuantLinearModel", + "FloatQuantLinearModel", + "mf_to_raw", + "raw_to_mf", + "train_for_epoch", + "test_model", +] diff --git a/experiments/mnist/utils/dataset.py b/experiments/mnist/utils/dataset.py index cd9325e..2d13494 100644 --- a/experiments/mnist/utils/dataset.py +++ b/experiments/mnist/utils/dataset.py @@ -8,21 +8,22 @@ def get_mnist_dataloaders( batch_size_train: int = 64, batch_size_test: int = 1024, pin_memory: bool = True, + pin_memory_device: str = "cpu", download: bool = False, ): - image_transform = torchvision.transforms.Compose([ - torchvision.transforms.ToTensor(), - torchvision.transforms.Normalize((0.1307, ), (0.3081, )), - ]) + image_transform = torchvision.transforms.Compose( + [ + torchvision.transforms.ToTensor(), + torchvision.transforms.Normalize((0.1307,), (0.3081,)), + ] + ) - train_dataset = torchvision.datasets.MNIST(data_root, - train=True, - download=download, - transform=image_transform) - test_dataset = torchvision.datasets.MNIST(data_root, - train=False, - download=download, - transform=image_transform) + train_dataset = torchvision.datasets.MNIST( + data_root, train=True, download=download, transform=image_transform + ) + test_dataset = torchvision.datasets.MNIST( + data_root, train=False, download=download, transform=image_transform + ) train_loader = DataLoader( train_dataset, @@ -30,6 +31,7 @@ def get_mnist_dataloaders( shuffle=True, num_workers=num_workers, pin_memory=pin_memory, + pin_memory_device=pin_memory_device, ) test_loader = DataLoader( @@ -38,6 +40,7 @@ def get_mnist_dataloaders( shuffle=True, num_workers=num_workers, pin_memory=pin_memory, + pin_memory_device=pin_memory_device, ) return train_loader, test_loader diff --git a/experiments/mnist/utils/hardware.py b/experiments/mnist/utils/hardware.py index 962d7da..b1c5d6f 100644 --- a/experiments/mnist/utils/hardware.py +++ b/experiments/mnist/utils/hardware.py @@ -3,8 +3,9 @@ from arbolta import HardwareDesign -def run_systolic_array(design: HardwareDesign, x: torch.Tensor, - k: torch.Tensor) -> torch.Tensor: +def run_systolic_array( + design: HardwareDesign, x: torch.Tensor, k: torch.Tensor +) -> torch.Tensor: """ Run inputs through systolic array. Expects x: (K,R), k: (K,C) -> y: (C,R) diff --git a/experiments/mnist/utils/model.py b/experiments/mnist/utils/model.py index a01338d..fccb5cf 100644 --- a/experiments/mnist/utils/model.py +++ b/experiments/mnist/utils/model.py @@ -1,27 +1,195 @@ +from abc import ABCMeta, abstractmethod + import brevitas.nn as qnn +import torch import torch.nn as nn import torch.nn.functional as F -from brevitas.quant.scaled_int import (Int8ActPerTensorFloat, - Int8WeightPerTensorFloat) +from brevitas.inject import ExtendedInjector +from brevitas.quant.experimental.float_base import ( + FloatActBase, + FloatBase, + FloatWeightBase, +) +from brevitas.quant.scaled_int import Int8ActPerTensorFloat, Int8WeightPerTensorFloat +from brevitas.quant_tensor.float_quant_tensor import FloatQuantTensor +from brevitas.quant_tensor.int_quant_tensor import IntQuantTensor +from torch import Tensor +__all__ = ["IntQuantLinearModel", "FloatQuantLinearModel", "mf_to_raw", "raw_to_mf"] -class QuantModel(nn.Module): - def __init__(self, input_bit_width: int = 8, weight_bit_width: int = 8): - super(QuantModel, self).__init__() +class QuantLinearModel(nn.Module, metaclass=ABCMeta): + # Pass kwargs to QuantLinear layer + def __init__(self, **kwargs): + super(QuantLinearModel, self).__init__() self.flatten_inp = nn.Flatten() self.fc1 = qnn.QuantLinear( - in_features=28 * 28, - out_features=10, - bias=False, - input_quant=Int8ActPerTensorFloat, - input_bit_width=input_bit_width, - weight_quant=Int8WeightPerTensorFloat, - weight_bit_width=weight_bit_width, + in_features=28 * 28, out_features=10, bias=False, **kwargs ) - def forward(self, x): + def forward(self, x: Tensor) -> Tensor: out = self.flatten_inp(x) out = self.fc1(out) out = F.log_softmax(out, dim=-1) return out + + @abstractmethod + def quant_weight(self) -> tuple[Tensor, Tensor]: + """ + Get quantized model weights as uints + """ + pass + + @abstractmethod + def quant_input(self, x: Tensor) -> tuple[Tensor, Tensor]: + """ + Quantize tensor as uints + """ + pass + + +class IntQuantLinearModel(QuantLinearModel): + def __init__(self, input_bit_width: int = 8, weight_bit_width: int = 8): + super(IntQuantLinearModel, self).__init__( + input_quant=Int8ActPerTensorFloat, + input_bit_width=input_bit_width, + weight_quant=Int8WeightPerTensorFloat, + weight_bit_width=weight_bit_width, + ) + + def quant_weight(self) -> tuple[Tensor, Tensor]: + with torch.no_grad(): + weights: IntQuantTensor = self.fc1.quant_weight() + + return weights.int(), weights.scale + + def quant_input(self, x: Tensor) -> tuple[Tensor, Tensor]: + with torch.no_grad(): + inputs: IntQuantTensor = self.fc1.input_quant(x) + + return inputs.int(), inputs.scale + + +# Helper for converting quantized floats to raw binary +def mf_to_raw( + x: Tensor, + exponent_bit_width: int, + mantissa_bit_width: int, + exponent_bias: int, + eps: float, +) -> Tensor: + emin = -exponent_bias + 1 + + sign = torch.signbit(x).type(torch.int) + exp = torch.floor(torch.log2(torch.abs(x) + eps)).type(torch.int) + exp = torch.clamp_min(exp, emin) + man = torch.abs(x) / torch.exp2(exp) + + exp_bits = exp - (man < 1).type(torch.int) + exponent_bias # Denorm + man_bits = (man * (1 << mantissa_bit_width)).type(torch.int) + man_bits = man_bits & ((1 << mantissa_bit_width) - 1) + + return ( + (sign << (exponent_bit_width + mantissa_bit_width)) + | (exp_bits << mantissa_bit_width) + | man_bits + ) + + +# Helper for converting raw binary minifloats to floats +def raw_to_mf( + x: Tensor, + exponent_bit_width: int, + mantissa_bit_width: int, + exponent_bias: int, +) -> Tensor: + emin = -exponent_bias + 1 + + sign_mask = 1 << (exponent_bit_width + mantissa_bit_width) + exp_mask = ((1 << exponent_bit_width) - 1) << mantissa_bit_width + man_mask = (1 << mantissa_bit_width) - 1 + + sign = torch.where((x & sign_mask) == 0, 1.0, -1.0) + exp_denorm = ((x & exp_mask) >> mantissa_bit_width).type(torch.float) + man_denorm = (x & man_mask).type(torch.float) / (2**mantissa_bit_width) + + exp = torch.where(exp_denorm == 0, emin, exp_denorm - exponent_bias) + man = torch.where(exp_denorm == 0, man_denorm, 1.0 + man_denorm) + + return sign * man * torch.exp2(exp) + + +# Helper for creating custom minifloat quantizers +def fp_mixin_factory( + exponent_bit_width: int, mantissa_bit_width: int, base_class: FloatBase +): + bit_width = 1 + exponent_bit_width + mantissa_bit_width + name = f"Fp{bit_width}e{exponent_bit_width}m{mantissa_bit_width}" + mixin = type( + name + "Mixin", + (ExtendedInjector,), + { + # Add sign bit + "bit_width": bit_width, + "exponent_bit_width": exponent_bit_width, + "mantissa_bit_width": mantissa_bit_width, + "saturating": True, + }, + ) + + if base_class is FloatActBase: + class_name = name + "Act" + elif base_class is FloatWeightBase: + class_name = name + "Weight" + else: + raise TypeError("Unsupported Float Base") + + return type(class_name, (mixin, base_class), {}) + + +class FloatQuantLinearModel(QuantLinearModel): + def __init__( + self, + input_exponent_bit_width: int = 4, + input_mantissa_bit_width: int = 3, + weight_exponent_bit_width: int = 4, + weight_mantissa_bit_width: int = 3, + ): + input_quant = fp_mixin_factory( + input_exponent_bit_width, input_mantissa_bit_width, FloatActBase + ) + weight_quant = fp_mixin_factory( + weight_exponent_bit_width, weight_mantissa_bit_width, FloatWeightBase + ) + super(FloatQuantLinearModel, self).__init__( + input_quant=input_quant, + weight_quant=weight_quant, + ) + + def quant_weight(self) -> tuple[Tensor, Tensor]: + with torch.no_grad(): + weights: FloatQuantTensor = self.fc1.quant_weight() + + raw_weights = mf_to_raw( + weights.minifloat(), + weights.exponent_bit_width.int(), + weights.mantissa_bit_width.int(), + weights.exponent_bias.int(), + weights.eps, + ).type(torch.uint8) + + return raw_weights, weights.scale + + def quant_input(self, x: Tensor) -> tuple[Tensor, Tensor]: + with torch.no_grad(): + inputs: FloatQuantTensor = self.fc1.input_quant(x) + + raw_inputs = mf_to_raw( + inputs.minifloat(), + inputs.exponent_bit_width.int(), + inputs.mantissa_bit_width.int(), + inputs.exponent_bias.int(), + inputs.eps, + ).type(torch.uint8) + + return raw_inputs, inputs.scale diff --git a/experiments/mnist/utils/train.py b/experiments/mnist/utils/train.py index eed23b2..1af5175 100644 --- a/experiments/mnist/utils/train.py +++ b/experiments/mnist/utils/train.py @@ -1,3 +1,5 @@ +from typing import Optional + import torch import torch.nn as nn from torch import Tensor @@ -6,6 +8,8 @@ from torch.utils.data import DataLoader from tqdm.notebook import tqdm +__all__ = ["train_for_epoch", "test_model"] + def train_for_epoch( model: nn.Module, @@ -39,13 +43,24 @@ def train_for_epoch( tepoch.set_postfix(loss=loss.item(), acc=format(accuracy, "3.2%")) -def test_model(model: nn.Module, device: str, test_loader: DataLoader, - criterion: _Loss) -> None: +def test_model( + model: nn.Module, + device: str, + test_loader: DataLoader, + criterion: _Loss, + return_correct: bool = False, +) -> Optional[tuple[Tensor, Tensor]]: + """ + Optionally return correct (images, targets) when return_correct == True + """ model.eval() test_loss = 0 correct_total = 0 size_total = 0 + correct_images = [] + correct_targets = [] + with torch.no_grad(): with tqdm(test_loader) as tepoch: tepoch.set_description("Test") @@ -56,9 +71,15 @@ def test_model(model: nn.Module, device: str, test_loader: DataLoader, loss: Tensor = criterion(logits, targets, reduction="sum") preds = logits.argmax(dim=1, keepdim=True).squeeze() - num_correct = (preds == targets).sum().item() - correct_total += num_correct + correct_mask = preds == targets + + correct_total += correct_mask.sum().item() size_total += len(targets) + + if return_correct: + correct_images.extend(images[correct_mask]) + correct_targets.extend(targets[correct_mask]) + accuracy = correct_total / size_total test_loss += loss.item() @@ -69,3 +90,6 @@ def test_model(model: nn.Module, device: str, test_loader: DataLoader, acc=format(accuracy, "3.2%"), avg_loss=avg_loss, ) + + if return_correct: + return torch.vstack(correct_images), torch.vstack(correct_targets) From 03b66b8b6a783e905de1747a3bd69c68617fb42f Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 7 May 2025 23:00:59 +0000 Subject: [PATCH 14/64] Ran ruff linter+formatter --- .../py_src/arbolta/__init__.py | 5 ++- .../python_bindings/py_src/arbolta/design.py | 34 ++++++++----------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/crates/python_bindings/py_src/arbolta/__init__.py b/crates/python_bindings/py_src/arbolta/__init__.py index b923124..281827e 100644 --- a/crates/python_bindings/py_src/arbolta/__init__.py +++ b/crates/python_bindings/py_src/arbolta/__init__.py @@ -1,5 +1,8 @@ # Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. # SPDX-License-Identifier: MIT +# type: ignore from .arbolta import Design # For pickling -from .design import * +from .design import DesignConfig, HardwareDesign, PortConfig, load, save + +__all__ = ["Design", "PortConfig", "DesignConfig", "HardwareDesign", "save", "load"] diff --git a/crates/python_bindings/py_src/arbolta/design.py b/crates/python_bindings/py_src/arbolta/design.py index 519c969..a2706dc 100644 --- a/crates/python_bindings/py_src/arbolta/design.py +++ b/crates/python_bindings/py_src/arbolta/design.py @@ -29,6 +29,7 @@ class PortConfig: polarity: bool, optional Clock polarity of port. """ + shape: Tuple[int, int] = (1, 1) dtype: np.dtype = np.uint32 clock: bool = False @@ -47,6 +48,7 @@ class DesignConfig(TypedDict): config : PortConfig Configuration for port """ + port: str config: PortConfig @@ -58,17 +60,14 @@ class Port: class HardwarePorts: - def __init__(self, config: DesignConfig, design: Design): - _ports: Dict[str, Port] = {} port_name: str port_config: PortConfig for port_name, port_config in config.items(): if port_config.reset and port_config.clock: - raise AttributeError( - f"Port `{port_name}` cannot be a reset and clock") + raise AttributeError(f"Port `{port_name}` cannot be a reset and clock") if port_config.reset: design.set_reset(port_name, bool(port_config.polarity)) if port_config.clock: @@ -76,19 +75,20 @@ def __init__(self, config: DesignConfig, design: Design): design.set_port_shape(port_name, port_config.shape) _ports[port_name] = Port( - np.zeros(port_config.shape[1], dtype=port_config.dtype)) + np.zeros(port_config.shape[1], dtype=port_config.dtype) + ) - super().__setattr__('_ports', _ports) - super().__setattr__('_design', design) + super().__setattr__("_ports", _ports) + super().__setattr__("_design", design) def __getattr__(self, name: str) -> Any: - if '_ports' in self.__dict__ and '_design' in self.__dict__: - _ports: dict[str, Port] = self.__dict__['_ports'] + if "_ports" in self.__dict__ and "_design" in self.__dict__: + _ports: dict[str, Port] = self.__dict__["_ports"] if name not in _ports: raise AttributeError(f"Port `{name}` does not exist") - _design = self.__dict__['_design'] + _design = self.__dict__["_design"] if not _design.is_port_input(name): _design.get_port_numpy(name, _ports[name].data) @@ -99,8 +99,8 @@ def __getattr__(self, name: str) -> Any: raise AttributeError("Ports not initialized") def __setattr__(self, name: str, value: Any) -> None: - if '_ports' in self.__dict__ and '_design' in self.__dict__: - _ports: dict[str, Port] = self.__dict__['_ports'] + if "_ports" in self.__dict__ and "_design" in self.__dict__: + _ports: dict[str, Port] = self.__dict__["_ports"] if name not in _ports: raise AttributeError(f"Port `{name}` does not exist") @@ -113,9 +113,7 @@ def __setattr__(self, name: str, value: Any) -> None: class HardwareDesign: - - def __init__(self, top_module: str, netlist_path: str, - config: DesignConfig): + def __init__(self, top_module: str, netlist_path: str, config: DesignConfig): """ Parameters ---------- @@ -178,8 +176,7 @@ def eval_clocked(self, cycles: Optional[int] = 1): self.design.eval_clocked(cycles) - def cell_breakdown(self, - module_name: Optional[str] = None) -> Dict[str, int]: + def cell_breakdown(self, module_name: Optional[str] = None) -> Dict[str, int]: """ Get count of each cell type in module. @@ -245,8 +242,7 @@ def total_toggle_count(self, module_name: Optional[str] = None) -> int: AttributeError: Specified module doesn't exist in design. """ if module_name is None: - return self.design.get_module_total_toggle_count( - self.design.top_module) + return self.design.get_module_total_toggle_count(self.design.top_module) else: return self.design.get_module_total_toggle_count(module_name) From 473e957a7379b8a1c855c3f2a8f6d9492a978de8 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 7 May 2025 23:03:11 +0000 Subject: [PATCH 15/64] Updated cargo packages and added release profile --- Cargo.toml | 6 ++++++ crates/python_bindings/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d767148..dc89a22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,9 @@ [workspace] members = ["crates/*"] resolver = "2" + +[profile.release] +lto = true +codegen-units = 1 +opt-level = 3 +debug = false diff --git a/crates/python_bindings/Cargo.toml b/crates/python_bindings/Cargo.toml index 4bad0a2..b22cfb4 100644 --- a/crates/python_bindings/Cargo.toml +++ b/crates/python_bindings/Cargo.toml @@ -9,7 +9,7 @@ name = "arbolta" crate-type = ["cdylib"] [dependencies] -pyo3 = "0.24.1" +pyo3 = "0.24.2" numpy = "0.24.0" num-traits = "0.2" serde = { version = "1.0", features = ["derive"] } From dea2653c96b0bc5284379589735ca8c835c5faf3 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 7 May 2025 23:07:52 +0000 Subject: [PATCH 16/64] Ran clippy --- crates/arbolta/src/bit.rs | 6 +----- crates/arbolta/tests/test_bitvec.rs | 2 +- crates/arbolta/tests/test_cell.rs | 2 +- crates/python_bindings/src/design.rs | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/crates/arbolta/src/bit.rs b/crates/arbolta/src/bit.rs index c67187e..673153e 100644 --- a/crates/arbolta/src/bit.rs +++ b/crates/arbolta/src/bit.rs @@ -22,11 +22,7 @@ pub struct ParseBitError; impl From for Bit { fn from(val: bool) -> Self { - if val { - Self::ONE - } else { - Self::ZERO - } + if val { Self::ONE } else { Self::ZERO } } } diff --git a/crates/arbolta/tests/test_bitvec.rs b/crates/arbolta/tests/test_bitvec.rs index 8bce240..cbfbba9 100644 --- a/crates/arbolta/tests/test_bitvec.rs +++ b/crates/arbolta/tests/test_bitvec.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: MIT use arbolta::bit::{Bit, BitVec}; -use ndarray::{array, Array1}; +use ndarray::{Array1, array}; use rstest::rstest; diff --git a/crates/arbolta/tests/test_cell.rs b/crates/arbolta/tests/test_cell.rs index 4a92691..0e8d9b8 100644 --- a/crates/arbolta/tests/test_cell.rs +++ b/crates/arbolta/tests/test_cell.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: MIT use arbolta::bit::Bit; -use arbolta::cell::{create_cell, Cell, CellFn, Dff, DffReset}; +use arbolta::cell::{Cell, CellFn, Dff, DffReset, create_cell}; use arbolta::signal::Signal; use rstest::rstest; diff --git a/crates/python_bindings/src/design.rs b/crates/python_bindings/src/design.rs index 7ba399f..941ee15 100644 --- a/crates/python_bindings/src/design.rs +++ b/crates/python_bindings/src/design.rs @@ -287,7 +287,7 @@ impl PyDesign { _ => { return Err(PyValueError::new_err(format!( "Unsupported item type: {item_type}" - ))) + ))); } }; match self.module.set_port_bits(name, &bits) { From 9638e2310e8febfcc3278b1bccfb503b08490c71 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Thu, 8 May 2025 00:28:49 +0000 Subject: [PATCH 17/64] Updated and ran pre-commit --- .pre-commit-config.yaml | 41 +++--- crates/arbolta/Cargo.toml | 3 +- crates/arbolta/src/bit.rs | 37 +++--- crates/arbolta/src/hardware_module.rs | 177 ++++++++------------------ crates/arbolta/src/port.rs | 82 +++++------- crates/arbolta/tests/test_bitvec.rs | 2 +- crates/arbolta/tests/test_cell.rs | 6 +- crates/arbolta/tests/test_module.rs | 18 ++- crates/arbolta/tests/test_signal.rs | 12 +- crates/python_bindings/Cargo.toml | 2 +- examples/tutorial.ipynb | 73 ++++++----- 11 files changed, 191 insertions(+), 262 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eb0745c..efa6577 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,3 @@ -exclude: '(examples/pre_synthesized_demo/i8bxi8bx16_vector_mac.json)' - repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 @@ -14,35 +12,30 @@ repos: - id: check-json - id: check-added-large-files - id: requirements-txt-fixer - - repo: https://github.com/doublify/pre-commit-rust - rev: v1.0 + - repo: local hooks: - - id: fmt - name: fmt (rust) - - id: clippy - name: clippy (rust) + - id: cargo-fmt + name: cargo fmt + entry: cargo fmt --all -- --check + language: system + types: [rust] + - id: cargo-clippy + name: cargo clippy + entry: cargo clippy --workspace --all-targets -- -Dclippy::all + pass_filenames: false + language: system + types: [rust] - id: cargo-check - name: cargo-check (rust) + name: cargo check + entry: cargo check --workspace --all-targets + pass_filenames: false + language: system + types: [rust] - repo: https://github.com/astral-sh/ruff-pre-commit - # Ruff version. rev: v0.11.8 hooks: - # Run the linter. - id: ruff - # Run the formatter. - id: ruff-format - - # - repo: https://github.com/pycqa/isort - # rev: 5.13.2 - # hooks: - # - id: isort - # name: isort (python) - # - repo: https://github.com/google/yapf - # rev: v0.43.0 - # hooks: - # - id: yapf - # name: yapf (python) - # args: ["-i"] - repo: https://github.com/srstevenson/nb-clean rev: 4.0.1 hooks: diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index e4f85dc..5f5b220 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -2,9 +2,10 @@ name = "arbolta" version = "0.1.0" authors = ["AMD Research & Advanced Development"] -edition = "2021" +edition = "2024" [dependencies] +anyhow = "1.0.98" indexmap = "2.9.0" tempfile = "3.19.1" derive_more = { version = "2", features = ["full"] } diff --git a/crates/arbolta/src/bit.rs b/crates/arbolta/src/bit.rs index 673153e..4671269 100644 --- a/crates/arbolta/src/bit.rs +++ b/crates/arbolta/src/bit.rs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. // SPDX-License-Identifier: MIT +use anyhow::Result; use bincode::{Decode, Encode}; use core::fmt; use ndarray::{Array1, ArrayView1, ArrayViewMut1}; @@ -228,7 +229,7 @@ impl BitVec { /// # Arguments /// * `val` - Int to convert. /// * `size` - Number of bits to use. - fn int_to_bits_sized(val: T, size: usize) -> Result, ParseBitError> { + fn int_to_bits_sized(val: T, size: usize) -> Result> { let mut bits: Vec = vec![]; for n in 0..size { bits.push(Bit::from_int((val >> n) & T::one())?); @@ -296,7 +297,7 @@ impl BitVec { /// # Arguments /// * `val` - Int to convert. /// * `size` - Number of bits to use. - pub fn from_int_sized(val: T, size: usize) -> Result { + pub fn from_int_sized(val: T, size: usize) -> Result { let bits = Self::int_to_bits_sized(val, size)?; Ok(Self::from(bits)) } @@ -305,7 +306,7 @@ impl BitVec { /// /// # Arguments /// * `val` - Int to convert. - pub fn from_int(val: T) -> Result { + pub fn from_int(val: T) -> Result { let type_size = std::mem::size_of::() * 8; // bytes to bits let bits = Self::int_to_bits_sized(val, type_size)?; Ok(Self::from(bits)) @@ -321,7 +322,7 @@ impl BitVec { /// # Arguments /// * `vals` - Ints to convert. /// * `elem_size` - Number of bits per int. - pub fn from_ints_sized(vals: &[T], elem_size: usize) -> Result { + pub fn from_ints_sized(vals: &[T], elem_size: usize) -> Result { let mut bits: Vec = vec![]; for val in vals { bits.append(&mut Self::int_to_bits_sized(*val, elem_size)?); @@ -334,7 +335,7 @@ impl BitVec { /// /// # Arguments /// * `vals` - Ints to convert. - pub fn from_ints(vals: &[T]) -> Result { + pub fn from_ints(vals: &[T]) -> Result { let type_size = std::mem::size_of::() * 8; // bytes to bits Self::from_ints_sized(vals, type_size) } @@ -380,10 +381,7 @@ impl BitVec { /// # Arguments /// * `vals` - Ints to convert. /// * `elem_size` - Number of bits per int. - pub fn from_int_ndarray_sized( - vals: ArrayView1, - elem_size: usize, - ) -> Result { + pub fn from_int_ndarray_sized(vals: ArrayView1, elem_size: usize) -> Result { let mut bits: Vec = vec![]; for val in vals { bits.append(&mut Self::int_to_bits_sized(*val, elem_size)?); @@ -395,7 +393,7 @@ impl BitVec { /// /// # Arguments /// * `vals` - Ints to convert. - pub fn from_int_ndarray(vals: ArrayView1) -> Result { + pub fn from_int_ndarray(vals: ArrayView1) -> Result { let type_size = std::mem::size_of::() * 8; // bytes to bits Self::from_int_ndarray_sized(vals, type_size) } @@ -404,9 +402,9 @@ impl BitVec { /// /// # Arguments /// * `vals` - Bools to convert. - pub fn from_bool_ndarray(vals: ArrayView1) -> Result { + pub fn from_bool_ndarray(vals: ArrayView1) -> Result { match vals.as_slice() { - None => Err(ParseBitError), + None => Err(ParseBitError)?, Some(buffer_slice) => Ok(Self::from(buffer_slice)), } } @@ -431,9 +429,9 @@ impl BitVec { &self, elem_size: usize, mut buffer: ArrayViewMut1, - ) -> Result<(), ParseBitError> { + ) -> Result<()> { match buffer.as_slice_mut() { - None => Err(ParseBitError), + None => Err(ParseBitError)?, Some(buffer_slice) => { Self::bits_to_ints_buffer(&self.bits, elem_size, buffer_slice); Ok(()) @@ -448,10 +446,10 @@ impl BitVec { pub fn to_int_ndarray_buffer( &self, mut buffer: ArrayViewMut1, - ) -> Result<(), ParseBitError> { + ) -> Result<()> { let type_size = std::mem::size_of::() * 8; // bytes to bits match buffer.as_slice_mut() { - None => Err(ParseBitError), + None => Err(ParseBitError)?, Some(buffer_slice) => { Self::bits_to_ints_buffer(&self.bits, type_size, buffer_slice); Ok(()) @@ -469,12 +467,9 @@ impl BitVec { /// /// # Arguments /// * `buffer` - `ndarray` buffer to store bools. - pub fn to_bool_ndarray_buffer( - &self, - mut buffer: ArrayViewMut1, - ) -> Result<(), ParseBitError> { + pub fn to_bool_ndarray_buffer(&self, mut buffer: ArrayViewMut1) -> Result<()> { match buffer.as_slice_mut() { - None => Err(ParseBitError), + None => Err(ParseBitError)?, Some(buffer_slice) => { self .bits diff --git a/crates/arbolta/src/hardware_module.rs b/crates/arbolta/src/hardware_module.rs index 713e3a6..fee2179 100644 --- a/crates/arbolta/src/hardware_module.rs +++ b/crates/arbolta/src/hardware_module.rs @@ -1,10 +1,11 @@ // Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. // SPDX-License-Identifier: MIT -use super::port::{Port, PortDirection, PortError}; +use super::port::{Port, PortDirection}; use crate::bit::{Bit, BitVec}; -use crate::cell::{create_cell, Cell, CellError, CellFn}; +use crate::cell::{Cell, CellError, CellFn, create_cell}; use crate::signal::Signal; +use anyhow::Result; use bincode::{Decode, Encode}; use ndarray::{Array1, ArrayView1}; use num_traits::PrimInt; @@ -36,36 +37,22 @@ pub struct HardwareModule { pub enum ModuleError { #[error("couldn't load netlist `{0}`")] Netlist(String), - #[error("{0}")] - CellError(#[from] CellError), #[error("module does not have port `{0}`")] MissingPort(String), - #[error("error accessing port `{0}`: {1}")] - Port(String, PortError), #[error("module does not have signal `{0}`")] MissingSignal(String), #[error("module does not have net `{0}`")] MissingNet(usize), - #[error("module `{0}` does not exist")] - MissingModule(String), - #[error("{0}")] - FlexReaderError(#[from] flexbuffers::ReaderError), - #[error("{0}")] - DeserializeError(#[from] flexbuffers::DeserializationError), - #[error("{0}")] - SerializeError(#[from] flexbuffers::SerializationError), - #[error("{0}")] - IoError(#[from] std::io::Error), } impl HardwareModule { - pub fn load(path: &str) -> Result { + pub fn load(path: &str) -> Result { let serialized = std::fs::read(path)?; let reader = flexbuffers::Reader::get_root(serialized.as_slice())?; Ok(Self::deserialize(reader)?) } - pub fn save(&self, path: &str) -> Result<(), ModuleError> { + pub fn save(&self, path: &str) -> Result<()> { let mut serializer = flexbuffers::FlexbufferSerializer::new(); self.serialize(&mut serializer)?; let mut file_output = std::fs::File::create(path)?; @@ -73,30 +60,20 @@ impl HardwareModule { Ok(()) } - pub fn new_from_str(raw_netlist: &str, top_module: &str) -> Result { - let mut temp_netlist = match NamedTempFile::new() { - Ok(file) => file, - Err(err) => return Err(ModuleError::Netlist(err.to_string())), - }; + pub fn new_from_str(raw_netlist: &str, top_module: &str) -> Result { + let mut temp_netlist = NamedTempFile::new()?; // TODO: Better error handling - let _ = temp_netlist.write(raw_netlist.as_bytes()).unwrap(); + let _ = temp_netlist.write(raw_netlist.as_bytes())?; Self::new_from_path(temp_netlist.path().to_str().unwrap(), top_module) } - pub fn new_from_path(netlist_path: &str, top_module: &str) -> Result { - let temp_flattened = match NamedTempFile::new() { - Ok(file) => file, - Err(err) => return Err(ModuleError::Netlist(err.to_string())), - }; - - let mut temp_torder = match NamedTempFile::new() { - Ok(file) => file, - Err(err) => return Err(ModuleError::Netlist(err.to_string())), - }; + pub fn new_from_path(netlist_path: &str, top_module: &str) -> Result { + let temp_flattened = NamedTempFile::new()?; + let mut temp_torder = NamedTempFile::new()?; - let yosys_output = Command::new("yosys") + _ = Command::new("yosys") .arg("-f") .arg("json") .arg(netlist_path) @@ -106,21 +83,12 @@ impl HardwareModule { temp_flattened.path().display(), temp_torder.path().display() )) - .output(); - - if let Err(err) = yosys_output { - return Err(ModuleError::Netlist(err.to_string())); - } + .output()?; - let netlist = match yosys::Netlist::from_reader(temp_flattened) { - Ok(netlist) => Ok(netlist), - Err(err) => Err(ModuleError::Netlist(err.to_string())), - }?; + let netlist = yosys::Netlist::from_reader(temp_flattened)?; let mut topo_order = String::new(); - if let Err(err) = temp_torder.read_to_string(&mut topo_order) { - return Err(ModuleError::Netlist(err.to_string())); - } + _ = temp_torder.read_to_string(&mut topo_order)?; let mut clean_topo_order = vec![]; @@ -135,18 +103,13 @@ impl HardwareModule { Self::new(netlist, &clean_topo_order, top_module) } - pub fn new( - netlist: yosys::Netlist, - topo_order: &[&str], - top_module: &str, - ) -> Result { + pub fn new(netlist: yosys::Netlist, topo_order: &[&str], top_module: &str) -> Result { // Design must be flattened let Some(synth_module) = netlist.modules.get(top_module) else { - return Err(CellError::Unsupported(top_module.to_string()).into()); + return Err(CellError::Unsupported(top_module.to_string()))?; }; let mut cells = vec![]; - // let mut cell_map = CellMap::new(); let mut cell_info: HashMap = HashMap::new(); for cell_name in topo_order.iter().rev() { @@ -159,8 +122,6 @@ impl HardwareModule { let cell = create_cell(synth_cell)?; cells.push(cell); - // cell_map.insert(cell_name.to_string(), cells.len() - 1); - // cell_info.insert(cell_name.to_string(), synth_cell.clone()); cell_info.insert(cell_name.to_string(), synth_cell.cell_type.to_string()); } @@ -186,7 +147,7 @@ impl HardwareModule { let mut ports = PortMap::new(); for (name, port) in &synth_module.ports { - ports.insert(name.clone(), Port::new(port)); + ports.insert(name.clone(), Port::new(port)?); } // Temp? for (name, netname) in &synth_module.netnames { @@ -202,7 +163,7 @@ impl HardwareModule { signed: Default::default(), }; - ports.insert(name.clone(), Port::new(&port)); + ports.insert(name.clone(), Port::new(&port)?); } let mut signals = vec![Signal::default(); max_signal + 1]; @@ -221,46 +182,46 @@ impl HardwareModule { }) } - pub fn get_signal_nets(&self, name: &str) -> Result, ModuleError> { + pub fn get_signal_nets(&self, name: &str) -> Result> { match self.signal_map.get(name) { Some(net) => Ok(net.clone()), - None => Err(ModuleError::MissingSignal(name.to_string())), + None => Err(ModuleError::MissingSignal(name.to_string()))?, } } - pub fn stick_signal(&mut self, net: usize, value: Bit) -> Result<(), ModuleError> { + pub fn stick_signal(&mut self, net: usize, value: Bit) -> Result<()> { let Some(signal) = self.signals.get_mut(net) else { - return Err(ModuleError::MissingNet(net)); + return Err(ModuleError::MissingNet(net))?; }; signal.set_constant(value); Ok(()) } - pub fn unstick_signal(&mut self, net: usize) -> Result<(), ModuleError> { + pub fn unstick_signal(&mut self, net: usize) -> Result<()> { let Some(signal) = self.signals.get_mut(net) else { - return Err(ModuleError::MissingNet(net)); + return Err(ModuleError::MissingNet(net))?; }; signal.unset_constant(); Ok(()) } - pub fn set_clock(&mut self, net: usize, polarity: Bit) -> Result<(), ModuleError> { + pub fn set_clock(&mut self, net: usize, polarity: Bit) -> Result<()> { match self.signals.get(net) { Some(_) => { self.clock_net = Some((net, polarity)); Ok(()) } - None => Err(ModuleError::MissingNet(net)), + None => Err(ModuleError::MissingNet(net))?, } } - pub fn set_reset(&mut self, net: usize, polarity: Bit) -> Result<(), ModuleError> { + pub fn set_reset(&mut self, net: usize, polarity: Bit) -> Result<()> { match self.signals.get(net) { Some(_) => { self.reset_net = Some((net, polarity)); Ok(()) } - None => Err(ModuleError::MissingNet(net)), + None => Err(ModuleError::MissingNet(net))?, } } @@ -291,9 +252,9 @@ impl HardwareModule { } } - pub fn eval_clocked(&mut self, cycles: Option) -> Result<(), ModuleError> { + pub fn eval_clocked(&mut self, cycles: Option) -> Result<()> { let Some((clock_net, polarity)) = self.clock_net else { - return Err(ModuleError::MissingSignal("clock".to_string())); + return Err(ModuleError::MissingSignal("clock".to_string()))?; }; let cycles = cycles.unwrap_or(1); @@ -309,9 +270,9 @@ impl HardwareModule { Ok(()) } - pub fn eval_reset_clocked(&mut self, cycles: Option) -> Result<(), ModuleError> { + pub fn eval_reset_clocked(&mut self, cycles: Option) -> Result<()> { let Some((reset_net, polarity)) = self.reset_net else { - return Err(ModuleError::MissingSignal("reset".to_string())); + return Err(ModuleError::MissingSignal("reset".to_string()))?; }; self.signals[reset_net].set_value(polarity); @@ -329,68 +290,52 @@ impl HardwareModule { self.signals[1].set_constant(Bit::ONE); } - pub fn set_port_shape(&mut self, name: &str, shape: &[usize; 2]) -> Result<(), ModuleError> { + pub fn set_port_shape(&mut self, name: &str, shape: &[usize; 2]) -> Result<()> { match self.ports.get_mut(name) { - Some(port) => match port.set_shape(shape) { - Ok(()) => Ok(()), - Err(err) => Err(ModuleError::Port(name.to_string(), err)), - }, - None => Err(ModuleError::MissingPort(name.to_string())), + Some(port) => port.set_shape(shape), + None => Err(ModuleError::MissingPort(name.to_string()))?, } } - pub fn get_port_shape(&self, name: &str) -> Result<[usize; 2], ModuleError> { + pub fn get_port_shape(&self, name: &str) -> Result<[usize; 2]> { match self.ports.get(name) { Some(port) => Ok(port.get_shape()), - None => Err(ModuleError::MissingPort(name.to_string())), + None => Err(ModuleError::MissingPort(name.to_string()))?, } } - pub fn get_port_direction(&self, name: &str) -> Result { + pub fn get_port_direction(&self, name: &str) -> Result { match self.ports.get(name) { Some(port) => Ok(port.direction.clone()), - None => Err(ModuleError::MissingPort(name.to_string())), + None => Err(ModuleError::MissingPort(name.to_string()))?, } } - pub fn get_port_bits(&self, name: &str) -> Result { + pub fn get_port_bits(&self, name: &str) -> Result { match self.ports.get(name) { Some(port) => Ok(port.get_bits(&self.signals)), - None => Err(ModuleError::MissingPort(name.to_string())), + None => Err(ModuleError::MissingPort(name.to_string()))?, } } - pub fn set_port_bits(&mut self, name: &str, vals: &BitVec) -> Result<(), ModuleError> { + pub fn set_port_bits(&mut self, name: &str, vals: &BitVec) -> Result<()> { match self.ports.get_mut(name) { - Some(port) => match port.set_bits(vals, &mut self.signals) { - Ok(()) => Ok(()), - Err(err) => Err(ModuleError::Port(name.to_string(), err)), - }, - None => Err(ModuleError::MissingPort(name.to_string())), + Some(port) => port.set_bits(vals, &mut self.signals), + None => Err(ModuleError::MissingPort(name.to_string()))?, } } - pub fn get_port_int( - &self, - name: &str, - ) -> Result { + pub fn get_port_int(&self, name: &str) -> Result { match self.ports.get(name) { Some(port) => Ok(port.get_int(&self.signals)), - None => Err(ModuleError::MissingPort(name.to_string())), + None => Err(ModuleError::MissingPort(name.to_string()))?, } } - pub fn set_port_int( - &mut self, - name: &str, - val: T, - ) -> Result<(), ModuleError> { + pub fn set_port_int(&mut self, name: &str, val: T) -> Result<()> { match self.ports.get_mut(name) { - Some(port) => match port.set_int(val, &mut self.signals) { - Ok(()) => Ok(()), - Err(err) => Err(ModuleError::Port(name.to_string(), err)), - }, - None => Err(ModuleError::MissingPort(name.to_string())), + Some(port) => port.set_int(val, &mut self.signals), + None => Err(ModuleError::MissingPort(name.to_string()))?, } } @@ -404,17 +349,10 @@ impl HardwareModule { } } - pub fn set_port_int_vec( - &mut self, - name: &str, - vals: &[T], - ) -> Result<(), ModuleError> { + pub fn set_port_int_vec(&mut self, name: &str, vals: &[T]) -> Result<()> { match self.ports.get_mut(name) { - Some(port) => match port.set_int_vec(vals, &mut self.signals) { - Ok(()) => Ok(()), - Err(err) => Err(ModuleError::Port(name.to_string(), err)), - }, - None => Err(ModuleError::MissingPort(name.to_string())), + Some(port) => port.set_int_vec(vals, &mut self.signals), + None => Err(ModuleError::MissingPort(name.to_string()))?, } } @@ -432,13 +370,10 @@ impl HardwareModule { &mut self, name: &str, vals: ArrayView1, - ) -> Result<(), ModuleError> { + ) -> Result<()> { match self.ports.get(name) { - Some(port) => match port.set_ndarray(vals, &mut self.signals) { - Ok(()) => Ok(()), - Err(err) => Err(ModuleError::Port(name.to_string(), err)), - }, - None => Err(ModuleError::MissingPort(name.to_string())), + Some(port) => port.set_ndarray(vals, &mut self.signals), + None => Err(ModuleError::MissingPort(name.to_string()))?, } } diff --git a/crates/arbolta/src/port.rs b/crates/arbolta/src/port.rs index a7e7a54..db3782f 100644 --- a/crates/arbolta/src/port.rs +++ b/crates/arbolta/src/port.rs @@ -3,6 +3,7 @@ use crate::bit::{Bit, BitVec}; use crate::signal::Signal; +use anyhow::Result; use bincode::{Decode, Encode}; use ndarray::{Array1, ArrayView1}; use num_traits::PrimInt; @@ -27,8 +28,8 @@ pub struct Port { #[derive(Debug, Error)] pub enum PortError { - #[error("tried to set input port")] - Direction, + #[error("{0}")] + Direction(String), #[error("couldn't convert port to type")] Conversion, #[error("incompatible shapes: requested={requested:?}, actual={actual:?}")] @@ -39,9 +40,9 @@ pub enum PortError { } impl Port { - pub fn new(port: &yosys::Port) -> Self { + pub fn new(port: &yosys::Port) -> Result { let direction = match port.direction { - yosys::PortDirection::InOut => todo!("Inout not supported"), + yosys::PortDirection::InOut => Err(PortError::Direction("Inout not supported".to_string()))?, yosys::PortDirection::Input => PortDirection::Input, yosys::PortDirection::Output => PortDirection::Output, }; @@ -50,31 +51,31 @@ impl Port { .bits .iter() .map(|bit| match bit { - yosys::BitVal::N(net) => *net, + yosys::BitVal::N(net) => Ok(*net), yosys::BitVal::S(constant) => match constant { - yosys::SpecialBit::_0 => 0, // Global 0 - yosys::SpecialBit::_1 => 1, // Global 1 - yosys::SpecialBit::X => todo!("X not supported."), - yosys::SpecialBit::Z => todo!("Z not supported."), + yosys::SpecialBit::_0 => Ok(0), // Global 0 + yosys::SpecialBit::_1 => Ok(1), // Global 1 + yosys::SpecialBit::X => Err(PortError::Direction("X not supported.".to_string())), + yosys::SpecialBit::Z => Err(PortError::Direction("Z not supported.".to_string())), }, }) - .collect(); + .collect::>()?; let shape = [1, nets.len()]; - Self { + Ok(Self { direction, nets: nets.into(), shape, - } + }) } - pub fn set_shape(&mut self, shape: &[usize; 2]) -> Result<(), PortError> { + pub fn set_shape(&mut self, shape: &[usize; 2]) -> Result<()> { if shape[0] * shape[1] != self.nets.len() { - return Err(PortError::Shape { + Err(PortError::Shape { requested: *shape, actual: self.shape, - }); + })?; } (self.shape[0], self.shape[1]) = (shape[0], shape[1]); @@ -96,11 +97,8 @@ impl Port { ) } - pub fn set_bits(&self, vals: &BitVec, signals: &mut [Signal]) -> Result<(), PortError> { - if self.direction == PortDirection::Output { - return Err(PortError::Direction); - } - + pub fn set_bits(&self, vals: &BitVec, signals: &mut [Signal]) -> Result<()> { + // Should we check direction? for (dst, val) in self.nets.iter().zip(vals.bits.iter()) { signals[*dst].set_value(*val); } @@ -116,14 +114,9 @@ impl Port { &self, val: T, signals: &mut [Signal], - ) -> Result<(), PortError> { - if self.direction == PortDirection::Output { - return Err(PortError::Direction); - } - - let Ok(bits) = BitVec::from_int(val) else { - return Err(PortError::Direction); - }; + ) -> Result<()> { + // Should we check direction? + let bits = BitVec::from_int(val)?; self.set_bits(&bits, signals) } @@ -133,24 +126,17 @@ impl Port { self.get_bits(signals).to_ints_sized(elem_size) } - pub fn set_int_vec( - &self, - vals: &[T], - signals: &mut [Signal], - ) -> Result<(), PortError> { + pub fn set_int_vec(&self, vals: &[T], signals: &mut [Signal]) -> Result<()> { if vals.len() != self.shape[0] { - return Err(PortError::Shape { + Err(PortError::Shape { requested: [vals.len(), std::mem::size_of::() * 8], actual: self.shape, - }); + })?; } let elem_size = self.shape[1]; - - match BitVec::from_ints_sized(vals, elem_size) { - Ok(bits) => self.set_bits(&bits, signals), - Err(_) => Err(PortError::Conversion), - } + let bits = BitVec::from_ints_sized(vals, elem_size)?; + self.set_bits(&bits, signals) } pub fn get_ndarray(&self, signals: &[Signal]) -> Array1 { @@ -158,24 +144,18 @@ impl Port { self.get_bits(signals).to_int_ndarray_sized(elem_size) } - pub fn set_ndarray( - &self, - vals: ArrayView1, - signals: &mut [Signal], - ) -> Result<(), PortError> { + pub fn set_ndarray(&self, vals: ArrayView1, signals: &mut [Signal]) -> Result<()> { if vals.len() != self.shape[0] { - return Err(PortError::Shape { + Err(PortError::Shape { requested: [vals.len(), std::mem::size_of::() * 8], actual: self.shape, - }); + })?; } let elem_size = self.shape[1]; - match BitVec::from_int_ndarray_sized(vals, elem_size) { - Ok(bits) => self.set_bits(&bits, signals), - Err(_) => Err(PortError::Conversion), - } + let bits = BitVec::from_int_ndarray_sized(vals, elem_size)?; + self.set_bits(&bits, signals) } pub fn get_string(&self, signals: &[Signal]) -> String { diff --git a/crates/arbolta/tests/test_bitvec.rs b/crates/arbolta/tests/test_bitvec.rs index cbfbba9..0735800 100644 --- a/crates/arbolta/tests/test_bitvec.rs +++ b/crates/arbolta/tests/test_bitvec.rs @@ -18,7 +18,7 @@ use rstest::rstest; Bit::ZERO, ], "00100101")] fn test_bits_to_str(#[case] bits: Vec, #[case] expected: String) { - let bits = BitVec { bits: bits }; + let bits = BitVec { bits }; assert_eq!(bits.to_string(), expected); } diff --git a/crates/arbolta/tests/test_cell.rs b/crates/arbolta/tests/test_cell.rs index 0e8d9b8..31f45dd 100644 --- a/crates/arbolta/tests/test_cell.rs +++ b/crates/arbolta/tests/test_cell.rs @@ -15,8 +15,10 @@ fn generate_cell( outputs: HashMap<&str, usize>, parameters: Option>, ) -> Cell { - let mut cell = yosys::Cell::default(); - cell.cell_type = cell_type.to_string(); + let mut cell = yosys::Cell { + cell_type: cell_type.to_string(), + ..Default::default() + }; let mut num_nets = 0; for (name, size) in inputs.iter() { diff --git a/crates/arbolta/tests/test_module.rs b/crates/arbolta/tests/test_module.rs index fe2651a..a2ff13a 100644 --- a/crates/arbolta/tests/test_module.rs +++ b/crates/arbolta/tests/test_module.rs @@ -14,16 +14,20 @@ fn generate_module( parameters: Option>, ) -> HardwareModule { let mut module = yosys::Module::default(); - let mut cell = yosys::Cell::default(); - cell.cell_type = cell_type.to_string(); + let mut cell = yosys::Cell { + cell_type: cell_type.to_string(), + ..Default::default() + }; let mut num_nets = 2; for (name, size) in inputs.iter() { let bits: Vec = (0..*size).map(|i| yosys::BitVal::N(num_nets + i)).collect(); num_nets += size; - let mut netname = yosys::Netname::default(); - netname.bits = bits.clone(); + let netname = yosys::Netname { + bits: bits.clone(), + ..Default::default() + }; let port = yosys::Port { direction: yosys::PortDirection::Input, @@ -45,8 +49,10 @@ fn generate_module( let bits: Vec = (0..*size).map(|i| yosys::BitVal::N(num_nets + i)).collect(); num_nets += size; - let mut netname = yosys::Netname::default(); - netname.bits = bits.clone(); + let netname = yosys::Netname { + bits: bits.clone(), + ..Default::default() + }; let port = yosys::Port { direction: yosys::PortDirection::Output, diff --git a/crates/arbolta/tests/test_signal.rs b/crates/arbolta/tests/test_signal.rs index 7df9363..0015586 100644 --- a/crates/arbolta/tests/test_signal.rs +++ b/crates/arbolta/tests/test_signal.rs @@ -40,8 +40,10 @@ fn test_signal_net_toggle_rising() { #[test] fn test_signal_net_toggle_falling() { - let mut x = Signal::default(); - x.value = Bit::ONE; + let mut x = Signal { + value: Bit::ONE, + ..Default::default() + }; assert_eq!(x.get_total_toggle_count(), 0); assert_eq!(x.get_toggle_count_falling(), 0); @@ -71,8 +73,10 @@ fn test_signal_net_toggle_same_zero() { #[test] fn test_signal_net_toggle_same_one() { - let mut x = Signal::default(); - x.value = Bit::ONE; + let mut x = Signal { + value: Bit::ONE, + ..Default::default() + }; assert_eq!(x.get_total_toggle_count(), 0); assert_eq!(x.get_toggle_count_falling(), 0); diff --git a/crates/python_bindings/Cargo.toml b/crates/python_bindings/Cargo.toml index b22cfb4..bc8f851 100644 --- a/crates/python_bindings/Cargo.toml +++ b/crates/python_bindings/Cargo.toml @@ -2,7 +2,7 @@ name = "arbolta-python" version = "0.1.0" authors = ["AMD Research & Advanced Development"] -edition = "2021" +edition = "2024" [lib] name = "arbolta" diff --git a/examples/tutorial.ipynb b/examples/tutorial.ipynb index 0a379e2..1078ca0 100644 --- a/examples/tutorial.ipynb +++ b/examples/tutorial.ipynb @@ -74,16 +74,18 @@ "from typing import TypedDict\n", "import subprocess\n", "\n", + "\n", "# Class to hold configuration for our int vector MAC\n", "class SynthConfig(TypedDict):\n", " DataWidth: int\n", " Size: int\n", " AccumulatorWidth: int\n", "\n", + "\n", "# Run synth.tcl with our parameters\n", "def run_synth(synth_config: SynthConfig) -> None:\n", " synth_params = [f\"{p_name}={p_val}\" for (p_name, p_val) in synth_config.items()]\n", - " command = ['yosys', '-c', 'synth.tcl', '--', *synth_params]\n", + " command = [\"yosys\", \"-c\", \"synth.tcl\", \"--\", *synth_params]\n", " p = subprocess.Popen(command, stdout=subprocess.PIPE)\n", " out, err = p.communicate()\n", " assert err is None" @@ -113,18 +115,14 @@ "from arbolta import HardwareDesign, DesignConfig, PortConfig\n", "\n", "DESIGN_CONFIG = DesignConfig(\n", - " clock = PortConfig(clock=True), # Don't need to specify shape\n", - " reset_i = PortConfig(reset=True), # Don't need to specify shape\n", - " op0_vec_i = PortConfig(shape=(1, 16), dtype=np.int8),\n", - " op1_vec_i = PortConfig(shape=(1, 16), dtype=np.int8),\n", - " mac_o = PortConfig(shape=(1, 1), dtype=np.int32)\n", + " clock=PortConfig(clock=True), # Don't need to specify shape\n", + " reset_i=PortConfig(reset=True), # Don't need to specify shape\n", + " op0_vec_i=PortConfig(shape=(1, 16), dtype=np.int8),\n", + " op1_vec_i=PortConfig(shape=(1, 16), dtype=np.int8),\n", + " mac_o=PortConfig(shape=(1, 1), dtype=np.int32),\n", ")\n", "\n", - "SYNTH_CONFIG = SynthConfig(\n", - " DataWidth = 8,\n", - " Size = 16,\n", - " AccumulatorWidth = 32\n", - ")\n", + "SYNTH_CONFIG = SynthConfig(DataWidth=8, Size=16, AccumulatorWidth=32)\n", "\n", "run_synth(SYNTH_CONFIG)\n", "\n", @@ -146,7 +144,7 @@ "source": [ "from graphviz_anywidget import graphviz_widget\n", "\n", - "graphviz_widget(open(\"output/schematic.dot\", 'r').read())" + "graphviz_widget(open(\"output/schematic.dot\", \"r\").read())" ] }, { @@ -187,13 +185,19 @@ "source": [ "# Extract HDL name from each nested module instance\n", "def filter_module_name(name: str) -> str:\n", - " return list(filter(lambda x: not x.startswith('$') and '=' not in x, \n", - " name.split(\"\\\\\")))[0]\n", + " return list(\n", + " filter(lambda x: not x.startswith(\"$\") and \"=\" not in x, name.split(\"\\\\\"))\n", + " )[0]\n", + "\n", "\n", "# Get area breakdown for design\n", "# Area ~= transistor count\n", - "area_df = DataFrame([{'module': filter_module_name(module), \n", - " 'area': design.area(module)} for module in design.module_names()])\n", + "area_df = DataFrame(\n", + " [\n", + " {\"module\": filter_module_name(module), \"area\": design.area(module)}\n", + " for module in design.module_names()\n", + " ]\n", + ")\n", "\n", "area_df" ] @@ -208,7 +212,7 @@ "from matplotlib import pyplot as plt\n", "\n", "# Plot area breakdown\n", - "area_df.plot(kind='barh', x='module', xlabel='Area (# of Transistors)', ylabel='')\n", + "area_df.plot(kind=\"barh\", x=\"module\", xlabel=\"Area (# of Transistors)\", ylabel=\"\")\n", "plt.tight_layout()\n", "plt.show()" ] @@ -236,20 +240,20 @@ " runs = inputs.shape[0]\n", " actual_mac = np.zeros(runs, dtype=np.int32)\n", "\n", - " design.reset() # Reset all toggle counts, signals, and flip-flops\n", + " design.reset() # Reset all toggle counts, signals, and flip-flops\n", " for i, input_pair in enumerate(inputs):\n", - " design.reset_clocked() # Reset accumulator\n", + " design.reset_clocked() # Reset accumulator\n", " design.ports.op0_vec_i = input_pair[:, 0]\n", " design.ports.op1_vec_i = input_pair[:, 1]\n", - " design.eval_clocked() # Do MAC\n", - " \n", + " design.eval_clocked() # Do MAC\n", + "\n", " actual_mac[i] = design.ports.mac_o.item()\n", "\n", " # Check correctness of design\n", - " expected_mac = (inputs[:,:,0] * inputs[:,:,1]).sum(axis=1)\n", + " expected_mac = (inputs[:, :, 0] * inputs[:, :, 1]).sum(axis=1)\n", " assert np.allclose(actual_mac, expected_mac)\n", "\n", - " return design.total_toggle_count() / runs" + " return design.total_toggle_count() / runs" ] }, { @@ -258,9 +262,9 @@ "metadata": {}, "outputs": [], "source": [ - "INT8_MIN, INT8_MAX = -128, 127 # Range of our operand datatype\n", - "VECTOR_SIZE = 16 # Size of int vector MAC\n", - "RUNS = 1000 # Number of MAC operations to average over\n", + "INT8_MIN, INT8_MAX = -128, 127 # Range of our operand datatype\n", + "VECTOR_SIZE = 16 # Size of int vector MAC\n", + "RUNS = 1000 # Number of MAC operations to average over\n", "\n", "# Generate random, uniform inputs\n", "inputs = np.random.randint(INT8_MIN, INT8_MAX + 1, (RUNS, VECTOR_SIZE, 2))\n", @@ -276,9 +280,16 @@ "outputs": [], "source": [ "# Get power breakdown per-submodule\n", - "power_df = DataFrame([{'module': filter_module_name(module), \n", - " 'toggles': design.total_toggle_count(module)} for module in design.module_names()])\n", - "power_df['toggles'] = power_df['toggles'].div(RUNS)\n", + "power_df = DataFrame(\n", + " [\n", + " {\n", + " \"module\": filter_module_name(module),\n", + " \"toggles\": design.total_toggle_count(module),\n", + " }\n", + " for module in design.module_names()\n", + " ]\n", + ")\n", + "power_df[\"toggles\"] = power_df[\"toggles\"].div(RUNS)\n", "\n", "power_df" ] @@ -290,7 +301,9 @@ "outputs": [], "source": [ "# Plot power breakdown\n", - "power_df.plot(kind='barh', x='module', xlabel='Avg. Bit-Flips per MAC Operation', ylabel='')\n", + "power_df.plot(\n", + " kind=\"barh\", x=\"module\", xlabel=\"Avg. Bit-Flips per MAC Operation\", ylabel=\"\"\n", + ")\n", "plt.tight_layout()\n", "plt.show()" ] From 2a034ff3c293b73cc6dcf1ec880bb9dfb936e15a Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Mon, 26 May 2025 05:58:08 +0000 Subject: [PATCH 18/64] Improving minifloat support --- experiments/mnist/utils/__init__.py | 15 -- experiments/mnist/utils/hardware.py | 206 ++++++++++++++++++++++----- experiments/mnist/utils/minifloat.py | 156 ++++++++++++++++++++ experiments/mnist/utils/model.py | 127 ++++------------- 4 files changed, 358 insertions(+), 146 deletions(-) delete mode 100644 experiments/mnist/utils/__init__.py create mode 100644 experiments/mnist/utils/minifloat.py diff --git a/experiments/mnist/utils/__init__.py b/experiments/mnist/utils/__init__.py deleted file mode 100644 index e57e7cb..0000000 --- a/experiments/mnist/utils/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -from .dataset import get_mnist_dataloaders -from .hardware import run_systolic_array -from .model import IntQuantLinearModel, FloatQuantLinearModel, mf_to_raw, raw_to_mf -from .train import train_for_epoch, test_model - -__all__ = [ - "get_mnist_dataloaders", - "run_systolic_array", - "IntQuantLinearModel", - "FloatQuantLinearModel", - "mf_to_raw", - "raw_to_mf", - "train_for_epoch", - "test_model", -] diff --git a/experiments/mnist/utils/hardware.py b/experiments/mnist/utils/hardware.py index b1c5d6f..1bd9b2a 100644 --- a/experiments/mnist/utils/hardware.py +++ b/experiments/mnist/utils/hardware.py @@ -1,47 +1,187 @@ +import subprocess +from typing import Any, Literal, Optional + import numpy as np import torch -from arbolta import HardwareDesign +from arbolta import DesignConfig, HardwareDesign, PortConfig +from numpy import ndarray +from torch import Tensor + +__all__ = ["SystolicArray"] + + +def require(name: str, val: Any) -> Any: + if val is None: + raise ValueError(f"Must set `{name}`") + return val + + +def width_to_int(w: int, signed: bool) -> np.dtype: + if signed: + dtype = ( + np.int8 + if w <= 8 + else np.int16 + if w <= 16 + else np.int32 + if w <= 32 + else np.int64 + ) + else: + dtype = ( + np.uint8 + if w <= 8 + else np.uint16 + if w <= 16 + else np.uint32 + if w <= 32 + else np.uint64 + ) + return dtype + + +class SystolicArray: + def __init__( + self, + mac_type: Literal["integer", "minifloat"], + rows: int, + columns: int, + accumulator_bit_width: int, + # Integer parameters + x_bit_width: Optional[int] = None, + k_bit_width: Optional[int] = None, + # Minifloat parameters + x_exponent_bit_width: Optional[int] = None, + x_mantissa_bit_width: Optional[int] = None, + k_exponent_bit_width: Optional[int] = None, + k_mantissa_bit_width: Optional[int] = None, + ): + params: dict[str, int] + x_dtype: np.dtype + match mac_type: + case "integer": + params = { + "MacType": 0, + "WidthX": require("x_bit_width", x_bit_width), + "WidthK": require("k_bit_width", k_bit_width), + } + x_dtype = width_to_int(x_bit_width, signed=True) + k_dtype = width_to_int(k_bit_width, signed=True) + + case "minifloat": + params = { + "MacType": 1, + "ExpWidthX": require("x_exponent_bit_width", x_exponent_bit_width), + "ManWidthX": require("x_mantissa_bit_width", x_mantissa_bit_width), + "ExpWidthK": require("k_exponent_bit_width", k_exponent_bit_width), + "ManWidthK": require("k_mantissa_bit_width", k_mantissa_bit_width), + } + + # For calculating datatype + # TODO: Conversion w/ ml_dtypes + x_bit_width = 1 + x_exponent_bit_width + x_mantissa_bit_width + k_bit_width = 1 + k_exponent_bit_width + k_mantissa_bit_width + x_dtype = width_to_int(x_bit_width, signed=False) + k_dtype = width_to_int(k_bit_width, signed=False) + + case _: + raise ValueError(f"Unsupported MAC type `{mac_type}`") + + params.update({"Rows": rows, "Cols": columns, "WidthY": accumulator_bit_width}) + + self.params = params + self.acc_dtype = width_to_int(accumulator_bit_width, signed=True) + self.x_dtype = x_dtype + self.k_dtype = k_dtype + self.design: HardwareDesign = None + + def load(self, netlist_path: str = "./synth_output/0_proc/synth.json") -> None: + """ + Load systolic array from Yosys JSON netlist. + """ + rows, columns = self.params["Rows"], self.params["Cols"] + config = DesignConfig( + clk_i=PortConfig(clock=True, polarity=1), + rst_ni=PortConfig(reset=True, polarity=0), + s_valid_i=PortConfig(shape=(1, 1)), + s_last_i=PortConfig(shape=(1, 1)), + m_ready_i=PortConfig(shape=(1, 1)), + s_ready_o=PortConfig(shape=(1, 1)), + m_valid_o=PortConfig(shape=(1, 1)), + m_last_o=PortConfig(shape=(1, 1)), + sx_data_i=PortConfig(shape=(1, rows), dtype=self.x_dtype), + sk_data_i=PortConfig(shape=(1, columns), dtype=self.k_dtype), + m_data_o=PortConfig(shape=(1, rows), dtype=self.acc_dtype), + ) + self.design = HardwareDesign("axis_sa", netlist_path, config) + + def synthesize( + self, + script_path: str = "../systolic_array/axis-systolic-array/tcl/synth.tcl", + output_dir: str = "./synth_output", + load: bool = True, + ) -> None: + """ + Synthesizes systolic array with Yosys. + Saves netlists at `./synth_output` and loads. + """ + synth_params = [f"{p_name}={p_val}" for (p_name, p_val) in self.params.items()] + synth_params.append(f"output_dir={output_dir}") + command = ["yosys", "-c", script_path, "--", *synth_params] + p = subprocess.Popen(command, stdout=subprocess.PIPE) + # Ignore output for now + _, err = p.communicate() + + assert err is None, err + + if load: + # Default to process netlist + self.load(f"{output_dir}/0_proc/synth.json") + def run_matmul(self, x: ndarray | Tensor, k: ndarray | Tensor) -> ndarray | Tensor: + """ + Run inputs through systolic array. + Expects x: (K,R), k: (K,C) -> y: (C,R) + """ + if self.design is None: + raise AttributeError("Must load or synthesize design") -def run_systolic_array( - design: HardwareDesign, x: torch.Tensor, k: torch.Tensor -) -> torch.Tensor: - """ - Run inputs through systolic array. - Expects x: (K,R), k: (K,C) -> y: (C,R) - """ - K, R, C = x.shape[0], x.shape[1], k.shape[1] - actual = np.zeros((C, R), dtype=np.int32) + K, R, C = x.shape[0], x.shape[1], k.shape[1] + actual = np.zeros((C, R), dtype=self.acc_dtype) - # Start simulation - design.eval_reset_clocked() + # Start simulation + self.design.eval_reset_clocked() + for i in range(K): + while self.design.ports.s_ready_o == 0: + self.design.eval_clocked() - for i in range(K): - while design.ports.s_ready == 0: - design.eval_clocked() + self.design.ports.s_valid_i = 1 + self.design.ports.sx_data_i = x[i] + self.design.ports.sk_data_i = k[i] - design.ports.s_valid = 1 - design.ports.sx_data = x[i] - design.ports.sk_data = k[i] + if i == K - 1: + self.design.ports.s_last_i = 1 - if i == K - 1: - design.ports.s_last = 1 + self.design.eval_clocked() - design.eval_clocked() + self.design.ports.m_ready_i = 1 + self.design.ports.s_valid_i = 0 + self.design.ports.s_last_i = 0 - design.ports.m_ready = 1 - design.ports.s_valid = 0 - design.ports.s_last = 0 + idx = 0 + while True: + if self.design.ports.m_valid_o.item() == 1: + actual[idx] = self.design.ports.m_data_o + idx += 1 - idx = 0 - while True: - if design.ports.m_valid.item() == 1: - actual[idx] = design.ports.m_data - idx += 1 + if self.design.ports.m_last_o.item() == 1: + break - if design.ports.m_last.item() == 1: - break + self.design.eval_clocked() - design.eval_clocked() + # TODO: CALCULATE FRACTION BITS - return torch.from_numpy(actual) + if isinstance(x, Tensor) or isinstance(k, Tensor): + return torch.from_numpy(actual) + else: + return actual diff --git a/experiments/mnist/utils/minifloat.py b/experiments/mnist/utils/minifloat.py new file mode 100644 index 0000000..5eeeab7 --- /dev/null +++ b/experiments/mnist/utils/minifloat.py @@ -0,0 +1,156 @@ +from typing import Optional + +import array_api_compat +import brevitas.nn as qnn +import torch.nn as nn +from brevitas.inject import ExtendedInjector +from brevitas.quant.experimental.float_base import ( + FloatActBase, + FloatBase, + FloatWeightBase, +) +from numpy import ndarray +from torch import FloatTensor, IntTensor + +__all__ = ["mf_to_raw", "raw_to_mf", "fp_mixin_factory", "MinifloatQuantizer"] + + +# Helper for converting quantized floats to raw binary +def mf_to_raw( + x: FloatTensor | ndarray, + exponent_bit_width: int, + mantissa_bit_width: int, + exponent_bias: int, + eps: float, +) -> IntTensor | ndarray: + xp = array_api_compat.array_namespace(x) + + emin = -exponent_bias + 1 + + sign = xp.astype(xp.signbit(x), int) + exp = xp.astype(xp.floor(xp.log2(xp.abs(x) + eps)), int) + exp = xp.clip(exp, min=emin) + man = xp.abs(x) / xp.exp2(exp) + + exp_bits = exp - xp.astype((man < 1), int) + exponent_bias # Denorm + man_bits = xp.astype((man * (1 << mantissa_bit_width)), int) + man_bits = man_bits & ((1 << mantissa_bit_width) - 1) + + out = ( + (sign << (exponent_bit_width + mantissa_bit_width)) + | (exp_bits << mantissa_bit_width) + | man_bits + ) + + format_width = 1 + exponent_bit_width + mantissa_bit_width + # Cast to unsigned int for interfacing w/ Arbolta + out_dtype = ( + xp.uint8 + if format_width <= 8 + else xp.uint16 + if format_width <= 16 + else xp.uint32 + if format_width <= 32 + else xp.uint64 + ) + + return xp.astype(out, out_dtype) + + +# Helper for converting raw binary minifloats to floats +def raw_to_mf( + x: IntTensor | ndarray, + exponent_bit_width: int, + mantissa_bit_width: int, + exponent_bias: int, +) -> FloatTensor | ndarray: + xp = array_api_compat.array_namespace(x) + x = xp.astype(x, int) # Cast to signed int to allow for bit shifting + + emin = -exponent_bias + 1 + + sign_mask = 1 << (exponent_bit_width + mantissa_bit_width) + exp_mask = ((1 << exponent_bit_width) - 1) << mantissa_bit_width + man_mask = (1 << mantissa_bit_width) - 1 + + sign = xp.where((x & sign_mask) == 0, 1.0, -1.0) + exp_denorm = xp.astype((x & exp_mask) >> mantissa_bit_width, xp.float32) + man_denorm = xp.astype(x & man_mask, xp.float32) / (2**mantissa_bit_width) + + exp = xp.where(exp_denorm == 0, emin, exp_denorm - exponent_bias) + man = xp.where(exp_denorm == 0, man_denorm, 1.0 + man_denorm) + + return sign * man * xp.exp2(exp) + + +# Helper for creating custom minifloat quantizers +def fp_mixin_factory( + exponent_bit_width: int, mantissa_bit_width: int, base_class: FloatBase +): + bit_width = 1 + exponent_bit_width + mantissa_bit_width + name = f"Fp{bit_width}e{exponent_bit_width}m{mantissa_bit_width}" + mixin = type( + name + "Mixin", + (ExtendedInjector,), + { + # Add sign bit + "bit_width": bit_width, + "exponent_bit_width": exponent_bit_width, + "mantissa_bit_width": mantissa_bit_width, + "saturating": True, + }, + ) + + if base_class is FloatActBase: + class_name = name + "Act" + elif base_class is FloatWeightBase: + class_name = name + "Weight" + else: + raise TypeError("Unsupported Float Base") + + return type(class_name, (mixin, base_class), {}) + + +class MinifloatQuantizer(nn.Module): + def __init__( + self, + exponent_bit_width: int, + mantissa_bit_width: int, + exponent_bias: Optional[int] = None, + eps: float = 1e-9, + ): + super(MinifloatQuantizer, self).__init__() + if exponent_bias is None: + exponent_bias = (2 ** (exponent_bit_width - 1)) - 1 + + self.exponent_bit_width = exponent_bit_width + self.mantissa_bit_width = mantissa_bit_width + self.exponent_bias = exponent_bias + self.eps = eps + self.ident = qnn.QuantIdentity( + act_quant=fp_mixin_factory( + exponent_bit_width, mantissa_bit_width, FloatActBase + ) + ) + + def forward( + self, x: FloatTensor, return_raw: bool = False + ) -> IntTensor | FloatTensor: + out = self.ident(x) + + if return_raw: + out = mf_to_raw( + out, + self.exponent_bit_width, + self.mantissa_bit_width, + self.exponent_bias, + self.eps, + ) + + return out + + def dequantize_raw(self, x: IntTensor) -> FloatTensor: + out = raw_to_mf( + x, self.exponent_bit_width, self.mantissa_bit_width, self.exponent_bias + ) + return out diff --git a/experiments/mnist/utils/model.py b/experiments/mnist/utils/model.py index fccb5cf..8d855cf 100644 --- a/experiments/mnist/utils/model.py +++ b/experiments/mnist/utils/model.py @@ -4,18 +4,20 @@ import torch import torch.nn as nn import torch.nn.functional as F -from brevitas.inject import ExtendedInjector from brevitas.quant.experimental.float_base import ( FloatActBase, - FloatBase, FloatWeightBase, ) from brevitas.quant.scaled_int import Int8ActPerTensorFloat, Int8WeightPerTensorFloat from brevitas.quant_tensor.float_quant_tensor import FloatQuantTensor from brevitas.quant_tensor.int_quant_tensor import IntQuantTensor from torch import Tensor +from .minifloat import mf_to_raw, fp_mixin_factory -__all__ = ["IntQuantLinearModel", "FloatQuantLinearModel", "mf_to_raw", "raw_to_mf"] +__all__ = [ + "IntQuantLinearModel", + "FloatQuantLinearModel", +] class QuantLinearModel(nn.Module, metaclass=ABCMeta): @@ -70,83 +72,6 @@ def quant_input(self, x: Tensor) -> tuple[Tensor, Tensor]: return inputs.int(), inputs.scale -# Helper for converting quantized floats to raw binary -def mf_to_raw( - x: Tensor, - exponent_bit_width: int, - mantissa_bit_width: int, - exponent_bias: int, - eps: float, -) -> Tensor: - emin = -exponent_bias + 1 - - sign = torch.signbit(x).type(torch.int) - exp = torch.floor(torch.log2(torch.abs(x) + eps)).type(torch.int) - exp = torch.clamp_min(exp, emin) - man = torch.abs(x) / torch.exp2(exp) - - exp_bits = exp - (man < 1).type(torch.int) + exponent_bias # Denorm - man_bits = (man * (1 << mantissa_bit_width)).type(torch.int) - man_bits = man_bits & ((1 << mantissa_bit_width) - 1) - - return ( - (sign << (exponent_bit_width + mantissa_bit_width)) - | (exp_bits << mantissa_bit_width) - | man_bits - ) - - -# Helper for converting raw binary minifloats to floats -def raw_to_mf( - x: Tensor, - exponent_bit_width: int, - mantissa_bit_width: int, - exponent_bias: int, -) -> Tensor: - emin = -exponent_bias + 1 - - sign_mask = 1 << (exponent_bit_width + mantissa_bit_width) - exp_mask = ((1 << exponent_bit_width) - 1) << mantissa_bit_width - man_mask = (1 << mantissa_bit_width) - 1 - - sign = torch.where((x & sign_mask) == 0, 1.0, -1.0) - exp_denorm = ((x & exp_mask) >> mantissa_bit_width).type(torch.float) - man_denorm = (x & man_mask).type(torch.float) / (2**mantissa_bit_width) - - exp = torch.where(exp_denorm == 0, emin, exp_denorm - exponent_bias) - man = torch.where(exp_denorm == 0, man_denorm, 1.0 + man_denorm) - - return sign * man * torch.exp2(exp) - - -# Helper for creating custom minifloat quantizers -def fp_mixin_factory( - exponent_bit_width: int, mantissa_bit_width: int, base_class: FloatBase -): - bit_width = 1 + exponent_bit_width + mantissa_bit_width - name = f"Fp{bit_width}e{exponent_bit_width}m{mantissa_bit_width}" - mixin = type( - name + "Mixin", - (ExtendedInjector,), - { - # Add sign bit - "bit_width": bit_width, - "exponent_bit_width": exponent_bit_width, - "mantissa_bit_width": mantissa_bit_width, - "saturating": True, - }, - ) - - if base_class is FloatActBase: - class_name = name + "Act" - elif base_class is FloatWeightBase: - class_name = name + "Weight" - else: - raise TypeError("Unsupported Float Base") - - return type(class_name, (mixin, base_class), {}) - - class FloatQuantLinearModel(QuantLinearModel): def __init__( self, @@ -166,30 +91,36 @@ def __init__( weight_quant=weight_quant, ) - def quant_weight(self) -> tuple[Tensor, Tensor]: + def quant_weight(self, return_raw: bool = False) -> tuple[Tensor, Tensor]: with torch.no_grad(): weights: FloatQuantTensor = self.fc1.quant_weight() - raw_weights = mf_to_raw( - weights.minifloat(), - weights.exponent_bit_width.int(), - weights.mantissa_bit_width.int(), - weights.exponent_bias.int(), - weights.eps, - ).type(torch.uint8) + out = weights.minifloat() - return raw_weights, weights.scale + if return_raw: + out = mf_to_raw( + out, + weights.exponent_bit_width.int(), + weights.mantissa_bit_width.int(), + weights.exponent_bias.int(), + weights.eps, + ).type(torch.uint8) - def quant_input(self, x: Tensor) -> tuple[Tensor, Tensor]: + return out, weights.scale + + def quant_input(self, x: Tensor, return_raw: bool = False) -> tuple[Tensor, Tensor]: with torch.no_grad(): inputs: FloatQuantTensor = self.fc1.input_quant(x) - raw_inputs = mf_to_raw( - inputs.minifloat(), - inputs.exponent_bit_width.int(), - inputs.mantissa_bit_width.int(), - inputs.exponent_bias.int(), - inputs.eps, - ).type(torch.uint8) + out = inputs.minifloat() + + if return_raw: + out = mf_to_raw( + out, + inputs.exponent_bit_width.int(), + inputs.mantissa_bit_width.int(), + inputs.exponent_bias.int(), + inputs.eps, + ).type(torch.uint8) - return raw_inputs, inputs.scale + return out, inputs.scale From dfe52759670b4b7dad2ff9a8ad6c3b315862c7be Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Thu, 29 May 2025 00:09:09 +0000 Subject: [PATCH 19/64] Fixed overflow errors and added experimental fault crate --- crates/arbolta/src/cell.rs | 10 +++- crates/fault/Cargo.toml | 16 ++++++ crates/fault/src/lib.rs | 1 + crates/fault/src/main.rs | 112 +++++++++++++++++++++++++++++++++++++ crates/fault/src/utils.rs | 56 +++++++++++++++++++ 5 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 crates/fault/Cargo.toml create mode 100644 crates/fault/src/lib.rs create mode 100644 crates/fault/src/main.rs create mode 100644 crates/fault/src/utils.rs diff --git a/crates/arbolta/src/cell.rs b/crates/arbolta/src/cell.rs index a74720f..9ec1d70 100644 --- a/crates/arbolta/src/cell.rs +++ b/crates/arbolta/src/cell.rs @@ -673,7 +673,11 @@ impl CellFn for Sub { let y = if self.signed { BitVec::from_int_sized(a.to_int::() - b.to_int::(), self.y_nets.len()).unwrap() } else { - BitVec::from_int_sized(a.to_int::() - b.to_int::(), self.y_nets.len()).unwrap() + BitVec::from_int_sized( + a.to_int::().wrapping_sub(b.to_int::()), + self.y_nets.len(), + ) + .unwrap() }; self @@ -781,10 +785,12 @@ impl CellFn for Neg { ); // Hard code as i64 add, fix later + // let y = BitVec::from_int_sized(-a.to_int::(), self.y_nets.len()).unwrap(); + let y = if self.signed { BitVec::from_int_sized(-a.to_int::(), self.y_nets.len()).unwrap() } else { - BitVec::from_int_sized(!(a.to_int::()) + 1, self.y_nets.len()).unwrap() + BitVec::from_int_sized((!a.to_int::()).wrapping_add(1), self.y_nets.len()).unwrap() }; self diff --git a/crates/fault/Cargo.toml b/crates/fault/Cargo.toml new file mode 100644 index 0000000..78258a0 --- /dev/null +++ b/crates/fault/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "fault" +version = "0.1.0" +edition = "2024" + +[dependencies] +ndarray-npy = "0.9.1" +ndarray = { version = "0.16.1", features = ["rayon"] } +ndarray-stats = "0.6.0" +num-traits = "0.2" +rayon = "1.10.0" +arbolta = { path = "../arbolta" } +once_cell = "1.21.3" +clap = { version = "4.4.18", features = ["derive"] } +anyhow = "1.0" +indicatif = { version = "0.17.11", features = ["rayon"] } diff --git a/crates/fault/src/lib.rs b/crates/fault/src/lib.rs new file mode 100644 index 0000000..b5614dd --- /dev/null +++ b/crates/fault/src/lib.rs @@ -0,0 +1 @@ +pub mod utils; diff --git a/crates/fault/src/main.rs b/crates/fault/src/main.rs new file mode 100644 index 0000000..dac4d14 --- /dev/null +++ b/crates/fault/src/main.rs @@ -0,0 +1,112 @@ +use anyhow::{Context, Ok, Result}; +use arbolta::bit::Bit; +use arbolta::hardware_module::HardwareModule; +use clap::Parser; +use fault::utils::run_sa; +use indicatif::{ParallelProgressIterator, ProgressIterator}; +use ndarray::parallel::prelude::*; +use ndarray::{Array1, Array2, Array3, Axis}; +use ndarray_npy::read_npy; +use ndarray_stats::QuantileExt; +use std::path::PathBuf; +use std::sync::Mutex; + +#[derive(Parser, Debug)] +struct Args { + /// Name of top module in netlist. + #[clap(long)] + top_module: String, + /// Path to Yosys netlist JSON file. + #[clap(long)] + netlist: String, + /// Path to NumPy inputs file. + #[clap(long)] + inputs: PathBuf, + /// Path to NumPy weights file. + #[clap(long)] + weights: PathBuf, + /// Path to NumPy targets file. + #[clap(long)] + targets: PathBuf, + #[clap(long)] + rows: usize, + #[clap(long)] + cols: usize, + #[clap(long)] + iterations: usize, + #[clap(long)] + sx_size: usize, + #[clap(long)] + sk_size: usize, + #[clap(long)] + m_size: usize, +} + +fn main() -> Result<()> { + let flags = Args::parse(); + + // Load everything + let mut design = HardwareModule::new_from_path(&flags.netlist, &flags.top_module)?; + let inputs: Array3 = read_npy(&flags.inputs) + .with_context(|| format!("Failed to read NumPy array from {:?}", flags.inputs))?; // (N, 28, 28) + let weights: Array2 = read_npy(&flags.weights) + .with_context(|| format!("Failed to read NumPy array from {:?}", flags.weights))?; // (10, 784) + let targets: Array2 = read_npy(&flags.targets) + .with_context(|| format!("Failed to read NumPy array from {:?}", flags.targets))?; + + // Setup designs + design.set_clock(2, Bit::ONE)?; + design.set_reset(3, Bit::ZERO)?; + design.set_port_shape("sx_data_i", &[flags.rows, flags.sx_size])?; + design.set_port_shape("sk_data_i", &[flags.cols, flags.sk_size])?; + design.set_port_shape("m_data_o", &[1, flags.m_size])?; + + // let mut sa = SystolicArray::new(design); + + // let preds: Vec = inputs + // .axis_iter(Axis(0)) + // // .progress() + // .map(|image| { + // let x = image.to_shape((1, flags.iterations)).unwrap(); + // let mut logits = Array2::::zeros((10, 1)); + // sa.run_matmul(&x.t().view(), &weights.t().view(), &mut logits) + // .unwrap(); + + // logits.flatten().argmax().unwrap() as u8 + // }) + // .collect(); + + // let image = inputs.index_axis(Axis(0), 0); + // let image = image.to_shape((1, flags.iterations)).unwrap(); + // let mut logits = Array2::::zeros((10, 1)); + // sa.run_matmul(&image.t().view(), &weights.t().view(), &mut logits)?; + // println!("{logits}"); + + let designs: Vec> = (0..rayon::current_num_threads()) + .map(|_| Mutex::new(design.clone())) + .collect(); + + // Start simulation + let preds: Vec = inputs + .axis_iter(Axis(0)) + .into_par_iter() + // .progress() + .map(|image| { + let x = image.to_shape((1, flags.iterations)).unwrap(); + let mut logits = Array2::::zeros((10, 1)); + + let thread_idx = rayon::current_thread_index().unwrap(); + // let thread_idx = 0; + let design = &mut designs[thread_idx].lock().unwrap(); + run_sa(design, &x.t().view(), &weights.t().view(), &mut logits).unwrap(); + + logits.flatten().argmax().unwrap() as u8 + }) + .collect(); + + let preds: Array1 = preds.into(); + let temp: Array1 = targets.into_flat(); + assert_eq!(preds, temp); + + Ok(()) +} diff --git a/crates/fault/src/utils.rs b/crates/fault/src/utils.rs new file mode 100644 index 0000000..e9f9a58 --- /dev/null +++ b/crates/fault/src/utils.rs @@ -0,0 +1,56 @@ +use anyhow::{Ok, Result}; +use arbolta::hardware_module::HardwareModule; +use ndarray::{Array2, ArrayView2}; +use num_traits::PrimInt; +use std::fmt::Debug; + +/// Run inputs through systolic array. +/// Expects x: (K,R), k: (K,C) -> y: (C,R) +pub fn run_sa< + A: PrimInt + std::ops::BitXorAssign, + B: PrimInt + std::ops::BitXorAssign, + C: PrimInt + std::ops::BitXorAssign + Debug, +>( + design: &mut HardwareModule, + x: &ArrayView2, + k: &ArrayView2, + y: &mut Array2, +) -> Result<()> { + let iterations: usize = x.shape()[0]; // K + + design.eval_reset_clocked(Some(1))?; + for i in 0..iterations { + while design.get_port_int::("s_ready_o")? == 0 { + design.eval_clocked(Some(1))?; + } + design.set_port_int("s_valid_i", 1_u32)?; + design.set_port_ndarray("sx_data_i", x.row(i))?; + design.set_port_ndarray("sk_data_i", k.row(i))?; + + if i == iterations - 1 { + design.set_port_int("s_last_i", 1_u32)?; + } + + design.eval_clocked(Some(1))?; + } + + design.set_port_int("m_ready_i", 1_u32)?; + design.set_port_int("s_valid_i", 0_u32)?; + design.set_port_int("s_last_i", 0_u32)?; + + let mut idx = 0; + loop { + if design.get_port_int::("m_valid_o")? == 1 { + let m_data = design.get_port_ndarray::("m_data_o")?; + m_data.assign_to(y.row_mut(idx)); + idx += 1; + } + + if design.get_port_int::("m_last_o")? == 1 { + break; + } + design.eval_clocked(Some(1))?; + } + + Ok(()) +} From a814cc85914f46ea4f13cf2af12c5a51b246311f Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Thu, 29 May 2025 00:54:44 +0000 Subject: [PATCH 20/64] Updated fault crate --- crates/fault/src/main.rs | 60 +++++++++++---------------------------- crates/fault/src/utils.rs | 36 +++++++++++++++++++++-- 2 files changed, 51 insertions(+), 45 deletions(-) diff --git a/crates/fault/src/main.rs b/crates/fault/src/main.rs index dac4d14..f9ac371 100644 --- a/crates/fault/src/main.rs +++ b/crates/fault/src/main.rs @@ -2,7 +2,7 @@ use anyhow::{Context, Ok, Result}; use arbolta::bit::Bit; use arbolta::hardware_module::HardwareModule; use clap::Parser; -use fault::utils::run_sa; +use fault::utils::worker; use indicatif::{ParallelProgressIterator, ProgressIterator}; use ndarray::parallel::prelude::*; use ndarray::{Array1, Array2, Array3, Axis}; @@ -26,8 +26,11 @@ struct Args { #[clap(long)] weights: PathBuf, /// Path to NumPy targets file. + // #[clap(long)] + // targets: PathBuf, + /// Path to list of nets for fault injection campaign. #[clap(long)] - targets: PathBuf, + nets: PathBuf, #[clap(long)] rows: usize, #[clap(long)] @@ -51,8 +54,13 @@ fn main() -> Result<()> { .with_context(|| format!("Failed to read NumPy array from {:?}", flags.inputs))?; // (N, 28, 28) let weights: Array2 = read_npy(&flags.weights) .with_context(|| format!("Failed to read NumPy array from {:?}", flags.weights))?; // (10, 784) - let targets: Array2 = read_npy(&flags.targets) - .with_context(|| format!("Failed to read NumPy array from {:?}", flags.targets))?; + // let targets: Array2 = read_npy(&flags.targets) + // .with_context(|| format!("Failed to read NumPy array from {:?}", flags.targets))?; + + let nets: Vec = std::fs::read_to_string(&flags.nets)? + .split_whitespace() + .map(|x| x.parse().unwrap()) + .collect(); // Setup designs design.set_clock(2, Bit::ONE)?; @@ -61,52 +69,18 @@ fn main() -> Result<()> { design.set_port_shape("sk_data_i", &[flags.cols, flags.sk_size])?; design.set_port_shape("m_data_o", &[1, flags.m_size])?; - // let mut sa = SystolicArray::new(design); - - // let preds: Vec = inputs - // .axis_iter(Axis(0)) - // // .progress() - // .map(|image| { - // let x = image.to_shape((1, flags.iterations)).unwrap(); - // let mut logits = Array2::::zeros((10, 1)); - // sa.run_matmul(&x.t().view(), &weights.t().view(), &mut logits) - // .unwrap(); - - // logits.flatten().argmax().unwrap() as u8 - // }) - // .collect(); - - // let image = inputs.index_axis(Axis(0), 0); - // let image = image.to_shape((1, flags.iterations)).unwrap(); - // let mut logits = Array2::::zeros((10, 1)); - // sa.run_matmul(&image.t().view(), &weights.t().view(), &mut logits)?; - // println!("{logits}"); - let designs: Vec> = (0..rayon::current_num_threads()) .map(|_| Mutex::new(design.clone())) .collect(); // Start simulation - let preds: Vec = inputs - .axis_iter(Axis(0)) - .into_par_iter() - // .progress() - .map(|image| { - let x = image.to_shape((1, flags.iterations)).unwrap(); - let mut logits = Array2::::zeros((10, 1)); - + nets.into_par_iter().for_each(|n| { + for stuck_val in [Bit::ZERO, Bit::ONE] { let thread_idx = rayon::current_thread_index().unwrap(); - // let thread_idx = 0; let design = &mut designs[thread_idx].lock().unwrap(); - run_sa(design, &x.t().view(), &weights.t().view(), &mut logits).unwrap(); - - logits.flatten().argmax().unwrap() as u8 - }) - .collect(); - - let preds: Array1 = preds.into(); - let temp: Array1 = targets.into_flat(); - assert_eq!(preds, temp); + worker(design, &inputs.view(), &weights.view(), n, stuck_val).unwrap(); + } + }); Ok(()) } diff --git a/crates/fault/src/utils.rs b/crates/fault/src/utils.rs index e9f9a58..8c5bfb0 100644 --- a/crates/fault/src/utils.rs +++ b/crates/fault/src/utils.rs @@ -1,9 +1,41 @@ use anyhow::{Ok, Result}; -use arbolta::hardware_module::HardwareModule; -use ndarray::{Array2, ArrayView2}; +use arbolta::{bit::Bit, hardware_module::HardwareModule}; +use ndarray::{Array1, Array2, ArrayView2, ArrayView3, Axis}; +use ndarray_npy::write_npy; +use ndarray_stats::QuantileExt; use num_traits::PrimInt; use std::fmt::Debug; +pub fn worker( + design: &mut HardwareModule, + inputs: &ArrayView3, + weights: &ArrayView2, + net: usize, + stuck_val: Bit, +) -> Result<()> { + design.reset(); + design.stick_signal(net, stuck_val)?; + + let preds: Array1 = inputs + .axis_iter(Axis(0)) + .map(|image| { + // TODO: Fix hardcoded + let x = image.to_shape((1, 28 * 28)).unwrap(); + let mut logits = Array2::::zeros((10, 1)); + + run_sa(design, &x.t().view(), &weights.t().view(), &mut logits).unwrap(); + + logits.flatten().argmax().unwrap() as u8 + }) + .collect(); + + design.unstick_signal(net)?; + + write_npy(format!("net_{net}_val_{stuck_val}.npy",), &preds)?; + + Ok(()) +} + /// Run inputs through systolic array. /// Expects x: (K,R), k: (K,C) -> y: (C,R) pub fn run_sa< From 94934983af09bbc235893aa1c7d4fa3429f0f1ef Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Thu, 29 May 2025 01:10:16 +0000 Subject: [PATCH 21/64] Added progress bar --- crates/fault/src/main.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/fault/src/main.rs b/crates/fault/src/main.rs index f9ac371..81d08d5 100644 --- a/crates/fault/src/main.rs +++ b/crates/fault/src/main.rs @@ -3,11 +3,10 @@ use arbolta::bit::Bit; use arbolta::hardware_module::HardwareModule; use clap::Parser; use fault::utils::worker; -use indicatif::{ParallelProgressIterator, ProgressIterator}; +use indicatif::ParallelProgressIterator; use ndarray::parallel::prelude::*; -use ndarray::{Array1, Array2, Array3, Axis}; +use ndarray::{Array2, Array3}; use ndarray_npy::read_npy; -use ndarray_stats::QuantileExt; use std::path::PathBuf; use std::sync::Mutex; @@ -74,7 +73,7 @@ fn main() -> Result<()> { .collect(); // Start simulation - nets.into_par_iter().for_each(|n| { + nets.into_par_iter().progress().for_each(|n| { for stuck_val in [Bit::ZERO, Bit::ONE] { let thread_idx = rayon::current_thread_index().unwrap(); let design = &mut designs[thread_idx].lock().unwrap(); From c312f0fdf7b3dd8c38a10251ba5e0f997d19daf3 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Mon, 18 Aug 2025 20:46:56 +0000 Subject: [PATCH 22/64] Massive updates to modular cells and yosys handling --- Cargo.toml | 22 + crates/arbolta/Cargo.toml | 41 +- crates/arbolta/src/bit.rs | 498 +- crates/arbolta/src/cell.rs | 903 -- crates/arbolta/src/cell/mod.rs | 255 + crates/arbolta/src/cell/simcells.rs | 176 + crates/arbolta/src/cell/simlib/arithmetic.rs | 135 + crates/arbolta/src/cell/simlib/bool_ops.rs | 131 + .../src/cell/simlib/logic_reduce_ops.rs | 115 + crates/arbolta/src/cell/simlib/mod.rs | 27 + crates/arbolta/src/cell/simlib/registers.rs | 43 + crates/arbolta/src/cell/simlib/various.rs | 71 + crates/arbolta/src/cell/test_macros.rs | 125 + crates/arbolta/src/hardware_module.rs | 436 +- crates/arbolta/src/lib.rs | 1 + crates/arbolta/src/port.rs | 130 +- crates/arbolta/src/signal.rs | 140 +- crates/arbolta/src/yosys.rs | 173 + .../arbolta/tests/deps/simcells_wrappers.json | 11967 ++++++++++++++++ crates/arbolta/tests/deps/simlib_wrappers.sv | 54 + crates/arbolta/tests/test_bit.rs | 17 +- crates/arbolta/tests/test_bitvec.rs | 209 +- crates/arbolta/tests/test_cell.rs | 211 - crates/arbolta/tests/test_module.rs | 366 +- .../tests/test_netlists/4b_adder_netlist.json | 133 - .../4b_nested_adder_netlist.json | 89 - crates/arbolta/tests/test_signal.rs | 92 +- crates/arbolta/tests/test_simcell.rs | 201 + crates/arbolta/tests/test_simlib.rs | 77 + crates/fault/src/lib.rs | 2 +- crates/fault/src/main.rs | 148 +- crates/fault/src/utils.rs | 146 +- crates/python_bindings/Cargo.toml | 5 + .../py_src/arbolta/__init__.py | 4 +- .../python_bindings/py_src/arbolta/design.py | 45 +- crates/python_bindings/src/conversion.rs | 41 +- crates/python_bindings/src/design.rs | 62 +- crates/yosys_server/Cargo.toml | 20 + crates/yosys_server/src/lib.rs | 41 + crates/yosys_server/src/main.rs | 54 + crates/yosys_server/src/server.rs | 246 + 41 files changed, 14988 insertions(+), 2664 deletions(-) delete mode 100644 crates/arbolta/src/cell.rs create mode 100644 crates/arbolta/src/cell/mod.rs create mode 100644 crates/arbolta/src/cell/simcells.rs create mode 100644 crates/arbolta/src/cell/simlib/arithmetic.rs create mode 100644 crates/arbolta/src/cell/simlib/bool_ops.rs create mode 100644 crates/arbolta/src/cell/simlib/logic_reduce_ops.rs create mode 100644 crates/arbolta/src/cell/simlib/mod.rs create mode 100644 crates/arbolta/src/cell/simlib/registers.rs create mode 100644 crates/arbolta/src/cell/simlib/various.rs create mode 100644 crates/arbolta/src/cell/test_macros.rs create mode 100644 crates/arbolta/src/yosys.rs create mode 100644 crates/arbolta/tests/deps/simcells_wrappers.json create mode 100644 crates/arbolta/tests/deps/simlib_wrappers.sv delete mode 100644 crates/arbolta/tests/test_cell.rs delete mode 100644 crates/arbolta/tests/test_netlists/4b_adder_netlist.json delete mode 100644 crates/arbolta/tests/test_netlists/4b_nested_adder_netlist.json create mode 100644 crates/arbolta/tests/test_simcell.rs create mode 100644 crates/arbolta/tests/test_simlib.rs create mode 100644 crates/yosys_server/Cargo.toml create mode 100644 crates/yosys_server/src/lib.rs create mode 100644 crates/yosys_server/src/main.rs create mode 100644 crates/yosys_server/src/server.rs diff --git a/Cargo.toml b/Cargo.toml index dc89a22..19eaa80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,28 @@ members = ["crates/*"] resolver = "2" +[workspace.dependencies] +anyhow = "1" +bincode = { version = "2", features = ["serde"] } +clap = { version = "4.5", features = ["derive"] } +derive_more = { version = "2", features = ["full"] } +enum_dispatch = "0.3" +flexbuffers = "25.2" +futures = "0.3" +indexmap = "2.9" +ndarray = "0.16.1" +num-traits = "0.2" +rstest = "0.26" +serde = { version = "1", features = ["derive"] } +serde-error = "0.1" +smallvec = { version = "1", features = ["serde", "impl_bincode"] } +tarpc = { version = "0.36", features = ["full"] } +tempfile = "3.19" +thiserror = "2.0" +tokio = { version = "1", features = ["full"] } +yosys-netlist-json = { git = "https://github.com/alexredd99/yosys-netlist-json.git" } +once_cell = "1.21" + [profile.release] lto = true codegen-units = 1 diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index 5f5b220..11c7311 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -4,18 +4,31 @@ version = "0.1.0" authors = ["AMD Research & Advanced Development"] edition = "2024" +[[bench]] +name = "benchmark" +harness = false + [dependencies] -anyhow = "1.0.98" -indexmap = "2.9.0" -tempfile = "3.19.1" -derive_more = { version = "2", features = ["full"] } -serde = { version = "1.0", features = ["derive"] } -bincode = {version = "2.0.1", features = ["serde"] } -enum_dispatch = "0.3" -ndarray = "0.16.1" -num-traits = "0.2" -rstest = "0.25.0" -flexbuffers = "25.2.10" -yosys-netlist-json = { git = "https://github.com/alexredd99/yosys-netlist-json.git" } -thiserror = "2.0.12" -smallvec = {version = "1.15.0", features = ["serde", "impl_bincode"] } +anyhow = { workspace = true } +bincode = { workspace = true } +derive_more = { workspace = true } +enum_dispatch = { workspace = true } +flexbuffers = { workspace = true } +futures = { workspace = true } +indexmap = { workspace = true } +ndarray = { workspace = true } +num-traits = { workspace = true } +rstest = { workspace = true } +serde = { workspace = true } +serde-error = { workspace = true } +smallvec = { workspace = true } +tarpc = { workspace = true } +tempfile = { workspace = true } +thiserror = { workspace = true } +tokio = { workspace = true } +yosys-netlist-json = { workspace = true } +derive-new = "0.7" +yosys_server = { path = "../yosys_server" } +criterion = "0.5" +pprof = { version = "0.15", features = ["criterion", "flamegraph"] } +once_cell = { workspace = true } diff --git a/crates/arbolta/src/bit.rs b/crates/arbolta/src/bit.rs index 4671269..f5129ba 100644 --- a/crates/arbolta/src/bit.rs +++ b/crates/arbolta/src/bit.rs @@ -4,72 +4,44 @@ use anyhow::Result; use bincode::{Decode, Encode}; use core::fmt; -use ndarray::{Array1, ArrayView1, ArrayViewMut1}; -use num_traits::PrimInt; +use derive_more::{BitAnd, BitOr, BitXor, IntoIterator, Not}; +use num_traits::{PrimInt, WrappingAdd, WrappingShl, WrappingSub}; use serde::{Deserialize, Serialize}; use std::convert::{From, Into}; use std::fmt::Debug; -use std::ops::{BitAnd, BitOr, BitXor, Not}; use std::str::FromStr; use thiserror::Error; /// Primitive signal value -#[derive(Debug, Clone, Eq, Copy, PartialEq, Deserialize, Serialize, Default, Encode, Decode)] -pub struct Bit(pub bool); +#[repr(transparent)] +#[derive( + derive_more::Debug, + Clone, + Eq, + Copy, + PartialEq, + Deserialize, + derive_more::From, + derive_more::Into, + Serialize, + Default, + Encode, + Decode, + BitAnd, + BitOr, + BitXor, + Not, +)] +pub struct Bit(#[debug("{}", if *_0 {"1"} else {"0"})] pub bool); #[derive(Debug, PartialEq, Eq, Error)] -#[error("error converting bits")] -pub struct ParseBitError; - -impl From for Bit { - fn from(val: bool) -> Self { - if val { Self::ONE } else { Self::ZERO } - } -} - -impl From for bool { - fn from(val: Bit) -> Self { - match val { - Bit::ZERO => false, - Bit::ONE => true, - } - } -} - -impl TryFrom for Bit { - type Error = ParseBitError; - fn try_from(val: char) -> Result { - match val { - '0' => Ok(Self(false)), - '1' => Ok(Self(true)), - _ => Err(ParseBitError), - } - } -} - -impl From for char { - fn from(bit: Bit) -> Self { - match bit { - Bit(false) => '0', - Bit(true) => '1', - } - } -} +#[error("Couldn't convert `{0}`")] +pub struct ParseBitError(char); impl Bit { pub const ZERO: Bit = Bit(false); pub const ONE: Bit = Bit(true); - pub fn from_int(val: T) -> Result { - if val == T::zero() { - Ok(Self(false)) - } else if val == T::one() { - Ok(Self(true)) - } else { - Err(ParseBitError) - } - } - pub fn to_int(self) -> T { match self { Self(false) => T::zero(), @@ -78,44 +50,35 @@ impl Bit { } } -impl FromStr for Bit { - type Err = ParseBitError; - - fn from_str(s: &str) -> Result { - let int_val = s.parse::().or(Err(ParseBitError))?; - Self::from_int(int_val) - } -} - -impl Not for Bit { - type Output = Self; - - fn not(self) -> Self::Output { - Bit(!self.0) +impl TryFrom for Bit { + type Error = ParseBitError; + fn try_from(val: char) -> Result { + match val { + '0' => Ok(Bit::ZERO), + '1' => Ok(Bit::ONE), + _ => Err(ParseBitError(val)), + } } } -impl BitAnd for Bit { - type Output = Self; - - fn bitand(self, rhs: Self) -> Self::Output { - Bit(self.0 & rhs.0) +impl From<&Bit> for Bit { + fn from(val: &Bit) -> Self { + *val } } -impl BitOr for Bit { - type Output = Self; - - fn bitor(self, rhs: Self) -> Self::Output { - Bit(self.0 | rhs.0) +impl From<&bool> for Bit { + fn from(val: &bool) -> Self { + (*val).into() } } -impl BitXor for Bit { - type Output = Self; - - fn bitxor(self, rhs: Self) -> Self::Output { - Bit(self.0 ^ rhs.0) +impl From for char { + fn from(bit: Bit) -> Self { + match bit { + Bit::ZERO => '0', + Bit::ONE => '1', + } } } @@ -126,16 +89,24 @@ impl fmt::Display for Bit { } /// Structure for storing+manipulating a vector of `Bit`s -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Default, IntoIterator)] pub struct BitVec { + #[into_iterator(owned, ref, ref_mut)] pub bits: Vec, + pub shape: [usize; 2], +} + +impl From> for BitVec { + fn from(bits: Vec) -> Self { + let shape = [1, bits.len()]; + Self { bits, shape } + } } impl fmt::Display for BitVec { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let bit_string: String = self - .bits - .iter() + .into_iter() .rev() .map(|b| >::into(*b)) .collect(); @@ -151,44 +122,7 @@ impl FromStr for BitVec { for c in s.chars().rev() { bits.push(Bit::try_from(c)?); } - Ok(Self { bits }) - } -} - -impl From> for BitVec { - fn from(bits: Vec) -> Self { - Self { bits } - } -} - -impl From for Vec { - fn from(val: BitVec) -> Self { - val.bits - } -} - -impl From<&BitVec> for Vec { - fn from(val: &BitVec) -> Self { - val.bits.iter().rev().map(|b| b.0).collect() - } -} - -impl From for Vec { - fn from(val: BitVec) -> Self { - val.bits.iter().rev().map(|b| b.0).collect() - } -} - -impl From<&[bool]> for BitVec { - fn from(vals: &[bool]) -> Self { - let bits: Vec = vals.iter().rev().map(|b| Bit(*b)).collect(); - Self::from(bits) - } -} - -impl From> for BitVec { - fn from(vals: Vec) -> Self { - Self::from(vals.as_slice()) + Ok(bits.into()) } } @@ -199,285 +133,119 @@ impl TryFrom<&str> for BitVec { for c in value.chars().rev() { bits.push(Bit::try_from(c)?); } - Ok(Self { bits }) + Ok(bits.into()) } } impl From for String { fn from(val: BitVec) -> Self { val - .bits - .iter() + .into_iter() .rev() - .map(|b| >::into(*b)) + .map(>::into) .collect() } } -impl Default for BitVec { - fn default() -> Self { - Self::new() - } -} impl BitVec { - pub fn new() -> Self { - Self { bits: vec![] } - } - - /// Convert int to vector of `Bit`s. - /// - /// # Arguments - /// * `val` - Int to convert. - /// * `size` - Number of bits to use. - fn int_to_bits_sized(val: T, size: usize) -> Result> { - let mut bits: Vec = vec![]; - for n in 0..size { - bits.push(Bit::from_int((val >> n) & T::one())?); - } - - Ok(bits) - } - - /// Convert `Bit`s to int. - /// Automatically extends sign if target int type is signed. - fn bits_to_int(bits: &[Bit]) -> T { - let mut val: T; - let bit_ints: Vec = bits.iter().map(|b| b.to_int()).collect(); - - // Signed bits, need to sign extend - if *bit_ints.last().unwrap() == T::one() && T::min_value() != T::zero() { - val = !T::zero(); - bit_ints - .iter() - .enumerate() - .for_each(|(i, b)| val ^= (*b ^ T::one()) << i); - } else { - val = T::zero(); - bit_ints - .iter() - .enumerate() - .for_each(|(i, b)| val ^= *b << i); - } - - val - } - - /// Convert slice of `Bit`s to vector of ints. - /// - /// # Arguments - /// * `bits` - Slice of `Bit`s to convert. - /// * `elem_size` - Number of bits per int. - fn bits_to_ints(bits: &[Bit], elem_size: usize) -> Vec { - bits - .chunks(elem_size) - .map(|chunk| Self::bits_to_int(chunk)) - .collect() - } - - /// Convert slice of `Bit`s to vector of ints and store in buffer. + /// Create from iterator of bools. /// /// # Arguments - /// * `bits` - Slice of `Bit`s to convert. - /// * `elem_size` - Number of bits per int. - /// * `buffer` - Buffer to store ints. - fn bits_to_ints_buffer( - bits: &[Bit], - elem_size: usize, - buffer: &mut [T], - ) { - bits - .chunks(elem_size) - .enumerate() - .for_each(|(i, chunk)| buffer[i] = Self::bits_to_int(chunk)); - } - // --- Integer Conversion Helpers --- - - /// Create from int. - /// - /// # Arguments - /// * `val` - Int to convert. - /// * `size` - Number of bits to use. - pub fn from_int_sized(val: T, size: usize) -> Result { - let bits = Self::int_to_bits_sized(val, size)?; - Ok(Self::from(bits)) + /// * `vals` - Bools to convert. + pub fn from_bools(vals: I) -> Self + where + I: IntoIterator, + { + vals + .into_iter() + .map(|b| b.into()) + .collect::>() + .into() } /// Create from int. /// /// # Arguments /// * `val` - Int to convert. - pub fn from_int(val: T) -> Result { - let type_size = std::mem::size_of::() * 8; // bytes to bits - let bits = Self::int_to_bits_sized(val, type_size)?; - Ok(Self::from(bits)) - } - - /// Convert to int. - pub fn to_int(&self) -> T { - Self::bits_to_int(&self.bits) - } - - /// Create from slice of ints. - /// - /// # Arguments - /// * `vals` - Ints to convert. - /// * `elem_size` - Number of bits per int. - pub fn from_ints_sized(vals: &[T], elem_size: usize) -> Result { - let mut bits: Vec = vec![]; - for val in vals { - bits.append(&mut Self::int_to_bits_sized(*val, elem_size)?); + /// * `size` - Number of bits to use. Defaults to `sizeof(T)` * 8. + pub fn from_int(val: T, size: Option) -> Self { + let bit_width = std::mem::size_of::() * 8; + let size = size.unwrap_or(bit_width); + // Have to do this since no support for right shift without panic + let mut bits = (0..size.min(bit_width)) + .map(|n| Bit::from((val >> n) & T::one() == T::one())) + .collect::>(); + + // Have to pad + if bits.len() < size { + let is_signed = T::min_value() != T::zero(); + let sign_bit_set: bool = bits.last().is_some_and(|&b| b.into()); + let pad_bit: Bit = (is_signed && sign_bit_set).into(); + + let pad_size = size - bits.len(); + bits.extend(std::iter::repeat_n(pad_bit, pad_size)); } - Ok(Self::from(bits)) - } - - /// Create from slice of ints. - /// - /// # Arguments - /// * `vals` - Ints to convert. - pub fn from_ints(vals: &[T]) -> Result { - let type_size = std::mem::size_of::() * 8; // bytes to bits - Self::from_ints_sized(vals, type_size) - } - - /// Convert to vector of ints. - pub fn to_ints(&self) -> Vec { - let type_size = std::mem::size_of::() * 8; // bytes to bits - Self::bits_to_ints(&self.bits, type_size) - } - - /// Convert to ints and store in buffer. - /// - /// # Arguments - /// * `buffer` - Buffer to store ints. - pub fn to_ints_buffer(&self, buffer: &mut [T]) { - let type_size = std::mem::size_of::() * 8; // bytes to bits - Self::bits_to_ints_buffer(&self.bits, type_size, buffer); - } - - /// Convert to vector of ints. - /// - /// # Arguments - /// * `elem_size` - Number of bits per int. - pub fn to_ints_sized(&self, elem_size: usize) -> Vec { - Self::bits_to_ints(&self.bits, elem_size) + bits.into() } - /// Convert to ints and store in buffer. - /// - /// # Arguments - /// * `elem_size` - Number of bits per int. - /// * `buffer` - Buffer to store ints. - pub fn to_ints_sized_buffer( - &self, - elem_size: usize, - buffer: &mut [T], - ) { - Self::bits_to_ints_buffer(&self.bits, elem_size, buffer); - } - - /// Create from `ndarray` of ints. + /// Create from iterator of ints. /// /// # Arguments /// * `vals` - Ints to convert. - /// * `elem_size` - Number of bits per int. - pub fn from_int_ndarray_sized(vals: ArrayView1, elem_size: usize) -> Result { - let mut bits: Vec = vec![]; - for val in vals { - bits.append(&mut Self::int_to_bits_sized(*val, elem_size)?); + /// * `elem_size` - Number of bits per int. Defaults to `sizeof(T)` * 8. + pub fn from_ints(vals: I, elem_size: Option) -> Self + where + T: PrimInt, + I: IntoIterator, + { + let elem_size = elem_size.unwrap_or(std::mem::size_of::() * 8); // bytes to bits + let bits = vals + .into_iter() + .flat_map(|v| Self::from_int(v, Some(elem_size))) + .collect::>(); + let size = bits.len() / elem_size; + Self { + bits, + shape: [size, elem_size], } - Ok(Self::from(bits)) - } - - /// Create from `ndarray` of ints. - /// - /// # Arguments - /// * `vals` - Ints to convert. - pub fn from_int_ndarray(vals: ArrayView1) -> Result { - let type_size = std::mem::size_of::() * 8; // bytes to bits - Self::from_int_ndarray_sized(vals, type_size) } - /// Create from `ndarray` of bools. - /// - /// # Arguments - /// * `vals` - Bools to convert. - pub fn from_bool_ndarray(vals: ArrayView1) -> Result { - match vals.as_slice() { - None => Err(ParseBitError)?, - Some(buffer_slice) => Ok(Self::from(buffer_slice)), + /// Convert to int. + /// Automatically extends sign if target int type is signed. + pub fn to_int(&self) -> T { + let mut val = T::zero(); + self + .bits + .iter() + .enumerate() + .for_each(|(i, bit)| val = val.wrapping_add(&(*bit).to_int::().wrapping_shl(i as u32))); + + // Signed int and negative value, need to sign extend + if T::min_value() != T::zero() && self.bits.last().is_some_and(|&b| b.into()) { + let mask = !T::one() + .wrapping_shl(self.bits.len() as u32 - 1) + .wrapping_sub(&T::one()); + val = val | mask; } - } - - /// Convert to `ndarray` of ints. - /// - /// # Arguments - /// * `elem_size` - Number of bits per int. - pub fn to_int_ndarray_sized( - &self, - elem_size: usize, - ) -> Array1 { - Array1::from_vec(Self::bits_to_ints(&self.bits, elem_size)) - } - /// Convert to ints and store in `ndarray`. - /// - /// # Arguments - /// * `elem_size` - Number of bits per int. - /// * `buffer` - `ndarray` buffer to store ints. - pub fn to_int_ndarray_sized_buffer( - &self, - elem_size: usize, - mut buffer: ArrayViewMut1, - ) -> Result<()> { - match buffer.as_slice_mut() { - None => Err(ParseBitError)?, - Some(buffer_slice) => { - Self::bits_to_ints_buffer(&self.bits, elem_size, buffer_slice); - Ok(()) - } - } + val } - /// Convert to ints and store in `ndarray`. + /// Convert to iterator of ints. + /// Automatically extends sign if target int type is signed. /// /// # Arguments - /// * `buffer` - `ndarray` buffer to store ints. - pub fn to_int_ndarray_buffer( + /// * `elem_size` - Number of bits per int. Defaults to `sizeof(T)` * 8. + pub fn to_ints( &self, - mut buffer: ArrayViewMut1, - ) -> Result<()> { - let type_size = std::mem::size_of::() * 8; // bytes to bits - match buffer.as_slice_mut() { - None => Err(ParseBitError)?, - Some(buffer_slice) => { - Self::bits_to_ints_buffer(&self.bits, type_size, buffer_slice); - Ok(()) - } - } - } - - /// Convert to `ndarray` of ints. - pub fn to_int_ndarray(&self) -> Array1 { - let type_size = std::mem::size_of::() * 8; // bytes to bits - Array1::from_vec(Self::bits_to_ints(&self.bits, type_size)) - } - - /// Convert to bools and store in `ndarray`. - /// - /// # Arguments - /// * `buffer` - `ndarray` buffer to store bools. - pub fn to_bool_ndarray_buffer(&self, mut buffer: ArrayViewMut1) -> Result<()> { - match buffer.as_slice_mut() { - None => Err(ParseBitError)?, - Some(buffer_slice) => { - self - .bits - .iter() - .enumerate() - .for_each(|(i, b)| buffer_slice[i] = b.0); - Ok(()) - } - } + elem_size: Option, + ) -> impl Iterator { + // let elem_size = elem_size.unwrap_or(std::mem::size_of::() * 8); + let elem_size = elem_size.unwrap_or(self.shape[1]); + self + .bits + .chunks(elem_size) + .map(|chunk| Self::from(chunk.to_vec()).to_int()) } } diff --git a/crates/arbolta/src/cell.rs b/crates/arbolta/src/cell.rs deleted file mode 100644 index 9ec1d70..0000000 --- a/crates/arbolta/src/cell.rs +++ /dev/null @@ -1,903 +0,0 @@ -use crate::bit::{Bit, BitVec}; -use crate::signal::Signal; -use bincode::{Decode, Encode}; -use derive_more::Constructor; -use enum_dispatch::enum_dispatch; -use indexmap::IndexMap; -use serde::{Deserialize, Serialize}; -use smallvec::SmallVec; -use std::fmt::Debug; -use thiserror::Error; -use yosys_netlist_json as yosys; - -#[enum_dispatch] -pub trait CellFn { - fn eval(&mut self, signals: &mut [Signal]); - fn reset(&mut self); -} - -#[enum_dispatch(CellFn)] -#[derive(Debug, Serialize, Deserialize, Clone, Encode, Decode)] -/// Proxy for a standard-cell and basic unit of 'compute'. -pub enum Cell { - Inverter, - Buf, - Nand, - And, - AndNot, - Or, - Nor, - Xor, - Xnor, - OrNot, - Dff, - DffReset, - Reg, - Mux, - Add, - Sub, - Mul, - Pos, - Neg, - Shl, - LogicAnd, - LogicNot, -} - -#[derive(Debug, Error)] -pub enum CellError { - #[error("unsupported cell `{0}`")] - Unsupported(String), -} - -/// Generate a cell given its Yosys netlist description -/// # Arguments -/// * `cell` - Yosys cell -pub fn create_cell(cell: &yosys::Cell) -> Result { - let mut input_connections: IndexMap> = IndexMap::new(); - let mut output_connections: IndexMap> = IndexMap::new(); - - for (port_name, port_bits) in cell.connections.iter() { - // Skip ports with no direction - let Some(direction) = cell.port_directions.get(port_name) else { - continue; - }; - - if *direction == yosys::PortDirection::InOut { - todo!("Inout not supported.") - }; - - for bit in port_bits { - let net = match bit { - yosys::BitVal::N(net) => *net, - yosys::BitVal::S(constant) => match constant { - yosys::SpecialBit::_0 => 0, // Global 0 - yosys::SpecialBit::_1 => 1, // Global 1 - yosys::SpecialBit::X => todo!("X not supported."), - yosys::SpecialBit::Z => todo!("Z not supported."), - }, - }; - - if *direction == yosys::PortDirection::Input { - input_connections - .entry(port_name.to_string()) - .or_default() - .push(net); - } else { - output_connections - .entry(port_name.to_string()) - .or_default() - .push(net); - } - } - } - - let new_cell: Cell = match cell.cell_type.as_str() { - "BUF" | "$_BUF_" => Cell::Buf(Buf::new( - input_connections["A"][0], - output_connections["Y"][0], - )), - "NOT" | "$_NOT_" | "$not" => Cell::Inverter(Inverter::new( - input_connections["A"][0], - output_connections["Y"][0], - )), - "$_NAND_" | "NAND" => Cell::Nand(Nand::new( - input_connections - .into_values() - .flatten() - .collect::>() - .into(), - output_connections["Y"][0], - )), - "OR" | "$_OR_" | "$reduce_or" => Cell::Or(Or::new( - input_connections - .into_values() - .flatten() - .collect::>() - .into(), - output_connections["Y"][0], - )), - "ORNOT" | "$_ORNOT_" => Cell::OrNot(OrNot::new( - input_connections - .into_values() - .flatten() - .collect::>() - .into(), - output_connections["Y"][0], - )), - "$_NOR_" | "NOR" => Cell::Nor(Nor::new( - input_connections - .into_values() - .flatten() - .collect::>() - .into(), - output_connections["Y"][0], - )), - "XOR" | "$_XOR_" => Cell::Xor(Xor::new( - input_connections - .into_values() - .flatten() - .collect::>() - .into(), - output_connections["Y"][0], - )), - "$_XNOR_" | "XNOR" => Cell::Xnor(Xnor::new( - input_connections - .into_values() - .flatten() - .collect::>() - .into(), - output_connections["Y"][0], - )), - "AND" | "$_AND_" => Cell::And(And::new( - input_connections - .into_values() - .flatten() - .collect::>() - .into(), - output_connections["Y"][0], - )), - "ANDNOT" | "$_ANDNOT_" => Cell::AndNot(AndNot::new( - input_connections - .into_values() - .flatten() - .collect::>() - .into(), - output_connections["Y"][0], - )), - "DFF" | "$_DFF_P_" => Cell::Dff(Dff::new( - Bit::ONE, - input_connections["C"][0], - input_connections["D"][0], - output_connections["Q"][0], - )), - "$_SDFF_PP0_" => Cell::DffReset(DffReset::new( - Bit::ONE, - input_connections["D"][0], - input_connections["C"][0], - input_connections["R"][0], - output_connections["Q"][0], - )), - "$pos" => Cell::Pos(Pos::new( - cell.parameters["A_SIGNED"].to_number().unwrap() == 1, - input_connections["A"].clone().into(), - output_connections["Y"].clone().into(), - )), - "$neg" => Cell::Neg(Neg::new( - cell.parameters["A_SIGNED"].to_number().unwrap() == 1, - input_connections["A"].clone().into(), - output_connections["Y"].clone().into(), - )), - // Proc Cells - "$mux" | "$_MUX_" => Cell::Mux(Mux::new( - input_connections["S"][0], - input_connections["A"].clone().into(), - input_connections["B"].clone().into(), - output_connections["Y"].clone().into(), - )), - "$dff" => Cell::Reg(Reg::new( - Bit::ONE, - input_connections["CLK"][0], - input_connections["D"].clone().into(), - output_connections["Q"].clone().into(), - )), - "$add" => Cell::Add(Add::new( - // Hacky, fix later - cell.parameters["A_SIGNED"].to_number().unwrap() == 1, - input_connections["A"].clone().into(), - input_connections["B"].clone().into(), - output_connections["Y"].clone().into(), - )), - "$sub" => Cell::Sub(Sub::new( - // Hacky, fix later - cell.parameters["A_SIGNED"].to_number().unwrap() == 1, - input_connections["A"].clone().into(), - input_connections["B"].clone().into(), - output_connections["Y"].clone().into(), - )), - "$mul" => Cell::Mul(Mul::new( - // Hacky, fix later - cell.parameters["A_SIGNED"].to_number().unwrap() - == cell.parameters["B_SIGNED"].to_number().unwrap(), - input_connections["A"].clone().into(), - input_connections["B"].clone().into(), - output_connections["Y"].clone().into(), - )), - "$shl" => Cell::Shl(Shl::new( - // Hacky, fix later - cell.parameters["A_SIGNED"].to_number().unwrap() == 1, - input_connections["A"].clone().into(), - input_connections["B"].clone().into(), - output_connections["Y"].clone().into(), - )), - "$logic_and" => Cell::LogicAnd(LogicAnd::new( - // Hacky, fix later - input_connections["A"].clone().into(), - input_connections["B"].clone().into(), - output_connections["Y"].clone().into(), - )), - "$logic_not" => Cell::LogicNot(LogicNot::new( - // Hacky, fix later - input_connections["A"].clone().into(), - output_connections["Y"].clone().into(), - )), - _ => return Err(CellError::Unsupported(cell.cell_type.to_string())), - }; - - Ok(new_cell) -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct Inverter { - input_net: usize, - output_net: usize, -} - -impl CellFn for Inverter { - fn eval(&mut self, signals: &mut [Signal]) { - signals[self.output_net].set_value(!signals[self.input_net].get_value()); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct Buf { - input_net: usize, - output_net: usize, -} - -impl CellFn for Buf { - fn eval(&mut self, signals: &mut [Signal]) { - signals[self.output_net].set_value(signals[self.input_net].get_value()); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct Nand { - input_nets: SmallVec<[usize; 64]>, - output_net: usize, -} - -impl CellFn for Nand { - fn eval(&mut self, signals: &mut [Signal]) { - signals[self.output_net].set_value( - !self - .input_nets - .iter() - .skip(1) - .fold(signals[self.input_nets[0]].get_value(), |acc, net| { - acc & signals[*net].get_value() - }), - ); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct And { - input_nets: SmallVec<[usize; 64]>, - output_net: usize, -} - -impl CellFn for And { - fn eval(&mut self, signals: &mut [Signal]) { - signals[self.output_net].set_value( - self - .input_nets - .iter() - .skip(1) - .fold(signals[self.input_nets[0]].get_value(), |acc, net| { - acc & signals[*net].get_value() - }), - ); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct AndNot { - input_nets: SmallVec<[usize; 64]>, - output_net: usize, -} - -impl CellFn for AndNot { - fn eval(&mut self, signals: &mut [Signal]) { - signals[self.output_net].set_value(self.input_nets.iter().rev().skip(1).fold( - !signals[*self.input_nets.last().unwrap()].get_value(), - |acc, net| acc & signals[*net].get_value(), - )); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct Or { - input_nets: SmallVec<[usize; 64]>, - output_net: usize, -} - -impl CellFn for Or { - fn eval(&mut self, signals: &mut [Signal]) { - signals[self.output_net].set_value( - self - .input_nets - .iter() - .skip(1) - .fold(signals[self.input_nets[0]].get_value(), |acc, net| { - acc | signals[*net].get_value() - }), - ); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct Nor { - input_nets: SmallVec<[usize; 64]>, - output_net: usize, -} - -impl CellFn for Nor { - fn eval(&mut self, signals: &mut [Signal]) { - signals[self.output_net].set_value( - !self - .input_nets - .iter() - .skip(1) - .fold(signals[self.input_nets[0]].get_value(), |acc, net| { - acc | signals[*net].get_value() - }), - ); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct Xor { - input_nets: SmallVec<[usize; 64]>, - output_net: usize, -} - -impl CellFn for Xor { - fn eval(&mut self, signals: &mut [Signal]) { - signals[self.output_net].set_value( - self - .input_nets - .iter() - .skip(1) - .fold(signals[self.input_nets[0]].get_value(), |acc, net| { - acc ^ signals[*net].get_value() - }), - ); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct Xnor { - input_nets: SmallVec<[usize; 64]>, - output_net: usize, -} - -impl CellFn for Xnor { - fn eval(&mut self, signals: &mut [Signal]) { - signals[self.output_net].set_value( - !self - .input_nets - .iter() - .skip(1) - .fold(signals[self.input_nets[0]].get_value(), |acc, net| { - acc ^ signals[*net].get_value() - }), - ); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct OrNot { - input_nets: SmallVec<[usize; 64]>, - output_net: usize, -} - -impl CellFn for OrNot { - fn eval(&mut self, signals: &mut [Signal]) { - signals[self.output_net].set_value(self.input_nets.iter().rev().skip(1).fold( - !signals[*self.input_nets.last().unwrap()].get_value(), - |acc, net| acc | signals[*net].get_value(), - )); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] -pub struct DffReset { - polarity: Bit, - data_in_net: usize, - clock_net: usize, - reset_net: usize, - data_out_net: usize, - last_clock: Bit, -} - -impl DffReset { - pub fn new( - polarity: Bit, - data_in_net: usize, - clock_net: usize, - reset_net: usize, - data_out_net: usize, - ) -> Self { - Self { - polarity, - data_in_net, - clock_net, - reset_net, - data_out_net, - last_clock: Bit::ZERO, - } - } -} - -impl CellFn for DffReset { - fn eval(&mut self, signals: &mut [Signal]) { - let clock = !(signals[self.clock_net].get_value() ^ self.polarity); - - if clock == Bit::ONE && self.last_clock == Bit::ZERO { - if signals[self.reset_net].get_value() == Bit::ONE { - // Reset - signals[self.data_out_net].set_value(Bit::ZERO); - } else { - signals[self.data_out_net].set_value(signals[self.data_in_net].get_value()); - } - } - - self.last_clock = clock; - } - - fn reset(&mut self) { - self.last_clock = Bit::ZERO; - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] -pub struct Dff { - polarity: Bit, - data_in_net: usize, - clock_net: usize, - data_out_net: usize, - last_clock: Bit, -} - -impl Dff { - pub fn new(polarity: Bit, clock_net: usize, data_in_net: usize, data_out_net: usize) -> Self { - Self { - polarity, - data_in_net, - clock_net, - data_out_net, - last_clock: Bit::ZERO, - } - } -} - -impl CellFn for Dff { - fn eval(&mut self, signals: &mut [Signal]) { - let clock = !(signals[self.clock_net].get_value() ^ self.polarity); - - if clock == Bit::ONE && self.last_clock == Bit::ZERO { - signals[self.data_out_net].set_value(signals[self.data_in_net].get_value()); - } - - self.last_clock = clock; - } - - fn reset(&mut self) { - self.last_clock = Bit::ZERO; - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] -pub struct Reg { - polarity: Bit, - data_in_nets: SmallVec<[usize; 64]>, - clock_net: usize, - data_out_nets: SmallVec<[usize; 64]>, - last_clock: Bit, -} - -impl Reg { - pub fn new( - polarity: Bit, - clock_net: usize, - data_in_nets: SmallVec<[usize; 64]>, - data_out_nets: SmallVec<[usize; 64]>, - ) -> Self { - Self { - polarity, - data_in_nets, - clock_net, - data_out_nets, - last_clock: Bit::ZERO, - } - } -} - -impl CellFn for Reg { - fn eval(&mut self, signals: &mut [Signal]) { - let clock = !(signals[self.clock_net].get_value() ^ self.polarity); - - if clock == Bit::ONE && self.last_clock == Bit::ZERO { - self - .data_out_nets - .iter() - .zip(self.data_in_nets.iter()) - .for_each(|(out_net, in_net)| signals[*out_net].set_value(signals[*in_net].get_value())); - } - - self.last_clock = clock; - } - - fn reset(&mut self) { - self.last_clock = Bit::ZERO; - } -} - -// +++ Proc Cells +++ -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct Mux { - select_net: usize, - a_nets: SmallVec<[usize; 64]>, - b_nets: SmallVec<[usize; 64]>, - y_nets: SmallVec<[usize; 64]>, -} - -impl CellFn for Mux { - fn eval(&mut self, signals: &mut [Signal]) { - let sel = signals[self.select_net].get_value(); - let src_nets = if sel == Bit::ONE { - &self.b_nets - } else { - &self.a_nets - }; - - for (src, dst) in src_nets.iter().zip(self.y_nets.iter()) { - signals[*dst].set_value(signals[*src].get_value()); - } - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct Add { - signed: bool, - a_nets: SmallVec<[usize; 64]>, - b_nets: SmallVec<[usize; 64]>, - y_nets: SmallVec<[usize; 64]>, -} - -impl CellFn for Add { - fn eval(&mut self, signals: &mut [Signal]) { - let a = BitVec::from( - self - .a_nets - .iter() - .map(|net| signals[*net].get_value()) - .collect::>(), - ); - - let b = BitVec::from( - self - .b_nets - .iter() - .map(|net| signals[*net].get_value()) - .collect::>(), - ); - - // Hard code as i64 add, fix later - let y = if self.signed { - BitVec::from_int_sized(a.to_int::() + b.to_int::(), self.y_nets.len()).unwrap() - } else { - BitVec::from_int_sized(a.to_int::() + b.to_int::(), self.y_nets.len()).unwrap() - }; - - self - .y_nets - .iter() - .enumerate() - .for_each(|(i, net)| signals[*net].set_value(y.bits[i])); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct Sub { - signed: bool, - a_nets: SmallVec<[usize; 64]>, - b_nets: SmallVec<[usize; 64]>, - y_nets: SmallVec<[usize; 64]>, -} - -impl CellFn for Sub { - fn eval(&mut self, signals: &mut [Signal]) { - let a = BitVec::from( - self - .a_nets - .iter() - .map(|net| signals[*net].get_value()) - .collect::>(), - ); - - let b = BitVec::from( - self - .b_nets - .iter() - .map(|net| signals[*net].get_value()) - .collect::>(), - ); - - // Hard code as i64 add, fix later - let y = if self.signed { - BitVec::from_int_sized(a.to_int::() - b.to_int::(), self.y_nets.len()).unwrap() - } else { - BitVec::from_int_sized( - a.to_int::().wrapping_sub(b.to_int::()), - self.y_nets.len(), - ) - .unwrap() - }; - - self - .y_nets - .iter() - .enumerate() - .for_each(|(i, net)| signals[*net].set_value(y.bits[i])); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct Mul { - signed: bool, - a_nets: SmallVec<[usize; 64]>, - b_nets: SmallVec<[usize; 64]>, - y_nets: SmallVec<[usize; 64]>, -} - -impl CellFn for Mul { - fn eval(&mut self, signals: &mut [Signal]) { - let a = BitVec::from( - self - .a_nets - .iter() - .map(|net| signals[*net].get_value()) - .collect::>(), - ); - - let b = BitVec::from( - self - .b_nets - .iter() - .map(|net| signals[*net].get_value()) - .collect::>(), - ); - - // Hard code as i64 add, fix later - let y = if self.signed { - BitVec::from_int_sized(a.to_int::() * b.to_int::(), self.y_nets.len()).unwrap() - } else { - BitVec::from_int_sized(a.to_int::() * b.to_int::(), self.y_nets.len()).unwrap() - }; - - self - .y_nets - .iter() - .enumerate() - .for_each(|(i, net)| signals[*net].set_value(y.bits[i])); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct Pos { - signed: bool, - a_nets: SmallVec<[usize; 64]>, - y_nets: SmallVec<[usize; 64]>, -} - -impl CellFn for Pos { - fn eval(&mut self, signals: &mut [Signal]) { - let a = BitVec::from( - self - .a_nets - .iter() - .map(|net| signals[*net].get_value()) - .collect::>(), - ); - - // Hard code as i64 add, fix later - let y = if self.signed { - BitVec::from_int_sized(a.to_int::(), self.y_nets.len()).unwrap() - } else { - BitVec::from_int_sized(a.to_int::(), self.y_nets.len()).unwrap() - }; - - self - .y_nets - .iter() - .enumerate() - .for_each(|(i, net)| signals[*net].set_value(y.bits[i])); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct Neg { - signed: bool, - a_nets: SmallVec<[usize; 64]>, - y_nets: SmallVec<[usize; 64]>, -} - -impl CellFn for Neg { - fn eval(&mut self, signals: &mut [Signal]) { - let a = BitVec::from( - self - .a_nets - .iter() - .map(|net| signals[*net].get_value()) - .collect::>(), - ); - - // Hard code as i64 add, fix later - // let y = BitVec::from_int_sized(-a.to_int::(), self.y_nets.len()).unwrap(); - - let y = if self.signed { - BitVec::from_int_sized(-a.to_int::(), self.y_nets.len()).unwrap() - } else { - BitVec::from_int_sized((!a.to_int::()).wrapping_add(1), self.y_nets.len()).unwrap() - }; - - self - .y_nets - .iter() - .enumerate() - .for_each(|(i, net)| signals[*net].set_value(y.bits[i])); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct Shl { - signed: bool, - a_nets: SmallVec<[usize; 64]>, - b_nets: SmallVec<[usize; 64]>, - y_nets: SmallVec<[usize; 64]>, -} - -impl CellFn for Shl { - fn eval(&mut self, signals: &mut [Signal]) { - let a = BitVec::from( - self - .a_nets - .iter() - .map(|net| signals[*net].get_value()) - .collect::>(), - ); - - let b = BitVec::from( - self - .b_nets - .iter() - .map(|net| signals[*net].get_value()) - .collect::>(), - ); - - let shift = b.to_int::(); - // Hard code as i64 add, fix later - let y = if self.signed { - BitVec::from_int_sized(a.to_int::() << shift, self.y_nets.len()).unwrap() - } else { - BitVec::from_int_sized(a.to_int::() << shift, self.y_nets.len()).unwrap() - }; - - self - .y_nets - .iter() - .enumerate() - .for_each(|(i, net)| signals[*net].set_value(y.bits[i])); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct LogicAnd { - a_nets: SmallVec<[usize; 64]>, - b_nets: SmallVec<[usize; 64]>, - y_nets: SmallVec<[usize; 64]>, -} - -impl CellFn for LogicAnd { - fn eval(&mut self, signals: &mut [Signal]) { - let a = BitVec::from( - self - .a_nets - .iter() - .map(|net| signals[*net].get_value()) - .collect::>(), - ); - - let b = BitVec::from( - self - .b_nets - .iter() - .map(|net| signals[*net].get_value()) - .collect::>(), - ); - - // Hard code as u64 add, fix later - // Assume only need to set LSB - let a = Bit(a.to_int::() != 0); - let b = Bit(b.to_int::() != 0); - signals[self.y_nets[0]].set_value(a & b); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct LogicNot { - a_nets: SmallVec<[usize; 64]>, - y_nets: SmallVec<[usize; 64]>, -} - -impl CellFn for LogicNot { - fn eval(&mut self, signals: &mut [Signal]) { - let mut val = Bit::ZERO; - self - .a_nets - .iter() - .for_each(|net| val = val | signals[*net].get_value()); - - signals[self.y_nets[0]].set_value(!val); - } - - fn reset(&mut self) {} -} diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs new file mode 100644 index 0000000..a700c51 --- /dev/null +++ b/crates/arbolta/src/cell/mod.rs @@ -0,0 +1,255 @@ +mod simcells; +mod simlib; +mod test_macros; + +// Re-export +pub use simcells::*; +pub use simlib::*; + +use crate::{bit::Bit, signal::Signals}; +use bincode::{Decode, Encode}; +use enum_dispatch::enum_dispatch; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use thiserror::Error; +use yosys_netlist_json as yosys_json; + +#[enum_dispatch] +pub trait CellFn { + fn eval(&mut self, signals: &mut Signals); + fn reset(&mut self); +} + +#[enum_dispatch(CellFn)] +#[derive(Debug, Serialize, Deserialize, Clone, Decode, Encode)] +/// Proxy for a standard-cell and basic unit of 'compute'. +pub enum Cell { + // Sim Cells + Buffer, + Inverter, + And, + Nand, + Or, + Nor, + Xor, + Xnor, + AndNot, + OrNot, + Mux2, + NMux2, + AndOrInvert, + OrAndInvert, + Dff, + DffReset, + // Sim Lib + Not, + Pos, + Add, + Sub, + Mul, + Div, + Reg, + Mux, + LogicAnd, + LogicNot, + ReduceOr, +} + +#[derive(Debug, Error)] +pub enum CellError { + #[error("Unsupported cell `{0}`")] + Unsupported(String), + #[error("Cell `{0}` not found in netlist")] + NotFound(String), + #[error("Direction `{0}` not supported")] + Direction(String), + #[error("Bad parameter `{0}` = `{1:?}`")] + Parameter(String, yosys_json::AttributeVal), +} + +/// Parse global net from `BitVal`. +/// Errors if bit direction is not supported. +fn parse_bit(bit: &yosys_json::BitVal) -> Result { + match bit { + yosys_json::BitVal::N(net) => Ok(*net), + yosys_json::BitVal::S(constant) => match constant { + yosys_json::SpecialBit::_0 => Ok(0), // Global 0 + yosys_json::SpecialBit::_1 => Ok(1), // Global 1 + yosys_json::SpecialBit::X => Err(CellError::Direction("X".to_string())), + yosys_json::SpecialBit::Z => Err(CellError::Direction("Z".to_string())), + }, + } +} + +/// Generate a cell given its Yosys netlist description +/// # Arguments +/// * `cell` - Yosys cell +pub fn create_cell(cell: &yosys_json::Cell) -> Result { + // Port name -> net + let mut connections: HashMap> = HashMap::new(); + for (port_name, port_bits) in cell.connections.iter() { + for bit in port_bits { + let net = parse_bit(bit)?; + connections + .entry(port_name.to_string()) + .or_default() + .push(net); + } + } + + let mut parameters: HashMap = HashMap::new(); + for (param_name, param) in cell.parameters.iter() { + let Some(param) = param.to_number() else { + return Err(CellError::Parameter(param_name.to_string(), param.clone())); + }; + parameters.insert(param_name.to_string(), param); + } + + let new_cell: Cell = match cell.cell_type.as_str() { + // Sim cells + "BUF" | "$_BUF_" => Cell::Buffer(Buffer::new(connections["A"][0], connections["Y"][0])), + "NOT" | "$_NOT_" => Cell::Inverter(Inverter::new(connections["A"][0], connections["Y"][0])), + "AND" | "$_AND_" => Cell::And(And::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "NAND" | "$_NAND_" => Cell::Nand(Nand::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "OR" | "$_OR_" => Cell::Or(Or::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "NOR" | "$_NOR_" => Cell::Nor(Nor::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "XOR" | "$_XOR_" => Cell::Xor(Xor::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "XNOR" | "$_XNOR_" => Cell::Xnor(Xnor::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "ANDNOT" | "$_ANDNOT_" => Cell::AndNot(AndNot::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "ORNOT" | "$_ORNOT_" => Cell::OrNot(OrNot::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "$_MUX_" => Cell::Mux2(Mux2::new( + connections["A"][0], + connections["B"][0], + connections["S"][0], + connections["Y"][0], + )), + "$_NMUX_" => Cell::NMux2(NMux2::new( + connections["A"][0], + connections["B"][0], + connections["S"][0], + connections["Y"][0], + )), + "$_AOI3_" => Cell::AndOrInvert(AndOrInvert::new( + connections["A"][0], + connections["B"][0], + connections["C"][0], + connections["Y"][0], + )), + "$_OAI3_ " => Cell::OrAndInvert(OrAndInvert::new( + connections["A"][0], + connections["B"][0], + connections["C"][0], + connections["Y"][0], + )), + "DFF" | "$_DFF_P_" => Cell::Dff(Dff::new( + Bit::ONE, + connections["C"][0], + connections["D"][0], + connections["Q"][0], + )), + "$_SDFF_PP0_ " => Cell::DffReset(DffReset::new( + Bit::ONE, + Bit::ONE, + Bit::ZERO, + connections["C"][0], + connections["R"][0], + connections["D"][0], + connections["Q"][0], + )), + // Sim lib + "$not" => Cell::Not(Not::new( + parameters["A_SIGNED"] != 0, + connections["A"].clone().into(), + connections["Y"].clone().into(), + )), + "$pos" => Cell::Pos(Pos::new( + parameters["A_SIGNED"] != 0, + connections["A"].clone().into(), + connections["Y"].clone().into(), + )), + "$add" => Cell::Add(Add::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), + "$sub" => Cell::Sub(Sub::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), + "$mul" => Cell::Mul(Mul::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), + "$div" => Cell::Div(Div::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), + "$dff" => Cell::Reg(Reg::new( + (parameters["CLK_POLARITY"] != 0).into(), + connections["CLK"][0], + connections["D"].clone().into(), + connections["Q"].clone().into(), + )), + "$mux" => Cell::Mux(Mux::new( + connections["S"][0], + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), + "$logic_and" => Cell::LogicAnd(LogicAnd::new( + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), + "$logic_not" => Cell::LogicNot(LogicNot::new( + connections["A"].clone().into(), + connections["Y"].clone().into(), + )), + "$reduce_or" => Cell::ReduceOr(ReduceOr::new( + connections["A"].clone().into(), + connections["Y"].clone().into(), + )), + _ => return Err(CellError::Unsupported(cell.cell_type.to_string())), + }; + + Ok(new_cell) +} diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs new file mode 100644 index 0000000..014ab15 --- /dev/null +++ b/crates/arbolta/src/cell/simcells.rs @@ -0,0 +1,176 @@ +use super::CellFn; +use crate::{bit::Bit, signal::Signals}; +use bincode::{Decode, Encode}; +use derive_more::Constructor; +use serde::{Deserialize, Serialize}; + +macro_rules! define_unary_cell { + ($name:ident, $body:expr) => { + #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] + pub struct $name { + a_net: usize, + y_net: usize, + } + + impl CellFn for $name { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let a: Bit = signals.get_net(self.a_net); + signals.set_net(self.y_net, $body(a)); + } + + fn reset(&mut self) {} + } + }; +} + +define_unary_cell!(Buffer, |x: Bit| x); +define_unary_cell!(Inverter, |x: Bit| !x); + +macro_rules! define_binary_cell { + ($name:ident, $body:expr) => { + #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] + pub struct $name { + a_net: usize, + b_net: usize, + y_net: usize, + } + + impl CellFn for $name { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let inputs: [Bit; 2] = [signals.get_net(self.a_net), signals.get_net(self.b_net)]; + signals.set_net(self.y_net, $body(inputs)); + } + + fn reset(&mut self) {} + } + }; +} + +define_binary_cell!(And, |x: [Bit; 2]| x[0] & x[1]); +define_binary_cell!(Nand, |x: [Bit; 2]| !(x[0] & x[1])); +define_binary_cell!(Or, |x: [Bit; 2]| x[0] | x[1]); +define_binary_cell!(Nor, |x: [Bit; 2]| !(x[0] | x[1])); +define_binary_cell!(Xor, |x: [Bit; 2]| x[0] ^ x[1]); +define_binary_cell!(Xnor, |x: [Bit; 2]| !(x[0] ^ x[1])); +define_binary_cell!(AndNot, |x: [Bit; 2]| x[0] & !x[1]); +define_binary_cell!(OrNot, |x: [Bit; 2]| x[0] | !x[1]); + +macro_rules! define_ternary_cell { + ($name:ident, $op0_net:ident, $op1_net:ident, $op2_net:ident, $body:expr) => { + #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] + pub struct $name { + $op0_net: usize, + $op1_net: usize, + $op2_net: usize, + y_net: usize, + } + + impl CellFn for $name { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let inputs: [Bit; 3] = [ + signals.get_net(self.$op0_net), + signals.get_net(self.$op1_net), + signals.get_net(self.$op2_net), + ]; + signals.set_net(self.y_net, $body(inputs)); + } + + fn reset(&mut self) {} + } + }; +} + +define_ternary_cell!(AndOrInvert, a_net, b_net, c_net, |x: [Bit; 3]| !((x[0] + & x[1]) + | x[2])); +define_ternary_cell!(OrAndInvert, a_net, b_net, c_net, |x: [Bit; 3]| !((x[0] + | x[1]) + & x[2])); + +define_ternary_cell!( + Mux2, + a_net, + b_net, + select_net, + |x: [Bit; 3]| if x[2].into() { x[1] } else { x[0] } +); +define_ternary_cell!( + NMux2, + a_net, + b_net, + select_net, + |x: [Bit; 3]| if x[2].into() { !x[1] } else { !x[0] } +); + +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, derive_new::new)] +pub struct Dff { + polarity: Bit, + clock_net: usize, + data_in_net: usize, + data_out_net: usize, + #[new(default)] + last_clock: Bit, +} + +impl CellFn for Dff { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + // Check if clock active for any polarity + let clock = !(signals.get_net(self.clock_net) ^ self.polarity); + + // Rising edge + if clock == Bit::ONE && self.last_clock == Bit::ZERO { + let data_in = signals.get_net(self.data_in_net); + signals.set_net(self.data_out_net, data_in); + } + + self.last_clock = clock; + } + + fn reset(&mut self) { + self.last_clock = Bit::ZERO; + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, derive_new::new)] +pub struct DffReset { + clock_polarity: Bit, + reset_polarity: Bit, + reset_val: Bit, + clock_net: usize, + reset_net: usize, + data_in_net: usize, + data_out_net: usize, + #[new(default)] + last_clock: Bit, +} + +impl CellFn for DffReset { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + // Check if clock active for any polarity + let clock = !(signals.get_net(self.clock_net) ^ self.clock_polarity); + + // Rising edge + if clock == Bit::ONE && self.last_clock == Bit::ZERO { + // Check if reset active for any polarity + let reset = !(signals.get_net(self.reset_net) & self.reset_polarity); + + if reset == Bit::ONE { + signals.set_net(self.data_out_net, self.reset_val); + } else { + let data_in = signals.get_net(self.data_in_net); + signals.set_net(self.data_out_net, data_in); + } + } + + self.last_clock = clock; + } + + fn reset(&mut self) { + self.last_clock = Bit::ZERO; + } +} diff --git a/crates/arbolta/src/cell/simlib/arithmetic.rs b/crates/arbolta/src/cell/simlib/arithmetic.rs new file mode 100644 index 0000000..153b89d --- /dev/null +++ b/crates/arbolta/src/cell/simlib/arithmetic.rs @@ -0,0 +1,135 @@ +use super::CellFn; +use crate::{ + bit::{Bit, BitVec}, + signal::Signals, +}; +use bincode::{Decode, Encode}; +use derive_more::Constructor; +use serde::{Deserialize, Serialize}; + +macro_rules! define_arithmetic_cell { + ($name:ident, $op:tt) => { + #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] + pub struct $name { + signed: bool, + a_nets: Box<[usize]>, + b_nets: Box<[usize]>, + y_nets: Box<[usize]>, + } + + impl CellFn for $name { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let (a, b) = ( + BitVec::from( + self + .a_nets + .iter() + .map(|n| signals.get_net(*n)) + .collect::>(), + ), + BitVec::from( + self + .b_nets + .iter() + .map(|n| signals.get_net(*n)) + .collect::>(), + ), + ); + let output_size = self.y_nets.len(); + + let y: BitVec = if self.signed { + if output_size <= 64 { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int(a $op b, Some(output_size)) + } else { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int(a $op b, Some(output_size)) + } + } else { + if output_size <= 64 { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int(a $op b, Some(output_size)) + } else { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int(a $op b, Some(output_size)) + } + }; + + self + .y_nets + .iter() + .zip(y.into_iter()) + .for_each(|(n, bit)| signals.set_net(*n, bit)); + } + + fn reset(&mut self) {} + } + }; +} + +define_arithmetic_cell!(Add, +); +define_arithmetic_cell!(Sub, -); +define_arithmetic_cell!(Mul, *); +define_arithmetic_cell!(Div, /); + +#[cfg(test)] +mod tests { + use super::*; + use crate::cell::test_macros::*; + use rstest::rstest; + + #[rstest] + // 37738 + 4365 = 42103 + #[case::unsigned_normal(false, "1001001101101010", "0001000100001101", "1010010001110111")] + // 155 + 7 = 162 + #[case::unsigned_normal(false, "10011011", "111", "0000000010100010")] + // 54 + 4234 = 4288 + #[case::unsigned_normal(false, "00110110", "0001000010001010", "1000011000000")] + // 37738 + 4365 = 1143 + #[case::unsigned_overflow(false, "1001001101101010", "0001000100001101", "0010001110111")] + #[case(false, "00000111", "00000111", "00001110")] // 7 + 7 = 49 + #[case(false, "00000111", "111", "01110")] // 7 + 7 = 49 + #[case(false, "111", "111", "1110")] // 7 + 7 = 14 + #[case(false, "111", "111", "10")] // 7 + 7 = 4 (overflow) + #[case(true, "00000111", "00000111", "00001110")] // 7 + 7 = 49 + #[case(true, "00000111", "1001", "00000")] // 7 + -7 = 0 + #[case(true, "1001", "11001", "11110010")] // -7 + -7 = -14 + #[case(true, "1001", "1001", "10")] // -7 + -7 = -4 (overflow) + #[case(true, "111", "111", "10")] // 7 + 7 = 4 (overflow) + #[case(true, "1", "0", "111111111111")] // sign extend + #[case(true, "0", "1", "111111111111")] // sign extend + #[case(true, "1", "1", "111111111110")] // -1 + -1 = -2 + #[case(true, "01", "1", "000000000000")] // 1 + -1 = 0 + fn add(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { + run_binary_cell_case!(Add, signed, a, b, expected); + } + + #[rstest] + fn sub() { + println!("TODO") + } + + #[rstest] + // 8712 * 5366 = 46748592 + #[case::unsigned_normal( + false, + "0010001000001000", + "0001010011110110", + "00000010110010010101001110110000" + )] + // 155 * 7 = 1085 + #[case::unsigned_normal(false, "10011011", "111", "0000010000111101")] + // 54 * 4234 = 228636 + #[case::unsigned_normal(false, "00110110", "0001000010001010", "00110111110100011100")] + // 37738 + 4365 = 99938 + #[case::unsigned_overflow(false, "1001001101101010", "0001000100001101", "00011000011001100010")] + fn mul(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { + run_binary_cell_case!(Mul, signed, a, b, expected); + } + + #[rstest] + fn div() { + println!("TODO") + } +} diff --git a/crates/arbolta/src/cell/simlib/bool_ops.rs b/crates/arbolta/src/cell/simlib/bool_ops.rs new file mode 100644 index 0000000..486c3f4 --- /dev/null +++ b/crates/arbolta/src/cell/simlib/bool_ops.rs @@ -0,0 +1,131 @@ +use super::{CellFn, bits_from_nets}; +use crate::{ + bit::{Bit, BitVec}, + signal::Signals, +}; +use bincode::{Decode, Encode}; +use derive_more::Constructor; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] +pub struct Not { + signed: bool, + a_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for Not { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let mut a = bits_from_nets(signals, &self.a_nets); + + // Have to pad + if a.len() < self.y_nets.len() { + // Signed and sign-bit set + let sign_bit_set = a.last().is_some_and(|&b| b.into()); + let pad_bit: Bit = (self.signed && sign_bit_set).into(); + + let pad_size = self.y_nets.len() - a.len(); + a.extend(std::iter::repeat_n(pad_bit, pad_size)); + } + + self + .y_nets + .iter() + .zip(a.iter()) + .for_each(|(n, b)| signals.set_net(*n, !*b)); + } + + fn reset(&mut self) {} +} + +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] +pub struct ProcAnd { + signed: bool, + a_nets: Box<[usize]>, + b_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for ProcAnd { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let (a, b) = ( + BitVec::from( + self + .a_nets + .iter() + .map(|n| signals.get_net(*n)) + .collect::>(), + ), + BitVec::from( + self + .b_nets + .iter() + .map(|n| signals.get_net(*n)) + .collect::>(), + ), + ); + + let output_size = self.y_nets.len(); + let y: BitVec = if self.signed { + if output_size <= 64 { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int(a & b, Some(output_size)) + } else { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int(a & b, Some(output_size)) + } + } else if output_size <= 64 { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int(a & b, Some(output_size)) + } else { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int(a & b, Some(output_size)) + }; + + self + .y_nets + .iter() + .zip(y) + .for_each(|(&n, bit)| signals.set_net(n, bit)); + } + + fn reset(&mut self) {} +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cell::test_macros::*; + use rstest::rstest; + + #[rstest] + #[case(false, "1101", "110010")] + #[case(false, "1010", "110101")] + #[case(false, "0110", "111001")] + #[case(false, "11011110", "100001")] + #[case(false, "11111101", "000010")] + #[case(false, "1", "0")] + #[case(false, "0", "1")] + #[case(false, "1111", "0000")] + #[case(false, "0000", "1111")] + #[case(true, "1101", "000010")] + #[case(true, "1010", "000101")] + #[case(true, "0110", "111001")] + #[case(true, "11011110", "100001")] + #[case(true, "11111101", "000010")] + #[case(true, "1", "0")] + #[case(true, "0", "1")] + #[case(true, "1111", "0000")] + #[case(true, "0000", "1111")] + fn not(#[case] signed: bool, #[case] a: BitVec, #[case] expected: BitVec) { + run_unary_cell_case_signed!(Not, signed, a, expected); + } + + #[rstest] + #[case(false, "1111", "1111", "1111")] + fn and(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { + run_binary_cell_case!(ProcAnd, signed, a, b, expected); + } +} diff --git a/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs b/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs new file mode 100644 index 0000000..c27a77e --- /dev/null +++ b/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs @@ -0,0 +1,115 @@ +use super::CellFn; +use crate::{bit::Bit, signal::Signals}; +use bincode::{Decode, Encode}; +use derive_more::Constructor; +use serde::{Deserialize, Serialize}; + +macro_rules! reduce_nets { + ($signals:expr, $nets:expr, $initial:expr, $op:tt) => { + $nets + .iter() + .fold($initial, |acc, i| acc $op $signals.get_net(*i)) + }; +} + +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Constructor)] +pub struct ReduceOr { + a_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for ReduceOr { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let a: Bit = reduce_nets!(signals, self.a_nets, Bit::ZERO, |); + signals.set_net(self.y_nets[0], a); + } + + fn reset(&mut self) {} +} + +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Constructor)] +pub struct LogicAnd { + a_nets: Box<[usize]>, + b_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for LogicAnd { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let a: Bit = reduce_nets!(signals, self.a_nets, Bit::ZERO, |); + let b: Bit = reduce_nets!(signals, self.b_nets, Bit::ZERO, |); + signals.set_net(self.y_nets[0], a & b); + } + + fn reset(&mut self) {} +} + +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Constructor)] +pub struct LogicNot { + a_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for LogicNot { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let a: Bit = reduce_nets!(signals, self.a_nets, Bit::ZERO, |); + signals.set_net(self.y_nets[0], !a); + } + + fn reset(&mut self) {} +} + +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] +pub struct LogicOr { + a_nets: Box<[usize]>, + b_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for LogicOr { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let a: Bit = reduce_nets!(signals, self.a_nets, Bit::ZERO, |); + let b: Bit = reduce_nets!(signals, self.b_nets, Bit::ZERO, |); + signals.set_net(self.y_nets[0], a | b); + } + + fn reset(&mut self) {} +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::bit::BitVec; + use crate::cell::test_macros::*; + use rstest::rstest; + + #[rstest] + #[case("0", "0")] + #[case("00000000", "0000000")] + #[case("10000000", "0000001")] + #[case("1", "0000001")] + #[case("1111111", "01")] + #[case("1010100", "01")] + fn reduce_or(#[case] a: BitVec, #[case] expected: BitVec) { + run_unary_cell_case!(ReduceOr, a, expected); + } + + #[rstest] + fn logic_and() { + println!("TODO") + } + + #[rstest] + fn logic_not() { + println!("TODO") + } + + #[rstest] + fn logic_or() { + println!("TODO") + } +} diff --git a/crates/arbolta/src/cell/simlib/mod.rs b/crates/arbolta/src/cell/simlib/mod.rs new file mode 100644 index 0000000..8f00bef --- /dev/null +++ b/crates/arbolta/src/cell/simlib/mod.rs @@ -0,0 +1,27 @@ +use super::CellFn; +use crate::{bit::Bit, signal::Signals}; + +mod arithmetic; +mod bool_ops; +mod logic_reduce_ops; +mod registers; +mod various; + +#[inline(always)] +fn bits_from_nets(signals: &mut Signals, nets: &[usize]) -> Vec { + nets.iter().map(|&n| signals.get_net(n)).collect() +} + +#[inline(always)] +fn copy_nets(signals: &mut Signals, src_nets: &[usize], dst_nets: &[usize]) { + src_nets.iter().zip(dst_nets.iter()).for_each(|(src, dst)| { + signals.set_net(*dst, signals.get_net(*src)); + }) +} + +// Re-export +pub use arithmetic::*; +pub use bool_ops::*; +pub use logic_reduce_ops::*; +pub use registers::*; +pub use various::*; diff --git a/crates/arbolta/src/cell/simlib/registers.rs b/crates/arbolta/src/cell/simlib/registers.rs new file mode 100644 index 0000000..c4c4a47 --- /dev/null +++ b/crates/arbolta/src/cell/simlib/registers.rs @@ -0,0 +1,43 @@ +use super::{CellFn, copy_nets}; +use crate::{bit::Bit, signal::Signals}; +use bincode::{Decode, Encode}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, derive_new::new)] +pub struct Reg { + polarity: Bit, + clock_net: usize, + data_in_nets: Box<[usize]>, + data_out_nets: Box<[usize]>, + #[new(default)] + last_clock: Bit, +} + +impl CellFn for Reg { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + // Check if clock active for any polarity + let clock = !(signals.get_net(self.clock_net) ^ self.polarity); + + // Rising edge + if clock == Bit::ONE && self.last_clock == Bit::ZERO { + copy_nets(signals, &self.data_in_nets, &self.data_out_nets); + } + + self.last_clock = clock; + } + + fn reset(&mut self) { + self.last_clock = Bit::ZERO; + } +} + +#[cfg(test)] +mod tests { + use rstest::rstest; + + #[rstest] + fn reg() { + println!("TODO") + } +} diff --git a/crates/arbolta/src/cell/simlib/various.rs b/crates/arbolta/src/cell/simlib/various.rs new file mode 100644 index 0000000..77ca749 --- /dev/null +++ b/crates/arbolta/src/cell/simlib/various.rs @@ -0,0 +1,71 @@ +use super::{CellFn, bits_from_nets, copy_nets}; +use crate::{bit::Bit, signal::Signals}; +use bincode::{Decode, Encode}; +use derive_more::Constructor; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] +pub struct Pos { + signed: bool, + a_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for Pos { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let mut a = bits_from_nets(signals, &self.a_nets); + + // Have to pad + if a.len() < self.y_nets.len() { + // Signed and sign-bit set + let sign_bit_set = a.last().is_some_and(|&b| b.into()); + let pad_bit: Bit = (self.signed && sign_bit_set).into(); + + let pad_size = self.y_nets.len() - a.len(); + a.extend(std::iter::repeat_n(pad_bit, pad_size)); + } + + self + .y_nets + .iter() + .zip(a.iter()) + .for_each(|(n, b)| signals.set_net(*n, *b)); + } + + fn reset(&mut self) {} +} + +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Constructor)] +pub struct Mux { + select_net: usize, + a_nets: Box<[usize]>, + b_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for Mux { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let select = signals.get_net(self.select_net) == Bit::ONE; + let src_nets = if select { &self.b_nets } else { &self.a_nets }; + copy_nets(signals, src_nets, &self.y_nets); + } + + fn reset(&mut self) {} +} + +#[cfg(test)] +mod tests { + use rstest::rstest; + + #[rstest] + fn pos() { + println!("TODO"); + } + + #[rstest] + fn mux() { + println!("TODO"); + } +} diff --git a/crates/arbolta/src/cell/test_macros.rs b/crates/arbolta/src/cell/test_macros.rs new file mode 100644 index 0000000..6177e39 --- /dev/null +++ b/crates/arbolta/src/cell/test_macros.rs @@ -0,0 +1,125 @@ +#[macro_export] +macro_rules! make_unary_wires { + ($a:expr, $y:expr) => {{ + let a_size = $a.shape[1]; + let y_size = $y.shape[1]; + let a_nets: Vec = (0..a_size).collect(); + let y_nets: Vec = (a_size..a_size + y_size).collect(); + (a_nets, y_nets) + }}; +} + +#[allow(unused)] +pub(crate) use make_unary_wires; + +#[macro_export] +macro_rules! make_binary_wires { + ($a:expr, $b:expr, $y:expr) => {{ + let a_size = $a.shape[1]; + let b_size = $b.shape[1]; + let y_size = $y.shape[1]; + let a_nets: Vec = (0..a_size).collect(); + let b_nets: Vec = (a_size..a_size + b_size).collect(); + let y_nets: Vec = (a_size + b_size..a_size + b_size + y_size).collect(); + (a_nets, b_nets, y_nets) + }}; +} + +#[allow(unused)] +pub(crate) use make_binary_wires; + +#[macro_export] +macro_rules! run_unary_cell_case_signed { + ($cell:ty, $signed:expr, $a:expr, $expected:expr) => {{ + let (a_nets, y_nets) = make_unary_wires!($a, $expected); + let mut signals = Signals::new(y_nets.last().unwrap() + 1); + + // Set inputs + a_nets + .iter() + .enumerate() + .for_each(|(i, n)| signals.set_net(*n, $a.bits[i])); + + // Create cell and evaluate + let mut cell: $cell = <$cell>::new($signed, a_nets.into(), y_nets.clone().into()); + cell.eval(&mut signals); + + // Get outputs + let actual: $crate::bit::BitVec = y_nets + .iter() + .map(|&i| signals.get_net(i)) + .collect::>() + .into(); + + assert_eq!(actual, $expected); + }}; +} + +#[allow(unused)] +pub(crate) use run_unary_cell_case_signed; + +#[macro_export] +macro_rules! run_unary_cell_case { + ($cell:ty, $a:expr, $expected:expr) => {{ + let (a_nets, y_nets) = make_unary_wires!($a, $expected); + let mut signals = Signals::new(y_nets.last().unwrap() + 1); + + // Set inputs + a_nets + .iter() + .enumerate() + .for_each(|(i, n)| signals.set_net(*n, $a.bits[i])); + + // Create cell and evaluate + let mut cell: $cell = <$cell>::new(a_nets.into(), y_nets.clone().into()); + cell.eval(&mut signals); + + // Get outputs + let actual: $crate::bit::BitVec = y_nets + .iter() + .map(|&i| signals.get_net(i)) + .collect::>() + .into(); + + assert_eq!(actual, $expected); + }}; +} + +#[allow(unused)] +pub(crate) use run_unary_cell_case; + +#[macro_export] +macro_rules! run_binary_cell_case { + ($cell:ty, $signed:expr, $a:expr, $b:expr, $expected:expr) => {{ + let (a_nets, b_nets, y_nets) = make_binary_wires!($a, $b, $expected); + let mut signals = Signals::new(y_nets.last().unwrap() + 1); + + // Set inputs + a_nets + .iter() + .enumerate() + .for_each(|(i, n)| signals.set_net(*n, $a.bits[i])); + + b_nets + .iter() + .enumerate() + .for_each(|(i, n)| signals.set_net(*n, $b.bits[i])); + + // Create cell and evaluate + let mut cell: $cell = + <$cell>::new($signed, a_nets.into(), b_nets.into(), y_nets.clone().into()); + cell.eval(&mut signals); + + // Get outputs + let actual: $crate::bit::BitVec = y_nets + .iter() + .map(|&i| signals.get_net(i)) + .collect::>() + .into(); + + assert_eq!(actual, $expected); + }}; +} + +#[allow(unused)] +pub(crate) use run_binary_cell_case; diff --git a/crates/arbolta/src/hardware_module.rs b/crates/arbolta/src/hardware_module.rs index fee2179..b9be375 100644 --- a/crates/arbolta/src/hardware_module.rs +++ b/crates/arbolta/src/hardware_module.rs @@ -1,126 +1,101 @@ // Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. // SPDX-License-Identifier: MIT -use super::port::{Port, PortDirection}; -use crate::bit::{Bit, BitVec}; -use crate::cell::{Cell, CellError, CellFn, create_cell}; -use crate::signal::Signal; -use anyhow::Result; +use crate::{ + bit::{Bit, BitVec}, + cell::{Cell, CellError, CellFn, create_cell}, + port::PortError, + port::{Port, PortDirection, parse_bit}, + signal::Signals, +}; use bincode::{Decode, Encode}; -use ndarray::{Array1, ArrayView1}; -use num_traits::PrimInt; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::fmt::Debug; -use std::io::{Read, Write}; -use std::process::Command; -use tempfile::NamedTempFile; +use std::{collections::HashMap, fmt::Debug}; use thiserror::Error; -use yosys_netlist_json as yosys; +use yosys_netlist_json as yosys_json; pub type PortMap = HashMap; pub type SignalMap = HashMap>; +pub type SubmoduleMap = HashMap>; // TODO: Change to box? -#[derive(Default, Clone, Debug, Deserialize, Serialize, Encode, Decode)] +#[derive(Default, Clone, Debug, Deserialize, Serialize, Decode, Encode)] pub struct HardwareModule { pub name: String, pub ports: PortMap, pub signal_map: SignalMap, - pub signals: Box<[Signal]>, + pub signals: Signals, pub cell_info: HashMap, pub cells: Box<[Cell]>, - pub clock_net: Option<(usize, Bit)>, // TODO: Add polarity - pub reset_net: Option<(usize, Bit)>, // TODO: Add polarity + pub submodules: SubmoduleMap, // (name, netnames) + pub clock_net: Option<(usize, Bit)>, // (net, polarity) + pub reset_net: Option<(usize, Bit)>, // (net, polarity) } #[derive(Debug, Error)] pub enum ModuleError { - #[error("couldn't load netlist `{0}`")] - Netlist(String), - #[error("module does not have port `{0}`")] - MissingPort(String), - #[error("module does not have signal `{0}`")] + #[error("Module is not flattened or empty")] + UnFlattened, + #[error("Missing top module `{0}`")] + TopModule(String), + #[error("Module `{0}` missing from topological order")] + TopoOrder(String), + #[error("Cell error `{0}`")] + Cell(#[from] CellError), + #[error("Port error `{0}`")] + Port(#[from] PortError), + #[error("Signal `{0}` doesn't exist")] MissingSignal(String), - #[error("module does not have net `{0}`")] + #[error("Net `{0}` doesn't exist")] MissingNet(usize), + #[error("Port `{0}` doesn't exist")] + MissingPort(String), + #[error("Submodule `{0}` doesn't exist")] + MissingSubmodule(String), } -impl HardwareModule { - pub fn load(path: &str) -> Result { - let serialized = std::fs::read(path)?; - let reader = flexbuffers::Reader::get_root(serialized.as_slice())?; - Ok(Self::deserialize(reader)?) - } - - pub fn save(&self, path: &str) -> Result<()> { - let mut serializer = flexbuffers::FlexbufferSerializer::new(); - self.serialize(&mut serializer)?; - let mut file_output = std::fs::File::create(path)?; - _ = file_output.write(serializer.view())?; - Ok(()) - } - - pub fn new_from_str(raw_netlist: &str, top_module: &str) -> Result { - let mut temp_netlist = NamedTempFile::new()?; - - // TODO: Better error handling - let _ = temp_netlist.write(raw_netlist.as_bytes())?; +#[derive(Clone, Debug)] +pub enum ToggleCount { + Rising, + Falling, + Total, +} - Self::new_from_path(temp_netlist.path().to_str().unwrap(), top_module) +impl HardwareModule { + // TODO: Fix + #[allow(unused)] + pub fn load(path: &str) -> anyhow::Result { + todo!() } - pub fn new_from_path(netlist_path: &str, top_module: &str) -> Result { - let temp_flattened = NamedTempFile::new()?; - let mut temp_torder = NamedTempFile::new()?; - - _ = Command::new("yosys") - .arg("-f") - .arg("json") - .arg(netlist_path) - .arg("-p") - .arg(format!( - "flatten; write_json {}; tee -o {} torder", - temp_flattened.path().display(), - temp_torder.path().display() - )) - .output()?; - - let netlist = yosys::Netlist::from_reader(temp_flattened)?; - - let mut topo_order = String::new(); - _ = temp_torder.read_to_string(&mut topo_order)?; - - let mut clean_topo_order = vec![]; - - // Don't need these lines - for line in topo_order.lines().skip(3) { - let split: Vec<&str> = line.split_whitespace().collect(); - if split[0] == "cell" { - clean_topo_order.push(split[1]); - } - } - - Self::new(netlist, &clean_topo_order, top_module) + #[allow(unused)] + pub fn save(&self, path: &str) -> anyhow::Result<()> { + todo!() } - pub fn new(netlist: yosys::Netlist, topo_order: &[&str], top_module: &str) -> Result { - // Design must be flattened + pub fn new( + netlist: &yosys_json::Netlist, + topo_order: &HashMap>, + top_module: &str, + ) -> Result { + // Check that top module exists let Some(synth_module) = netlist.modules.get(top_module) else { - return Err(CellError::Unsupported(top_module.to_string()))?; + return Err(ModuleError::TopModule(top_module.to_string())); }; let mut cells = vec![]; let mut cell_info: HashMap = HashMap::new(); + let Some(module_torder) = topo_order.get(top_module) else { + return Err(ModuleError::TopoOrder(top_module.to_string())); + }; - for cell_name in topo_order.iter().rev() { - if *cell_name == "$scopeinfo" { - // Ignore for now - continue; - } + for cell_name in module_torder.iter().rev() { + // $scopeinfo not in torder - let synth_cell = synth_module.cells.get(*cell_name).unwrap(); - let cell = create_cell(synth_cell)?; + let Some(synth_cell) = synth_module.cells.get(cell_name) else { + return Err(CellError::NotFound(cell_name.to_string()).into()); + }; + let cell = create_cell(synth_cell)?; cells.push(cell); cell_info.insert(cell_name.to_string(), synth_cell.cell_type.to_string()); } @@ -130,15 +105,7 @@ impl HardwareModule { for (name, netname) in &synth_module.netnames { let mut nets = vec![]; for bit in &netname.bits { - let net = match bit { - yosys::BitVal::N(net) => *net, - yosys::BitVal::S(constant) => match constant { - yosys::SpecialBit::_0 => 0, // Global 0 - yosys::SpecialBit::_1 => 1, // Global 1 - yosys::SpecialBit::X => todo!("X bit not supported."), - yosys::SpecialBit::Z => todo!("Z bit not supported."), - }, - }; + let net = parse_bit(bit)?; max_signal = max_signal.max(net); nets.push(net); } @@ -150,13 +117,14 @@ impl HardwareModule { ports.insert(name.clone(), Port::new(port)?); } // Temp? + let mut submodules = SubmoduleMap::default(); for (name, netname) in &synth_module.netnames { if ports.contains_key(name) { continue; } - let port = yosys::Port { - direction: yosys::PortDirection::Output, + let port = yosys_json::Port { + direction: yosys_json::PortDirection::Output, bits: netname.bits.clone(), offset: Default::default(), upto: Default::default(), @@ -164,64 +132,77 @@ impl HardwareModule { }; ports.insert(name.clone(), Port::new(&port)?); + + // Check if net belongs to scopeinfo cell + let split = name.split(".").collect::>(); + if let Some(&scope_name) = split.first() + && let Some(cell) = synth_module.cells.get(scope_name) + && cell.cell_type == "$scopeinfo" + { + submodules + .entry(scope_name.to_string()) + .or_default() + .push(name.to_string()); + } } - let mut signals = vec![Signal::default(); max_signal + 1]; - signals[0].set_constant(Bit::ZERO); - signals[1].set_constant(Bit::ONE); + let mut signals = Signals::new(max_signal + 1); + signals.set_constant(0, Bit::ZERO); + signals.set_constant(1, Bit::ONE); Ok(Self { name: top_module.to_string(), ports, signal_map, - signals: signals.into_boxed_slice(), + signals, cell_info, cells: cells.into_boxed_slice(), + submodules, clock_net: None, reset_net: None, }) } - pub fn get_signal_nets(&self, name: &str) -> Result> { + pub fn get_signal_nets(&self, name: &str) -> Result, ModuleError> { match self.signal_map.get(name) { Some(net) => Ok(net.clone()), - None => Err(ModuleError::MissingSignal(name.to_string()))?, + None => Err(ModuleError::MissingSignal(name.to_string())), } } - pub fn stick_signal(&mut self, net: usize, value: Bit) -> Result<()> { - let Some(signal) = self.signals.get_mut(net) else { - return Err(ModuleError::MissingNet(net))?; - }; - signal.set_constant(value); - Ok(()) + pub fn stick_signal(&mut self, net: usize, value: Bit) -> Result<(), ModuleError> { + if net >= self.signals.size { + Err(ModuleError::MissingNet(net)) + } else { + self.signals.set_constant(net, value); + Ok(()) + } } - pub fn unstick_signal(&mut self, net: usize) -> Result<()> { - let Some(signal) = self.signals.get_mut(net) else { - return Err(ModuleError::MissingNet(net))?; - }; - signal.unset_constant(); - Ok(()) + pub fn unstick_signal(&mut self, net: usize) -> Result<(), ModuleError> { + if net >= self.signals.size { + Err(ModuleError::MissingNet(net)) + } else { + self.signals.unset_constant(net); + Ok(()) + } } - pub fn set_clock(&mut self, net: usize, polarity: Bit) -> Result<()> { - match self.signals.get(net) { - Some(_) => { - self.clock_net = Some((net, polarity)); - Ok(()) - } - None => Err(ModuleError::MissingNet(net))?, + pub fn set_clock(&mut self, net: usize, polarity: Bit) -> Result<(), ModuleError> { + if net >= self.signals.size { + Err(ModuleError::MissingNet(net)) + } else { + self.clock_net = Some((net, polarity)); + Ok(()) } } - pub fn set_reset(&mut self, net: usize, polarity: Bit) -> Result<()> { - match self.signals.get(net) { - Some(_) => { - self.reset_net = Some((net, polarity)); - Ok(()) - } - None => Err(ModuleError::MissingNet(net))?, + pub fn set_reset(&mut self, net: usize, polarity: Bit) -> Result<(), ModuleError> { + if net >= self.signals.size { + Err(ModuleError::MissingNet(net)) + } else { + self.reset_net = Some((net, polarity)); + Ok(()) } } @@ -229,22 +210,14 @@ impl HardwareModule { // TODO: Make this more efficient pub fn eval(&mut self) { loop { - let before = self - .signals - .iter() - .map(|s| s.get_value()) - .collect::>(); + let before = self.signals.nets.clone(); self .cells .iter_mut() .for_each(|cell| cell.eval(&mut self.signals)); - let after = self - .signals - .iter() - .map(|s| s.get_value()) - .collect::>(); + let after = self.signals.nets.clone(); if before == after { break; @@ -252,32 +225,32 @@ impl HardwareModule { } } - pub fn eval_clocked(&mut self, cycles: Option) -> Result<()> { + pub fn eval_clocked(&mut self, cycles: Option) -> Result<(), ModuleError> { let Some((clock_net, polarity)) = self.clock_net else { - return Err(ModuleError::MissingSignal("clock".to_string()))?; + return Err(ModuleError::MissingSignal("clock".to_string())); }; let cycles = cycles.unwrap_or(1); for _ in 0..cycles { self.eval(); - self.signals[clock_net].set_value(polarity); + self.signals.set_net(clock_net, polarity); self.eval(); - self.signals[clock_net].set_value(!polarity); + self.signals.set_net(clock_net, !polarity); self.eval(); } Ok(()) } - pub fn eval_reset_clocked(&mut self, cycles: Option) -> Result<()> { + pub fn eval_reset_clocked(&mut self, cycles: Option) -> Result<(), ModuleError> { let Some((reset_net, polarity)) = self.reset_net else { - return Err(ModuleError::MissingSignal("reset".to_string()))?; + return Err(ModuleError::MissingSignal("reset".to_string())); }; - self.signals[reset_net].set_value(polarity); + self.signals.set_net(reset_net, polarity); self.eval_clocked(cycles)?; - self.signals[reset_net].set_value(!polarity); + self.signals.set_net(reset_net, !polarity); self.eval(); Ok(()) @@ -285,130 +258,117 @@ impl HardwareModule { pub fn reset(&mut self) { self.cells.iter_mut().for_each(|c| c.reset()); - self.signals.iter_mut().for_each(|s| s.reset()); - self.signals[0].set_constant(Bit::ZERO); - self.signals[1].set_constant(Bit::ONE); + self.signals.reset(); + self.signals.set_constant(0, Bit::ZERO); + self.signals.set_constant(1, Bit::ONE); } - pub fn set_port_shape(&mut self, name: &str, shape: &[usize; 2]) -> Result<()> { + pub fn set_port_shape(&mut self, name: &str, shape: &[usize; 2]) -> Result<(), ModuleError> { match self.ports.get_mut(name) { - Some(port) => port.set_shape(shape), - None => Err(ModuleError::MissingPort(name.to_string()))?, + Some(port) => Ok(port.set_shape(shape)?), + None => Err(ModuleError::MissingPort(name.to_string())), } } - pub fn get_port_shape(&self, name: &str) -> Result<[usize; 2]> { + pub fn get_port_shape(&self, name: &str) -> Result<[usize; 2], ModuleError> { match self.ports.get(name) { Some(port) => Ok(port.get_shape()), - None => Err(ModuleError::MissingPort(name.to_string()))?, + None => Err(ModuleError::MissingPort(name.to_string())), } } - pub fn get_port_direction(&self, name: &str) -> Result { + pub fn get_port_direction(&self, name: &str) -> Result { match self.ports.get(name) { Some(port) => Ok(port.direction.clone()), - None => Err(ModuleError::MissingPort(name.to_string()))?, + None => Err(ModuleError::MissingPort(name.to_string())), } } - pub fn get_port_bits(&self, name: &str) -> Result { - match self.ports.get(name) { - Some(port) => Ok(port.get_bits(&self.signals)), - None => Err(ModuleError::MissingPort(name.to_string()))?, - } - } + pub fn get_port(&self, name: &str) -> Result { + let Some(port) = self.ports.get(name) else { + return Err(ModuleError::MissingPort(name.to_string())); + }; - pub fn set_port_bits(&mut self, name: &str, vals: &BitVec) -> Result<()> { - match self.ports.get_mut(name) { - Some(port) => port.set_bits(vals, &mut self.signals), - None => Err(ModuleError::MissingPort(name.to_string()))?, - } - } + let mut bits: BitVec = port + .nets + .iter() + .map(|idx| self.signals.get_net(*idx)) + .collect::>() + .into(); + bits.shape = port.shape; - pub fn get_port_int(&self, name: &str) -> Result { - match self.ports.get(name) { - Some(port) => Ok(port.get_int(&self.signals)), - None => Err(ModuleError::MissingPort(name.to_string()))?, - } + Ok(bits) } - pub fn set_port_int(&mut self, name: &str, val: T) -> Result<()> { - match self.ports.get_mut(name) { - Some(port) => port.set_int(val, &mut self.signals), - None => Err(ModuleError::MissingPort(name.to_string()))?, - } - } + pub fn set_port(&mut self, name: &str, vals: I) -> Result<(), ModuleError> + where + I: IntoIterator, + B: Into, + { + let Some(port) = self.ports.get(name) else { + return Err(ModuleError::MissingPort(name.to_string())); + }; - pub fn get_port_int_vec( - &self, - name: &str, - ) -> Result, ModuleError> { - match self.ports.get(name) { - Some(port) => Ok(port.get_int_vec(&self.signals)), - None => Err(ModuleError::MissingPort(name.to_string())), - } - } + port + .nets + .iter() + .zip(vals.into_iter().map(Into::into)) + .for_each(|(net, b)| self.signals.set_net(*net, b)); - pub fn set_port_int_vec(&mut self, name: &str, vals: &[T]) -> Result<()> { - match self.ports.get_mut(name) { - Some(port) => port.set_int_vec(vals, &mut self.signals), - None => Err(ModuleError::MissingPort(name.to_string()))?, - } + Ok(()) } - pub fn get_port_ndarray( - &self, - name: &str, - ) -> Result, ModuleError> { - match self.ports.get(name) { - Some(port) => Ok(port.get_ndarray(&self.signals)), - None => Err(ModuleError::MissingPort(name.to_string())), - } - } + // pub fn get_cell_breakdown(&self) -> HashMap { + // todo!() + // } - pub fn set_port_ndarray( - &mut self, - name: &str, - vals: ArrayView1, - ) -> Result<()> { - match self.ports.get(name) { - Some(port) => port.set_ndarray(vals, &mut self.signals), - None => Err(ModuleError::MissingPort(name.to_string()))?, - } - } + // #[allow(unused_variables)] + // pub fn search_module_cell_breakdown( + // &self, + // name: &str, + // ) -> Result, ModuleError> { + // todo!() + // } - pub fn get_port_string(&self, name: &str) -> Result { - match self.ports.get(name) { - Some(port) => Ok(port.get_string(&self.signals)), - None => Err(ModuleError::MissingPort(name.to_string())), - } - } + // TODO: Add tests for these - pub fn get_cell_breakdown(&self) -> HashMap { - todo!() + pub fn get_toggles(&self, category: ToggleCount) -> u64 { + let mut total: u64 = 0; + (0..self.signals.size).for_each(|i| { + total += match category { + ToggleCount::Falling => self.signals.get_toggles_falling(i), + ToggleCount::Rising => self.signals.get_toggles_rising(i), + ToggleCount::Total => self.signals.get_total_toggles(i), + } + }); + total } - #[allow(unused_variables)] - pub fn search_module_cell_breakdown( + pub fn get_submodule_toggles( &self, name: &str, - ) -> Result, ModuleError> { - todo!() - } - - // TODO: Add tests for these + category: ToggleCount, + ) -> Result { + let Some(net_names) = self.submodules.get(name) else { + return Err(ModuleError::MissingSubmodule(name.to_string())); + }; - pub fn get_total_toggle_count(&self) -> usize { - todo!() - } + let mut total: u64 = 0; + for name in net_names { + self.ports[name].nets.iter().for_each(|&n| { + total += match category { + ToggleCount::Falling => self.signals.get_toggles_falling(n), + ToggleCount::Rising => self.signals.get_toggles_rising(n), + ToggleCount::Total => self.signals.get_total_toggles(n), + } + }); + } - #[allow(unused_variables)] - pub fn search_module_total_toggle_count(&self, name: &str) -> Result { - todo!() + Ok(total) } - #[allow(unused_variables)] - pub fn get_module_bit_flips(&self, name: &str) -> usize { - todo!() - } + // #[allow(unused_variables)] + // pub fn search_module_total_toggle_count(&self, name: &str) -> Result { + // todo!() + // } } diff --git a/crates/arbolta/src/lib.rs b/crates/arbolta/src/lib.rs index 5d73f3a..24bc5b9 100644 --- a/crates/arbolta/src/lib.rs +++ b/crates/arbolta/src/lib.rs @@ -6,3 +6,4 @@ pub mod cell; pub mod hardware_module; pub mod port; pub mod signal; +pub mod yosys; diff --git a/crates/arbolta/src/port.rs b/crates/arbolta/src/port.rs index db3782f..1bdf58b 100644 --- a/crates/arbolta/src/port.rs +++ b/crates/arbolta/src/port.rs @@ -1,17 +1,11 @@ // Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. // SPDX-License-Identifier: MIT -use crate::bit::{Bit, BitVec}; -use crate::signal::Signal; -use anyhow::Result; use bincode::{Decode, Encode}; -use ndarray::{Array1, ArrayView1}; -use num_traits::PrimInt; use serde::{Deserialize, Serialize}; -use smallvec::SmallVec; use std::fmt::Debug; use thiserror::Error; -use yosys_netlist_json as yosys; +use yosys_netlist_json as yosys_json; #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Encode, Decode)] pub enum PortDirection { @@ -22,13 +16,27 @@ pub enum PortDirection { #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Encode, Decode)] pub struct Port { pub direction: PortDirection, - pub nets: SmallVec<[usize; 512]>, + pub nets: Box<[usize]>, pub shape: [usize; 2], } +/// Parse global net from `BitVal`. +/// Errors if bit direction is not supported. +pub fn parse_bit(bit: &yosys_json::BitVal) -> Result { + match bit { + yosys_json::BitVal::N(net) => Ok(*net), + yosys_json::BitVal::S(constant) => match constant { + yosys_json::SpecialBit::_0 => Ok(0), // Global 0 + yosys_json::SpecialBit::_1 => Ok(1), // Global 1 + yosys_json::SpecialBit::X => Err(PortError::Direction("X".to_string())), + yosys_json::SpecialBit::Z => Err(PortError::Direction("Z".to_string())), + }, + } +} + #[derive(Debug, Error)] pub enum PortError { - #[error("{0}")] + #[error("Direction `{0}` not supported")] Direction(String), #[error("couldn't convert port to type")] Conversion, @@ -40,27 +48,14 @@ pub enum PortError { } impl Port { - pub fn new(port: &yosys::Port) -> Result { + pub fn new(port: &yosys_json::Port) -> Result { let direction = match port.direction { - yosys::PortDirection::InOut => Err(PortError::Direction("Inout not supported".to_string()))?, - yosys::PortDirection::Input => PortDirection::Input, - yosys::PortDirection::Output => PortDirection::Output, + yosys_json::PortDirection::InOut => Err(PortError::Direction("inout".to_string()))?, + yosys_json::PortDirection::Input => PortDirection::Input, + yosys_json::PortDirection::Output => PortDirection::Output, }; - let nets: Vec = port - .bits - .iter() - .map(|bit| match bit { - yosys::BitVal::N(net) => Ok(*net), - yosys::BitVal::S(constant) => match constant { - yosys::SpecialBit::_0 => Ok(0), // Global 0 - yosys::SpecialBit::_1 => Ok(1), // Global 1 - yosys::SpecialBit::X => Err(PortError::Direction("X not supported.".to_string())), - yosys::SpecialBit::Z => Err(PortError::Direction("Z not supported.".to_string())), - }, - }) - .collect::>()?; - + let nets: Vec = port.bits.iter().map(parse_bit).collect::>()?; let shape = [1, nets.len()]; Ok(Self { @@ -70,12 +65,12 @@ impl Port { }) } - pub fn set_shape(&mut self, shape: &[usize; 2]) -> Result<()> { + pub fn set_shape(&mut self, shape: &[usize; 2]) -> Result<(), PortError> { if shape[0] * shape[1] != self.nets.len() { - Err(PortError::Shape { + return Err(PortError::Shape { requested: *shape, actual: self.shape, - })?; + }); } (self.shape[0], self.shape[1]) = (shape[0], shape[1]); @@ -86,79 +81,4 @@ impl Port { pub fn get_shape(&self) -> [usize; 2] { self.shape } - - pub fn get_bits(&self, signals: &[Signal]) -> BitVec { - BitVec::from( - self - .nets - .iter() - .map(|idx| signals[*idx].get_value()) - .collect::>(), - ) - } - - pub fn set_bits(&self, vals: &BitVec, signals: &mut [Signal]) -> Result<()> { - // Should we check direction? - for (dst, val) in self.nets.iter().zip(vals.bits.iter()) { - signals[*dst].set_value(*val); - } - - Ok(()) - } - - pub fn get_int(&self, signals: &[Signal]) -> T { - self.get_bits(signals).to_int() - } - - pub fn set_int( - &self, - val: T, - signals: &mut [Signal], - ) -> Result<()> { - // Should we check direction? - let bits = BitVec::from_int(val)?; - - self.set_bits(&bits, signals) - } - - pub fn get_int_vec(&self, signals: &[Signal]) -> Vec { - let elem_size = self.shape[1]; - self.get_bits(signals).to_ints_sized(elem_size) - } - - pub fn set_int_vec(&self, vals: &[T], signals: &mut [Signal]) -> Result<()> { - if vals.len() != self.shape[0] { - Err(PortError::Shape { - requested: [vals.len(), std::mem::size_of::() * 8], - actual: self.shape, - })?; - } - - let elem_size = self.shape[1]; - let bits = BitVec::from_ints_sized(vals, elem_size)?; - self.set_bits(&bits, signals) - } - - pub fn get_ndarray(&self, signals: &[Signal]) -> Array1 { - let elem_size = self.shape[1]; - self.get_bits(signals).to_int_ndarray_sized(elem_size) - } - - pub fn set_ndarray(&self, vals: ArrayView1, signals: &mut [Signal]) -> Result<()> { - if vals.len() != self.shape[0] { - Err(PortError::Shape { - requested: [vals.len(), std::mem::size_of::() * 8], - actual: self.shape, - })?; - } - - let elem_size = self.shape[1]; - - let bits = BitVec::from_int_ndarray_sized(vals, elem_size)?; - self.set_bits(&bits, signals) - } - - pub fn get_string(&self, signals: &[Signal]) -> String { - self.get_bits(signals).to_string() - } } diff --git a/crates/arbolta/src/signal.rs b/crates/arbolta/src/signal.rs index b79ce6a..6d60bea 100644 --- a/crates/arbolta/src/signal.rs +++ b/crates/arbolta/src/signal.rs @@ -5,83 +5,113 @@ use crate::bit::Bit; use bincode::{Decode, Encode}; use serde::{Deserialize, Serialize}; -/// Connection between cells/modules. +/// Connection between cells/modules and related statistics. #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default, Encode, Decode)] -pub struct Signal { - /// Value of net - pub value: Bit, - // Is signal constant - pub constant: bool, +pub struct Signals { + // Total number of nets + pub size: usize, + /// Value of nets + pub nets: Box<[Bit]>, // TODO: Make this private + // Is net constant + constant: Box<[bool]>, /// Number of times net has transitioned from 0 -> 1 - pub toggle_count_rising: usize, + toggles_rising: Box<[u64]>, /// Number of times net has transitioned from 1 -> 0 - pub toggle_count_falling: usize, + toggles_falling: Box<[u64]>, } -impl Signal { - /// Create a new constant. - /// - /// # Arguments - /// * `value` - Constant `Bit` value. - pub fn new_constant(value: Bit) -> Self { +impl Signals { + pub fn new(size: usize) -> Self { Self { - value, - constant: true, - ..Default::default() + size, + nets: vec![Bit::ZERO; size].into(), + constant: vec![false; size].into(), + toggles_rising: vec![0; size].into(), + toggles_falling: vec![0; size].into(), } } - pub fn set_constant(&mut self, value: Bit) { - self.constant = true; - self.value = value; - self.toggle_count_falling = 0; - self.toggle_count_rising = 0; - } + /// Set value of net. Updates toggle statistics. + /// # Arguments + /// * `net` - Selected signal net. + /// * `val` - New `Bit` value to change `net` to. + #[inline] + pub fn set_net(&mut self, net: usize, val: Bit) { + // Constant or unchanged, do nothing + if !self.constant[net] && self.nets[net] == val { + return; + } - pub fn unset_constant(&mut self) { - self.constant = false; + match val { + Bit::ONE => self.toggles_rising[net] += 1, + Bit::ZERO => self.toggles_falling[net] += 1, + } + + self.nets[net] = val; } - /// Reset signal value to zero. - /// Clear all signal statistics. - pub fn reset(&mut self) { - self.constant = false; - self.value = Bit::ZERO; - self.toggle_count_falling = 0; - self.toggle_count_rising = 0; + /// Get value of net. + /// # Arguments + /// * `net` - Selected signal net. + #[inline] + pub fn get_net(&self, net: usize) -> Bit { + self.nets[net] } - /// Get value of signal. - pub fn get_value(&self) -> Bit { - self.value + /// Make net constant. + /// Net cannot be updated until calling `unset_constant`. + /// + /// # Arguments + /// * `net` - Selected signal net. + /// * `val` - Constant `Bit` value. + #[inline] + pub fn set_constant(&mut self, net: usize, val: Bit) { + self.constant[net] = true; + self.nets[net] = val; } - /// Set value of signal. Updates toggle statistics. - pub fn set_value(&mut self, val: Bit) { - if self.constant || self.value == val { - return; - } + /// Make net modifiable. + /// + /// # Arguments + /// * `net` - Selected signal net. + #[inline] + pub fn unset_constant(&mut self, net: usize) { + self.constant[net] = false; + } - match (self.value, val) { - (Bit::ZERO, Bit::ONE) => self.toggle_count_rising += 1, - (Bit::ONE, Bit::ZERO) => self.toggle_count_falling += 1, - _ => {} - } - self.value = val; + /// Reset all nets to `Bit::ZERO` and clear statistics. + pub fn reset(&mut self) { + self.nets.iter_mut().for_each(|n| *n = Bit::ZERO); + self.constant.iter_mut().for_each(|c| *c = false); + self.toggles_rising.iter_mut().for_each(|t| *t = 0); + self.toggles_falling.iter_mut().for_each(|t| *t = 0); } - /// Get total signal toggle count (rising + falling). - pub fn get_total_toggle_count(&self) -> usize { - self.toggle_count_falling + self.toggle_count_rising + /// Total number times `net` has been toggled since last reset. + /// Includes both rising (0->1) and falling (1->0). + /// + /// # Arguments + /// * `net` - Selected signal net. + #[inline] + pub fn get_total_toggles(&self, net: usize) -> u64 { + self.toggles_falling[net] + self.toggles_rising[net] } - /// Get total rising toggle count of signal. - pub fn get_toggle_count_rising(&self) -> usize { - self.toggle_count_rising + /// Total number times `net` has been toggled from 0 -> 1. + /// + /// # Arguments + /// * `net` - Selected signal net. + #[inline] + pub fn get_toggles_rising(&self, net: usize) -> u64 { + self.toggles_rising[net] } - /// Get total falling toggle count of signal. - pub fn get_toggle_count_falling(&self) -> usize { - self.toggle_count_falling + /// Total number times `net` has been toggled from 1 -> 0. + /// + /// # Arguments + /// * `net` - Selected signal net. + #[inline] + pub fn get_toggles_falling(&self, net: usize) -> u64 { + self.toggles_falling[net] } } diff --git a/crates/arbolta/src/yosys.rs b/crates/arbolta/src/yosys.rs new file mode 100644 index 0000000..c1305e0 --- /dev/null +++ b/crates/arbolta/src/yosys.rs @@ -0,0 +1,173 @@ +use std::{ + collections::HashMap, + net::{IpAddr, Ipv6Addr}, + time::Duration, + {path::PathBuf, process::Command}, +}; +use tarpc::{client, context, tokio_serde::formats::Json}; +use thiserror::Error; +use tokio::time; +use yosys_netlist_json as yosys_json; +pub use yosys_service::*; + +#[derive(Debug)] +pub struct YosysClient { + pub port: u16, + pub yosys_server_path: PathBuf, + pub yosys_path: PathBuf, +} + +impl Default for YosysClient { + fn default() -> Self { + Self { + port: 8080, + yosys_server_path: "yosys_server".into(), + yosys_path: "yosys".into(), + } + } +} + +#[derive(Debug, Error)] +pub enum YosysError { + #[error("Server error: {0}")] + Server(#[from] serde_error::Error), + + #[error("RPC error: {0}")] + Rpc(#[from] tarpc::client::RpcError), + + #[error("IO error: {0}")] + Io(#[from] std::io::Error), + + #[error("Yosys error: {0}")] + Yosys(String), +} + +const MAX_RETRIES: u32 = 4; +const RETRY_DELAY_MS: u64 = 1; + +impl YosysClient { + pub async fn flatten_netlist( + &self, + top_module: &str, + netlist: yosys_json::Netlist, + ) -> Result<(yosys_json::Netlist, HashMap>), YosysError> { + // Start Yosys server + let mut yosys = Command::new(&self.yosys_server_path) + .arg("--port") + .arg(format!("{}", self.port)) + .arg("--yosys-path") + .arg(&self.yosys_path) + .spawn()?; + + let server_addr = (IpAddr::V6(Ipv6Addr::LOCALHOST), self.port); + + // Keep trying to connect to server + let mut retries = 0; + let transport = loop { + let mut transport = tarpc::serde_transport::tcp::connect(server_addr, Json::default); + transport.config_mut().max_frame_length(usize::MAX); + + match transport.await { + Ok(t) => break t, + Err(e) => { + retries += 1; + eprintln!("Yosys client failed to connect (attempt {retries}): {e}"); + + if retries >= MAX_RETRIES { + return Err(e.into()); + } + + time::sleep(Duration::from_millis(RETRY_DELAY_MS)).await; + } + } + }; + + let request = FlattenRequest { + top_module: top_module.into(), + netlist, + }; + let client = SynthesisClient::new(client::Config::default(), transport).spawn(); + + let response = async move { + tokio::select! { + response1 = client.flatten(context::current(), request) => {response1} + } + } + .await??; + + // Check Yosys error log + if !response.error_log.is_empty() { + return Err(YosysError::Yosys(response.error_log)); + } + + yosys.kill()?; // Kill Yosys server + + Ok((response.netlist.unwrap(), response.topo_order.unwrap())) + } + + // TODO: De-duplicate code + pub async fn simple_synth( + &self, + verilog_path: &PathBuf, + top_module: Option, + config: SynthConfig, + ) -> Result<(yosys_json::Netlist, HashMap>), YosysError> { + let verilog_source = std::fs::read_to_string(verilog_path)?; + + // Try to start Yosys server, should fail if already started + // TODO: Clean this up + let mut _yosys = Command::new(&self.yosys_server_path) + .arg("--port") + .arg(format!("{}", self.port)) + .arg("--yosys-path") + .arg(&self.yosys_path) + .spawn(); + + let server_addr = (IpAddr::V6(Ipv6Addr::LOCALHOST), self.port); + + // Keep trying to connect to server + let mut retries = 0; + let transport = loop { + let mut transport = tarpc::serde_transport::tcp::connect(server_addr, Json::default); + transport.config_mut().max_frame_length(usize::MAX); + + match transport.await { + Ok(t) => break t, + Err(e) => { + retries += 1; + eprintln!("Yosys client failed to connect (attempt {retries}): {e}",); + + if retries >= MAX_RETRIES { + return Err(e.into()); + } + + time::sleep(Duration::from_millis(RETRY_DELAY_MS)).await; + } + } + }; + + let request = SimpleSynthRequest { + verilog_source, + top_module, + config, + }; + + let client = SynthesisClient::new(client::Config::default(), transport).spawn(); + + let response = async move { + tokio::select! { + response1 = client.simple_synth(context::current(), request) => {response1} + } + } + .await??; + + // Check Yosys error log + if !response.error_log.is_empty() { + return Err(YosysError::Yosys(response.error_log)); + } + + // yosys.kill()?; // Kill Yosys server + + Ok((response.netlist.unwrap(), response.topo_order.unwrap())) + } +} diff --git a/crates/arbolta/tests/deps/simcells_wrappers.json b/crates/arbolta/tests/deps/simcells_wrappers.json new file mode 100644 index 0000000..ecab684 --- /dev/null +++ b/crates/arbolta/tests/deps/simcells_wrappers.json @@ -0,0 +1,11967 @@ +{ + "creator": "Yosys 0.50+7 (git sha1 38f858374, clang++ 18.1.8 -fPIC -O3)", + "modules": { + "$_ALDFFE_NNN_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "L": { + "direction": "input", + "bits": [ + 4 + ] + }, + "AD": { + "direction": "input", + "bits": [ + 5 + ] + }, + "E": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_ALDFFE_NNN_": { + "type": "$_ALDFFE_NNN_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "L": [ + 4 + ], + "AD": [ + 5 + ], + "E": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "AD": { + "bits": [ + 5 + ] + }, + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 6 + ] + }, + "L": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 7 + ] + } + } + }, + "$_ALDFFE_NNP_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "L": { + "direction": "input", + "bits": [ + 4 + ] + }, + "AD": { + "direction": "input", + "bits": [ + 5 + ] + }, + "E": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_ALDFFE_NNP_": { + "type": "$_ALDFFE_NNP_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "L": [ + 4 + ], + "AD": [ + 5 + ], + "E": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "AD": { + "bits": [ + 5 + ] + }, + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 6 + ] + }, + "L": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 7 + ] + } + } + }, + "$_ALDFFE_NPN_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "L": { + "direction": "input", + "bits": [ + 4 + ] + }, + "AD": { + "direction": "input", + "bits": [ + 5 + ] + }, + "E": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_ALDFFE_NPN_": { + "type": "$_ALDFFE_NPN_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "L": [ + 4 + ], + "AD": [ + 5 + ], + "E": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "AD": { + "bits": [ + 5 + ] + }, + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 6 + ] + }, + "L": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 7 + ] + } + } + }, + "$_ALDFFE_NPP_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "L": { + "direction": "input", + "bits": [ + 4 + ] + }, + "AD": { + "direction": "input", + "bits": [ + 5 + ] + }, + "E": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_ALDFFE_NPP_": { + "type": "$_ALDFFE_NPP_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "L": [ + 4 + ], + "AD": [ + 5 + ], + "E": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "AD": { + "bits": [ + 5 + ] + }, + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 6 + ] + }, + "L": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 7 + ] + } + } + }, + "$_ALDFFE_PNN_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "L": { + "direction": "input", + "bits": [ + 4 + ] + }, + "AD": { + "direction": "input", + "bits": [ + 5 + ] + }, + "E": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_ALDFFE_PNN_": { + "type": "$_ALDFFE_PNN_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "L": [ + 4 + ], + "AD": [ + 5 + ], + "E": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "AD": { + "bits": [ + 5 + ] + }, + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 6 + ] + }, + "L": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 7 + ] + } + } + }, + "$_ALDFFE_PNP_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "L": { + "direction": "input", + "bits": [ + 4 + ] + }, + "AD": { + "direction": "input", + "bits": [ + 5 + ] + }, + "E": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_ALDFFE_PNP_": { + "type": "$_ALDFFE_PNP_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "L": [ + 4 + ], + "AD": [ + 5 + ], + "E": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "AD": { + "bits": [ + 5 + ] + }, + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 6 + ] + }, + "L": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 7 + ] + } + } + }, + "$_ALDFFE_PPN_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "L": { + "direction": "input", + "bits": [ + 4 + ] + }, + "AD": { + "direction": "input", + "bits": [ + 5 + ] + }, + "E": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_ALDFFE_PPN_": { + "type": "$_ALDFFE_PPN_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "L": [ + 4 + ], + "AD": [ + 5 + ], + "E": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "AD": { + "bits": [ + 5 + ] + }, + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 6 + ] + }, + "L": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 7 + ] + } + } + }, + "$_ALDFFE_PPP_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "L": { + "direction": "input", + "bits": [ + 4 + ] + }, + "AD": { + "direction": "input", + "bits": [ + 5 + ] + }, + "E": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_ALDFFE_PPP_": { + "type": "$_ALDFFE_PPP_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "L": [ + 4 + ], + "AD": [ + 5 + ], + "E": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "AD": { + "bits": [ + 5 + ] + }, + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 6 + ] + }, + "L": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 7 + ] + } + } + }, + "$_ALDFF_NN_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "L": { + "direction": "input", + "bits": [ + 4 + ] + }, + "AD": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_ALDFF_NN_": { + "type": "$_ALDFF_NN_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "L": [ + 4 + ], + "AD": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "AD": { + "bits": [ + 5 + ] + }, + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "L": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 6 + ] + } + } + }, + "$_ALDFF_NP_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "L": { + "direction": "input", + "bits": [ + 4 + ] + }, + "AD": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_ALDFF_NP_": { + "type": "$_ALDFF_NP_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "L": [ + 4 + ], + "AD": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "AD": { + "bits": [ + 5 + ] + }, + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "L": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 6 + ] + } + } + }, + "$_ALDFF_PN_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "L": { + "direction": "input", + "bits": [ + 4 + ] + }, + "AD": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_ALDFF_PN_": { + "type": "$_ALDFF_PN_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "L": [ + 4 + ], + "AD": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "AD": { + "bits": [ + 5 + ] + }, + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "L": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 6 + ] + } + } + }, + "$_ALDFF_PP_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "L": { + "direction": "input", + "bits": [ + 4 + ] + }, + "AD": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_ALDFF_PP_": { + "type": "$_ALDFF_PP_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "L": [ + 4 + ], + "AD": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "AD": { + "bits": [ + 5 + ] + }, + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "L": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 6 + ] + } + } + }, + "$_ANDNOT_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_ANDNOT_": { + "type": "$_ANDNOT_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "Y": [ + 4 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "Y": { + "bits": [ + 4 + ] + } + } + }, + "$_AND_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_AND_": { + "type": "$_AND_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "Y": [ + 4 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "Y": { + "bits": [ + 4 + ] + } + } + }, + "$_AOI3_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "C": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_AOI3_": { + "type": "$_AOI3_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "C": [ + 4 + ], + "Y": [ + 5 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "C": { + "bits": [ + 4 + ] + }, + "Y": { + "bits": [ + 5 + ] + } + } + }, + "$_AOI4_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "C": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_AOI4_": { + "type": "$_AOI4_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "C": [ + 4 + ], + "D": [ + 5 + ], + "Y": [ + 6 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "C": { + "bits": [ + 4 + ] + }, + "D": { + "bits": [ + 5 + ] + }, + "Y": { + "bits": [ + 6 + ] + } + } + }, + "$_BUF_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 3 + ] + } + }, + "cells": { + "$_BUF_": { + "type": "$_BUF_", + "connections": { + "A": [ + 2 + ], + "Y": [ + 3 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "Y": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFE_NN0N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_NN0N_": { + "type": "$_DFFE_NN0N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_NN0P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_NN0P_": { + "type": "$_DFFE_NN0P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_NN1N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_NN1N_": { + "type": "$_DFFE_NN1N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_NN1P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_NN1P_": { + "type": "$_DFFE_NN1P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_NN_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "E": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DFFE_NN_": { + "type": "$_DFFE_NN_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "E": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 5 + ] + } + } + }, + "$_DFFE_NP0N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_NP0N_": { + "type": "$_DFFE_NP0N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_NP0P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_NP0P_": { + "type": "$_DFFE_NP0P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_NP1N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_NP1N_": { + "type": "$_DFFE_NP1N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_NP1P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_NP1P_": { + "type": "$_DFFE_NP1P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_NP_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "E": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DFFE_NP_": { + "type": "$_DFFE_NP_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "E": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 5 + ] + } + } + }, + "$_DFFE_PN0N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_PN0N_": { + "type": "$_DFFE_PN0N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_PN0P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_PN0P_": { + "type": "$_DFFE_PN0P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_PN1N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_PN1N_": { + "type": "$_DFFE_PN1N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_PN1P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_PN1P_": { + "type": "$_DFFE_PN1P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_PN_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "E": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DFFE_PN_": { + "type": "$_DFFE_PN_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "E": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 5 + ] + } + } + }, + "$_DFFE_PP0N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_PP0N_": { + "type": "$_DFFE_PP0N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_PP0P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_PP0P_": { + "type": "$_DFFE_PP0P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_PP1N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_PP1N_": { + "type": "$_DFFE_PP1N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_PP1P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFE_PP1P_": { + "type": "$_DFFE_PP1P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFFE_PP_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "E": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DFFE_PP_": { + "type": "$_DFFE_PP_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "E": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 4 + ] + }, + "Q": { + "bits": [ + 5 + ] + } + } + }, + "$_DFFSRE_NNNN_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_NNNN_": { + "type": "$_DFFSRE_NNNN_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSRE_NNNP_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_NNNP_": { + "type": "$_DFFSRE_NNNP_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSRE_NNPN_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_NNPN_": { + "type": "$_DFFSRE_NNPN_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSRE_NNPP_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_NNPP_": { + "type": "$_DFFSRE_NNPP_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSRE_NPNN_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_NPNN_": { + "type": "$_DFFSRE_NPNN_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSRE_NPNP_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_NPNP_": { + "type": "$_DFFSRE_NPNP_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSRE_NPPN_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_NPPN_": { + "type": "$_DFFSRE_NPPN_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSRE_NPPP_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_NPPP_": { + "type": "$_DFFSRE_NPPP_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSRE_PNNN_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_PNNN_": { + "type": "$_DFFSRE_PNNN_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSRE_PNNP_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_PNNP_": { + "type": "$_DFFSRE_PNNP_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSRE_PNPN_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_PNPN_": { + "type": "$_DFFSRE_PNPN_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSRE_PNPP_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_PNPP_": { + "type": "$_DFFSRE_PNPP_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSRE_PPNN_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_PPNN_": { + "type": "$_DFFSRE_PPNN_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSRE_PPNP_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_PPNP_": { + "type": "$_DFFSRE_PPNP_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSRE_PPPN_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_PPPN_": { + "type": "$_DFFSRE_PPPN_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSRE_PPPP_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "D": { + "direction": "input", + "bits": [ + 6 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 7 + ] + } + }, + "cells": { + "$_DFFSRE_PPPP_": { + "type": "$_DFFSRE_PPPP_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "D": [ + 6 + ], + "Q": [ + 7 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 6 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 7 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSR_NNN_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFSR_NNN_": { + "type": "$_DFFSR_NNN_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSR_NNP_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFSR_NNP_": { + "type": "$_DFFSR_NNP_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSR_NPN_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFSR_NPN_": { + "type": "$_DFFSR_NPN_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSR_NPP_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFSR_NPP_": { + "type": "$_DFFSR_NPP_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSR_PNN_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFSR_PNN_": { + "type": "$_DFFSR_PNN_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSR_PNP_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFSR_PNP_": { + "type": "$_DFFSR_PNP_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSR_PPN_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFSR_PPN_": { + "type": "$_DFFSR_PPN_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFFSR_PPP_": { + "ports": { + "C": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DFFSR_PPP_": { + "type": "$_DFFSR_PPP_", + "connections": { + "C": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 2 + ] + }, + "D": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DFF_NN0_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DFF_NN0_": { + "type": "$_DFF_NN0_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFF_NN1_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DFF_NN1_": { + "type": "$_DFF_NN1_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFF_NP0_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DFF_NP0_": { + "type": "$_DFF_NP0_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFF_NP1_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DFF_NP1_": { + "type": "$_DFF_NP1_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFF_N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_DFF_N_": { + "type": "$_DFF_N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "Q": [ + 4 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 4 + ] + } + } + }, + "$_DFF_PN0_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DFF_PN0_": { + "type": "$_DFF_PN0_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFF_PN1_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DFF_PN1_": { + "type": "$_DFF_PN1_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFF_PP0_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DFF_PP0_": { + "type": "$_DFF_PP0_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFF_PP1_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DFF_PP1_": { + "type": "$_DFF_PP1_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_DFF_P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_DFF_P_": { + "type": "$_DFF_P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "Q": [ + 4 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 4 + ] + } + } + }, + "$_DLATCHSR_NNN_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DLATCHSR_NNN_": { + "type": "$_DLATCHSR_NNN_", + "connections": { + "E": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 5 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCHSR_NNP_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DLATCHSR_NNP_": { + "type": "$_DLATCHSR_NNP_", + "connections": { + "E": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 5 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCHSR_NPN_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DLATCHSR_NPN_": { + "type": "$_DLATCHSR_NPN_", + "connections": { + "E": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 5 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCHSR_NPP_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DLATCHSR_NPP_": { + "type": "$_DLATCHSR_NPP_", + "connections": { + "E": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 5 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCHSR_PNN_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DLATCHSR_PNN_": { + "type": "$_DLATCHSR_PNN_", + "connections": { + "E": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 5 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCHSR_PNP_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DLATCHSR_PNP_": { + "type": "$_DLATCHSR_PNP_", + "connections": { + "E": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 5 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCHSR_PPN_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DLATCHSR_PPN_": { + "type": "$_DLATCHSR_PPN_", + "connections": { + "E": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 5 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCHSR_PPP_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "S": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_DLATCHSR_PPP_": { + "type": "$_DLATCHSR_PPP_", + "connections": { + "E": [ + 2 + ], + "S": [ + 3 + ], + "R": [ + 4 + ], + "D": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 5 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + }, + "S": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCH_NN0_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "R": { + "direction": "input", + "bits": [ + 3 + ] + }, + "D": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DLATCH_NN0_": { + "type": "$_DLATCH_NN0_", + "connections": { + "E": [ + 2 + ], + "R": [ + 3 + ], + "D": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 4 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCH_NN1_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "R": { + "direction": "input", + "bits": [ + 3 + ] + }, + "D": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DLATCH_NN1_": { + "type": "$_DLATCH_NN1_", + "connections": { + "E": [ + 2 + ], + "R": [ + 3 + ], + "D": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 4 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCH_NP0_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "R": { + "direction": "input", + "bits": [ + 3 + ] + }, + "D": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DLATCH_NP0_": { + "type": "$_DLATCH_NP0_", + "connections": { + "E": [ + 2 + ], + "R": [ + 3 + ], + "D": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 4 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCH_NP1_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "R": { + "direction": "input", + "bits": [ + 3 + ] + }, + "D": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DLATCH_NP1_": { + "type": "$_DLATCH_NP1_", + "connections": { + "E": [ + 2 + ], + "R": [ + 3 + ], + "D": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 4 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCH_N_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "D": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_DLATCH_N_": { + "type": "$_DLATCH_N_", + "connections": { + "E": [ + 2 + ], + "D": [ + 3 + ], + "Q": [ + 4 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 3 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 4 + ] + } + } + }, + "$_DLATCH_PN0_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "R": { + "direction": "input", + "bits": [ + 3 + ] + }, + "D": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DLATCH_PN0_": { + "type": "$_DLATCH_PN0_", + "connections": { + "E": [ + 2 + ], + "R": [ + 3 + ], + "D": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 4 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCH_PN1_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "R": { + "direction": "input", + "bits": [ + 3 + ] + }, + "D": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DLATCH_PN1_": { + "type": "$_DLATCH_PN1_", + "connections": { + "E": [ + 2 + ], + "R": [ + 3 + ], + "D": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 4 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCH_PP0_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "R": { + "direction": "input", + "bits": [ + 3 + ] + }, + "D": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DLATCH_PP0_": { + "type": "$_DLATCH_PP0_", + "connections": { + "E": [ + 2 + ], + "R": [ + 3 + ], + "D": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 4 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCH_PP1_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "R": { + "direction": "input", + "bits": [ + 3 + ] + }, + "D": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_DLATCH_PP1_": { + "type": "$_DLATCH_PP1_", + "connections": { + "E": [ + 2 + ], + "R": [ + 3 + ], + "D": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 4 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 3 + ] + } + } + }, + "$_DLATCH_P_": { + "ports": { + "E": { + "direction": "input", + "bits": [ + 2 + ] + }, + "D": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_DLATCH_P_": { + "type": "$_DLATCH_P_", + "connections": { + "E": [ + 2 + ], + "D": [ + 3 + ], + "Q": [ + 4 + ] + } + } + }, + "netnames": { + "D": { + "bits": [ + 3 + ] + }, + "E": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 4 + ] + } + } + }, + "$_MUX16_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "C": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "E": { + "direction": "input", + "bits": [ + 6 + ] + }, + "F": { + "direction": "input", + "bits": [ + 7 + ] + }, + "G": { + "direction": "input", + "bits": [ + 8 + ] + }, + "H": { + "direction": "input", + "bits": [ + 9 + ] + }, + "I": { + "direction": "input", + "bits": [ + 10 + ] + }, + "J": { + "direction": "input", + "bits": [ + 11 + ] + }, + "K": { + "direction": "input", + "bits": [ + 12 + ] + }, + "L": { + "direction": "input", + "bits": [ + 13 + ] + }, + "M": { + "direction": "input", + "bits": [ + 14 + ] + }, + "N": { + "direction": "input", + "bits": [ + 15 + ] + }, + "O": { + "direction": "input", + "bits": [ + 16 + ] + }, + "P": { + "direction": "input", + "bits": [ + 17 + ] + }, + "S": { + "direction": "input", + "bits": [ + 18 + ] + }, + "T": { + "direction": "input", + "bits": [ + 19 + ] + }, + "U": { + "direction": "input", + "bits": [ + 20 + ] + }, + "V": { + "direction": "input", + "bits": [ + 21 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 22 + ] + } + }, + "cells": { + "$_MUX16_": { + "type": "$_MUX16_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "C": [ + 4 + ], + "D": [ + 5 + ], + "E": [ + 6 + ], + "F": [ + 7 + ], + "G": [ + 8 + ], + "H": [ + 9 + ], + "I": [ + 10 + ], + "J": [ + 11 + ], + "K": [ + 12 + ], + "L": [ + 13 + ], + "M": [ + 14 + ], + "N": [ + 15 + ], + "O": [ + 16 + ], + "P": [ + 17 + ], + "S": [ + 18 + ], + "T": [ + 19 + ], + "U": [ + 20 + ], + "V": [ + 21 + ], + "Y": [ + 22 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "C": { + "bits": [ + 4 + ] + }, + "D": { + "bits": [ + 5 + ] + }, + "E": { + "bits": [ + 6 + ] + }, + "F": { + "bits": [ + 7 + ] + }, + "G": { + "bits": [ + 8 + ] + }, + "H": { + "bits": [ + 9 + ] + }, + "I": { + "bits": [ + 10 + ] + }, + "J": { + "bits": [ + 11 + ] + }, + "K": { + "bits": [ + 12 + ] + }, + "L": { + "bits": [ + 13 + ] + }, + "M": { + "bits": [ + 14 + ] + }, + "N": { + "bits": [ + 15 + ] + }, + "O": { + "bits": [ + 16 + ] + }, + "P": { + "bits": [ + 17 + ] + }, + "S": { + "bits": [ + 18 + ] + }, + "T": { + "bits": [ + 19 + ] + }, + "U": { + "bits": [ + 20 + ] + }, + "V": { + "bits": [ + 21 + ] + }, + "Y": { + "bits": [ + 22 + ] + } + } + }, + "$_MUX4_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "C": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "S": { + "direction": "input", + "bits": [ + 6 + ] + }, + "T": { + "direction": "input", + "bits": [ + 7 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 8 + ] + } + }, + "cells": { + "$_MUX4_": { + "type": "$_MUX4_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "C": [ + 4 + ], + "D": [ + 5 + ], + "S": [ + 6 + ], + "T": [ + 7 + ], + "Y": [ + 8 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "C": { + "bits": [ + 4 + ] + }, + "D": { + "bits": [ + 5 + ] + }, + "S": { + "bits": [ + 6 + ] + }, + "T": { + "bits": [ + 7 + ] + }, + "Y": { + "bits": [ + 8 + ] + } + } + }, + "$_MUX8_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "C": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "E": { + "direction": "input", + "bits": [ + 6 + ] + }, + "F": { + "direction": "input", + "bits": [ + 7 + ] + }, + "G": { + "direction": "input", + "bits": [ + 8 + ] + }, + "H": { + "direction": "input", + "bits": [ + 9 + ] + }, + "S": { + "direction": "input", + "bits": [ + 10 + ] + }, + "T": { + "direction": "input", + "bits": [ + 11 + ] + }, + "U": { + "direction": "input", + "bits": [ + 12 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 13 + ] + } + }, + "cells": { + "$_MUX8_": { + "type": "$_MUX8_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "C": [ + 4 + ], + "D": [ + 5 + ], + "E": [ + 6 + ], + "F": [ + 7 + ], + "G": [ + 8 + ], + "H": [ + 9 + ], + "S": [ + 10 + ], + "T": [ + 11 + ], + "U": [ + 12 + ], + "Y": [ + 13 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "C": { + "bits": [ + 4 + ] + }, + "D": { + "bits": [ + 5 + ] + }, + "E": { + "bits": [ + 6 + ] + }, + "F": { + "bits": [ + 7 + ] + }, + "G": { + "bits": [ + 8 + ] + }, + "H": { + "bits": [ + 9 + ] + }, + "S": { + "bits": [ + 10 + ] + }, + "T": { + "bits": [ + 11 + ] + }, + "U": { + "bits": [ + 12 + ] + }, + "Y": { + "bits": [ + 13 + ] + } + } + }, + "$_MUX_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "S": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_MUX_": { + "type": "$_MUX_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "S": [ + 4 + ], + "Y": [ + 5 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "S": { + "bits": [ + 4 + ] + }, + "Y": { + "bits": [ + 5 + ] + } + } + }, + "$_NAND_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_NAND_": { + "type": "$_NAND_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "Y": [ + 4 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "Y": { + "bits": [ + 4 + ] + } + } + }, + "$_NMUX_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "S": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_NMUX_": { + "type": "$_NMUX_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "S": [ + 4 + ], + "Y": [ + 5 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "S": { + "bits": [ + 4 + ] + }, + "Y": { + "bits": [ + 5 + ] + } + } + }, + "$_NOR_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_NOR_": { + "type": "$_NOR_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "Y": [ + 4 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "Y": { + "bits": [ + 4 + ] + } + } + }, + "$_NOT_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 3 + ] + } + }, + "cells": { + "$_NOT_": { + "type": "$_NOT_", + "connections": { + "A": [ + 2 + ], + "Y": [ + 3 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "Y": { + "bits": [ + 3 + ] + } + } + }, + "$_OAI3_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "C": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_OAI3_": { + "type": "$_OAI3_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "C": [ + 4 + ], + "Y": [ + 5 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "C": { + "bits": [ + 4 + ] + }, + "Y": { + "bits": [ + 5 + ] + } + } + }, + "$_OAI4_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "C": { + "direction": "input", + "bits": [ + 4 + ] + }, + "D": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_OAI4_": { + "type": "$_OAI4_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "C": [ + 4 + ], + "D": [ + 5 + ], + "Y": [ + 6 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "C": { + "bits": [ + 4 + ] + }, + "D": { + "bits": [ + 5 + ] + }, + "Y": { + "bits": [ + 6 + ] + } + } + }, + "$_ORNOT_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_ORNOT_": { + "type": "$_ORNOT_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "Y": [ + 4 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "Y": { + "bits": [ + 4 + ] + } + } + }, + "$_OR_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_OR_": { + "type": "$_OR_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "Y": [ + 4 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "Y": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_NN0N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_NN0N_": { + "type": "$_SDFFCE_NN0N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_NN0P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_NN0P_": { + "type": "$_SDFFCE_NN0P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_NN1N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_NN1N_": { + "type": "$_SDFFCE_NN1N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_NN1P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_NN1P_": { + "type": "$_SDFFCE_NN1P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_NP0N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_NP0N_": { + "type": "$_SDFFCE_NP0N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_NP0P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_NP0P_": { + "type": "$_SDFFCE_NP0P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_NP1N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_NP1N_": { + "type": "$_SDFFCE_NP1N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_NP1P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_NP1P_": { + "type": "$_SDFFCE_NP1P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_PN0N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_PN0N_": { + "type": "$_SDFFCE_PN0N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_PN0P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_PN0P_": { + "type": "$_SDFFCE_PN0P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_PN1N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_PN1N_": { + "type": "$_SDFFCE_PN1N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_PN1P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_PN1P_": { + "type": "$_SDFFCE_PN1P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_PP0N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_PP0N_": { + "type": "$_SDFFCE_PP0N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_PP0P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_PP0P_": { + "type": "$_SDFFCE_PP0P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_PP1N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_PP1N_": { + "type": "$_SDFFCE_PP1N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFCE_PP1P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFCE_PP1P_": { + "type": "$_SDFFCE_PP1P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_NN0N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_NN0N_": { + "type": "$_SDFFE_NN0N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_NN0P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_NN0P_": { + "type": "$_SDFFE_NN0P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_NN1N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_NN1N_": { + "type": "$_SDFFE_NN1N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_NN1P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_NN1P_": { + "type": "$_SDFFE_NN1P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_NP0N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_NP0N_": { + "type": "$_SDFFE_NP0N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_NP0P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_NP0P_": { + "type": "$_SDFFE_NP0P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_NP1N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_NP1N_": { + "type": "$_SDFFE_NP1N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_NP1P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_NP1P_": { + "type": "$_SDFFE_NP1P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_PN0N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_PN0N_": { + "type": "$_SDFFE_PN0N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_PN0P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_PN0P_": { + "type": "$_SDFFE_PN0P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_PN1N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_PN1N_": { + "type": "$_SDFFE_PN1N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_PN1P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_PN1P_": { + "type": "$_SDFFE_PN1P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_PP0N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_PP0N_": { + "type": "$_SDFFE_PP0N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_PP0P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_PP0P_": { + "type": "$_SDFFE_PP0P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_PP1N_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_PP1N_": { + "type": "$_SDFFE_PP1N_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFFE_PP1P_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "E": { + "direction": "input", + "bits": [ + 5 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 6 + ] + } + }, + "cells": { + "$_SDFFE_PP1P_": { + "type": "$_SDFFE_PP1P_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "E": [ + 5 + ], + "Q": [ + 6 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 5 + ] + }, + "Q": { + "bits": [ + 6 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFF_NN0_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_SDFF_NN0_": { + "type": "$_SDFF_NN0_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFF_NN1_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_SDFF_NN1_": { + "type": "$_SDFF_NN1_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFF_NP0_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_SDFF_NP0_": { + "type": "$_SDFF_NP0_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFF_NP1_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_SDFF_NP1_": { + "type": "$_SDFF_NP1_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFF_PN0_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_SDFF_PN0_": { + "type": "$_SDFF_PN0_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFF_PN1_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_SDFF_PN1_": { + "type": "$_SDFF_PN1_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFF_PP0_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_SDFF_PP0_": { + "type": "$_SDFF_PP0_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SDFF_PP1_": { + "ports": { + "D": { + "direction": "input", + "bits": [ + 2 + ] + }, + "C": { + "direction": "input", + "bits": [ + 3 + ] + }, + "R": { + "direction": "input", + "bits": [ + 4 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 5 + ] + } + }, + "cells": { + "$_SDFF_PP1_": { + "type": "$_SDFF_PP1_", + "connections": { + "D": [ + 2 + ], + "C": [ + 3 + ], + "R": [ + 4 + ], + "Q": [ + 5 + ] + } + } + }, + "netnames": { + "C": { + "bits": [ + 3 + ] + }, + "D": { + "bits": [ + 2 + ] + }, + "Q": { + "bits": [ + 5 + ] + }, + "R": { + "bits": [ + 4 + ] + } + } + }, + "$_SR_NN_": { + "ports": { + "S": { + "direction": "input", + "bits": [ + 2 + ] + }, + "R": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_SR_NN_": { + "type": "$_SR_NN_", + "connections": { + "S": [ + 2 + ], + "R": [ + 3 + ], + "Q": [ + 4 + ] + } + } + }, + "netnames": { + "Q": { + "bits": [ + 4 + ] + }, + "R": { + "bits": [ + 3 + ] + }, + "S": { + "bits": [ + 2 + ] + } + } + }, + "$_SR_NP_": { + "ports": { + "S": { + "direction": "input", + "bits": [ + 2 + ] + }, + "R": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_SR_NP_": { + "type": "$_SR_NP_", + "connections": { + "S": [ + 2 + ], + "R": [ + 3 + ], + "Q": [ + 4 + ] + } + } + }, + "netnames": { + "Q": { + "bits": [ + 4 + ] + }, + "R": { + "bits": [ + 3 + ] + }, + "S": { + "bits": [ + 2 + ] + } + } + }, + "$_SR_PN_": { + "ports": { + "S": { + "direction": "input", + "bits": [ + 2 + ] + }, + "R": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_SR_PN_": { + "type": "$_SR_PN_", + "connections": { + "S": [ + 2 + ], + "R": [ + 3 + ], + "Q": [ + 4 + ] + } + } + }, + "netnames": { + "Q": { + "bits": [ + 4 + ] + }, + "R": { + "bits": [ + 3 + ] + }, + "S": { + "bits": [ + 2 + ] + } + } + }, + "$_SR_PP_": { + "ports": { + "S": { + "direction": "input", + "bits": [ + 2 + ] + }, + "R": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Q": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_SR_PP_": { + "type": "$_SR_PP_", + "connections": { + "S": [ + 2 + ], + "R": [ + 3 + ], + "Q": [ + 4 + ] + } + } + }, + "netnames": { + "Q": { + "bits": [ + 4 + ] + }, + "R": { + "bits": [ + 3 + ] + }, + "S": { + "bits": [ + 2 + ] + } + } + }, + "$_TBUF_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "E": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_TBUF_": { + "type": "$_TBUF_", + "connections": { + "A": [ + 2 + ], + "E": [ + 3 + ], + "Y": [ + 4 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "E": { + "bits": [ + 3 + ] + }, + "Y": { + "bits": [ + 4 + ] + } + } + }, + "$_XNOR_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_XNOR_": { + "type": "$_XNOR_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "Y": [ + 4 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "Y": { + "bits": [ + 4 + ] + } + } + }, + "$_XOR_": { + "ports": { + "A": { + "direction": "input", + "bits": [ + 2 + ] + }, + "B": { + "direction": "input", + "bits": [ + 3 + ] + }, + "Y": { + "direction": "output", + "bits": [ + 4 + ] + } + }, + "cells": { + "$_XOR_": { + "type": "$_XOR_", + "connections": { + "A": [ + 2 + ], + "B": [ + 3 + ], + "Y": [ + 4 + ] + } + } + }, + "netnames": { + "A": { + "bits": [ + 2 + ] + }, + "B": { + "bits": [ + 3 + ] + }, + "Y": { + "bits": [ + 4 + ] + } + } + } + } +} diff --git a/crates/arbolta/tests/deps/simlib_wrappers.sv b/crates/arbolta/tests/deps/simlib_wrappers.sv new file mode 100644 index 0000000..3b74983 --- /dev/null +++ b/crates/arbolta/tests/deps/simlib_wrappers.sv @@ -0,0 +1,54 @@ +`include "/opt/oss-cad-suite/share/yosys/simlib.v" + +module not_wrapper ( + A, + Y +); + + parameter A_SIGNED = 0; + parameter A_WIDTH = 0; + parameter Y_WIDTH = 0; + + input [A_WIDTH-1:0] A; + output [Y_WIDTH-1:0] Y; + + \$not #( + .A_SIGNED(A_SIGNED), + .A_WIDTH (A_WIDTH), + .Y_WIDTH (Y_WIDTH) + ) _TECHMAP_REPLACE_ ( + .A(A), + .Y(Y) + ); + +endmodule + +module add_wrapper ( + A, + B, + Y +); + + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 0; + parameter B_WIDTH = 0; + parameter Y_WIDTH = 0; + + input [A_WIDTH-1:0] A; + input [B_WIDTH-1:0] B; + output [Y_WIDTH-1:0] Y; + + \$add #( + .A_SIGNED(A_SIGNED), + .B_SIGNED(B_SIGNED), + .A_WIDTH (A_WIDTH), + .B_WIDTH (B_WIDTH), + .Y_WIDTH (Y_WIDTH) + ) _TECHMAP_REPLACE_ ( + .A(A), + .B(B), + .Y(Y) + ); + +endmodule diff --git a/crates/arbolta/tests/test_bit.rs b/crates/arbolta/tests/test_bit.rs index 4462c4c..c727cfc 100644 --- a/crates/arbolta/tests/test_bit.rs +++ b/crates/arbolta/tests/test_bit.rs @@ -2,15 +2,13 @@ // SPDX-License-Identifier: MIT use arbolta::bit::Bit; -use std::str::FromStr; - use rstest::rstest; #[rstest] -#[case("0", Bit::ZERO)] -#[case("1", Bit::ONE)] -fn test_bit_from_str(#[case] val: String, #[case] expected: Bit) { - assert_eq!(Bit::from_str(&val).unwrap(), expected); +#[case('0', Bit::ZERO)] +#[case('1', Bit::ONE)] +fn test_bit_from_char(#[case] val: char, #[case] expected: Bit) { + assert_eq!(Bit::try_from(val).unwrap(), expected); } #[rstest] @@ -34,13 +32,6 @@ fn test_bit_to_bool(#[case] bit: Bit, #[case] expected: bool) { assert_eq!(bit.0, expected); } -#[rstest] -#[case(0, Bit::ZERO)] -#[case(1, Bit::ONE)] -fn test_bit_from_int(#[case] val: usize, #[case] expected: Bit) { - assert_eq!(Bit::from_int(val).unwrap(), expected); -} - #[test] fn test_bit_not() { assert_eq!(!Bit::ZERO, Bit::ONE); diff --git a/crates/arbolta/tests/test_bitvec.rs b/crates/arbolta/tests/test_bitvec.rs index 0735800..03074db 100644 --- a/crates/arbolta/tests/test_bitvec.rs +++ b/crates/arbolta/tests/test_bitvec.rs @@ -3,7 +3,6 @@ use arbolta::bit::{Bit, BitVec}; use ndarray::{Array1, array}; - use rstest::rstest; #[rstest] // TODO: Generate random bit patterns and check @@ -18,7 +17,7 @@ use rstest::rstest; Bit::ZERO, ], "00100101")] fn test_bits_to_str(#[case] bits: Vec, #[case] expected: String) { - let bits = BitVec { bits }; + let bits: BitVec = bits.into(); assert_eq!(bits.to_string(), expected); } @@ -35,32 +34,23 @@ fn test_bits_to_str(#[case] bits: Vec, #[case] expected: String) { ] )] fn test_str_to_bits(#[case] val: String, #[case] expected: Vec) { - assert_eq!(BitVec::try_from(val.as_str()).unwrap().bits, expected) + assert_eq!(BitVec::try_from(val.as_str()).unwrap(), expected.into()) } #[rstest] #[case(vec![ - false, + true, false, true, false, false, true, false, - true, -], vec![ - Bit::ONE, - Bit::ZERO, - Bit::ONE, - Bit::ZERO, - Bit::ZERO, - Bit::ONE, - Bit::ZERO, - Bit::ZERO, -] + false, +], "00100101" )] -fn test_bools_to_bits(#[case] vals: Vec, #[case] expected: Vec) { - assert_eq!(BitVec::from(vals).bits, expected) +fn test_bools_to_bits(#[case] vals: Vec, #[case] expected: BitVec) { + assert_eq!(BitVec::from_bools(vals), expected) } #[rstest] @@ -236,7 +226,7 @@ fn test_bits_to_i32(#[case] bits: BitVec, #[case] expected: i32) { #[case(182, "10110110")] #[case(171, "10101011")] fn test_u8_to_bits(#[case] val: u8, #[case] expected: BitVec) { - assert_eq!(BitVec::from_int(val).unwrap(), expected) + assert_eq!(BitVec::from_int(val, None), expected) } #[rstest] @@ -253,83 +243,87 @@ fn test_u8_to_bits(#[case] val: u8, #[case] expected: BitVec) { #[case(39090, "1001100010110010")] #[case(36192, "1000110101100000")] fn test_u16_to_bits(#[case] val: u16, #[case] expected: BitVec) { - assert_eq!(BitVec::from_int(val).unwrap(), expected) + assert_eq!(BitVec::from_int(val, None), expected) } // TODO: Test other data types #[rstest] // Reversed element order for bits -#[case(&[124, 70], "0100011001111100")] -#[case(&[253, 43], "0010101111111101")] -#[case(&[114, 74], "0100101001110010")] -#[case(&[179, 61], "0011110110110011")] -#[case(&[27, 184], "1011100000011011")] -#[case(&[190, 97], "0110000110111110")] -#[case(&[205, 117], "0111010111001101")] -#[case(&[255, 111], "0110111111111111")] -#[case(&[253, 176], "1011000011111101")] -#[case(&[220, 231], "1110011111011100")] -fn test_u8_vec_to_bits(#[case] vals: &[u8], #[case] expected: BitVec) { - assert_eq!(BitVec::from_ints(vals).unwrap(), expected); +#[case(vec![124, 70], "0100011001111100")] +#[case(vec![253, 43], "0010101111111101")] +#[case(vec![114, 74], "0100101001110010")] +#[case(vec![179, 61], "0011110110110011")] +#[case(vec![27, 184], "1011100000011011")] +#[case(vec![190, 97], "0110000110111110")] +#[case(vec![205, 117], "0111010111001101")] +#[case(vec![255, 111], "0110111111111111")] +#[case(vec![253, 176], "1011000011111101")] +#[case(vec![220, 231], "1110011111011100")] +fn test_u8_vec_to_bits(#[case] vals: Vec, #[case] expected: BitVec) { + let actual = BitVec::from_ints(vals, None); + assert_eq!(actual.bits, expected.bits); + assert_eq!(actual.shape, [2, 8]); } #[rstest] -#[case(&[0, -114], "1000111000000000")] -#[case(&[-107, 89], "0101100110010101")] -#[case(&[59, -99], "1001110100111011")] -#[case(&[115, -117], "1000101101110011")] -#[case(&[-90, 87], "0101011110100110")] -#[case(&[-80, -49], "1100111110110000")] -#[case(&[-88, 51], "0011001110101000")] -#[case(&[-101, 62], "0011111010011011")] -#[case(&[15, -27], "1110010100001111")] -#[case(&[-58, -95], "1010000111000110")] -fn test_i8_vec_to_bits(#[case] vals: &[i8], #[case] expected: BitVec) { - assert_eq!(BitVec::from_ints(vals).unwrap(), expected); -} - -#[rstest] -#[case(&[30, -44], 7, "10101000011110")] -#[case(&[-19, -42], 7, "10101101101101")] -#[case(&[3, -4], 4, "11000011")] -#[case(&[1, -1], 2, "1101")] -fn test_i8_vec_to_bits_sized( - #[case] vals: &[i8], - #[case] elem_size: usize, +#[case(vec![0, -114], None, "1000111000000000")] +#[case(vec![-107, 89], None, "0101100110010101")] +#[case(vec![59, -99], None, "1001110100111011")] +#[case(vec![115, -117], None, "1000101101110011")] +#[case(vec![-90, 87], None, "0101011110100110")] +#[case(vec![-80, -49], None, "1100111110110000")] +#[case(vec![-88, 51], None, "0011001110101000")] +#[case(vec![-101, 62], None, "0011111010011011")] +#[case(vec![15, -27], None, "1110010100001111")] +#[case(vec![-58, -95], None, "1010000111000110")] +#[case(vec![30, -44], Some(7), "10101000011110")] +#[case(vec![-19, -42], Some(7), "10101101101101")] +#[case(vec![3, -4], Some(4), "11000011")] +#[case(vec![1, -1], Some(2), "1101")] +#[case(vec![1, -1], Some(10), "11111111110000000001")] +fn test_i8_vec_to_bits( + #[case] vals: Vec, + #[case] elem_size: Option, #[case] expected: BitVec, ) { - assert_eq!(BitVec::from_ints_sized(vals, elem_size).unwrap(), expected); + let expected_shape = [vals.len(), elem_size.unwrap_or(i8::BITS as usize)]; + let actual = BitVec::from_ints(vals, elem_size); + assert_eq!(actual.bits, expected.bits); + assert_eq!(actual.shape, expected_shape); } #[rstest] -#[case("0100011001111100", &[124, 70])] -#[case("0010101111111101", &[253, 43])] -#[case("0100101001110010", &[114, 74])] -#[case("0011110110110011", &[179, 61])] -#[case("1011100000011011", &[27, 184])] -#[case("0110000110111110", &[190, 97])] -#[case("0111010111001101", &[205, 117])] -#[case("0110111111111111", &[255, 111])] -#[case("1011000011111101", &[253, 176])] -#[case("1110011111011100", &[220, 231])] -fn test_bits_to_u8_vec(#[case] bits: BitVec, #[case] expected: &[u8]) { - let actual: Vec = bits.to_ints(); +#[case("0100011001111100", vec![124, 70])] +#[case("0010101111111101", vec![253, 43])] +#[case("0100101001110010", vec![114, 74])] +#[case("0011110110110011", vec![179, 61])] +#[case("1011100000011011", vec![27, 184])] +#[case("0110000110111110", vec![190, 97])] +#[case("0111010111001101", vec![205, 117])] +#[case("0110111111111111", vec![255, 111])] +#[case("1011000011111101", vec![253, 176])] +#[case("1110011111011100", vec![220, 231])] +fn test_bits_to_u8_vec(#[case] bits: BitVec, #[case] expected: Vec) { + let actual: Vec = bits.to_ints(Some(u8::BITS as usize)).collect(); assert_eq!(actual, expected); } #[rstest] -#[case("0100011001111100", &[124, 70])] -#[case("0010101111111101", &[253, 43])] -#[case("0100101001110010", &[114, 74])] -#[case("0011110110110011", &[179, 61])] -#[case("1011100000011011", &[27, 184])] -#[case("0110000110111110", &[190, 97])] -#[case("0111010111001101", &[205, 117])] -#[case("0110111111111111", &[255, 111])] -#[case("1011000011111101", &[253, 176])] -#[case("1110011111011100", &[220, 231])] -fn test_bits_to_u8_vec_buffer(#[case] bits: BitVec, #[case] expected: &[u8]) { +#[case("0100011001111100", vec![124, 70])] +#[case("0010101111111101", vec![253, 43])] +#[case("0100101001110010", vec![114, 74])] +#[case("0011110110110011", vec![179, 61])] +#[case("1011100000011011", vec![27, 184])] +#[case("0110000110111110", vec![190, 97])] +#[case("0111010111001101", vec![205, 117])] +#[case("0110111111111111", vec![255, 111])] +#[case("1011000011111101", vec![253, 176])] +#[case("1110011111011100", vec![220, 231])] +fn test_bits_to_u8_vec_buffer(#[case] bits: BitVec, #[case] expected: Vec) { let mut buffer: Vec = vec![0; expected.len()]; - bits.to_ints_buffer(buffer.as_mut_slice()); + bits + .to_ints(Some(u8::BITS as usize)) + .enumerate() + .for_each(|(i, val)| buffer[i] = val); assert_eq!(buffer, expected); } @@ -346,7 +340,8 @@ fn test_bits_to_u8_vec_buffer(#[case] bits: BitVec, #[case] expected: &[u8]) { #[case("1011000011111101", array![253, 176])] #[case("1110011111011100", array![220, 231])] fn test_bits_to_u8_ndarray(#[case] bits: BitVec, #[case] expected: Array1) { - let actual: Array1 = bits.to_int_ndarray(); + let elem_size = bits.shape[1] / expected.len(); + let actual: Array1 = bits.to_ints(Some(elem_size)).collect(); assert_eq!(actual, expected); } @@ -362,22 +357,27 @@ fn test_bits_to_u8_ndarray(#[case] bits: BitVec, #[case] expected: Array1) { #[case("1011000011111101", array![253, 176])] #[case("1110011111011100", array![220, 231])] fn test_bits_to_u8_ndarray_buffer(#[case] bits: BitVec, #[case] expected: Array1) { + let elem_size = bits.shape[1] / expected.len(); let mut buffer: Array1 = Array1::zeros([expected.len()]); - bits.to_int_ndarray_buffer(buffer.view_mut()).unwrap(); + bits + .to_ints(Some(elem_size)) + .zip(buffer.iter_mut()) + .for_each(|(src, dst)| *dst = src); + assert_eq!(buffer, expected); } #[rstest] -#[case("10101000011110", 7, &[30, -44])] -#[case("10101101101101", 7, &[-19, -42])] -#[case("11000011", 4, &[3, -4])] -#[case("1101", 2, &[1, -1])] +#[case("10101000011110", 7, vec![30, -44])] +#[case("10101101101101", 7, vec![-19, -42])] +#[case("11000011", 4, vec![3, -4])] +#[case("1101", 2, vec![1, -1])] fn test_bits_sized_to_i8_vec( #[case] bits: BitVec, #[case] elem_size: usize, - #[case] expected: &[i8], + #[case] expected: Vec, ) { - let actual: Vec = bits.to_ints_sized(elem_size); + let actual: Vec = bits.to_ints(Some(elem_size)).collect(); assert_eq!(actual, expected); } @@ -392,7 +392,11 @@ fn test_bits_sized_to_i8_vec_buffer( #[case] expected: &[i8], ) { let mut buffer: Vec = vec![0; expected.len()]; - bits.to_ints_sized_buffer(elem_size, buffer.as_mut_slice()); + bits + .to_ints(Some(elem_size)) + .zip(buffer.iter_mut()) + .for_each(|(src, dst)| *dst = src); + assert_eq!(buffer, expected); } @@ -408,23 +412,26 @@ fn test_bits_sized_to_i8_ndarray_buffer( ) { let mut buffer: Array1 = Array1::zeros([expected.len()]); bits - .to_int_ndarray_sized_buffer(elem_size, buffer.view_mut()) - .unwrap(); + .to_ints(Some(elem_size)) + .zip(buffer.iter_mut()) + .for_each(|(src, dst)| *dst = src); + assert_eq!(buffer, expected); } #[rstest] -#[case("1000111000000000", &[0, -114])] -#[case("0101100110010101", &[-107, 89])] -#[case("1001110100111011", &[59, -99])] -#[case("1000101101110011", &[115, -117])] -#[case("0101011110100110", &[-90, 87])] -#[case("1100111110110000", &[-80, -49])] -#[case("0011001110101000", &[-88, 51])] -#[case("0011111010011011", &[-101, 62])] -#[case("1110010100001111", &[15, -27])] -#[case("1010000111000110", &[-58, -95])] -fn test_bits_to_i8_vec(#[case] bits: BitVec, #[case] expected: &[i8]) { - let actual: Vec = bits.to_ints(); +#[case("1000111000000000", vec![0, -114])] +#[case("0101100110010101", vec![-107, 89])] +#[case("1001110100111011", vec![59, -99])] +#[case("1000101101110011", vec![115, -117])] +#[case("0101011110100110", vec![-90, 87])] +#[case("1100111110110000", vec![-80, -49])] +#[case("0011001110101000", vec![-88, 51])] +#[case("0011111010011011", vec![-101, 62])] +#[case("1110010100001111", vec![15, -27])] +#[case("1010000111000110", vec![-58, -95])] +fn test_bits_to_i8_vec(#[case] bits: BitVec, #[case] expected: Vec) { + let elem_size = bits.shape[1] / expected.len(); + let actual: Vec = bits.to_ints(Some(elem_size)).collect(); assert_eq!(actual, expected); } diff --git a/crates/arbolta/tests/test_cell.rs b/crates/arbolta/tests/test_cell.rs deleted file mode 100644 index 31f45dd..0000000 --- a/crates/arbolta/tests/test_cell.rs +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. -// SPDX-License-Identifier: MIT - -use arbolta::bit::Bit; -use arbolta::cell::{Cell, CellFn, Dff, DffReset, create_cell}; -use arbolta::signal::Signal; - -use rstest::rstest; -use std::collections::HashMap; -use yosys_netlist_json as yosys; - -fn generate_cell( - cell_type: &str, - inputs: HashMap<&str, usize>, - outputs: HashMap<&str, usize>, - parameters: Option>, -) -> Cell { - let mut cell = yosys::Cell { - cell_type: cell_type.to_string(), - ..Default::default() - }; - - let mut num_nets = 0; - for (name, size) in inputs.iter() { - let bits: Vec = (0..*size).map(|i| yosys::BitVal::N(num_nets + i)).collect(); - num_nets += size; - cell.connections.insert(name.to_string(), bits); - cell - .port_directions - .insert(name.to_string(), yosys::PortDirection::Input); - } - - for (name, size) in outputs.iter() { - let bits: Vec = (0..*size).map(|i| yosys::BitVal::N(num_nets + i)).collect(); - num_nets += size; - cell.connections.insert(name.to_string(), bits); - cell - .port_directions - .insert(name.to_string(), yosys::PortDirection::Output); - } - - if let Some(parameters) = parameters { - for (param, val) in parameters.iter() { - cell - .parameters - .insert(param.to_string(), yosys::AttributeVal::S(val.to_string())); - } - } - - create_cell(&cell).unwrap() -} - -#[rstest] -#[case("NOT", Bit::ZERO, Bit::ONE)] -#[case("NOT", Bit::ONE, Bit::ZERO)] -#[case("BUF", Bit::ZERO, Bit::ZERO)] -#[case("BUF", Bit::ONE, Bit::ONE)] -fn test_cell_1_input(#[case] cell_type: &str, #[case] a: Bit, #[case] expected: Bit) { - let inputs = HashMap::from([("A", 1)]); - let outputs = HashMap::from([("Y", 1)]); - let mut cell = generate_cell(cell_type, inputs, outputs, None); - let mut signals = vec![Signal::new_constant(a), Signal::default()].into_boxed_slice(); - cell.eval(&mut signals); - assert_eq!(signals[1].get_value(), expected) -} - -#[rstest] -#[case("AND", Bit::ZERO, Bit::ZERO, Bit::ZERO)] -#[case("AND", Bit::ZERO, Bit::ONE, Bit::ZERO)] -#[case("AND", Bit::ONE, Bit::ZERO, Bit::ZERO)] -#[case("AND", Bit::ONE, Bit::ONE, Bit::ONE)] -#[case("ANDNOT", Bit::ZERO, Bit::ZERO, Bit::ZERO)] -#[case("ANDNOT", Bit::ZERO, Bit::ONE, Bit::ZERO)] -#[case("ANDNOT", Bit::ONE, Bit::ZERO, Bit::ONE)] -#[case("ANDNOT", Bit::ONE, Bit::ONE, Bit::ZERO)] -#[case("NOR", Bit::ZERO, Bit::ZERO, Bit::ONE)] -#[case("NOR", Bit::ZERO, Bit::ONE, Bit::ZERO)] -#[case("NOR", Bit::ONE, Bit::ZERO, Bit::ZERO)] -#[case("NOR", Bit::ONE, Bit::ONE, Bit::ZERO)] -#[case("NAND", Bit::ZERO, Bit::ZERO, Bit::ONE)] -#[case("NAND", Bit::ZERO, Bit::ONE, Bit::ONE)] -#[case("NAND", Bit::ONE, Bit::ZERO, Bit::ONE)] -#[case("NAND", Bit::ONE, Bit::ONE, Bit::ZERO)] -#[case("OR", Bit::ZERO, Bit::ZERO, Bit::ZERO)] -#[case("OR", Bit::ZERO, Bit::ONE, Bit::ONE)] -#[case("OR", Bit::ONE, Bit::ZERO, Bit::ONE)] -#[case("OR", Bit::ONE, Bit::ONE, Bit::ONE)] -#[case("XOR", Bit::ZERO, Bit::ZERO, Bit::ZERO)] -#[case("XOR", Bit::ZERO, Bit::ONE, Bit::ONE)] -#[case("XOR", Bit::ONE, Bit::ZERO, Bit::ONE)] -#[case("XOR", Bit::ONE, Bit::ONE, Bit::ZERO)] -#[case("XNOR", Bit::ZERO, Bit::ZERO, Bit::ONE)] -#[case("XNOR", Bit::ZERO, Bit::ONE, Bit::ZERO)] -#[case("XNOR", Bit::ONE, Bit::ZERO, Bit::ZERO)] -#[case("XNOR", Bit::ONE, Bit::ONE, Bit::ONE)] -#[case("ORNOT", Bit::ZERO, Bit::ZERO, Bit::ONE)] -#[case("ORNOT", Bit::ZERO, Bit::ONE, Bit::ZERO)] -#[case("ORNOT", Bit::ONE, Bit::ZERO, Bit::ONE)] -#[case("ORNOT", Bit::ONE, Bit::ONE, Bit::ONE)] -fn test_cell_2_input( - #[case] cell_type: &str, - #[case] a: Bit, - #[case] b: Bit, - #[case] expected: Bit, -) { - let inputs = HashMap::from([("A", 1), ("B", 1)]); - let outputs = HashMap::from([("Y", 1)]); - let mut cell = generate_cell(cell_type, inputs, outputs, None); - let mut signals = vec![ - Signal::new_constant(a), - Signal::new_constant(b), - Signal::default(), - ] - .into_boxed_slice(); - cell.eval(&mut signals); - - assert_eq!(signals[2].get_value(), expected); -} - -#[rstest] -#[case("OR", Bit::ZERO, Bit::ZERO, Bit::ZERO, Bit::ZERO)] -#[case("OR", Bit::ZERO, Bit::ZERO, Bit::ONE, Bit::ONE)] -#[case("OR", Bit::ZERO, Bit::ONE, Bit::ZERO, Bit::ONE)] -#[case("OR", Bit::ZERO, Bit::ONE, Bit::ONE, Bit::ONE)] -#[case("OR", Bit::ONE, Bit::ZERO, Bit::ZERO, Bit::ONE)] -#[case("OR", Bit::ONE, Bit::ZERO, Bit::ONE, Bit::ONE)] -#[case("OR", Bit::ONE, Bit::ONE, Bit::ZERO, Bit::ONE)] -#[case("OR", Bit::ONE, Bit::ONE, Bit::ONE, Bit::ONE)] -fn test_cell_3_input( - #[case] cell_type: &str, - #[case] a: Bit, - #[case] b: Bit, - #[case] c: Bit, - #[case] expected: Bit, -) { - let inputs = HashMap::from([("A", 1), ("B", 1), ("C", 1)]); - let outputs = HashMap::from([("Y", 1)]); - let mut cell = generate_cell(cell_type, inputs, outputs, None); - let mut signals = vec![ - Signal::new_constant(a), - Signal::new_constant(b), - Signal::new_constant(c), - Signal::default(), - ] - .into_boxed_slice(); - cell.eval(&mut signals); - - assert_eq!(signals[3].get_value(), expected); -} - -#[rstest] -fn test_cell_dff_p() { - // D, C, Q - let (data_in, clock, data_out) = (0, 1, 2); - let mut cell = Dff::new(Bit::ONE, clock, data_in, data_out); - let mut signals = vec![Signal::default(); 3].into_boxed_slice(); - - cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ZERO); - - signals[data_in].set_value(Bit::ONE); - cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ZERO); - - signals[clock].set_value(Bit::ONE); // Rising edge - cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ONE); - - signals[clock].set_value(Bit::ZERO); // Falling edge - cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ONE); - - signals[data_in].set_value(Bit::ZERO); - cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ONE); - - signals[clock].set_value(Bit::ONE); // Rising edge - cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ZERO); -} - -#[rstest] -fn test_cell_sdff_pp() { - // D, C, R, Q - let (data_in, clock, reset, data_out) = (0, 1, 2, 3); - let mut cell = DffReset::new(Bit::ONE, data_in, clock, reset, data_out); - let mut signals = vec![Signal::default(); 4].into_boxed_slice(); - - cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ZERO); - - signals[data_in].set_value(Bit::ONE); - cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ZERO); - - signals[clock].set_value(Bit::ONE); // Rising edge - cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ONE); - - signals[clock].set_value(Bit::ZERO); // Falling edge - cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ONE); - - signals[reset].set_value(Bit::ONE); - cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ONE); - - signals[clock].set_value(Bit::ONE); // Rising edge - cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ZERO); -} diff --git a/crates/arbolta/tests/test_module.rs b/crates/arbolta/tests/test_module.rs index a2ff13a..71e6665 100644 --- a/crates/arbolta/tests/test_module.rs +++ b/crates/arbolta/tests/test_module.rs @@ -1,149 +1,162 @@ // Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. // SPDX-License-Identifier: MIT -use arbolta::hardware_module::HardwareModule; - +use arbolta::{ + bit::BitVec, + hardware_module::HardwareModule, + yosys::{SynthConfig, YosysClient}, +}; +use once_cell::sync::Lazy; use rstest::rstest; -use std::collections::HashMap; -use yosys_netlist_json as yosys; - -fn generate_module( - cell_type: &str, - inputs: HashMap<&str, usize>, - outputs: HashMap<&str, usize>, - parameters: Option>, -) -> HardwareModule { - let mut module = yosys::Module::default(); - let mut cell = yosys::Cell { - cell_type: cell_type.to_string(), - ..Default::default() - }; +use std::{collections::HashMap, path::PathBuf}; +use yosys_netlist_json::Netlist; - let mut num_nets = 2; - for (name, size) in inputs.iter() { - let bits: Vec = (0..*size).map(|i| yosys::BitVal::N(num_nets + i)).collect(); - num_nets += size; - - let netname = yosys::Netname { - bits: bits.clone(), - ..Default::default() - }; - - let port = yosys::Port { - direction: yosys::PortDirection::Input, - bits: bits.clone(), - offset: Default::default(), - upto: Default::default(), - signed: Default::default(), - }; - - cell.connections.insert(name.to_string(), bits); - cell - .port_directions - .insert(name.to_string(), port.direction); - module.ports.insert(name.to_string(), port); - module.netnames.insert(name.to_string(), netname); - } +const CELL_WRAPPER_NETLIST: &str = include_str!("deps/simcells_wrappers.json"); - for (name, size) in outputs.iter() { - let bits: Vec = (0..*size).map(|i| yosys::BitVal::N(num_nets + i)).collect(); - num_nets += size; - - let netname = yosys::Netname { - bits: bits.clone(), - ..Default::default() - }; - - let port = yosys::Port { - direction: yosys::PortDirection::Output, - bits: bits.clone(), - offset: Default::default(), - upto: Default::default(), - signed: Default::default(), - }; - - cell.connections.insert(name.to_string(), bits); - cell - .port_directions - .insert(name.to_string(), port.direction); - module.ports.insert(name.to_string(), port); - module.netnames.insert(name.to_string(), netname); - } +static CELL_WRAPPER: Lazy = + Lazy::new(|| Netlist::from_slice(CELL_WRAPPER_NETLIST.as_bytes()).unwrap()); - if let Some(parameters) = parameters { - for (param, val) in parameters.iter() { - cell - .parameters - .insert(param.to_string(), yosys::AttributeVal::S(val.to_string())); - } +#[rstest] +#[case::buffer("$_BUF_", [ + (0, 0), + (1, 1), +])] +#[case::inverter("$_NOT_", [ // (A, Y) + (0, 1), + (1, 0), +])] +fn test_module_unary_cell(#[case] cell: &str, #[case] cases: [(u8, u8); 2]) { + let mut module = HardwareModule::new( + &CELL_WRAPPER, + &HashMap::from([(cell.to_string(), vec![cell.to_string()])]), + cell, + ) + .unwrap(); + + for (a, expected) in cases { + module.set_port("A", BitVec::from_int(a, None)).unwrap(); + module.eval(); + let actual: u8 = module.get_port("Y").unwrap().to_int(); + assert_eq!(actual, expected, "input: `{a}`"); } - module.cells.insert(cell_type.to_string(), cell); - - let mut netlist = yosys::Netlist::default(); - netlist.modules.insert("test".to_string(), module); - let raw_netlist = netlist.to_string().unwrap(); - - HardwareModule::new_from_str(&raw_netlist, "test").unwrap() } #[rstest] -#[case("$_NOT_", 0, 1)] -#[case("$_NOT_", 1, 0)] -#[case("$_BUF_", 0, 0)] -#[case("$_BUF_", 1, 1)] -fn test_module_1_input_cell(#[case] cell_type: &str, #[case] a: u8, #[case] expected: u8) { - let inputs = HashMap::from([("A", 1)]); - let outputs = HashMap::from([("Y", 1)]); - let mut cell_module = generate_module(cell_type, inputs, outputs, None); - cell_module.set_port_int("A", a).unwrap(); - cell_module.eval(); - - let actual: u8 = cell_module.get_port_int("Y").unwrap(); - assert_eq!(actual, expected); +#[case("$_AND_", [ + (0, 0, 0), + (0, 1, 0), + (1, 0, 0), + (1, 1, 1), +])] +#[case("$_NOR_", [ + (0, 0, 1), + (0, 1, 0), + (1, 0, 0), + (1, 1, 0), +])] +#[case("$_NAND_", [ + (0, 0, 1), + (0, 1, 1), + (1, 0, 1), + (1, 1, 0), +])] +#[case("$_OR_", [ + (0, 0, 0), + (0, 1, 1), + (1, 0, 1), + (1, 1, 1), +])] +#[case("$_XOR_", [ + (0, 0, 0), + (0, 1, 1), + (1, 0, 1), + (1, 1, 0), +])] +#[case("$_XNOR_", [ + (0, 0, 1), + (0, 1, 0), + (1, 0, 0), + (1, 1, 1), +])] +fn test_module_binary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8); 4]) { + let mut module = HardwareModule::new( + &CELL_WRAPPER, + &HashMap::from([(cell.to_string(), vec![cell.to_string()])]), + cell, + ) + .unwrap(); + + for (a, b, expected) in cases { + module.set_port("A", BitVec::from_int(a, None)).unwrap(); + module.set_port("B", BitVec::from_int(b, None)).unwrap(); + module.eval(); + let actual: u8 = module.get_port("Y").unwrap().to_int(); + assert_eq!(actual, expected, "inputs: `{a}`, `{b}`"); + } } #[rstest] -#[case("$_AND_", 0, 0, 0)] -#[case("$_AND_", 0, 1, 0)] -#[case("$_AND_", 1, 0, 0)] -#[case("$_AND_", 1, 1, 1)] -#[case("$_NOR_", 0, 0, 1)] -#[case("$_NOR_", 0, 1, 0)] -#[case("$_NOR_", 1, 0, 0)] -#[case("$_NOR_", 1, 1, 0)] -#[case("$_NAND_", 0, 0, 1)] -#[case("$_NAND_", 0, 1, 1)] -#[case("$_NAND_", 1, 0, 1)] -#[case("$_NAND_", 1, 1, 0)] -#[case("$_OR_", 0, 0, 0)] -#[case("$_OR_", 0, 1, 1)] -#[case("$_OR_", 1, 0, 1)] -#[case("$_OR_", 1, 1, 1)] -#[case("$_XOR_", 0, 0, 0)] -#[case("$_XOR_", 0, 1, 1)] -#[case("$_XOR_", 1, 0, 1)] -#[case("$_XOR_", 1, 1, 0)] -#[case("$_XNOR_", 0, 0, 1)] -#[case("$_XNOR_", 0, 1, 0)] -#[case("$_XNOR_", 1, 0, 0)] -#[case("$_XNOR_", 1, 1, 1)] -fn test_module_2_input_cell( - #[case] cell_type: &str, - #[case] a: u8, - #[case] b: u8, - #[case] expected: u8, +#[tokio::test] +#[case::add_signed("add_wrapper", true, vec![ + (0, 0, 0), + (-45, 20, -25), + (0, 0, 0), + (-1, 1, 0) +])] +#[case::add_unsigned("add_wrapper", false, vec![ + (2, 3, 5), + (0, 0, 0) +])] +async fn test_module_simcell_2_input_signed( + #[case] module_name: &str, + #[case] signed: bool, + #[case] cases: Vec<(i32, i32, i32)>, ) { - let inputs = HashMap::from([("A", 1), ("B", 1)]); - let outputs = HashMap::from([("Y", 1)]); - let mut cell_module = generate_module(cell_type, inputs, outputs, None); - cell_module.set_port_int("A", a).unwrap(); - cell_module.set_port_int("B", b).unwrap(); - cell_module.eval(); + let client = YosysClient { + // TODO: Fix + yosys_server_path: "/home/alexander/research/arbolta/scopefix/target/debug/yosys_server".into(), + ..Default::default() + }; - let actual: u8 = cell_module.get_port_int("Y").unwrap(); - assert_eq!(actual, expected); + let signed_param = if signed { "1" } else { "0" }; + + let synth_config = SynthConfig { + defer: true, + defines: Some(vec!["SIMLIB_NOCHECKS".to_string()]), + parameters: Some(HashMap::from([ + ("A_SIGNED".to_string(), signed_param.to_string()), + ("B_SIGNED".to_string(), signed_param.to_string()), + ("A_WIDTH".to_string(), "8".to_string()), + ("B_WIDTH".to_string(), "8".to_string()), + ("Y_WIDTH".to_string(), "8".to_string()), + ])), + ..Default::default() + }; + + let (netlist, torder) = client + .simple_synth( + &PathBuf::from( + "/home/alexander/research/arbolta/scopefix/crates/arbolta/tests/deps/simlib_wrappers.sv", + ), + Some(module_name.to_string()), + synth_config, + ) + .await + .unwrap(); + + let mut module = HardwareModule::new(&netlist, &torder, module_name).unwrap(); + + for (a, b, expected) in cases { + module.set_port("A", BitVec::from_int(a, None)).unwrap(); + module.set_port("B", BitVec::from_int(b, None)).unwrap(); + module.eval(); + let actual: i32 = module.get_port("Y").unwrap().to_int(); + + assert_eq!(actual, expected, "inputs: `{a}`, `{b}`") + } } +/* #[rstest] #[case("$reduce_or", 0, 0, 0, 0)] #[case("$reduce_or", 0, 0, 1, 1)] @@ -165,40 +178,16 @@ fn test_module_3_input_cell( let params = HashMap::from([("A_SIGNED", "0"), ("A_WIDTH", "11"), ("Y_WIDTH", "1")]); let mut cell_module = generate_module(cell_type, inputs, outputs, Some(params)); cell_module.set_port_shape("A", &[3, 1]).unwrap(); - cell_module.set_port_int_vec("A", &[a, b, c]).unwrap(); + cell_module + .set_port("A", BitVec::from_ints([a, b, c], Some(1))) + .unwrap(); cell_module.eval(); - let actual: u8 = cell_module.get_port_int("Y").unwrap(); + let actual: u8 = cell_module.get_port("Y").unwrap().to_int(); assert_eq!(actual, expected); } -#[rstest] -#[case(false, 2, 3)] -#[case(true, -45, 20)] -#[case(false, 0, 0)] -#[case(true, 0, 0)] -#[case(true, -1, 1)] -fn test_module_add(#[case] signed: bool, #[case] a: i32, #[case] b: i32) { - let signed_param = if signed { "1" } else { "0" }; - let inputs = HashMap::from([("A", 8), ("B", 8)]); - let outputs = HashMap::from([("Y", 8)]); - let params = HashMap::from([ - ("A_SIGNED", signed_param), - ("B_SIGNED", signed_param), - ("A_WIDTH", "1000"), - ("B_WIDTH", "1000"), - ("Y_WIDTH", "1000"), - ]); - - let mut cell_module = generate_module("$add", inputs, outputs, Some(params)); - cell_module.set_port_int("A", a).unwrap(); - cell_module.set_port_int("B", b).unwrap(); - cell_module.eval(); - let actual: i32 = cell_module.get_port_int("Y").unwrap(); - let expected = a + b; - assert_eq!(actual, expected); -} #[rstest] #[case(true, 2, 3)] @@ -219,11 +208,15 @@ fn test_module_sub(#[case] signed: bool, #[case] a: i32, #[case] b: i32) { let outputs = HashMap::from([("Y", 8)]); let mut cell_module = generate_module("$sub", inputs, outputs, Some(params)); - cell_module.set_port_int("A", a).unwrap(); - cell_module.set_port_int("B", b).unwrap(); + cell_module + .set_port("A", BitVec::from_int(a, None)) + .unwrap(); + cell_module + .set_port("B", BitVec::from_int(b, None)) + .unwrap(); cell_module.eval(); - let actual: i32 = cell_module.get_port_int("Y").unwrap(); + let actual: i32 = cell_module.get_port("Y").unwrap().to_int(); let expected = a - b; assert_eq!(actual, expected); } @@ -247,11 +240,15 @@ fn test_module_mul(#[case] signed: bool, #[case] a: i32, #[case] b: i32) { let outputs = HashMap::from([("Y", 16)]); let mut cell_module = generate_module("$mul", inputs, outputs, Some(params)); - cell_module.set_port_int("A", a).unwrap(); - cell_module.set_port_int("B", b).unwrap(); + cell_module + .set_port("A", BitVec::from_int(a, None)) + .unwrap(); + cell_module + .set_port("B", BitVec::from_int(b, None)) + .unwrap(); cell_module.eval(); - let actual: i32 = cell_module.get_port_int("Y").unwrap(); + let actual: i32 = cell_module.get_port("Y").unwrap().to_int(); let expected = a * b; assert_eq!(actual, expected); } @@ -275,11 +272,15 @@ fn test_module_shl(#[case] signed: bool, #[case] a: i16, #[case] b: i16) { let outputs = HashMap::from([("Y", 16)]); let mut cell_module = generate_module("$shl", inputs, outputs, Some(params)); - cell_module.set_port_int("A", a).unwrap(); - cell_module.set_port_int("B", b).unwrap(); + cell_module + .set_port("A", BitVec::from_int(a, None)) + .unwrap(); + cell_module + .set_port("B", BitVec::from_int(b, None)) + .unwrap(); cell_module.eval(); - let actual: i16 = cell_module.get_port_int("Y").unwrap(); + let actual: i16 = cell_module.get_port("Y").unwrap().to_int(); let expected = a << b; assert_eq!(actual, expected); } @@ -300,10 +301,41 @@ fn test_module_neg(#[case] signed: bool, #[case] a: i16) { let outputs = HashMap::from([("Y", 16)]); let mut cell_module = generate_module("$neg", inputs, outputs, Some(params)); - cell_module.set_port_int("A", a).unwrap(); + cell_module + .set_port("A", BitVec::from_int(a, None)) + .unwrap(); cell_module.eval(); - let actual: i16 = cell_module.get_port_int("Y").unwrap(); + let actual: i16 = cell_module.get_port("Y").unwrap().to_int(); let expected = -a; assert_eq!(actual, expected); } + +#[rstest] +#[case(vec![30, -44], "1101010000011110")] +#[case(vec![-19, -42], "1101011011101101")] +#[case(vec![3, -4], "1111110000000011")] +#[case(vec![1, -1], "1111111100000001")] +fn test_module_i8_port_array(#[case] vals: Vec, #[case] expected: BitVec) { + let params = HashMap::from([ + ("A_SIGNED", "1"), + ("A_WIDTH", "10000"), + ("Y_WIDTH", "10000"), + ]); + let inputs = HashMap::from([("A", 16)]); + let outputs = HashMap::from([("Y", 16)]); + // Don't actually eval $neg + let mut cell_module = generate_module("$neg", inputs, outputs, Some(params)); + + cell_module + .set_port_shape("A", &[vals.len(), i8::BITS as usize]) + .unwrap(); + cell_module + .set_port("A", BitVec::from_ints(vals, Some(i8::BITS as usize))) + .unwrap(); + + let actual = cell_module.get_port("A").unwrap(); + + assert_eq!(actual.bits, expected.bits) +} +*/ diff --git a/crates/arbolta/tests/test_netlists/4b_adder_netlist.json b/crates/arbolta/tests/test_netlists/4b_adder_netlist.json deleted file mode 100644 index a10acb5..0000000 --- a/crates/arbolta/tests/test_netlists/4b_adder_netlist.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "modules": { - "adder": { - "ports": { - "op0_i": {"direction": "input", "bits": [ 2, 3, 4, 5 ]}, - "op1_i": {"direction": "input", "bits": [ 6, 7, 8, 9 ]}, - "sum_o": {"direction": "output", "bits": [ 10, 11, 12, 13, 14 ]} - }, - "cells": { - "$202": {"type": "NOT", "connections": {"A": [ 3 ], "Y": [ 15 ]}}, - "$203": {"type": "NOT", "connections": {"A": [ 7 ], "Y": [ 16 ]}}, - "$204": {"type": "NOT", "connections": {"A": [ 2 ], "Y": [ 17 ]}}, - "$205": {"type": "NOT", "connections": {"A": [ 6 ], "Y": [ 18 ]}}, - "$206": {"type": "NOT", "connections": {"A": [ 4 ], "Y": [ 19 ]}}, - "$207": {"type": "NOT", "connections": {"A": [ 8 ], "Y": [ 20 ]}}, - "$208": {"type": "NOT", "connections": {"A": [ 5 ], "Y": [ 21 ]}}, - "$209": {"type": "NOT", "connections": {"A": [ 9 ], "Y": [ 22 ]}}, - "$210": {"type": "NOR", "connections": {"A": [ 17 ], "B": [ 18 ], "Y": [ 23 ]}}, - "$211": {"type": "NAND", "connections": {"A": [ 2 ], "B": [ 6 ], "Y": [ 24 ]}}, - "$212": {"type": "NOR", "connections": {"A": [ 15 ], "B": [ 16 ], "Y": [ 25 ]}}, - "$213": {"type": "NAND", "connections": {"A": [ 3 ], "B": [ 7 ], "Y": [ 26 ]}}, - "$214": {"type": "NOR", "connections": {"A": [ 3 ], "B": [ 7 ], "Y": [ 27 ]}}, - "$215": {"type": "NOT", "connections": {"A": [ 27 ], "Y": [ 28 ]}}, - "$216": {"type": "NOR", "connections": {"A": [ 25 ], "B": [ 27 ], "Y": [ 29 ]}}, - "$217": {"type": "NAND", "connections": {"A": [ 26 ], "B": [ 28 ], "Y": [ 30 ]}}, - "$218": {"type": "NOR", "connections": {"A": [ 24 ], "B": [ 30 ], "Y": [ 31 ]}}, - "$219": {"type": "NAND", "connections": {"A": [ 23 ], "B": [ 29 ], "Y": [ 32 ]}}, - "$220": {"type": "NOR", "connections": {"A": [ 23 ], "B": [ 29 ], "Y": [ 33 ]}}, - "$221": {"type": "NOR", "connections": {"A": [ 31 ], "B": [ 33 ], "Y": [ 11 ]}}, - "$222": {"type": "NOR", "connections": {"A": [ 25 ], "B": [ 31 ], "Y": [ 34 ]}}, - "$223": {"type": "NAND", "connections": {"A": [ 26 ], "B": [ 32 ], "Y": [ 35 ]}}, - "$224": {"type": "NOR", "connections": {"A": [ 19 ], "B": [ 20 ], "Y": [ 36 ]}}, - "$225": {"type": "NAND", "connections": {"A": [ 4 ], "B": [ 8 ], "Y": [ 37 ]}}, - "$226": {"type": "NOR", "connections": {"A": [ 4 ], "B": [ 8 ], "Y": [ 38 ]}}, - "$227": {"type": "NOR", "connections": {"A": [ 36 ], "B": [ 38 ], "Y": [ 39 ]}}, - "$228": {"type": "NOT", "connections": {"A": [ 39 ], "Y": [ 40 ]}}, - "$229": {"type": "NOR", "connections": {"A": [ 34 ], "B": [ 40 ], "Y": [ 41 ]}}, - "$230": {"type": "NAND", "connections": {"A": [ 35 ], "B": [ 39 ], "Y": [ 42 ]}}, - "$231": {"type": "NOR", "connections": {"A": [ 35 ], "B": [ 39 ], "Y": [ 43 ]}}, - "$232": {"type": "NOR", "connections": {"A": [ 41 ], "B": [ 43 ], "Y": [ 12 ]}}, - "$233": {"type": "NOR", "connections": {"A": [ 36 ], "B": [ 41 ], "Y": [ 44 ]}}, - "$234": {"type": "NAND", "connections": {"A": [ 37 ], "B": [ 42 ], "Y": [ 45 ]}}, - "$235": {"type": "NAND", "connections": {"A": [ 5 ], "B": [ 9 ], "Y": [ 46 ]}}, - "$236": {"type": "NOT", "connections": {"A": [ 46 ], "Y": [ 47 ]}}, - "$237": {"type": "NOR", "connections": {"A": [ 5 ], "B": [ 9 ], "Y": [ 48 ]}}, - "$238": {"type": "NAND", "connections": {"A": [ 21 ], "B": [ 22 ], "Y": [ 49 ]}}, - "$239": {"type": "NOR", "connections": {"A": [ 47 ], "B": [ 48 ], "Y": [ 50 ]}}, - "$240": {"type": "NAND", "connections": {"A": [ 46 ], "B": [ 49 ], "Y": [ 51 ]}}, - "$241": {"type": "NAND", "connections": {"A": [ 44 ], "B": [ 50 ], "Y": [ 52 ]}}, - "$242": {"type": "NAND", "connections": {"A": [ 45 ], "B": [ 51 ], "Y": [ 53 ]}}, - "$243": {"type": "NAND", "connections": {"A": [ 52 ], "B": [ 53 ], "Y": [ 13 ]}}, - "$244": {"type": "NOR", "connections": {"A": [ 2 ], "B": [ 6 ], "Y": [ 54 ]}}, - "$245": {"type": "NOR", "connections": {"A": [ 23 ], "B": [ 54 ], "Y": [ 10 ]}}, - "$246": {"type": "NAND", "connections": {"A": [ 45 ], "B": [ 49 ], "Y": [ 55 ]}}, - "$247": {"type": "NAND", "connections": {"A": [ 46 ], "B": [ 55 ], "Y": [ 14 ]}} - }, - "netnames": { - "$180$new_n14": {"bits": [ 56 ]}, - "$180$new_n15": {"bits": [ 57 ]}, - "$180$new_n17": {"bits": [ 58 ]}, - "$180$new_n18": {"bits": [ 59 ]}, - "$180$new_n19": {"bits": [ 60 ]}, - "$180$new_n20": {"bits": [ 61 ]}, - "$180$new_n22": {"bits": [ 62 ]}, - "$180$new_n23": {"bits": [ 63 ]}, - "$180$new_n24": {"bits": [ 64 ]}, - "$180$new_n25": {"bits": [ 65 ]}, - "$180$new_n28": {"bits": [ 66 ]}, - "$180$new_n29": {"bits": [ 67 ]}, - "$180$new_n30": {"bits": [ 68 ]}, - "$180$new_n31": {"bits": [ 69 ]}, - "$180$new_n32": {"bits": [ 70 ]}, - "$201$new_n14": {"bits": [ 15 ]}, - "$201$new_n15": {"bits": [ 16 ]}, - "$201$new_n16": {"bits": [ 17 ]}, - "$201$new_n17": {"bits": [ 18 ]}, - "$201$new_n18": {"bits": [ 19 ]}, - "$201$new_n19": {"bits": [ 20 ]}, - "$201$new_n20": {"bits": [ 21 ]}, - "$201$new_n21": {"bits": [ 22 ]}, - "$201$new_n22": {"bits": [ 23 ]}, - "$201$new_n23": {"bits": [ 24 ]}, - "$201$new_n24": {"bits": [ 25 ]}, - "$201$new_n25": {"bits": [ 26 ]}, - "$201$new_n26": {"bits": [ 27 ]}, - "$201$new_n27": {"bits": [ 28 ]}, - "$201$new_n28": {"bits": [ 29 ]}, - "$201$new_n29": {"bits": [ 30 ]}, - "$201$new_n30": {"bits": [ 31 ]}, - "$201$new_n31": {"bits": [ 32 ]}, - "$201$new_n32": {"bits": [ 33 ]}, - "$201$new_n34": {"bits": [ 34 ]}, - "$201$new_n35": {"bits": [ 35 ]}, - "$201$new_n36": {"bits": [ 36 ]}, - "$201$new_n37": {"bits": [ 37 ]}, - "$201$new_n38": {"bits": [ 38 ]}, - "$201$new_n39": {"bits": [ 39 ]}, - "$201$new_n40": {"bits": [ 40 ]}, - "$201$new_n41": {"bits": [ 41 ]}, - "$201$new_n42": {"bits": [ 42 ]}, - "$201$new_n43": {"bits": [ 43 ]}, - "$201$new_n45": {"bits": [ 44 ]}, - "$201$new_n46": {"bits": [ 45 ]}, - "$201$new_n47": {"bits": [ 46 ]}, - "$201$new_n48": {"bits": [ 47 ]}, - "$201$new_n49": {"bits": [ 48 ]}, - "$201$new_n50": {"bits": [ 49 ]}, - "$201$new_n51": {"bits": [ 50 ]}, - "$201$new_n52": {"bits": [ 51 ]}, - "$201$new_n53": {"bits": [ 52 ]}, - "$201$new_n54": {"bits": [ 53 ]}, - "$201$new_n56": {"bits": [ 54 ]}, - "$201$new_n58": {"bits": [ 55 ]}, - "$201$op0_i[0]": {"bits": [ 2 ]}, - "$201$op0_i[1]": {"bits": [ 3 ]}, - "$201$op0_i[2]": {"bits": [ 4 ]}, - "$201$op0_i[3]": {"bits": [ 5 ]}, - "$201$op1_i[0]": {"bits": [ 6 ]}, - "$201$op1_i[1]": {"bits": [ 7 ]}, - "$201$op1_i[2]": {"bits": [ 8 ]}, - "$201$op1_i[3]": {"bits": [ 9 ]}, - "$201$sum_o[0]": {"bits": [ 10 ]}, - "$201$sum_o[1]": {"bits": [ 11 ]}, - "$201$sum_o[2]": {"bits": [ 12 ]}, - "$201$sum_o[3]": {"bits": [ 13 ]}, - "$201$sum_o[4]": {"bits": [ 14 ]}, - "op0_i": {"bits": [ 2, 3, 4, 5 ]}, - "op1_i": {"bits": [ 6, 7, 8, 9 ]}, - "sum_o": {"bits": [ 10, 11, 12, 13, 14 ]} - } - } - } -} diff --git a/crates/arbolta/tests/test_netlists/4b_nested_adder_netlist.json b/crates/arbolta/tests/test_netlists/4b_nested_adder_netlist.json deleted file mode 100644 index 16f3527..0000000 --- a/crates/arbolta/tests/test_netlists/4b_nested_adder_netlist.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "modules": { - "adder": { - "ports": { - "op0_i": {"direction": "input", "bits": [ 2, 3, 4, 5 ]}, - "op1_i": {"direction": "input", "bits": [ 6, 7, 8, 9 ]}, - "sum_o": {"direction": "output","bits": [ 10, 11, 12, 13, 14 ]} - }, - "cells": { - "fa0": { - "type": "full_adder", - "port_directions": {"carry_i": "input", "carry_o": "output", "op0_i": "input", "op1_i": "input", "sum_o": "output"}, - "connections": {"carry_i": [ "0" ], "carry_o": [ 15 ], "op0_i": [ 2 ], "op1_i": [ 6 ], "sum_o": [ 10 ]} - }, - "fa1": { - "type": "full_adder", - "port_directions": {"carry_i": "input", "carry_o": "output", "op0_i": "input", "op1_i": "input", "sum_o": "output"}, - "connections": {"carry_i": [ 15 ], "carry_o": [ 16 ], "op0_i": [ 3 ], "op1_i": [ 7 ], "sum_o": [ 11 ]} - }, - "fa2": { - "type": "full_adder", - "port_directions": {"carry_i": "input", "carry_o": "output", "op0_i": "input", "op1_i": "input", "sum_o": "output"}, - "connections": {"carry_i": [ 16 ], "carry_o": [ 17 ], "op0_i": [ 4 ], "op1_i": [ 8 ], "sum_o": [ 12 ]} - }, - "fa3": { - "type": "full_adder", - "port_directions": {"carry_i": "input", "carry_o": "output", "op0_i": "input", "op1_i": "input", "sum_o": "output"}, - "connections": {"carry_i": [ 17 ], "carry_o": [ 14 ], "op0_i": [ 5 ], "op1_i": [ 9 ], "sum_o": [ 13 ]} - } - }, - "netnames": { - "fa0_carry": {"bits": [ 15 ]}, - "fa1_carry": {"bits": [ 16 ]}, - "fa2_carry": {"bits": [ 17 ]}, - "op0_i": {"bits": [ 2, 3, 4, 5 ]}, - "op1_i": {"bits": [ 6, 7, 8, 9 ]}, - "sum_o": {"bits": [ 10, 11, 12, 13, 14 ]} - } - }, - "full_adder": { - "ports": { - "carry_i": {"direction": "input", "bits": [ 2 ]}, - "op0_i": {"direction": "input", "bits": [ 3 ]}, - "op1_i": {"direction": "input", "bits": [ 4 ]}, - "sum_o": {"direction": "output", "bits": [ 5 ]}, - "carry_o": {"direction": "output", "bits": [ 6 ]} - }, - "cells": { - "$154": {"type": "NOT", "connections": {"A": [ 4 ], "Y": [ 7 ]}}, - "$155": {"type": "NOR", "connections": {"A": [ 2 ], "B": [ 3 ], "Y": [ 8 ]}}, - "$156": {"type": "NOT", "connections": {"A": [ 8 ], "Y": [ 9 ]}}, - "$157": {"type": "NAND", "connections": {"A": [ 2 ], "B": [ 3 ], "Y": [ 10 ]}}, - "$158": {"type": "NOT", "connections": {"A": [ 10 ], "Y": [ 11 ]}}, - "$159": {"type": "NOR", "connections": {"A": [ 8 ], "B": [ 11 ], "Y": [ 12 ]}}, - "$160": {"type": "NAND", "connections": {"A": [ 9 ], "B": [ 10 ], "Y": [ 13 ]}}, - "$161": {"type": "NOR", "connections": {"A": [ 7 ], "B": [ 13 ], "Y": [ 14 ]}}, - "$162": {"type": "NAND", "connections": {"A": [ 4 ], "B": [ 12 ], "Y": [ 15 ]}}, - "$163": {"type": "NOR", "connections": {"A": [ 4 ], "B": [ 12 ], "Y": [ 16 ]}}, - "$164": {"type": "NOR", "connections": {"A": [ 14 ], "B": [ 16 ], "Y": [ 5 ]}}, - "$165": {"type": "NAND", "connections": {"A": [ 10 ], "B": [ 15 ], "Y": [ 6 ]}} - }, - "netnames": { - "$146$new_n6": {"bits": [ 17 ]}, - "$146$new_n8": {"bits": [ 18 ]}, - "$146$new_n9": {"bits": [ 19 ]}, - "$153$carry_i": {"bits": [ 2 ]}, - "$153$carry_o": {"bits": [ 6 ]}, - "$153$new_n10": {"bits": [ 11 ]}, - "$153$new_n11": {"bits": [ 12 ]}, - "$153$new_n12": {"bits": [ 13 ]}, - "$153$new_n13": {"bits": [ 14 ]}, - "$153$new_n14": {"bits": [ 15 ]}, - "$153$new_n15": {"bits": [ 16 ]}, - "$153$new_n6": {"bits": [ 7 ]}, - "$153$new_n7": {"bits": [ 8 ]}, - "$153$new_n8": {"bits": [ 9 ]}, - "$153$new_n9": {"bits": [ 10 ]}, - "$153$op0_i": {"bits": [ 3 ]}, - "$153$op1_i": {"bits": [ 4 ]}, - "$153$sum_o": {"bits": [ 5 ]}, - "carry_i": {"bits": [ 2 ]}, - "carry_o": {"bits": [ 6 ]}, - "op0_i": {"bits": [ 3 ]}, - "op1_i": {"bits": [ 4 ]}, - "sum_o": {"bits": [ 5 ]} - } - } - } - } diff --git a/crates/arbolta/tests/test_signal.rs b/crates/arbolta/tests/test_signal.rs index 0015586..5ac84fd 100644 --- a/crates/arbolta/tests/test_signal.rs +++ b/crates/arbolta/tests/test_signal.rs @@ -2,89 +2,85 @@ // SPDX-License-Identifier: MIT use arbolta::bit::Bit; -use arbolta::signal::Signal; +use arbolta::signal::Signals; #[test] fn test_signal_net_init() { - let x = Signal::default(); + let x = Signals::new(1); - assert_eq!(x.get_value(), Bit::ZERO); - assert_eq!(x.get_toggle_count_falling(), 0); - assert_eq!(x.get_toggle_count_rising(), 0); - assert_eq!(x.get_total_toggle_count(), 0); + assert_eq!(x.get_net(0), Bit::ZERO); + assert_eq!(x.get_toggles_falling(0), 0); + assert_eq!(x.get_toggles_rising(0), 0); + assert_eq!(x.get_total_toggles(0), 0); } #[test] fn test_signal_net_set_value() { - let mut x = Signal::default(); + let mut x = Signals::new(1); - assert_eq!(x.get_value(), Bit::ZERO); - x.set_value(Bit::ONE); - assert_eq!(x.get_value(), Bit::ONE); + assert_eq!(x.get_net(0), Bit::ZERO); + x.set_net(0, Bit::ONE); + assert_eq!(x.get_net(0), Bit::ONE); } #[test] fn test_signal_net_toggle_rising() { - let mut x = Signal::default(); + let mut x = Signals::new(1); - assert_eq!(x.get_total_toggle_count(), 0); - assert_eq!(x.get_toggle_count_falling(), 0); - assert_eq!(x.get_toggle_count_rising(), 0); + assert_eq!(x.get_total_toggles(0), 0); + assert_eq!(x.get_toggles_falling(0), 0); + assert_eq!(x.get_toggles_rising(0), 0); - x.set_value(Bit::ONE); + x.set_net(0, Bit::ONE); - assert_eq!(x.get_total_toggle_count(), 1); - assert_eq!(x.get_toggle_count_falling(), 0); - assert_eq!(x.get_toggle_count_rising(), 1); + assert_eq!(x.get_total_toggles(0), 1); + assert_eq!(x.get_toggles_falling(0), 0); + assert_eq!(x.get_toggles_rising(0), 1); } #[test] fn test_signal_net_toggle_falling() { - let mut x = Signal { - value: Bit::ONE, - ..Default::default() - }; + let mut x = Signals::new(1); + x.nets[0] = Bit::ONE; - assert_eq!(x.get_total_toggle_count(), 0); - assert_eq!(x.get_toggle_count_falling(), 0); - assert_eq!(x.get_toggle_count_rising(), 0); + assert_eq!(x.get_total_toggles(0), 0); + assert_eq!(x.get_toggles_falling(0), 0); + assert_eq!(x.get_toggles_rising(0), 0); - x.set_value(Bit::ZERO); + x.set_net(0, Bit::ZERO); - assert_eq!(x.get_total_toggle_count(), 1); - assert_eq!(x.get_toggle_count_falling(), 1); - assert_eq!(x.get_toggle_count_rising(), 0); + assert_eq!(x.get_total_toggles(0), 1); + assert_eq!(x.get_toggles_falling(0), 1); + assert_eq!(x.get_toggles_rising(0), 0); } #[test] fn test_signal_net_toggle_same_zero() { - let mut x = Signal::default(); + let mut x = Signals::new(1); - assert_eq!(x.get_total_toggle_count(), 0); - assert_eq!(x.get_toggle_count_falling(), 0); - assert_eq!(x.get_toggle_count_rising(), 0); + assert_eq!(x.get_total_toggles(0), 0); + assert_eq!(x.get_toggles_falling(0), 0); + assert_eq!(x.get_toggles_rising(0), 0); - x.set_value(Bit::ZERO); + x.set_net(0, Bit::ZERO); - assert_eq!(x.get_total_toggle_count(), 0); - assert_eq!(x.get_toggle_count_falling(), 0); - assert_eq!(x.get_toggle_count_rising(), 0); + assert_eq!(x.get_total_toggles(0), 0); + assert_eq!(x.get_toggles_falling(0), 0); + assert_eq!(x.get_toggles_rising(0), 0); } #[test] fn test_signal_net_toggle_same_one() { - let mut x = Signal { - value: Bit::ONE, - ..Default::default() - }; + let mut x = Signals::new(1); + x.nets[0] = Bit::ONE; - assert_eq!(x.get_total_toggle_count(), 0); - assert_eq!(x.get_toggle_count_falling(), 0); - assert_eq!(x.get_toggle_count_rising(), 0); + assert_eq!(x.get_total_toggles(0), 0); + assert_eq!(x.get_toggles_falling(0), 0); + assert_eq!(x.get_toggles_rising(0), 0); - x.set_value(Bit::ONE); + x.set_net(0, Bit::ONE); - assert_eq!(x.get_total_toggle_count(), 0); - assert_eq!(x.get_toggle_count_falling(), 0); - assert_eq!(x.get_toggle_count_rising(), 0); + assert_eq!(x.get_total_toggles(0), 0); + assert_eq!(x.get_toggles_falling(0), 0); + assert_eq!(x.get_toggles_rising(0), 0); } diff --git a/crates/arbolta/tests/test_simcell.rs b/crates/arbolta/tests/test_simcell.rs new file mode 100644 index 0000000..ec0f31c --- /dev/null +++ b/crates/arbolta/tests/test_simcell.rs @@ -0,0 +1,201 @@ +// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// SPDX-License-Identifier: MIT + +use arbolta::bit::Bit; +use arbolta::cell::*; +use arbolta::signal::Signals; +use rstest::rstest; + +#[rstest] +#[case::buffer(Box::new(Buffer::new(0,1)), [ // (A, Y) + (Bit::ZERO, Bit::ZERO), + (Bit::ONE, Bit::ONE), +])] +#[case::inverter(Box::new(Inverter::new(0,1)), [ // (A, Y) + (Bit::ZERO, Bit::ONE), + (Bit::ONE, Bit::ZERO), +])] +fn test_cell_unary(#[case] mut cell: Box, #[case] cases: [(Bit, Bit); 2]) { + let mut signals = Signals::new(2); + for (a, expected) in cases { + signals.set_net(0, a); + cell.eval(&mut signals); + assert_eq!(signals.get_net(1), expected, "input `{a}`") + } +} + +#[rstest] +#[case::and(Box::new(And::new(0, 1, 2)), [ // (A, B, Y) + (Bit::ZERO, Bit::ZERO, Bit::ZERO), + (Bit::ZERO, Bit::ONE, Bit::ZERO), + (Bit::ONE, Bit::ZERO, Bit::ZERO), + (Bit::ONE, Bit::ONE, Bit::ONE), +])] +#[case::nand(Box::new(Nand::new(0, 1, 2)), [ // (A, B, Y) + (Bit::ZERO, Bit::ZERO, Bit::ONE), + (Bit::ZERO, Bit::ONE, Bit::ONE), + (Bit::ONE, Bit::ZERO, Bit::ONE), + (Bit::ONE, Bit::ONE, Bit::ZERO), +])] +#[case::or(Box::new(Or::new(0, 1, 2)), [ // (A, B, Y) + (Bit::ZERO, Bit::ZERO, Bit::ZERO), + (Bit::ZERO, Bit::ONE, Bit::ONE), + (Bit::ONE, Bit::ZERO, Bit::ONE), + (Bit::ONE, Bit::ONE, Bit::ONE), +])] +#[case::nor(Box::new(Nor::new(0, 1, 2)), [ // (A, B, Y) + (Bit::ZERO, Bit::ZERO, Bit::ONE), + (Bit::ZERO, Bit::ONE, Bit::ZERO), + (Bit::ONE, Bit::ZERO, Bit::ZERO), + (Bit::ONE, Bit::ONE, Bit::ZERO), +])] +#[case::xor(Box::new(Xor::new(0, 1, 2)), [ // (A, B, Y) + (Bit::ZERO, Bit::ZERO, Bit::ZERO), + (Bit::ZERO, Bit::ONE, Bit::ONE), + (Bit::ONE, Bit::ZERO, Bit::ONE), + (Bit::ONE, Bit::ONE, Bit::ZERO), +])] +#[case::xnor(Box::new(Xnor::new(0, 1, 2)), [ // (A, B, Y) + (Bit::ZERO, Bit::ZERO, Bit::ONE), + (Bit::ZERO, Bit::ONE, Bit::ZERO), + (Bit::ONE, Bit::ZERO, Bit::ZERO), + (Bit::ONE, Bit::ONE, Bit::ONE), +])] +#[case::andnot(Box::new(AndNot::new(0, 1, 2)), [ // (A, B, Y) + (Bit::ZERO, Bit::ZERO, Bit::ZERO), + (Bit::ZERO, Bit::ONE, Bit::ZERO), + (Bit::ONE, Bit::ZERO, Bit::ONE), + (Bit::ONE, Bit::ONE, Bit::ZERO), +])] +#[case::ornot(Box::new(OrNot::new(0, 1, 2)), [ // (A, B, Y) + (Bit::ZERO, Bit::ZERO, Bit::ONE), + (Bit::ZERO, Bit::ONE, Bit::ZERO), + (Bit::ONE, Bit::ZERO, Bit::ONE), + (Bit::ONE, Bit::ONE, Bit::ONE), +])] +fn test_cell_binary(#[case] mut cell: Box, #[case] cases: [(Bit, Bit, Bit); 4]) { + let mut signals = Signals::new(3); + for (a, b, expected) in cases { + signals.set_net(0, a); + signals.set_net(1, b); + + cell.eval(&mut signals); + assert_eq!(signals.get_net(2), expected, "inputs `{a}`, `{b}`") + } +} + +#[rstest] +#[case::mux2(Box::new(Mux2::new(0, 1, 2, 3)), [ // (A, B, S, Y) + (Bit::ZERO, Bit::ZERO, Bit::ZERO, Bit::ZERO), + (Bit::ZERO, Bit::ZERO, Bit::ONE, Bit::ZERO), + (Bit::ZERO, Bit::ONE, Bit::ZERO, Bit::ZERO), + (Bit::ZERO, Bit::ONE, Bit::ONE, Bit::ONE), + (Bit::ONE, Bit::ZERO, Bit::ZERO, Bit::ONE), + (Bit::ONE, Bit::ZERO, Bit::ONE, Bit::ZERO), + (Bit::ONE, Bit::ONE, Bit::ZERO, Bit::ONE), + (Bit::ONE, Bit::ONE, Bit::ONE, Bit::ONE), +])] +#[case::nmux2(Box::new(NMux2::new(0, 1, 2, 3)), [ // (A, B, S, Y) + (Bit::ZERO, Bit::ZERO, Bit::ZERO, Bit::ONE), + (Bit::ZERO, Bit::ZERO, Bit::ONE, Bit::ONE), + (Bit::ZERO, Bit::ONE, Bit::ZERO, Bit::ONE), + (Bit::ZERO, Bit::ONE, Bit::ONE, Bit::ZERO), + (Bit::ONE, Bit::ZERO, Bit::ZERO, Bit::ZERO), + (Bit::ONE, Bit::ZERO, Bit::ONE, Bit::ONE), + (Bit::ONE, Bit::ONE, Bit::ZERO, Bit::ZERO), + (Bit::ONE, Bit::ONE, Bit::ONE, Bit::ZERO), +])] +#[case::andorinvert(Box::new(AndOrInvert::new(0, 1, 2, 3)), [ // (A, B, C, Y) + (Bit::ZERO, Bit::ZERO, Bit::ZERO, Bit::ONE), + (Bit::ZERO, Bit::ZERO, Bit::ONE, Bit::ZERO), + (Bit::ZERO, Bit::ONE, Bit::ZERO, Bit::ONE), + (Bit::ZERO, Bit::ONE, Bit::ONE, Bit::ZERO), + (Bit::ONE, Bit::ZERO, Bit::ZERO, Bit::ONE), + (Bit::ONE, Bit::ZERO, Bit::ONE, Bit::ZERO), + (Bit::ONE, Bit::ONE, Bit::ZERO, Bit::ZERO), + (Bit::ONE, Bit::ONE, Bit::ONE, Bit::ZERO), +])] +#[case::orandinvert(Box::new(OrAndInvert::new(0, 1, 2, 3)), [ // (A, B, C, Y) + (Bit::ZERO, Bit::ZERO, Bit::ZERO, Bit::ONE), + (Bit::ZERO, Bit::ZERO, Bit::ONE, Bit::ONE), + (Bit::ZERO, Bit::ONE, Bit::ZERO, Bit::ONE), + (Bit::ZERO, Bit::ONE, Bit::ONE, Bit::ZERO), + (Bit::ONE, Bit::ZERO, Bit::ZERO, Bit::ONE), + (Bit::ONE, Bit::ZERO, Bit::ONE, Bit::ZERO), + (Bit::ONE, Bit::ONE, Bit::ZERO, Bit::ONE), + (Bit::ONE, Bit::ONE, Bit::ONE, Bit::ZERO), +])] +fn test_cell_ternary(#[case] mut cell: Box, #[case] cases: [(Bit, Bit, Bit, Bit); 8]) { + let mut signals = Signals::new(4); + for (a, b, c, expected) in cases { + signals.set_net(0, a); + signals.set_net(1, b); + signals.set_net(2, c); + cell.eval(&mut signals); + assert_eq!(signals.get_net(3), expected, "inputs `{a}`, `{b}`, `{c}`") + } +} + +#[rstest] +fn test_cell_dff_posedge() { + // D, C, Q + let (data_in, clock, data_out) = (0, 1, 2); + let mut cell = Dff::new(Bit::ONE, clock, data_in, data_out); + let mut signals = Signals::new(3); + + cell.eval(&mut signals); + assert_eq!(signals.get_net(data_out), Bit::ZERO); + + signals.set_net(data_in, Bit::ONE); + cell.eval(&mut signals); + assert_eq!(signals.get_net(data_out), Bit::ZERO); + + signals.set_net(clock, Bit::ONE); // Rising edge + cell.eval(&mut signals); + assert_eq!(signals.get_net(data_out), Bit::ONE); + + signals.set_net(clock, Bit::ZERO); // Falling edge + cell.eval(&mut signals); + assert_eq!(signals.get_net(data_out), Bit::ONE); + + signals.set_net(data_in, Bit::ZERO); + cell.eval(&mut signals); + assert_eq!(signals.get_net(data_out), Bit::ONE); + + signals.set_net(clock, Bit::ONE); // Rising edge + cell.eval(&mut signals); + assert_eq!(signals.get_net(data_out), Bit::ZERO); +} + +/* +#[rstest] +fn test_cell_sdff_pp() { + // D, C, R, Q + let (data_in, clock, reset, data_out) = (0, 1, 2, 3); + let mut cell = DffReset::new(Bit::ONE, data_in, clock, reset, data_out); + let mut signals = vec![Signal::default(); 4].into_boxed_slice(); + + cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::ZERO); + + signals[data_in].set_value(Bit::ONE); + cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::ZERO); + + signals[clock].set_value(Bit::ONE); // Rising edge + cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::ONE); + + signals[clock].set_value(Bit::ZERO); // Falling edge + cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::ONE); + + signals[reset].set_value(Bit::ONE); + cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::ONE); + + signals[clock].set_value(Bit::ONE); // Rising edge + cell.eval(&mut signals); + assert_eq!(signals[data_out].get_value(), Bit::ZERO); +} +*/ diff --git a/crates/arbolta/tests/test_simlib.rs b/crates/arbolta/tests/test_simlib.rs new file mode 100644 index 0000000..c54f4e6 --- /dev/null +++ b/crates/arbolta/tests/test_simlib.rs @@ -0,0 +1,77 @@ +// use arbolta::bit::{Bit, BitVec}; +// use arbolta::cell::*; +// use arbolta::signal::Signals; +// use rstest::rstest; +// use std::str::FromStr; + +// #[rstest] +// #[case::add_unsigned(|a, b, c| Box::new(Add::new(false, a, b, c)) as Box, vec![ +// ("00000111", "00000111", "00001110"), // 7 + 7 = 49 +// ("00000111", "111", "01110"), // 7 + 7 = 49 +// ("111", "111", "1110"), // 7 + 7 = 14 +// ("111", "111", "10"), // 7 + 7 = 4 (overflow) +// ])] +// #[case::add_signed(|a, b, c| Box::new(Add::new(true, a, b, c)) as Box, vec![ +// ("00000111", "00000111", "00001110"), // 7 + 7 = 49 +// ("00000111", "1001", "00000"), // 7 + -7 = 0 +// ("1001", "11001", "11110010"), // -7 + -7 = -14 +// ("1001", "1001", "10"), // -7 + -7 = -4 (overflow) +// ("111", "111", "10"), // 7 + 7 = 4 (overflow) +// ("1", "0", "111111111111"), // sign extend +// ("0", "1", "111111111111"), // sign extend +// ("1", "1", "111111111110"), // -1 + -1 = -2 +// ("01", "1", "000000000000"), // 1 + -1 = 0 +// ("11111111111111111111111111111111111111111111111111111111111111100010011111010100010011110001101110011011000110100110111110001011", +// "00000000000000000000000000000011000001000110100000010001100111010011111100101100011100101011100000110101000110100011101011011110", +// "00000000000000000000000000000011000001000110100000010001100110110110011100000000110000011101001111010000001101001010101001101001") +// ])] +// #[case::and_unsigned(|a, b, c| Box::new(And::new(false, a, b, c)) as Box, vec![ +// ("00000111", "00000111", "00001110"), // 7 + 7 = 49 +// ("00000111", "111", "01110"), // 7 + 7 = 49 +// ("111", "111", "1110"), // 7 + 7 = 14 +// ("111", "111", "10"), // 7 + 7 = 4 (overflow) +// ])] +// fn test_cell_input2( +// #[case] cell_new: impl Fn(Box<[usize]>, Box<[usize]>, Box<[usize]>) -> Box, +// #[case] cases: Vec<(&str, &str, &str)>, +// ) { +// for (a, b, expected) in cases { +// // Rstest won't automatically convert +// let (a, b, expected) = ( +// BitVec::from_str(a).unwrap(), +// BitVec::from_str(b).unwrap(), +// BitVec::from_str(expected).unwrap(), +// ); + +// let (a_nets, b_nets, y_nets) = ( +// (0..a.shape[1]).collect::>(), +// (a.shape[1]..a.shape[1] + b.shape[1]).collect::>(), +// (a.shape[1] + b.shape[1]..a.shape[1] + b.shape[1] + expected.shape[1]) +// .collect::>(), +// ); + +// let mut signals = Signals::new(a_nets.len() + b_nets.len() + y_nets.len()); + +// // Set inputs +// a_nets +// .iter() +// .enumerate() +// .for_each(|(i, n)| signals.set_net(*n, a.bits[i])); + +// b_nets +// .iter() +// .enumerate() +// .for_each(|(i, n)| signals.set_net(*n, b.bits[i])); + +// let mut cell = cell_new(a_nets.into(), b_nets.into(), y_nets.clone().into()); +// cell.eval(&mut signals); + +// // Get outputs +// let actual: BitVec = y_nets +// .iter() +// .map(|i| signals.get_net(*i)) +// .collect::>() +// .into(); +// assert_eq!(actual, expected, "inputs `{a}`, `{b}`"); +// } +// } diff --git a/crates/fault/src/lib.rs b/crates/fault/src/lib.rs index b5614dd..62a69e3 100644 --- a/crates/fault/src/lib.rs +++ b/crates/fault/src/lib.rs @@ -1 +1 @@ -pub mod utils; +// pub mod utils; diff --git a/crates/fault/src/main.rs b/crates/fault/src/main.rs index 81d08d5..e977cae 100644 --- a/crates/fault/src/main.rs +++ b/crates/fault/src/main.rs @@ -1,85 +1,85 @@ -use anyhow::{Context, Ok, Result}; -use arbolta::bit::Bit; -use arbolta::hardware_module::HardwareModule; -use clap::Parser; -use fault::utils::worker; -use indicatif::ParallelProgressIterator; -use ndarray::parallel::prelude::*; -use ndarray::{Array2, Array3}; -use ndarray_npy::read_npy; -use std::path::PathBuf; -use std::sync::Mutex; +use anyhow::Result; +// use arbolta::bit::Bit; +// use arbolta::hardware_module::HardwareModule; +// use clap::Parser; +// use fault::utils::worker; +// use indicatif::ParallelProgressIterator; +// use ndarray::parallel::prelude::*; +// use ndarray::{Array2, Array3}; +// use ndarray_npy::read_npy; +// use std::path::PathBuf; +// use std::sync::Mutex; -#[derive(Parser, Debug)] -struct Args { - /// Name of top module in netlist. - #[clap(long)] - top_module: String, - /// Path to Yosys netlist JSON file. - #[clap(long)] - netlist: String, - /// Path to NumPy inputs file. - #[clap(long)] - inputs: PathBuf, - /// Path to NumPy weights file. - #[clap(long)] - weights: PathBuf, - /// Path to NumPy targets file. - // #[clap(long)] - // targets: PathBuf, - /// Path to list of nets for fault injection campaign. - #[clap(long)] - nets: PathBuf, - #[clap(long)] - rows: usize, - #[clap(long)] - cols: usize, - #[clap(long)] - iterations: usize, - #[clap(long)] - sx_size: usize, - #[clap(long)] - sk_size: usize, - #[clap(long)] - m_size: usize, -} +// #[derive(Parser, Debug)] +// struct Args { +// /// Name of top module in netlist. +// #[clap(long)] +// top_module: String, +// /// Path to Yosys netlist JSON file. +// #[clap(long)] +// netlist: String, +// /// Path to NumPy inputs file. +// #[clap(long)] +// inputs: PathBuf, +// /// Path to NumPy weights file. +// #[clap(long)] +// weights: PathBuf, +// /// Path to NumPy targets file. +// // #[clap(long)] +// // targets: PathBuf, +// /// Path to list of nets for fault injection campaign. +// #[clap(long)] +// nets: PathBuf, +// #[clap(long)] +// rows: usize, +// #[clap(long)] +// cols: usize, +// #[clap(long)] +// iterations: usize, +// #[clap(long)] +// sx_size: usize, +// #[clap(long)] +// sk_size: usize, +// #[clap(long)] +// m_size: usize, +// } fn main() -> Result<()> { - let flags = Args::parse(); + // let flags = Args::parse(); - // Load everything - let mut design = HardwareModule::new_from_path(&flags.netlist, &flags.top_module)?; - let inputs: Array3 = read_npy(&flags.inputs) - .with_context(|| format!("Failed to read NumPy array from {:?}", flags.inputs))?; // (N, 28, 28) - let weights: Array2 = read_npy(&flags.weights) - .with_context(|| format!("Failed to read NumPy array from {:?}", flags.weights))?; // (10, 784) - // let targets: Array2 = read_npy(&flags.targets) - // .with_context(|| format!("Failed to read NumPy array from {:?}", flags.targets))?; + // // Load everything + // let mut design = HardwareModule::new_from_path(&flags.netlist, &flags.top_module)?; + // let inputs: Array3 = read_npy(&flags.inputs) + // .with_context(|| format!("Failed to read NumPy array from {:?}", flags.inputs))?; // (N, 28, 28) + // let weights: Array2 = read_npy(&flags.weights) + // .with_context(|| format!("Failed to read NumPy array from {:?}", flags.weights))?; // (10, 784) + // // let targets: Array2 = read_npy(&flags.targets) + // // .with_context(|| format!("Failed to read NumPy array from {:?}", flags.targets))?; - let nets: Vec = std::fs::read_to_string(&flags.nets)? - .split_whitespace() - .map(|x| x.parse().unwrap()) - .collect(); + // let nets: Vec = std::fs::read_to_string(&flags.nets)? + // .split_whitespace() + // .map(|x| x.parse().unwrap()) + // .collect(); - // Setup designs - design.set_clock(2, Bit::ONE)?; - design.set_reset(3, Bit::ZERO)?; - design.set_port_shape("sx_data_i", &[flags.rows, flags.sx_size])?; - design.set_port_shape("sk_data_i", &[flags.cols, flags.sk_size])?; - design.set_port_shape("m_data_o", &[1, flags.m_size])?; + // // Setup designs + // design.set_clock(2, Bit::ONE)?; + // design.set_reset(3, Bit::ZERO)?; + // design.set_port_shape("sx_data_i", &[flags.rows, flags.sx_size])?; + // design.set_port_shape("sk_data_i", &[flags.cols, flags.sk_size])?; + // design.set_port_shape("m_data_o", &[1, flags.m_size])?; - let designs: Vec> = (0..rayon::current_num_threads()) - .map(|_| Mutex::new(design.clone())) - .collect(); + // let designs: Vec> = (0..rayon::current_num_threads()) + // .map(|_| Mutex::new(design.clone())) + // .collect(); - // Start simulation - nets.into_par_iter().progress().for_each(|n| { - for stuck_val in [Bit::ZERO, Bit::ONE] { - let thread_idx = rayon::current_thread_index().unwrap(); - let design = &mut designs[thread_idx].lock().unwrap(); - worker(design, &inputs.view(), &weights.view(), n, stuck_val).unwrap(); - } - }); + // // Start simulation + // nets.into_par_iter().progress().for_each(|n| { + // for stuck_val in [Bit::ZERO, Bit::ONE] { + // let thread_idx = rayon::current_thread_index().unwrap(); + // let design = &mut designs[thread_idx].lock().unwrap(); + // worker(design, &inputs.view(), &weights.view(), n, stuck_val).unwrap(); + // } + // }); Ok(()) } diff --git a/crates/fault/src/utils.rs b/crates/fault/src/utils.rs index 8c5bfb0..be99355 100644 --- a/crates/fault/src/utils.rs +++ b/crates/fault/src/utils.rs @@ -1,88 +1,88 @@ -use anyhow::{Ok, Result}; -use arbolta::{bit::Bit, hardware_module::HardwareModule}; -use ndarray::{Array1, Array2, ArrayView2, ArrayView3, Axis}; -use ndarray_npy::write_npy; -use ndarray_stats::QuantileExt; -use num_traits::PrimInt; -use std::fmt::Debug; +// use anyhow::{Ok, Result}; +// use arbolta::{bit::Bit, hardware_module::HardwareModule}; +// use ndarray::{Array1, Array2, ArrayView2, ArrayView3, Axis}; +// use ndarray_npy::write_npy; +// use ndarray_stats::QuantileExt; +// use num_traits::PrimInt; +// use std::fmt::Debug; -pub fn worker( - design: &mut HardwareModule, - inputs: &ArrayView3, - weights: &ArrayView2, - net: usize, - stuck_val: Bit, -) -> Result<()> { - design.reset(); - design.stick_signal(net, stuck_val)?; +// pub fn worker( +// design: &mut HardwareModule, +// inputs: &ArrayView3, +// weights: &ArrayView2, +// net: usize, +// stuck_val: Bit, +// ) -> Result<()> { +// design.reset(); +// design.stick_signal(net, stuck_val)?; - let preds: Array1 = inputs - .axis_iter(Axis(0)) - .map(|image| { - // TODO: Fix hardcoded - let x = image.to_shape((1, 28 * 28)).unwrap(); - let mut logits = Array2::::zeros((10, 1)); +// let preds: Array1 = inputs +// .axis_iter(Axis(0)) +// .map(|image| { +// // TODO: Fix hardcoded +// let x = image.to_shape((1, 28 * 28)).unwrap(); +// let mut logits = Array2::::zeros((10, 1)); - run_sa(design, &x.t().view(), &weights.t().view(), &mut logits).unwrap(); +// run_sa(design, &x.t().view(), &weights.t().view(), &mut logits).unwrap(); - logits.flatten().argmax().unwrap() as u8 - }) - .collect(); +// logits.flatten().argmax().unwrap() as u8 +// }) +// .collect(); - design.unstick_signal(net)?; +// design.unstick_signal(net)?; - write_npy(format!("net_{net}_val_{stuck_val}.npy",), &preds)?; +// write_npy(format!("net_{net}_val_{stuck_val}.npy",), &preds)?; - Ok(()) -} +// Ok(()) +// } -/// Run inputs through systolic array. -/// Expects x: (K,R), k: (K,C) -> y: (C,R) -pub fn run_sa< - A: PrimInt + std::ops::BitXorAssign, - B: PrimInt + std::ops::BitXorAssign, - C: PrimInt + std::ops::BitXorAssign + Debug, ->( - design: &mut HardwareModule, - x: &ArrayView2, - k: &ArrayView2, - y: &mut Array2, -) -> Result<()> { - let iterations: usize = x.shape()[0]; // K +// /// Run inputs through systolic array. +// /// Expects x: (K,R), k: (K,C) -> y: (C,R) +// pub fn run_sa< +// A: PrimInt + std::ops::BitXorAssign, +// B: PrimInt + std::ops::BitXorAssign, +// C: PrimInt + std::ops::BitXorAssign + Debug, +// >( +// design: &mut HardwareModule, +// x: &ArrayView2, +// k: &ArrayView2, +// y: &mut Array2, +// ) -> Result<()> { +// let iterations: usize = x.shape()[0]; // K - design.eval_reset_clocked(Some(1))?; - for i in 0..iterations { - while design.get_port_int::("s_ready_o")? == 0 { - design.eval_clocked(Some(1))?; - } - design.set_port_int("s_valid_i", 1_u32)?; - design.set_port_ndarray("sx_data_i", x.row(i))?; - design.set_port_ndarray("sk_data_i", k.row(i))?; +// design.eval_reset_clocked(Some(1))?; +// for i in 0..iterations { +// while design.get_port_int::("s_ready_o")? == 0 { +// design.eval_clocked(Some(1))?; +// } +// design.set_port_int("s_valid_i", 1_u32)?; +// design.set_port_ndarray("sx_data_i", x.row(i))?; +// design.set_port_ndarray("sk_data_i", k.row(i))?; - if i == iterations - 1 { - design.set_port_int("s_last_i", 1_u32)?; - } +// if i == iterations - 1 { +// design.set_port_int("s_last_i", 1_u32)?; +// } - design.eval_clocked(Some(1))?; - } +// design.eval_clocked(Some(1))?; +// } - design.set_port_int("m_ready_i", 1_u32)?; - design.set_port_int("s_valid_i", 0_u32)?; - design.set_port_int("s_last_i", 0_u32)?; +// design.set_port_int("m_ready_i", 1_u32)?; +// design.set_port_int("s_valid_i", 0_u32)?; +// design.set_port_int("s_last_i", 0_u32)?; - let mut idx = 0; - loop { - if design.get_port_int::("m_valid_o")? == 1 { - let m_data = design.get_port_ndarray::("m_data_o")?; - m_data.assign_to(y.row_mut(idx)); - idx += 1; - } +// let mut idx = 0; +// loop { +// if design.get_port_int::("m_valid_o")? == 1 { +// let m_data = design.get_port_ndarray::("m_data_o")?; +// m_data.assign_to(y.row_mut(idx)); +// idx += 1; +// } - if design.get_port_int::("m_last_o")? == 1 { - break; - } - design.eval_clocked(Some(1))?; - } +// if design.get_port_int::("m_last_o")? == 1 { +// break; +// } +// design.eval_clocked(Some(1))?; +// } - Ok(()) -} +// Ok(()) +// } diff --git a/crates/python_bindings/Cargo.toml b/crates/python_bindings/Cargo.toml index bc8f851..576d01b 100644 --- a/crates/python_bindings/Cargo.toml +++ b/crates/python_bindings/Cargo.toml @@ -14,9 +14,14 @@ numpy = "0.24.0" num-traits = "0.2" serde = { version = "1.0", features = ["derive"] } bincode = {version = "2.0.1", features = ["serde"] } +yosys-netlist-json = { workspace = true } +tokio = { workspace = true } [dependencies.arbolta] path = "../arbolta" [features] defaut = ["pyo3/extension-module"] + +[package.metadata.maturin] +python-source = "py_src" diff --git a/crates/python_bindings/py_src/arbolta/__init__.py b/crates/python_bindings/py_src/arbolta/__init__.py index 281827e..3556f74 100644 --- a/crates/python_bindings/py_src/arbolta/__init__.py +++ b/crates/python_bindings/py_src/arbolta/__init__.py @@ -3,6 +3,6 @@ # type: ignore from .arbolta import Design # For pickling -from .design import DesignConfig, HardwareDesign, PortConfig, load, save +from .design import HardwareDesign, PortConfig, load, save -__all__ = ["Design", "PortConfig", "DesignConfig", "HardwareDesign", "save", "load"] +__all__ = ["Design", "PortConfig", "HardwareDesign", "save", "load"] diff --git a/crates/python_bindings/py_src/arbolta/design.py b/crates/python_bindings/py_src/arbolta/design.py index a2706dc..fa15ef3 100644 --- a/crates/python_bindings/py_src/arbolta/design.py +++ b/crates/python_bindings/py_src/arbolta/design.py @@ -2,13 +2,13 @@ # SPDX-License-Identifier: MIT from dataclasses import dataclass -from typing import Any, Dict, List, Optional, Tuple, TypedDict, Union +from typing import Any, Dict, List, Optional, Tuple, Union import numpy as np from .arbolta import Design -__all__ = ["PortConfig", "DesignConfig", "HardwareDesign", "save", "load"] +__all__ = ["PortConfig", "HardwareDesign", "save", "load"] @dataclass @@ -20,7 +20,7 @@ class PortConfig: ---------- shape : tuple Interpret port bits with shape. - dtype : np.dtype + dtype : np.dtype | type Interpret port bits as type. clock : bool, optional Port is a clock signal. @@ -31,26 +31,10 @@ class PortConfig: """ shape: Tuple[int, int] = (1, 1) - dtype: np.dtype = np.uint32 + dtype: Union[np.dtype, type] = np.uint32 clock: bool = False reset: bool = False - polarity: int = 1 - - -class DesignConfig(TypedDict): - """ - Configuration for HardwareDesign. - - Attributes - ---------- - port : str - Name of port. - config : PortConfig - Configuration for port - """ - - port: str - config: PortConfig + polarity: Optional[int] = None @dataclass @@ -60,7 +44,7 @@ class Port: class HardwarePorts: - def __init__(self, config: DesignConfig, design: Design): + def __init__(self, config: dict[str, PortConfig], design: Design): _ports: Dict[str, Port] = {} port_name: str @@ -70,7 +54,7 @@ def __init__(self, config: DesignConfig, design: Design): raise AttributeError(f"Port `{port_name}` cannot be a reset and clock") if port_config.reset: design.set_reset(port_name, bool(port_config.polarity)) - if port_config.clock: + elif port_config.clock: design.set_clock(port_name, bool(port_config.polarity)) design.set_port_shape(port_name, port_config.shape) @@ -113,7 +97,14 @@ def __setattr__(self, name: str, value: Any) -> None: class HardwareDesign: - def __init__(self, top_module: str, netlist_path: str, config: DesignConfig): + def __init__( + self, + top_module: str, + netlist_path: str, + config: dict[str, PortConfig], + yosys_path: str = "yosys", + yosys_server_path="yosys_server", + ): """ Parameters ---------- @@ -121,11 +112,11 @@ def __init__(self, top_module: str, netlist_path: str, config: DesignConfig): Name of top module. netlist_path : str Path to Yosys netlist JSON. - config : DesignConfig - Configuration for design. + config : dict[str, PortConfig] + Configuration for design ports. """ self.top_module = top_module - self.design = Design(top_module, netlist_path) + self.design = Design(top_module, netlist_path, yosys_path, yosys_server_path) self.ports = HardwarePorts(config, self.design) def reset(self): diff --git a/crates/python_bindings/src/conversion.rs b/crates/python_bindings/src/conversion.rs index 30b3baf..412477b 100644 --- a/crates/python_bindings/src/conversion.rs +++ b/crates/python_bindings/src/conversion.rs @@ -2,39 +2,40 @@ // SPDX-License-Identifier: MIT use arbol::bit::BitVec; -use num_traits::PrimInt; -use numpy::{PyReadonlyArray1, PyReadwriteArray1}; -use pyo3::exceptions::PyValueError; +use num_traits::{PrimInt, WrappingAdd, WrappingShl, WrappingSub}; +use numpy::{PyArrayMethods, PyReadonlyArray1, PyReadwriteArray1}; use pyo3::prelude::*; pub fn bits_to_bool_numpy(bits: &BitVec, numpy_array: &Bound<'_, PyAny>) -> PyResult<()> { let mut buffer = numpy_array.extract::>()?; - match bits.to_bool_ndarray_buffer(buffer.as_array_mut()) { - Ok(()) => Ok(()), - Err(err) => Err(PyValueError::new_err(format!("{err}"))), - } + + bits + .bits + .iter() + .zip(buffer.as_array_mut().iter_mut()) + .for_each(|(&bit, buf)| *buf = bit.0); + + Ok(()) } pub fn bool_numpy_to_bits(numpy_array: &Bound<'_, PyAny>) -> PyResult { let buffer = numpy_array.extract::>()?; - - match BitVec::from_bool_ndarray(buffer.as_array()) { - Ok(bits) => Ok(bits), - Err(err) => Err(PyValueError::new_err(format!("{err}"))), - } + Ok(BitVec::from_bools(buffer.to_owned_array())) } -pub fn bits_to_int_numpy( +pub fn bits_to_int_numpy( bits: &BitVec, elem_size: usize, numpy_array: &Bound<'_, PyAny>, ) -> PyResult<()> { let mut buffer = numpy_array.extract::>()?; - match bits.to_int_ndarray_sized_buffer(elem_size, buffer.as_array_mut()) { - Ok(()) => Ok(()), - Err(err) => Err(PyValueError::new_err(format!("{err}"))), - } + bits + .to_ints(Some(elem_size)) + .zip(buffer.as_array_mut().iter_mut()) + .for_each(|(src, dst)| *dst = src); + + Ok(()) } pub fn int_numpy_to_bits( @@ -42,9 +43,5 @@ pub fn int_numpy_to_bits( elem_size: usize, ) -> PyResult { let buffer = numpy_array.extract::>()?; - - match BitVec::from_int_ndarray_sized(buffer.as_array(), elem_size) { - Ok(bits) => Ok(bits), - Err(err) => Err(PyValueError::new_err(format!("{err}"))), - } + Ok(BitVec::from_ints(buffer.to_owned_array(), Some(elem_size))) } diff --git a/crates/python_bindings/src/design.rs b/crates/python_bindings/src/design.rs index 941ee15..f07e71a 100644 --- a/crates/python_bindings/src/design.rs +++ b/crates/python_bindings/src/design.rs @@ -4,15 +4,17 @@ use crate::conversion::{ bits_to_bool_numpy, bits_to_int_numpy, bool_numpy_to_bits, int_numpy_to_bits, }; - use arbol::hardware_module::HardwareModule; use arbol::port::PortDirection; +use arbol::yosys::YosysClient; use bincode::{Decode, Encode}; use pyo3::exceptions::{PyAttributeError, PyException, PyValueError}; use pyo3::prelude::*; use pyo3::types::{PyBytes, PyDict}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use std::path::PathBuf; +use yosys_netlist_json as yosys_json; #[pyclass(dict, module = "arbolta", name = "Design")] #[derive(Deserialize, Serialize, Decode, Encode)] @@ -26,12 +28,31 @@ pub struct PyDesign { #[pymethods] impl PyDesign { #[new] - fn __new__(top_module: &str, netlist_path: &str) -> PyResult { - let module = match HardwareModule::new_from_path(netlist_path, top_module) { - Ok(module) => module, - Err(err) => return Err(PyException::new_err(format!("{err}"))), + #[pyo3(signature = (top_module, netlist_path, yosys_path="yosys", yosys_server_path="yosys_server"))] + fn __new__( + top_module: &str, + netlist_path: &str, + yosys_path: &str, + yosys_server_path: &str, + ) -> PyResult { + let client = YosysClient { + yosys_server_path: PathBuf::from(yosys_server_path), + yosys_path: PathBuf::from(yosys_path), + ..Default::default() }; + let raw_netlist = std::fs::read(netlist_path)?; + let netlist = yosys_json::Netlist::from_slice(&raw_netlist) + .map_err(|e| PyValueError::new_err(format!("{e}")))?; + + let rt = tokio::runtime::Runtime::new()?; + let (netlist, torder) = rt + .block_on(client.flatten_netlist(top_module, netlist)) + .map_err(|e| PyValueError::new_err(format!("{e}")))?; + + let module = HardwareModule::new(&netlist, &torder, top_module) + .map_err(|e| PyValueError::new_err(format!("{e}")))?; + Ok(Self { top_module: top_module.to_string(), netlist_path: netlist_path.to_string(), @@ -102,7 +123,7 @@ impl PyDesign { } fn get_module_names(&self) -> Vec { - todo!() + self.module.submodules.keys().cloned().collect() } fn get_signal_map(&self) -> HashMap> { @@ -207,13 +228,6 @@ impl PyDesign { todo!() } - fn get_port_string(&self, name: &str) -> PyResult { - match self.module.get_port_string(name) { - Ok(bit_string) => Ok(bit_string), - Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), - } - } - fn is_port_input(&self, name: &str) -> PyResult { let direction = match self.module.get_port_direction(name) { Ok(direction) => direction, @@ -225,12 +239,13 @@ impl PyDesign { fn get_port_numpy(&self, name: &str, numpy_array: &Bound<'_, PyAny>) -> PyResult<()> { let item_type = numpy_array.getattr("dtype")?.getattr("str")?.to_string(); - let shape = self.get_port_shape(name)?; - let elem_size = shape[1]; - let bits = match self.module.get_port_bits(name) { - Ok(bits) => bits, - Err(err) => return Err(PyAttributeError::new_err(format!("{err}"))), - }; + let elem_size = self.get_port_shape(name)?[1]; + + let bits = self + .module + .get_port(name) + .map_err(|e| PyAttributeError::new_err(format!("{e}")))?; + match item_type.as_str() { "|b1" => bits_to_bool_numpy(&bits, numpy_array), "|u1" | " bits_to_int_numpy::(&bits, elem_size, numpy_array), @@ -290,9 +305,10 @@ impl PyDesign { ))); } }; - match self.module.set_port_bits(name, &bits) { - Ok(()) => Ok(()), - Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), - } + + self + .module + .set_port(name, bits) + .map_err(|e| PyAttributeError::new_err(format!("{e}"))) } } diff --git a/crates/yosys_server/Cargo.toml b/crates/yosys_server/Cargo.toml new file mode 100644 index 0000000..0944ff6 --- /dev/null +++ b/crates/yosys_server/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "yosys_server" +version = "0.1.0" +edition = "2024" + +[dependencies] +clap = { workspace = true } +tarpc = { workspace = true } +tokio = { workspace = true } +anyhow = { workspace = true } +futures = { workspace = true } +serde = { workspace = true } +serde-error = { workspace = true } +yosys-netlist-json = { workspace = true } +tempfile = { workspace = true } +thiserror = {workspace = true} + +[lib] +name = "yosys_service" +path = "src/lib.rs" diff --git a/crates/yosys_server/src/lib.rs b/crates/yosys_server/src/lib.rs new file mode 100644 index 0000000..dd36748 --- /dev/null +++ b/crates/yosys_server/src/lib.rs @@ -0,0 +1,41 @@ +pub mod server; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use yosys_netlist_json::Netlist; + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct FlattenRequest { + pub top_module: String, + pub netlist: Netlist, +} + +#[derive(Debug, Clone, Default, Deserialize, Serialize)] +pub struct SynthConfig { + pub run_synth: bool, // if false, just run proc + pub icells: bool, // -icells + pub lib: bool, // -lib + pub defer: bool, // -defer + pub defines: Option>, // -Dname[=definition] + pub parameters: Option>, // -chparam .. (if top module given) +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct SimpleSynthRequest { + pub verilog_source: String, + pub top_module: Option, // Hierarchy top if set + pub config: SynthConfig, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct NetlistResponse { + pub netlist: Option, + pub topo_order: Option>>, + pub error_log: String, +} + +#[tarpc::service] +pub trait Synthesis { + async fn flatten(request: FlattenRequest) -> Result; + async fn simple_synth(request: SimpleSynthRequest) + -> Result; +} diff --git a/crates/yosys_server/src/main.rs b/crates/yosys_server/src/main.rs new file mode 100644 index 0000000..fa079d1 --- /dev/null +++ b/crates/yosys_server/src/main.rs @@ -0,0 +1,54 @@ +use anyhow::Result; +use clap::Parser; +use futures::{future, prelude::*}; +use std::net::{IpAddr, Ipv6Addr}; +use std::path::PathBuf; +use tarpc::{ + server::{self, Channel}, + tokio_serde::formats::Json, +}; +use yosys_service::Synthesis; +use yosys_service::server::SynthesisServer; + +async fn spawn(fut: impl Future + Send + 'static) { + tokio::spawn(fut); +} + +#[derive(Parser, Debug)] +struct Args { + #[arg(long, default_value_t = 8080)] + port: u16, + #[arg(long, default_value = "yosys")] + yosys_path: PathBuf, +} + +#[tokio::main] +async fn main() -> Result<()> { + let flags = Args::parse(); + let server_addr = (IpAddr::V6(Ipv6Addr::LOCALHOST), flags.port); + + let mut listener = tarpc::serde_transport::tcp::listen(server_addr, Json::default).await?; + listener.config_mut().max_frame_length(usize::MAX); + + println!("yosys_server"); + println!("\tListening on port: {}", flags.port); + println!("\tUsing Yosys path: {}", flags.yosys_path.display()); + + listener + // Ignore accept errors. + .filter_map(|r| future::ready(r.ok())) + .map(server::BaseChannel::with_defaults) + // serve is generated by the service attribute. It takes as input any type implementing + // the generated World trait. + .map(|channel| { + let server = SynthesisServer { + yosys_path: flags.yosys_path.clone(), + }; + channel.execute(server.serve()).for_each(spawn) + }) + // Max 10 channels. + .buffer_unordered(10) + .for_each(|_| async {}) + .await; + Ok(()) +} diff --git a/crates/yosys_server/src/server.rs b/crates/yosys_server/src/server.rs new file mode 100644 index 0000000..5e4f84f --- /dev/null +++ b/crates/yosys_server/src/server.rs @@ -0,0 +1,246 @@ +use super::Synthesis; +use crate::*; +use anyhow::Result; +use std::fs::File; +use std::io::{Read, Write}; +use std::path::{Path, PathBuf}; +use std::process::Command; +use tarpc::context::Context; +use tempfile::TempDir; + +#[derive(Clone)] +pub struct SynthesisServer { + pub yosys_path: PathBuf, +} + +fn parse_torder(raw: &str) -> HashMap> { + let mut torder: HashMap> = HashMap::new(); + let mut current_module: Option<&str> = None; // If Some, save cells + + for line in raw.lines() { + let line = line.trim(); + + // Start new module + if let Some(module_name) = line.strip_prefix("module ") { + current_module = Some(module_name) + } else if let Some(module_name) = current_module + && let Some(cell_name) = line.strip_prefix("cell ") + { + torder + .entry(module_name.to_string()) + .or_default() + .push(cell_name.to_string()); + } + } + + torder +} + +fn generate_synth_command( + request: &SimpleSynthRequest, + rtl_path: &Path, + export_path: &Path, +) -> String { + // Default to System Verilog + let mut read_pass = vec![format!( + "read_verilog -sv {} {} {}", + if request.config.icells { "-icells" } else { "" }, + if request.config.lib { "-lib" } else { "" }, + if request.config.defer { "-defer" } else { "" }, + )]; + + if let Some(defines) = &request.config.defines { + defines + .iter() + .for_each(|d| read_pass.push(format!("-D{d}"))); + } + + read_pass.push(format!("{};", rtl_path.display())); + let read_pass = read_pass.join(" "); + + let mut hierarchy_pass = vec![]; + if let Some(top_module) = &request.top_module { + hierarchy_pass.push(format!("hierarchy -top {top_module}")); + + if let Some(parameters) = &request.config.parameters { + parameters + .iter() + .for_each(|(k, v)| hierarchy_pass.push(format!("-chparam {k} {v}"))); + } + + hierarchy_pass.push(";".to_string()); + } + let hierarchy_pass = hierarchy_pass.join(" "); + + let proc_pass = "proc; clean; autoname; setundef -zero; flatten -scopename;".to_string(); + let synth_pass = if request.config.run_synth { + format!("synth; {proc_pass}") + } else { + "".to_string() + }; + let export_pass = format!("write_json {}", export_path.display()); + + [ + read_pass, + hierarchy_pass, + proc_pass, + synth_pass, + export_pass, + ] + .join(" ") +} + +impl Synthesis for SynthesisServer { + async fn flatten( + self, + _context: Context, + request: FlattenRequest, + ) -> Result { + let temp_dir = TempDir::new().map_err(|e| serde_error::Error::new(&e))?; + + let original_netlist_path = temp_dir.path().join("original.json"); + let original_netlist_file = + File::create(&original_netlist_path).map_err(|e| serde_error::Error::new(&e))?; + + let flattened_netlist_path = temp_dir.path().join("flattened.json"); + let flattened_netlist_file = + File::create_new(&flattened_netlist_path).map_err(|e| serde_error::Error::new(&e))?; + + let topo_order_path = temp_dir.path().join("topo.txt"); + let mut topo_order_file = + File::create_new(&topo_order_path).map_err(|e| serde_error::Error::new(&e))?; + + // Write old netlist to file + request + .netlist + .to_writer(original_netlist_file) + .map_err(|e| serde_error::Error::new(&e))?; + + // Run Yosys + let command = Command::new(self.yosys_path) + .arg("-f") + .arg("json") + .arg(original_netlist_path) + .arg("-p") + .arg(format!( + "flatten -scopename; write_json {}; tee -o {} torder", + flattened_netlist_path.display(), + topo_order_path.display() + )) + .output() + .map_err(|e| serde_error::Error::new(&e))?; + + // Yosys failed + let error_log = String::from_utf8(command.stderr).unwrap(); + if !error_log.is_empty() { + return Ok(NetlistResponse { + netlist: None, + topo_order: None, + error_log, + }); + } + + // Load in flattened netlist + let flattened_netlist = yosys_netlist_json::Netlist::from_reader(flattened_netlist_file) + .map_err(|e| serde_error::Error::new(&e))?; + + // Load topological cell order + let mut topo_order = String::new(); + _ = topo_order_file + .read_to_string(&mut topo_order) + .map_err(|e| serde_error::Error::new(&e))?; + + let topo_order = parse_torder(&topo_order); + + Ok(NetlistResponse { + netlist: Some(flattened_netlist), + topo_order: Some(topo_order), + error_log, + }) + } + + // Can't have include unless full path... + async fn simple_synth( + self, + _context: Context, + request: SimpleSynthRequest, + ) -> Result { + let temp_dir = TempDir::new().map_err(|e| serde_error::Error::new(&e))?; + + let rtl_path = temp_dir.path().join("design.v"); + let mut rtl_file = File::create(&rtl_path).map_err(|e| serde_error::Error::new(&e))?; + + // Write source to file + rtl_file + .write(request.verilog_source.as_bytes()) + .map_err(|e| serde_error::Error::new(&e))?; + + let netlist_path = temp_dir.path().join("flattened.json"); + let netlist_file = File::create_new(&netlist_path).map_err(|e| serde_error::Error::new(&e))?; + + let torder_path = temp_dir.path().join("topo.txt"); + let mut torder_file = + File::create_new(&torder_path).map_err(|e| serde_error::Error::new(&e))?; + + let passes = generate_synth_command(&request, &rtl_path, &netlist_path); + // TODO: Hacky, fix this + let passes = [passes, format!("; tee -o {} torder", torder_path.display())].concat(); + println!("{passes}"); + + // Run Yosys + let command = Command::new(self.yosys_path) + .arg("-p") + .arg(passes) + .output() + .map_err(|e| serde_error::Error::new(&e))?; + + // Yosys failed + let error_log = String::from_utf8(command.stderr).unwrap(); + if !error_log.is_empty() { + return Ok(NetlistResponse { + netlist: None, + topo_order: None, + error_log, + }); + } + + // Load in netlist + let netlist = yosys_netlist_json::Netlist::from_reader(netlist_file) + .map_err(|e| serde_error::Error::new(&e))?; + + // Load topological cell order + let mut torder = String::new(); + _ = torder_file + .read_to_string(&mut torder) + .map_err(|e| serde_error::Error::new(&e))?; + + let torder = parse_torder(&torder); + + Ok(NetlistResponse { + netlist: Some(netlist), + topo_order: Some(torder), + error_log, + }) + } +} + +#[test] +fn test_parse_torder() { + let raw = r#" + + 8. Executing TORDER pass (print cells in topological order). + module alu + cell $flatten\i_adder.$add$ + cell $flatten\i_subtracter.$sub$ + cell $procmux$7 + + module alu2 + cell $flatten\i_adder.$add$ + cell $flatten\i_subtracter.$sub$ + cell $procmux$7 + + + "#; + let torder = parse_torder(raw); + println!("{torder:#?}"); +} From 24b248911afb29df073280e6cba1aedc3ed04852 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Mon, 18 Aug 2025 20:50:17 +0000 Subject: [PATCH 23/64] Updated axis systolic array submodule --- experiments/systolic_array/axis-systolic-array | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experiments/systolic_array/axis-systolic-array b/experiments/systolic_array/axis-systolic-array index e90c3e0..ebda1d5 160000 --- a/experiments/systolic_array/axis-systolic-array +++ b/experiments/systolic_array/axis-systolic-array @@ -1 +1 @@ -Subproject commit e90c3e076e765ee9c27d9cb0095f1bff44a7fb6c +Subproject commit ebda1d54b6a1572b64fcc7b30a030780c9886a14 From fb3a5b9af3422b2c93362039778ce6338706848f Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Tue, 19 Aug 2025 02:56:00 +0000 Subject: [PATCH 24/64] Added new cells. Need to add more tests --- crates/arbolta/src/cell/mod.rs | 96 +++++++++++- crates/arbolta/src/cell/simlib/arithmetic.rs | 141 ++++++++++-------- crates/arbolta/src/cell/simlib/bool_ops.rs | 91 +++-------- crates/arbolta/src/cell/simlib/compare_ops.rs | 23 +++ .../src/cell/simlib/logic_reduce_ops.rs | 29 +++- crates/arbolta/src/cell/simlib/mod.rs | 122 +++++++++++++++ crates/arbolta/src/cell/simlib/shift_ops.rs | 23 +++ crates/arbolta/src/cell/simlib/various.rs | 55 ++++--- 8 files changed, 420 insertions(+), 160 deletions(-) create mode 100644 crates/arbolta/src/cell/simlib/compare_ops.rs create mode 100644 crates/arbolta/src/cell/simlib/shift_ops.rs diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index a700c51..be3c7f0 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -43,16 +43,30 @@ pub enum Cell { DffReset, // Sim Lib Not, + Neg, Pos, Add, Sub, Mul, Div, + Modulus, + Le, + Ge, + Gt, + Shl, + Shr, Reg, Mux, + BMux, LogicAnd, LogicNot, ReduceOr, + ReduceAnd, + ProcAnd, + Eq, + Ne, + ProcOr, + ProcXor, } #[derive(Debug, Error)] @@ -199,6 +213,11 @@ pub fn create_cell(cell: &yosys_json::Cell) -> Result { connections["A"].clone().into(), connections["Y"].clone().into(), )), + "$neg" => Cell::Neg(Neg::new( + parameters["A_SIGNED"] != 0, + connections["A"].clone().into(), + connections["Y"].clone().into(), + )), "$add" => Cell::Add(Add::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), connections["A"].clone().into(), @@ -223,6 +242,42 @@ pub fn create_cell(cell: &yosys_json::Cell) -> Result { connections["B"].clone().into(), connections["Y"].clone().into(), )), + "$mod" => Cell::Modulus(Modulus::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), + "$le" => Cell::Le(Le::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), + "$ge" => Cell::Ge(Ge::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), + "$gt" => Cell::Gt(Gt::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), + "$shl" => Cell::Shl(Shl::new( + parameters["A_SIGNED"] != 0, + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), + "$shr" => Cell::Shr(Shr::new( + parameters["A_SIGNED"] != 0, + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), "$dff" => Cell::Reg(Reg::new( (parameters["CLK_POLARITY"] != 0).into(), connections["CLK"][0], @@ -235,6 +290,11 @@ pub fn create_cell(cell: &yosys_json::Cell) -> Result { connections["B"].clone().into(), connections["Y"].clone().into(), )), + "$bmux" => Cell::BMux(BMux::new( + connections["S"].clone().into(), + connections["A"].clone().into(), + connections["Y"].clone().into(), + )), "$logic_and" => Cell::LogicAnd(LogicAnd::new( connections["A"].clone().into(), connections["B"].clone().into(), @@ -244,8 +304,42 @@ pub fn create_cell(cell: &yosys_json::Cell) -> Result { connections["A"].clone().into(), connections["Y"].clone().into(), )), - "$reduce_or" => Cell::ReduceOr(ReduceOr::new( + "$reduce_or" | "$reduce_bool" => Cell::ReduceOr(ReduceOr::new( + connections["A"].clone().into(), + connections["Y"].clone().into(), + )), + "$reduce_and" => Cell::ReduceAnd(ReduceAnd::new( + connections["A"].clone().into(), + connections["Y"].clone().into(), + )), + "$and" => Cell::ProcAnd(ProcAnd::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), + "$or" => Cell::ProcOr(ProcOr::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), + "$xor" => Cell::ProcXor(ProcXor::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), + "$eq" => Cell::Eq(Eq::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), + "$ne" => Cell::Ne(Ne::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), connections["A"].clone().into(), + connections["B"].clone().into(), connections["Y"].clone().into(), )), _ => return Err(CellError::Unsupported(cell.cell_type.to_string())), diff --git a/crates/arbolta/src/cell/simlib/arithmetic.rs b/crates/arbolta/src/cell/simlib/arithmetic.rs index 153b89d..fd02d61 100644 --- a/crates/arbolta/src/cell/simlib/arithmetic.rs +++ b/crates/arbolta/src/cell/simlib/arithmetic.rs @@ -1,77 +1,52 @@ -use super::CellFn; -use crate::{ - bit::{Bit, BitVec}, - signal::Signals, -}; +use super::*; +use crate::{bit::BitVec, signal::Signals}; use bincode::{Decode, Encode}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; -macro_rules! define_arithmetic_cell { - ($name:ident, $op:tt) => { - #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] - pub struct $name { - signed: bool, - a_nets: Box<[usize]>, - b_nets: Box<[usize]>, - y_nets: Box<[usize]>, - } - - impl CellFn for $name { - #[inline] - fn eval(&mut self, signals: &mut Signals) { - let (a, b) = ( - BitVec::from( - self - .a_nets - .iter() - .map(|n| signals.get_net(*n)) - .collect::>(), - ), - BitVec::from( - self - .b_nets - .iter() - .map(|n| signals.get_net(*n)) - .collect::>(), - ), - ); - let output_size = self.y_nets.len(); - - let y: BitVec = if self.signed { - if output_size <= 64 { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int(a $op b, Some(output_size)) - } else { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int(a $op b, Some(output_size)) - } - } else { - if output_size <= 64 { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int(a $op b, Some(output_size)) - } else { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int(a $op b, Some(output_size)) - } - }; - - self - .y_nets - .iter() - .zip(y.into_iter()) - .for_each(|(n, bit)| signals.set_net(*n, bit)); - } - - fn reset(&mut self) {} - } - }; -} - define_arithmetic_cell!(Add, +); define_arithmetic_cell!(Sub, -); define_arithmetic_cell!(Mul, *); define_arithmetic_cell!(Div, /); +define_arithmetic_cell!(Modulus, %); +define_arithmetic_cell!(Le, <); +define_arithmetic_cell!(Gt, >); +define_arithmetic_cell!(Ge, >=); + +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] +pub struct Neg { + signed: bool, + a_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for Neg { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let a = BitVec::from(bits_from_nets(signals, &self.a_nets)); + let output_size = self.y_nets.len(); + + let y: BitVec = if output_size <= 64 { + let a = -a.to_int::(); + if self.signed { + BitVec::from_int(a, Some(output_size)) + } else { + BitVec::from_int(a as u64, Some(output_size)) + } + } else { + let a = -a.to_int::(); + if self.signed { + BitVec::from_int(a, Some(output_size)) + } else { + BitVec::from_int(a as u128, Some(output_size)) + } + }; + + copy_bits(signals, &self.y_nets, y); + } + + fn reset(&mut self) {} +} #[cfg(test)] mod tests { @@ -132,4 +107,38 @@ mod tests { fn div() { println!("TODO") } + + #[rstest] + fn modulus() { + println!("TODO") + } + + #[rstest] + // 37738 < 4365 = 0 + #[case::unsigned_normal(false, "1001001101101010", "0001000100001101", "0")] + #[case::signed_normal(true, "1001001101101010", "0001000100001101", "1")] + fn le(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { + run_binary_cell_case!(Le, signed, a, b, expected); + } + + #[rstest] + fn ge() { + println!("TODO") + } + + #[rstest] + fn gt() { + println!("TODO") + } + + #[rstest] + // -37738 = 27798 + #[case::unsigned_normal(false, "1001001101101010", "0110110010010110")] + // -37738 = -37738 + #[case::signed_normal(true, "001001001101101010", "11110110110010010110")] + // -(-27798) = 27798 + #[case::signed_normal(true, "1001001101101010", "0110110010010110")] + fn neg(#[case] signed: bool, #[case] a: BitVec, #[case] expected: BitVec) { + run_unary_cell_case_signed!(Neg, signed, a, expected); + } } diff --git a/crates/arbolta/src/cell/simlib/bool_ops.rs b/crates/arbolta/src/cell/simlib/bool_ops.rs index 486c3f4..f78fef4 100644 --- a/crates/arbolta/src/cell/simlib/bool_ops.rs +++ b/crates/arbolta/src/cell/simlib/bool_ops.rs @@ -1,8 +1,5 @@ -use super::{CellFn, bits_from_nets}; -use crate::{ - bit::{Bit, BitVec}, - signal::Signals, -}; +use super::*; +use crate::{bit::BitVec, signal::Signals}; use bincode::{Decode, Encode}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; @@ -17,82 +14,21 @@ pub struct Not { impl CellFn for Not { #[inline] fn eval(&mut self, signals: &mut Signals) { - let mut a = bits_from_nets(signals, &self.a_nets); - - // Have to pad - if a.len() < self.y_nets.len() { - // Signed and sign-bit set - let sign_bit_set = a.last().is_some_and(|&b| b.into()); - let pad_bit: Bit = (self.signed && sign_bit_set).into(); - - let pad_size = self.y_nets.len() - a.len(); - a.extend(std::iter::repeat_n(pad_bit, pad_size)); - } + let a = bits_from_nets_pad(self.signed, signals, &self.a_nets, self.y_nets.len()); self .y_nets .iter() - .zip(a.iter()) - .for_each(|(n, b)| signals.set_net(*n, !*b)); + .zip(a) + .for_each(|(&n, b)| signals.set_net(n, !b)); } fn reset(&mut self) {} } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -pub struct ProcAnd { - signed: bool, - a_nets: Box<[usize]>, - b_nets: Box<[usize]>, - y_nets: Box<[usize]>, -} - -impl CellFn for ProcAnd { - #[inline] - fn eval(&mut self, signals: &mut Signals) { - let (a, b) = ( - BitVec::from( - self - .a_nets - .iter() - .map(|n| signals.get_net(*n)) - .collect::>(), - ), - BitVec::from( - self - .b_nets - .iter() - .map(|n| signals.get_net(*n)) - .collect::>(), - ), - ); - - let output_size = self.y_nets.len(); - let y: BitVec = if self.signed { - if output_size <= 64 { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int(a & b, Some(output_size)) - } else { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int(a & b, Some(output_size)) - } - } else if output_size <= 64 { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int(a & b, Some(output_size)) - } else { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int(a & b, Some(output_size)) - }; - - self - .y_nets - .iter() - .zip(y) - .for_each(|(&n, bit)| signals.set_net(n, bit)); - } - - fn reset(&mut self) {} -} +define_arithmetic_cell!(ProcAnd, &); +define_arithmetic_cell!(ProcOr, |); +define_arithmetic_cell!(ProcXor, ^); #[cfg(test)] mod tests { @@ -125,7 +61,18 @@ mod tests { #[rstest] #[case(false, "1111", "1111", "1111")] + #[case(false, "0000", "1111", "0000")] fn and(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { run_binary_cell_case!(ProcAnd, signed, a, b, expected); } + + #[rstest] + fn or() { + println!("TODO") + } + + #[rstest] + fn xor() { + println!("TODO") + } } diff --git a/crates/arbolta/src/cell/simlib/compare_ops.rs b/crates/arbolta/src/cell/simlib/compare_ops.rs new file mode 100644 index 0000000..81fa0ce --- /dev/null +++ b/crates/arbolta/src/cell/simlib/compare_ops.rs @@ -0,0 +1,23 @@ +use super::*; +use crate::{bit::BitVec, signal::Signals}; +use bincode::{Decode, Encode}; +use derive_more::Constructor; +use serde::{Deserialize, Serialize}; + +define_arithmetic_cell!(Eq, ==); +define_arithmetic_cell!(Ne, !=); + +#[cfg(test)] +mod tests { + use rstest::rstest; + + #[rstest] + fn eq() { + println!("TODO") + } + + #[rstest] + fn ne() { + println!("TODO") + } +} diff --git a/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs b/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs index c27a77e..adc6a8e 100644 --- a/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs +++ b/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs @@ -12,6 +12,24 @@ macro_rules! reduce_nets { }; } +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Constructor)] +pub struct ReduceAnd { + a_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for ReduceAnd { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + signals.set_net( + self.y_nets[0], + reduce_nets!(signals, self.a_nets, Bit::ZERO, &), + ); + } + + fn reset(&mut self) {} +} + #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Constructor)] pub struct ReduceOr { a_nets: Box<[usize]>, @@ -21,8 +39,10 @@ pub struct ReduceOr { impl CellFn for ReduceOr { #[inline] fn eval(&mut self, signals: &mut Signals) { - let a: Bit = reduce_nets!(signals, self.a_nets, Bit::ZERO, |); - signals.set_net(self.y_nets[0], a); + signals.set_net( + self.y_nets[0], + reduce_nets!(signals, self.a_nets, Bit::ZERO, |), + ); } fn reset(&mut self) {} @@ -98,6 +118,11 @@ mod tests { run_unary_cell_case!(ReduceOr, a, expected); } + #[rstest] + fn reduce_and() { + println!("TODO") + } + #[rstest] fn logic_and() { println!("TODO") diff --git a/crates/arbolta/src/cell/simlib/mod.rs b/crates/arbolta/src/cell/simlib/mod.rs index 8f00bef..34d0b5e 100644 --- a/crates/arbolta/src/cell/simlib/mod.rs +++ b/crates/arbolta/src/cell/simlib/mod.rs @@ -3,8 +3,10 @@ use crate::{bit::Bit, signal::Signals}; mod arithmetic; mod bool_ops; +mod compare_ops; mod logic_reduce_ops; mod registers; +mod shift_ops; mod various; #[inline(always)] @@ -12,6 +14,25 @@ fn bits_from_nets(signals: &mut Signals, nets: &[usize]) -> Vec { nets.iter().map(|&n| signals.get_net(n)).collect() } +#[inline(always)] +fn bits_from_nets_pad( + signed: bool, + signals: &mut Signals, + nets: &[usize], + out_size: usize, +) -> Vec { + let mut bits = bits_from_nets(signals, nets); + if bits.len() < out_size { + let sign_bit_set = bits.last().is_some_and(|&b| b.into()); + let pad_bit: Bit = (signed && sign_bit_set).into(); + let pad_size = out_size - bits.len(); + + bits.extend(std::iter::repeat_n(pad_bit, pad_size)); + } + + bits +} + #[inline(always)] fn copy_nets(signals: &mut Signals, src_nets: &[usize], dst_nets: &[usize]) { src_nets.iter().zip(dst_nets.iter()).for_each(|(src, dst)| { @@ -19,9 +40,110 @@ fn copy_nets(signals: &mut Signals, src_nets: &[usize], dst_nets: &[usize]) { }) } +#[inline(always)] +fn copy_bits(signals: &mut Signals, dst_nets: &[usize], bits: impl IntoIterator) { + dst_nets + .iter() + .zip(bits) + .for_each(|(&n, b)| signals.set_net(n, b)); +} + +#[macro_export] +macro_rules! define_arithmetic_cell { + ($name:ident, $op:tt) => { + #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] + pub struct $name { + signed: bool, + a_nets: Box<[usize]>, + b_nets: Box<[usize]>, + y_nets: Box<[usize]>, + } + + impl CellFn for $name { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let a = BitVec::from(bits_from_nets(signals, &self.a_nets)); + let b = BitVec::from(bits_from_nets(signals, &self.b_nets)); + + let output_size = self.y_nets.len(); + + let y: BitVec = if self.signed { + if output_size <= 64 { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int((a $op b) as i64, Some(output_size)) + } else { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int((a $op b) as i128, Some(output_size)) + } + } else { + if output_size <= 64 { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int((a $op b) as u64, Some(output_size)) + } else { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int((a $op b) as u128, Some(output_size)) + } + }; + + copy_bits(signals, &self.y_nets, y); + } + + fn reset(&mut self) {} + } + } +} + +#[allow(unused)] +pub(crate) use define_arithmetic_cell; + +// #[macro_export] +// macro_rules! define_unary_arithmetic_cell { +// ($name:ident, $op:tt) => { +// #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] +// pub struct $name { +// signed: bool, +// a_nets: Box<[usize]>, +// y_nets: Box<[usize]>, +// } + +// impl CellFn for $name { +// #[inline] +// fn eval(&mut self, signals: &mut Signals) { +// let a = BitVec::from(bits_from_nets(signals, &self.a_nets)); +// let output_size = self.y_nets.len(); + +// let y: BitVec = if self.signed { +// if output_size <= 64 { +// let a = a.to_int::(); +// BitVec::from_int(($op a) as i64, Some(output_size)) +// } else { +// let a = a.to_int::(); +// BitVec::from_int(($op a) as i128, Some(output_size)) +// } +// } else if output_size <= 64 { +// let a = a.to_int::(); +// BitVec::from_int(($op a) as u64, Some(output_size)) +// } else { +// let a = a.to_int::(); +// BitVec::from_int(($op a) as u128, Some(output_size)) +// }; + +// copy_bits(signals, &self.y_nets, y); +// } + +// fn reset(&mut self) {} +// } +// } +// } + +// #[allow(unused)] +// pub(crate) use define_unary_arithmetic_cell; + // Re-export pub use arithmetic::*; pub use bool_ops::*; +pub use compare_ops::*; pub use logic_reduce_ops::*; pub use registers::*; +pub use shift_ops::*; pub use various::*; diff --git a/crates/arbolta/src/cell/simlib/shift_ops.rs b/crates/arbolta/src/cell/simlib/shift_ops.rs new file mode 100644 index 0000000..f5c5344 --- /dev/null +++ b/crates/arbolta/src/cell/simlib/shift_ops.rs @@ -0,0 +1,23 @@ +use super::*; +use crate::{bit::BitVec, signal::Signals}; +use bincode::{Decode, Encode}; +use derive_more::Constructor; +use serde::{Deserialize, Serialize}; + +define_arithmetic_cell!(Shl, <<); +define_arithmetic_cell!(Shr, >>); + +#[cfg(test)] +mod tests { + use rstest::rstest; + + #[rstest] + fn shl() { + println!("TODO") + } + + #[rstest] + fn shr() { + println!("TODO") + } +} diff --git a/crates/arbolta/src/cell/simlib/various.rs b/crates/arbolta/src/cell/simlib/various.rs index 77ca749..e119bf3 100644 --- a/crates/arbolta/src/cell/simlib/various.rs +++ b/crates/arbolta/src/cell/simlib/various.rs @@ -1,5 +1,9 @@ -use super::{CellFn, bits_from_nets, copy_nets}; -use crate::{bit::Bit, signal::Signals}; +use super::{CellFn, bits_from_nets_pad, copy_bits, copy_nets}; +use crate::{ + bit::{Bit, BitVec}, + cell::simlib::bits_from_nets, + signal::Signals, +}; use bincode::{Decode, Encode}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; @@ -14,23 +18,9 @@ pub struct Pos { impl CellFn for Pos { #[inline] fn eval(&mut self, signals: &mut Signals) { - let mut a = bits_from_nets(signals, &self.a_nets); - - // Have to pad - if a.len() < self.y_nets.len() { - // Signed and sign-bit set - let sign_bit_set = a.last().is_some_and(|&b| b.into()); - let pad_bit: Bit = (self.signed && sign_bit_set).into(); - - let pad_size = self.y_nets.len() - a.len(); - a.extend(std::iter::repeat_n(pad_bit, pad_size)); - } - - self - .y_nets - .iter() - .zip(a.iter()) - .for_each(|(n, b)| signals.set_net(*n, *b)); + // Passthrough with padding + let a = bits_from_nets_pad(self.signed, signals, &self.a_nets, self.y_nets.len()); + copy_bits(signals, &self.y_nets, a); } fn reset(&mut self) {} @@ -55,6 +45,29 @@ impl CellFn for Mux { fn reset(&mut self) {} } +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Constructor)] +pub struct BMux { + select_nets: Box<[usize]>, + a_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for BMux { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let select = BitVec::from(bits_from_nets(signals, &self.select_nets)); + let start_net: usize = select.to_int::() * self.y_nets.len(); + let end_net = start_net + self.y_nets.len(); + + let a = bits_from_nets(signals, &self.a_nets); + (start_net..end_net) + .zip(a) + .for_each(|(n, b)| signals.set_net(n, b)); + } + + fn reset(&mut self) {} +} + #[cfg(test)] mod tests { use rstest::rstest; @@ -68,4 +81,8 @@ mod tests { fn mux() { println!("TODO"); } + #[rstest] + fn bmux() { + println!("TODO"); + } } From 4a35d70b5d939297e540f2b193e9959e7aea7e11 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Thu, 28 Aug 2025 19:59:40 +0000 Subject: [PATCH 25/64] Fixed over/underflow issues --- crates/arbolta/src/cell/mod.rs | 17 ++ crates/arbolta/src/cell/simlib/arithmetic.rs | 19 +- crates/arbolta/src/cell/simlib/bool_ops.rs | 8 +- crates/arbolta/src/cell/simlib/compare_ops.rs | 4 +- crates/arbolta/src/cell/simlib/mod.rs | 98 +++---- crates/arbolta/src/cell/simlib/registers.rs | 44 +++ crates/arbolta/src/cell/simlib/shift_ops.rs | 92 ++++++- crates/arbolta/src/cell/simlib/various.rs | 44 ++- crates/arbolta/src/yosys.rs | 25 ++ crates/arbolta/tests/test_module.rs | 255 +----------------- crates/arbolta/tests/test_simlib.rs | 77 ------ .../python_bindings/py_src/arbolta/design.py | 11 +- crates/python_bindings/src/design.rs | 105 +++++--- requirements/requirements_experiments.txt | 4 + 14 files changed, 362 insertions(+), 441 deletions(-) delete mode 100644 crates/arbolta/tests/test_simlib.rs create mode 100644 requirements/requirements_experiments.txt diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index be3c7f0..bd6e375 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -56,8 +56,10 @@ pub enum Cell { Shl, Shr, Reg, + ALDff, Mux, BMux, + PMux, LogicAnd, LogicNot, ReduceOr, @@ -284,6 +286,15 @@ pub fn create_cell(cell: &yosys_json::Cell) -> Result { connections["D"].clone().into(), connections["Q"].clone().into(), )), + "$aldff" => Cell::ALDff(ALDff::new( + (parameters["CLK_POLARITY"] != 0).into(), + (parameters["ALOAD_POLARITY"] != 0).into(), + connections["CLK"][0], + connections["ALOAD"][0], + connections["AD"].clone().into(), + connections["D"].clone().into(), + connections["Q"].clone().into(), + )), "$mux" => Cell::Mux(Mux::new( connections["S"][0], connections["A"].clone().into(), @@ -295,6 +306,12 @@ pub fn create_cell(cell: &yosys_json::Cell) -> Result { connections["A"].clone().into(), connections["Y"].clone().into(), )), + "$pmux" => Cell::PMux(PMux::new( + connections["S"].clone().into(), + connections["A"].clone().into(), + connections["B"].clone().into(), + connections["Y"].clone().into(), + )), "$logic_and" => Cell::LogicAnd(LogicAnd::new( connections["A"].clone().into(), connections["B"].clone().into(), diff --git a/crates/arbolta/src/cell/simlib/arithmetic.rs b/crates/arbolta/src/cell/simlib/arithmetic.rs index fd02d61..d241706 100644 --- a/crates/arbolta/src/cell/simlib/arithmetic.rs +++ b/crates/arbolta/src/cell/simlib/arithmetic.rs @@ -3,15 +3,16 @@ use crate::{bit::BitVec, signal::Signals}; use bincode::{Decode, Encode}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; - -define_arithmetic_cell!(Add, +); -define_arithmetic_cell!(Sub, -); -define_arithmetic_cell!(Mul, *); -define_arithmetic_cell!(Div, /); -define_arithmetic_cell!(Modulus, %); -define_arithmetic_cell!(Le, <); -define_arithmetic_cell!(Gt, >); -define_arithmetic_cell!(Ge, >=); +use std::ops::Rem; + +define_arithmetic_cell!(Add, wrapping_add); +define_arithmetic_cell!(Sub, wrapping_sub); +define_arithmetic_cell!(Mul, wrapping_mul); +define_arithmetic_cell!(Div, wrapping_div); +define_arithmetic_cell!(Modulus, rem); +define_arithmetic_cell!(Le, <); +define_arithmetic_cell!(Gt, >); +define_arithmetic_cell!(Ge, &ge); #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct Neg { diff --git a/crates/arbolta/src/cell/simlib/bool_ops.rs b/crates/arbolta/src/cell/simlib/bool_ops.rs index f78fef4..2c1957f 100644 --- a/crates/arbolta/src/cell/simlib/bool_ops.rs +++ b/crates/arbolta/src/cell/simlib/bool_ops.rs @@ -1,3 +1,5 @@ +use std::ops::{BitAnd, BitOr, BitXor}; + use super::*; use crate::{bit::BitVec, signal::Signals}; use bincode::{Decode, Encode}; @@ -26,9 +28,9 @@ impl CellFn for Not { fn reset(&mut self) {} } -define_arithmetic_cell!(ProcAnd, &); -define_arithmetic_cell!(ProcOr, |); -define_arithmetic_cell!(ProcXor, ^); +define_arithmetic_cell!(ProcAnd, bitand); +define_arithmetic_cell!(ProcOr, bitor); +define_arithmetic_cell!(ProcXor, bitxor); #[cfg(test)] mod tests { diff --git a/crates/arbolta/src/cell/simlib/compare_ops.rs b/crates/arbolta/src/cell/simlib/compare_ops.rs index 81fa0ce..d503da0 100644 --- a/crates/arbolta/src/cell/simlib/compare_ops.rs +++ b/crates/arbolta/src/cell/simlib/compare_ops.rs @@ -4,8 +4,8 @@ use bincode::{Decode, Encode}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; -define_arithmetic_cell!(Eq, ==); -define_arithmetic_cell!(Ne, !=); +define_arithmetic_cell!(Eq, &eq); +define_arithmetic_cell!(Ne, &ne); #[cfg(test)] mod tests { diff --git a/crates/arbolta/src/cell/simlib/mod.rs b/crates/arbolta/src/cell/simlib/mod.rs index 34d0b5e..c91b47a 100644 --- a/crates/arbolta/src/cell/simlib/mod.rs +++ b/crates/arbolta/src/cell/simlib/mod.rs @@ -50,7 +50,8 @@ fn copy_bits(signals: &mut Signals, dst_nets: &[usize], bits: impl IntoIterator< #[macro_export] macro_rules! define_arithmetic_cell { - ($name:ident, $op:tt) => { + // Takes `b` by value + ($name:ident, $op:ident) => { #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] pub struct $name { signed: bool, @@ -70,18 +71,18 @@ macro_rules! define_arithmetic_cell { let y: BitVec = if self.signed { if output_size <= 64 { let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int((a $op b) as i64, Some(output_size)) + BitVec::from_int((a.$op(b)) as i64, Some(output_size)) } else { let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int((a $op b) as i128, Some(output_size)) + BitVec::from_int((a.$op(b)) as i128, Some(output_size)) } } else { if output_size <= 64 { let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int((a $op b) as u64, Some(output_size)) + BitVec::from_int((a.$op(b)) as u64, Some(output_size)) } else { let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int((a $op b) as u128, Some(output_size)) + BitVec::from_int((a.$op(b)) as u128, Some(output_size)) } }; @@ -90,55 +91,54 @@ macro_rules! define_arithmetic_cell { fn reset(&mut self) {} } - } + }; + // Takes `b` by reference + ($name:ident, & $op:ident) => { + #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] + pub struct $name { + signed: bool, + a_nets: Box<[usize]>, + b_nets: Box<[usize]>, + y_nets: Box<[usize]>, + } + + impl CellFn for $name { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let a = BitVec::from(bits_from_nets(signals, &self.a_nets)); + let b = BitVec::from(bits_from_nets(signals, &self.b_nets)); + + let output_size = self.y_nets.len(); + + let y: BitVec = if self.signed { + if output_size <= 64 { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int((a.$op(&b)) as i64, Some(output_size)) + } else { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int((a.$op(&b)) as i128, Some(output_size)) + } + } else { + if output_size <= 64 { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int((a.$op(&b)) as u64, Some(output_size)) + } else { + let (a, b) = (a.to_int::(), b.to_int::()); + BitVec::from_int((a.$op(&b)) as u128, Some(output_size)) + } + }; + + copy_bits(signals, &self.y_nets, y); + } + + fn reset(&mut self) {} + } + }; } #[allow(unused)] pub(crate) use define_arithmetic_cell; -// #[macro_export] -// macro_rules! define_unary_arithmetic_cell { -// ($name:ident, $op:tt) => { -// #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] -// pub struct $name { -// signed: bool, -// a_nets: Box<[usize]>, -// y_nets: Box<[usize]>, -// } - -// impl CellFn for $name { -// #[inline] -// fn eval(&mut self, signals: &mut Signals) { -// let a = BitVec::from(bits_from_nets(signals, &self.a_nets)); -// let output_size = self.y_nets.len(); - -// let y: BitVec = if self.signed { -// if output_size <= 64 { -// let a = a.to_int::(); -// BitVec::from_int(($op a) as i64, Some(output_size)) -// } else { -// let a = a.to_int::(); -// BitVec::from_int(($op a) as i128, Some(output_size)) -// } -// } else if output_size <= 64 { -// let a = a.to_int::(); -// BitVec::from_int(($op a) as u64, Some(output_size)) -// } else { -// let a = a.to_int::(); -// BitVec::from_int(($op a) as u128, Some(output_size)) -// }; - -// copy_bits(signals, &self.y_nets, y); -// } - -// fn reset(&mut self) {} -// } -// } -// } - -// #[allow(unused)] -// pub(crate) use define_unary_arithmetic_cell; - // Re-export pub use arithmetic::*; pub use bool_ops::*; diff --git a/crates/arbolta/src/cell/simlib/registers.rs b/crates/arbolta/src/cell/simlib/registers.rs index c4c4a47..5f8d89e 100644 --- a/crates/arbolta/src/cell/simlib/registers.rs +++ b/crates/arbolta/src/cell/simlib/registers.rs @@ -32,6 +32,45 @@ impl CellFn for Reg { } } +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, derive_new::new)] +pub struct ALDff { + clock_polarity: Bit, + al_polarity: Bit, + clock_net: usize, + al_net: usize, + al_data_in_nets: Box<[usize]>, + data_in_nets: Box<[usize]>, + data_out_nets: Box<[usize]>, + #[new(default)] + last_clock: Bit, + #[new(default)] + last_aload: Bit, +} + +impl CellFn for ALDff { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + // TODO: Change this to == + let clock = !(signals.get_net(self.clock_net) ^ self.clock_polarity); + let aload = !(signals.get_net(self.al_net) ^ self.al_polarity); + + // Do asynchronous load + if aload == Bit::ONE && self.last_aload == Bit::ZERO { + copy_nets(signals, &self.al_data_in_nets, &self.data_out_nets); + // Rising edge clock + } else if clock == Bit::ONE && self.last_clock == Bit::ZERO { + copy_nets(signals, &self.data_in_nets, &self.data_out_nets); + } + + self.last_clock = clock; + self.last_aload = aload; + } + + fn reset(&mut self) { + self.last_clock = Bit::ZERO; + } +} + #[cfg(test)] mod tests { use rstest::rstest; @@ -40,4 +79,9 @@ mod tests { fn reg() { println!("TODO") } + + #[rstest] + fn aldff() { + println!("TODO") + } } diff --git a/crates/arbolta/src/cell/simlib/shift_ops.rs b/crates/arbolta/src/cell/simlib/shift_ops.rs index f5c5344..aa1a590 100644 --- a/crates/arbolta/src/cell/simlib/shift_ops.rs +++ b/crates/arbolta/src/cell/simlib/shift_ops.rs @@ -4,8 +4,96 @@ use bincode::{Decode, Encode}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; -define_arithmetic_cell!(Shl, <<); -define_arithmetic_cell!(Shr, >>); +// define_arithmetic_cell!(Shl, <<); +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] +pub struct Shl { + signed: bool, + a_nets: Box<[usize]>, + b_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for Shl { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let a = BitVec::from(bits_from_nets(signals, &self.a_nets)); + let shift: u32 = BitVec::from(bits_from_nets(signals, &self.b_nets)).to_int(); + + let output_size = self.y_nets.len(); + let y: BitVec = if self.signed { + if output_size <= 64 { + BitVec::from_int( + (a.to_int::().wrapping_shl(shift)) as i64, + Some(output_size), + ) + } else { + BitVec::from_int( + (a.to_int::().wrapping_shl(shift)) as i128, + Some(output_size), + ) + } + } else if output_size <= 64 { + BitVec::from_int( + (a.to_int::().wrapping_shl(shift)) as u64, + Some(output_size), + ) + } else { + BitVec::from_int( + (a.to_int::().wrapping_shl(shift)) as u128, + Some(output_size), + ) + }; + + copy_bits(signals, &self.y_nets, y); + } + + fn reset(&mut self) {} +} + +#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] +pub struct Shr { + signed: bool, + a_nets: Box<[usize]>, + b_nets: Box<[usize]>, + y_nets: Box<[usize]>, +} + +impl CellFn for Shr { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let a = BitVec::from(bits_from_nets(signals, &self.a_nets)); + let shift: u32 = BitVec::from(bits_from_nets(signals, &self.b_nets)).to_int(); + + let output_size = self.y_nets.len(); + let y: BitVec = if self.signed { + if output_size <= 64 { + BitVec::from_int( + (a.to_int::().wrapping_shr(shift)) as i64, + Some(output_size), + ) + } else { + BitVec::from_int( + (a.to_int::().wrapping_shr(shift)) as i128, + Some(output_size), + ) + } + } else if output_size <= 64 { + BitVec::from_int( + (a.to_int::().wrapping_shr(shift)) as u64, + Some(output_size), + ) + } else { + BitVec::from_int( + (a.to_int::().wrapping_shr(shift)) as u128, + Some(output_size), + ) + }; + + copy_bits(signals, &self.y_nets, y); + } + + fn reset(&mut self) {} +} #[cfg(test)] mod tests { diff --git a/crates/arbolta/src/cell/simlib/various.rs b/crates/arbolta/src/cell/simlib/various.rs index e119bf3..7d38ec2 100644 --- a/crates/arbolta/src/cell/simlib/various.rs +++ b/crates/arbolta/src/cell/simlib/various.rs @@ -52,13 +52,15 @@ pub struct BMux { y_nets: Box<[usize]>, } +// "Selects between 'slices' of A where each value of S corresponds to a unique" impl CellFn for BMux { #[inline] fn eval(&mut self, signals: &mut Signals) { let select = BitVec::from(bits_from_nets(signals, &self.select_nets)); - let start_net: usize = select.to_int::() * self.y_nets.len(); + let start_net = select.to_int::() * self.y_nets.len(); let end_net = start_net + self.y_nets.len(); + // TODO: Don't read all bits let a = bits_from_nets(signals, &self.a_nets); (start_net..end_net) .zip(a) @@ -67,6 +69,40 @@ impl CellFn for BMux { fn reset(&mut self) {} } +// "Selects between 'slices' of B where each slice corresponds to a single bit +// of S. Outputs A when all bits of S are low." +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Constructor)] +pub struct PMux { + select_nets: Box<[usize]>, + a_nets: Box<[usize]>, // output when S all low + b_nets: Box<[usize]>, // slices source + y_nets: Box<[usize]>, // slice size +} + +impl CellFn for PMux { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + // Should be power of 2? + // 0, 2, 4, 8 + let select = BitVec::from(bits_from_nets(signals, &self.select_nets)).to_int::(); + // let src_nets = &self.a_nets; + if select == 0 { + let a = bits_from_nets(signals, &self.a_nets); + copy_bits(signals, &self.y_nets, a); + } else { + let start_net = (select.ilog2() as usize) * self.y_nets.len(); + let end_net = start_net + self.y_nets.len(); + + // TODO: Don't read all bits + let a = bits_from_nets(signals, &self.b_nets); + (start_net..end_net) + .zip(a) + .for_each(|(n, b)| signals.set_net(n, b)); + } + } + + fn reset(&mut self) {} +} #[cfg(test)] mod tests { @@ -81,8 +117,14 @@ mod tests { fn mux() { println!("TODO"); } + #[rstest] fn bmux() { println!("TODO"); } + + #[rstest] + fn pmux() { + println!("TODO"); + } } diff --git a/crates/arbolta/src/yosys.rs b/crates/arbolta/src/yosys.rs index c1305e0..23f3024 100644 --- a/crates/arbolta/src/yosys.rs +++ b/crates/arbolta/src/yosys.rs @@ -171,3 +171,28 @@ impl YosysClient { Ok((response.netlist.unwrap(), response.topo_order.unwrap())) } } + +// TODO: De-duplicate with yosys_server +/// Parse raw Yosys topological order output +pub fn parse_torder(raw: &str) -> HashMap> { + let mut torder: HashMap> = HashMap::new(); + let mut current_module: Option<&str> = None; // If Some, save cells + + for line in raw.lines() { + let line = line.trim(); + + // Start new module + if let Some(module_name) = line.strip_prefix("module ") { + current_module = Some(module_name) + } else if let Some(module_name) = current_module + && let Some(cell_name) = line.strip_prefix("cell ") + { + torder + .entry(module_name.to_string()) + .or_default() + .push(cell_name.to_string()); + } + } + + torder +} diff --git a/crates/arbolta/tests/test_module.rs b/crates/arbolta/tests/test_module.rs index 71e6665..c7f5497 100644 --- a/crates/arbolta/tests/test_module.rs +++ b/crates/arbolta/tests/test_module.rs @@ -1,14 +1,10 @@ // Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. // SPDX-License-Identifier: MIT -use arbolta::{ - bit::BitVec, - hardware_module::HardwareModule, - yosys::{SynthConfig, YosysClient}, -}; +use arbolta::{bit::BitVec, hardware_module::HardwareModule}; use once_cell::sync::Lazy; use rstest::rstest; -use std::{collections::HashMap, path::PathBuf}; +use std::collections::HashMap; use yosys_netlist_json::Netlist; const CELL_WRAPPER_NETLIST: &str = include_str!("deps/simcells_wrappers.json"); @@ -21,7 +17,7 @@ static CELL_WRAPPER: Lazy = (0, 0), (1, 1), ])] -#[case::inverter("$_NOT_", [ // (A, Y) +#[case::inverter("$_NOT_", [ (0, 1), (1, 0), ])] @@ -95,247 +91,4 @@ fn test_module_binary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8); 4]) } } -#[rstest] -#[tokio::test] -#[case::add_signed("add_wrapper", true, vec![ - (0, 0, 0), - (-45, 20, -25), - (0, 0, 0), - (-1, 1, 0) -])] -#[case::add_unsigned("add_wrapper", false, vec![ - (2, 3, 5), - (0, 0, 0) -])] -async fn test_module_simcell_2_input_signed( - #[case] module_name: &str, - #[case] signed: bool, - #[case] cases: Vec<(i32, i32, i32)>, -) { - let client = YosysClient { - // TODO: Fix - yosys_server_path: "/home/alexander/research/arbolta/scopefix/target/debug/yosys_server".into(), - ..Default::default() - }; - - let signed_param = if signed { "1" } else { "0" }; - - let synth_config = SynthConfig { - defer: true, - defines: Some(vec!["SIMLIB_NOCHECKS".to_string()]), - parameters: Some(HashMap::from([ - ("A_SIGNED".to_string(), signed_param.to_string()), - ("B_SIGNED".to_string(), signed_param.to_string()), - ("A_WIDTH".to_string(), "8".to_string()), - ("B_WIDTH".to_string(), "8".to_string()), - ("Y_WIDTH".to_string(), "8".to_string()), - ])), - ..Default::default() - }; - - let (netlist, torder) = client - .simple_synth( - &PathBuf::from( - "/home/alexander/research/arbolta/scopefix/crates/arbolta/tests/deps/simlib_wrappers.sv", - ), - Some(module_name.to_string()), - synth_config, - ) - .await - .unwrap(); - - let mut module = HardwareModule::new(&netlist, &torder, module_name).unwrap(); - - for (a, b, expected) in cases { - module.set_port("A", BitVec::from_int(a, None)).unwrap(); - module.set_port("B", BitVec::from_int(b, None)).unwrap(); - module.eval(); - let actual: i32 = module.get_port("Y").unwrap().to_int(); - - assert_eq!(actual, expected, "inputs: `{a}`, `{b}`") - } -} - -/* -#[rstest] -#[case("$reduce_or", 0, 0, 0, 0)] -#[case("$reduce_or", 0, 0, 1, 1)] -#[case("$reduce_or", 0, 1, 0, 1)] -#[case("$reduce_or", 0, 1, 1, 1)] -#[case("$reduce_or", 1, 0, 0, 1)] -#[case("$reduce_or", 1, 0, 1, 1)] -#[case("$reduce_or", 1, 1, 0, 1)] -#[case("$reduce_or", 1, 1, 1, 1)] -fn test_module_3_input_cell( - #[case] cell_type: &str, - #[case] a: u8, - #[case] b: u8, - #[case] c: u8, - #[case] expected: u8, -) { - let inputs = HashMap::from([("A", 3)]); - let outputs = HashMap::from([("Y", 1)]); - let params = HashMap::from([("A_SIGNED", "0"), ("A_WIDTH", "11"), ("Y_WIDTH", "1")]); - let mut cell_module = generate_module(cell_type, inputs, outputs, Some(params)); - cell_module.set_port_shape("A", &[3, 1]).unwrap(); - cell_module - .set_port("A", BitVec::from_ints([a, b, c], Some(1))) - .unwrap(); - cell_module.eval(); - - let actual: u8 = cell_module.get_port("Y").unwrap().to_int(); - assert_eq!(actual, expected); -} - - - -#[rstest] -#[case(true, 2, 3)] -#[case(true, -45, 20)] -#[case(false, 0, 0)] -#[case(true, 0, 0)] -#[case(true, -1, 1)] -fn test_module_sub(#[case] signed: bool, #[case] a: i32, #[case] b: i32) { - let signed_param = if signed { "1" } else { "0" }; - let params = HashMap::from([ - ("A_SIGNED", signed_param), - ("B_SIGNED", signed_param), - ("A_WIDTH", "1000"), - ("B_WIDTH", "1000"), - ("Y_WIDTH", "1000"), - ]); - let inputs = HashMap::from([("A", 8), ("B", 8)]); - let outputs = HashMap::from([("Y", 8)]); - - let mut cell_module = generate_module("$sub", inputs, outputs, Some(params)); - cell_module - .set_port("A", BitVec::from_int(a, None)) - .unwrap(); - cell_module - .set_port("B", BitVec::from_int(b, None)) - .unwrap(); - cell_module.eval(); - - let actual: i32 = cell_module.get_port("Y").unwrap().to_int(); - let expected = a - b; - assert_eq!(actual, expected); -} - -#[rstest] -#[case(true, 2, 3)] -#[case(true, -45, 20)] -#[case(false, 0, 0)] -#[case(true, 0, 0)] -#[case(true, -1, 1)] -fn test_module_mul(#[case] signed: bool, #[case] a: i32, #[case] b: i32) { - let signed_param = if signed { "1" } else { "0" }; - let params = HashMap::from([ - ("A_SIGNED", signed_param), - ("B_SIGNED", signed_param), - ("A_WIDTH", "10000"), - ("B_WIDTH", "10000"), - ("Y_WIDTH", "10000"), - ]); - let inputs = HashMap::from([("A", 16), ("B", 16)]); - let outputs = HashMap::from([("Y", 16)]); - - let mut cell_module = generate_module("$mul", inputs, outputs, Some(params)); - cell_module - .set_port("A", BitVec::from_int(a, None)) - .unwrap(); - cell_module - .set_port("B", BitVec::from_int(b, None)) - .unwrap(); - cell_module.eval(); - - let actual: i32 = cell_module.get_port("Y").unwrap().to_int(); - let expected = a * b; - assert_eq!(actual, expected); -} - -#[rstest] -#[case(true, 2, 3)] -#[case(true, -45, 1)] -#[case(false, 0, 0)] -#[case(true, 0, 0)] -#[case(true, -1, 1)] -fn test_module_shl(#[case] signed: bool, #[case] a: i16, #[case] b: i16) { - let signed_param = if signed { "1" } else { "0" }; - let params = HashMap::from([ - ("A_SIGNED", signed_param), - ("B_SIGNED", "0"), - ("A_WIDTH", "10000"), - ("B_WIDTH", "10000"), - ("Y_WIDTH", "10000"), - ]); - let inputs = HashMap::from([("A", 16), ("B", 16)]); - let outputs = HashMap::from([("Y", 16)]); - - let mut cell_module = generate_module("$shl", inputs, outputs, Some(params)); - cell_module - .set_port("A", BitVec::from_int(a, None)) - .unwrap(); - cell_module - .set_port("B", BitVec::from_int(b, None)) - .unwrap(); - cell_module.eval(); - - let actual: i16 = cell_module.get_port("Y").unwrap().to_int(); - let expected = a << b; - assert_eq!(actual, expected); -} - -#[rstest] -#[case(true, 2)] -#[case(true, -45)] -#[case(true, 0)] -#[case(true, -1)] -fn test_module_neg(#[case] signed: bool, #[case] a: i16) { - let signed_param = if signed { "1" } else { "0" }; - let params = HashMap::from([ - ("A_SIGNED", signed_param), - ("A_WIDTH", "10000"), - ("Y_WIDTH", "10000"), - ]); - let inputs = HashMap::from([("A", 16)]); - let outputs = HashMap::from([("Y", 16)]); - - let mut cell_module = generate_module("$neg", inputs, outputs, Some(params)); - cell_module - .set_port("A", BitVec::from_int(a, None)) - .unwrap(); - cell_module.eval(); - - let actual: i16 = cell_module.get_port("Y").unwrap().to_int(); - let expected = -a; - assert_eq!(actual, expected); -} - -#[rstest] -#[case(vec![30, -44], "1101010000011110")] -#[case(vec![-19, -42], "1101011011101101")] -#[case(vec![3, -4], "1111110000000011")] -#[case(vec![1, -1], "1111111100000001")] -fn test_module_i8_port_array(#[case] vals: Vec, #[case] expected: BitVec) { - let params = HashMap::from([ - ("A_SIGNED", "1"), - ("A_WIDTH", "10000"), - ("Y_WIDTH", "10000"), - ]); - let inputs = HashMap::from([("A", 16)]); - let outputs = HashMap::from([("Y", 16)]); - // Don't actually eval $neg - let mut cell_module = generate_module("$neg", inputs, outputs, Some(params)); - - cell_module - .set_port_shape("A", &[vals.len(), i8::BITS as usize]) - .unwrap(); - cell_module - .set_port("A", BitVec::from_ints(vals, Some(i8::BITS as usize))) - .unwrap(); - - let actual = cell_module.get_port("A").unwrap(); - - assert_eq!(actual.bits, expected.bits) -} -*/ +// TODO: Expand testing diff --git a/crates/arbolta/tests/test_simlib.rs b/crates/arbolta/tests/test_simlib.rs deleted file mode 100644 index c54f4e6..0000000 --- a/crates/arbolta/tests/test_simlib.rs +++ /dev/null @@ -1,77 +0,0 @@ -// use arbolta::bit::{Bit, BitVec}; -// use arbolta::cell::*; -// use arbolta::signal::Signals; -// use rstest::rstest; -// use std::str::FromStr; - -// #[rstest] -// #[case::add_unsigned(|a, b, c| Box::new(Add::new(false, a, b, c)) as Box, vec![ -// ("00000111", "00000111", "00001110"), // 7 + 7 = 49 -// ("00000111", "111", "01110"), // 7 + 7 = 49 -// ("111", "111", "1110"), // 7 + 7 = 14 -// ("111", "111", "10"), // 7 + 7 = 4 (overflow) -// ])] -// #[case::add_signed(|a, b, c| Box::new(Add::new(true, a, b, c)) as Box, vec![ -// ("00000111", "00000111", "00001110"), // 7 + 7 = 49 -// ("00000111", "1001", "00000"), // 7 + -7 = 0 -// ("1001", "11001", "11110010"), // -7 + -7 = -14 -// ("1001", "1001", "10"), // -7 + -7 = -4 (overflow) -// ("111", "111", "10"), // 7 + 7 = 4 (overflow) -// ("1", "0", "111111111111"), // sign extend -// ("0", "1", "111111111111"), // sign extend -// ("1", "1", "111111111110"), // -1 + -1 = -2 -// ("01", "1", "000000000000"), // 1 + -1 = 0 -// ("11111111111111111111111111111111111111111111111111111111111111100010011111010100010011110001101110011011000110100110111110001011", -// "00000000000000000000000000000011000001000110100000010001100111010011111100101100011100101011100000110101000110100011101011011110", -// "00000000000000000000000000000011000001000110100000010001100110110110011100000000110000011101001111010000001101001010101001101001") -// ])] -// #[case::and_unsigned(|a, b, c| Box::new(And::new(false, a, b, c)) as Box, vec![ -// ("00000111", "00000111", "00001110"), // 7 + 7 = 49 -// ("00000111", "111", "01110"), // 7 + 7 = 49 -// ("111", "111", "1110"), // 7 + 7 = 14 -// ("111", "111", "10"), // 7 + 7 = 4 (overflow) -// ])] -// fn test_cell_input2( -// #[case] cell_new: impl Fn(Box<[usize]>, Box<[usize]>, Box<[usize]>) -> Box, -// #[case] cases: Vec<(&str, &str, &str)>, -// ) { -// for (a, b, expected) in cases { -// // Rstest won't automatically convert -// let (a, b, expected) = ( -// BitVec::from_str(a).unwrap(), -// BitVec::from_str(b).unwrap(), -// BitVec::from_str(expected).unwrap(), -// ); - -// let (a_nets, b_nets, y_nets) = ( -// (0..a.shape[1]).collect::>(), -// (a.shape[1]..a.shape[1] + b.shape[1]).collect::>(), -// (a.shape[1] + b.shape[1]..a.shape[1] + b.shape[1] + expected.shape[1]) -// .collect::>(), -// ); - -// let mut signals = Signals::new(a_nets.len() + b_nets.len() + y_nets.len()); - -// // Set inputs -// a_nets -// .iter() -// .enumerate() -// .for_each(|(i, n)| signals.set_net(*n, a.bits[i])); - -// b_nets -// .iter() -// .enumerate() -// .for_each(|(i, n)| signals.set_net(*n, b.bits[i])); - -// let mut cell = cell_new(a_nets.into(), b_nets.into(), y_nets.clone().into()); -// cell.eval(&mut signals); - -// // Get outputs -// let actual: BitVec = y_nets -// .iter() -// .map(|i| signals.get_net(*i)) -// .collect::>() -// .into(); -// assert_eq!(actual, expected, "inputs `{a}`, `{b}`"); -// } -// } diff --git a/crates/python_bindings/py_src/arbolta/design.py b/crates/python_bindings/py_src/arbolta/design.py index fa15ef3..0ad9f19 100644 --- a/crates/python_bindings/py_src/arbolta/design.py +++ b/crates/python_bindings/py_src/arbolta/design.py @@ -97,13 +97,14 @@ def __setattr__(self, name: str, value: Any) -> None: class HardwareDesign: - def __init__( + def __init__( # TODO: de-duplicate defaults self, top_module: str, netlist_path: str, config: dict[str, PortConfig], - yosys_path: str = "yosys", - yosys_server_path="yosys_server", + torder_path: Optional[str] = None, + yosys_path: Optional[str] = "yosys", + yosys_server_path: Optional[str] = "yosys_server", ): """ Parameters @@ -116,7 +117,9 @@ def __init__( Configuration for design ports. """ self.top_module = top_module - self.design = Design(top_module, netlist_path, yosys_path, yosys_server_path) + self.design = Design( + top_module, netlist_path, torder_path, yosys_path, yosys_server_path + ) self.ports = HardwarePorts(config, self.design) def reset(self): diff --git a/crates/python_bindings/src/design.rs b/crates/python_bindings/src/design.rs index f07e71a..d773cae 100644 --- a/crates/python_bindings/src/design.rs +++ b/crates/python_bindings/src/design.rs @@ -6,7 +6,7 @@ use crate::conversion::{ }; use arbol::hardware_module::HardwareModule; use arbol::port::PortDirection; -use arbol::yosys::YosysClient; +use arbol::yosys::{YosysClient, parse_torder}; use bincode::{Decode, Encode}; use pyo3::exceptions::{PyAttributeError, PyException, PyValueError}; use pyo3::prelude::*; @@ -14,49 +14,68 @@ use pyo3::types::{PyBytes, PyDict}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::path::PathBuf; -use yosys_netlist_json as yosys_json; +use yosys_netlist_json::Netlist; #[pyclass(dict, module = "arbolta", name = "Design")] #[derive(Deserialize, Serialize, Decode, Encode)] pub struct PyDesign { #[pyo3(get)] pub top_module: String, - pub netlist_path: String, - module: HardwareModule, + pub netlist_path: String, // TODO: Replace this with actual netlist? + design: HardwareModule, // TODO: RENAME } #[pymethods] impl PyDesign { #[new] - #[pyo3(signature = (top_module, netlist_path, yosys_path="yosys", yosys_server_path="yosys_server"))] + #[pyo3(signature = (top_module, netlist_path, torder_path=None, yosys_path="yosys", yosys_server_path="yosys_server"))] fn __new__( top_module: &str, netlist_path: &str, - yosys_path: &str, - yosys_server_path: &str, + torder_path: Option<&str>, + yosys_path: Option<&str>, + yosys_server_path: Option<&str>, ) -> PyResult { - let client = YosysClient { - yosys_server_path: PathBuf::from(yosys_server_path), - yosys_path: PathBuf::from(yosys_path), - ..Default::default() - }; + // Setup Yosys client + let mut client = YosysClient::default(); - let raw_netlist = std::fs::read(netlist_path)?; - let netlist = yosys_json::Netlist::from_slice(&raw_netlist) - .map_err(|e| PyValueError::new_err(format!("{e}")))?; + if let Some(yosys_path) = yosys_path { + client.yosys_path = PathBuf::from(yosys_path) + } - let rt = tokio::runtime::Runtime::new()?; - let (netlist, torder) = rt - .block_on(client.flatten_netlist(top_module, netlist)) - .map_err(|e| PyValueError::new_err(format!("{e}")))?; + if let Some(yosys_server_path) = yosys_server_path { + client.yosys_server_path = PathBuf::from(yosys_server_path) + } + + // Read raw JSON netlist + let raw_netlist = std::fs::read(netlist_path)?; + let original_netlist = + Netlist::from_slice(&raw_netlist).map_err(|e| PyValueError::new_err(format!("{e}")))?; + + // Get topological cell order and final netlist + let netlist: Netlist; + let torder: HashMap>; + + if let Some(torder_path) = torder_path { + let raw_torder = + std::fs::read_to_string(torder_path).map_err(|e| PyValueError::new_err(format!("{e}")))?; + torder = parse_torder(&raw_torder); + netlist = original_netlist; + } else { + // TODO: Probably don't flatten here, just get topological ordering + let rt = tokio::runtime::Runtime::new()?; + (netlist, torder) = rt + .block_on(client.flatten_netlist(top_module, original_netlist)) + .map_err(|e| PyValueError::new_err(format!("{e}")))?; + } - let module = HardwareModule::new(&netlist, &torder, top_module) + let design = HardwareModule::new(&netlist, &torder, top_module) .map_err(|e| PyValueError::new_err(format!("{e}")))?; Ok(Self { top_module: top_module.to_string(), netlist_path: netlist_path.to_string(), - module, + design, }) } @@ -78,7 +97,7 @@ impl PyDesign { } fn save(&self, path: &str) -> PyResult<()> { - match self.module.save(path) { + match self.design.save(path) { Ok(()) => Ok(()), Err(err) => Err(PyValueError::new_err(format!("{err}"))), } @@ -86,22 +105,22 @@ impl PyDesign { #[staticmethod] fn load(path: &str) -> PyResult { - let module = match HardwareModule::load(path) { + let design = match HardwareModule::load(path) { Ok(module) => module, Err(err) => return Err(PyValueError::new_err(format!("{err}"))), }; - let top_module = module.name.clone(); + let top_module = design.name.clone(); Ok(Self { top_module, netlist_path: String::new(), // TODO: Fix - module, + design, }) } fn get_port_shape(&self, name: &str) -> PyResult<[usize; 2]> { - match self.module.get_port_shape(name) { + match self.design.get_port_shape(name) { Ok(shape) => Ok(shape), Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), } @@ -116,19 +135,19 @@ impl PyDesign { let internal_shape = self.get_port_shape(name)?; let (num_elems, elem_size) = (shape[1], internal_shape[1] / shape[1]); - match self.module.set_port_shape(name, &[num_elems, elem_size]) { + match self.design.set_port_shape(name, &[num_elems, elem_size]) { Ok(()) => Ok(()), Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), } } fn get_module_names(&self) -> Vec { - self.module.submodules.keys().cloned().collect() + self.design.submodules.keys().cloned().collect() } fn get_signal_map(&self) -> HashMap> { self - .module + .design .signal_map .iter() .map(|(name, nets)| (name.to_string(), nets.to_vec())) @@ -137,7 +156,7 @@ impl PyDesign { fn get_cell_info(&self, py: Python<'_>) -> PyResult { let all_cell_info = PyDict::new(py); - for (name, cell_type) in self.module.cell_info.iter() { + for (name, cell_type) in self.design.cell_info.iter() { let cell_info = PyDict::new(py); cell_info.set_item("type", cell_type)?; @@ -149,21 +168,21 @@ impl PyDesign { } pub fn stick_signal(&mut self, net: usize, val: bool) -> PyResult<()> { - match self.module.stick_signal(net, val.into()) { + match self.design.stick_signal(net, val.into()) { Ok(()) => Ok(()), Err(err) => Err(PyException::new_err(format!("{err}"))), } } pub fn unstick_signal(&mut self, net: usize) -> PyResult<()> { - match self.module.unstick_signal(net) { + match self.design.unstick_signal(net) { Ok(()) => Ok(()), Err(err) => Err(PyException::new_err(format!("{err}"))), } } fn set_clock(&mut self, name: &str, polarity: bool) -> PyResult<()> { - let Some(nets) = self.module.signal_map.get(name) else { + let Some(nets) = self.design.signal_map.get(name) else { return Err(PyException::new_err(format!("No signal `{name}`"))); }; @@ -171,13 +190,13 @@ impl PyDesign { return Err(PyException::new_err("Clock net ambiguous".to_string())); } - self.module.set_clock(nets[0], polarity.into()).unwrap(); + self.design.set_clock(nets[0], polarity.into()).unwrap(); Ok(()) } fn set_reset(&mut self, name: &str, polarity: bool) -> PyResult<()> { - let Some(nets) = self.module.signal_map.get(name) else { + let Some(nets) = self.design.signal_map.get(name) else { return Err(PyException::new_err(format!("No signal `{name}`"))); }; @@ -185,29 +204,29 @@ impl PyDesign { return Err(PyException::new_err("Reset net ambiguous".to_string())); } - self.module.set_reset(nets[0], polarity.into()).unwrap(); + self.design.set_reset(nets[0], polarity.into()).unwrap(); Ok(()) } fn reset(&mut self) { - self.module.reset(); + self.design.reset(); } fn eval(&mut self) { - self.module.eval(); + self.design.eval(); } // TODO: Fix this fn eval_clocked(&mut self, cycles: u32) -> PyResult<()> { - match self.module.eval_clocked(Some(cycles)) { + match self.design.eval_clocked(Some(cycles)) { Ok(()) => Ok(()), Err(err) => Err(PyException::new_err(format!("{err}"))), } } fn eval_reset_clocked(&mut self, cycles: u32) -> PyResult<()> { - match self.module.eval_reset_clocked(Some(cycles)) { + match self.design.eval_reset_clocked(Some(cycles)) { Ok(()) => Ok(()), Err(err) => Err(PyException::new_err(format!("{err}"))), } @@ -229,7 +248,7 @@ impl PyDesign { } fn is_port_input(&self, name: &str) -> PyResult { - let direction = match self.module.get_port_direction(name) { + let direction = match self.design.get_port_direction(name) { Ok(direction) => direction, Err(err) => return Err(PyAttributeError::new_err(format!("{err}"))), }; @@ -242,7 +261,7 @@ impl PyDesign { let elem_size = self.get_port_shape(name)?[1]; let bits = self - .module + .design .get_port(name) .map_err(|e| PyAttributeError::new_err(format!("{e}")))?; @@ -307,7 +326,7 @@ impl PyDesign { }; self - .module + .design .set_port(name, bits) .map_err(|e| PyAttributeError::new_err(format!("{e}"))) } diff --git a/requirements/requirements_experiments.txt b/requirements/requirements_experiments.txt new file mode 100644 index 0000000..4a3a7c7 --- /dev/null +++ b/requirements/requirements_experiments.txt @@ -0,0 +1,4 @@ +array-api-compat +brevitas @ git+https://github.com/Xilinx/brevitas.git@dev +fxpmath +tqdm From 0db26fab674dc474c49dd2b05c9ced0558119b81 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Fri, 29 Aug 2025 22:46:52 +0000 Subject: [PATCH 26/64] fixed fault runner --- crates/arbolta/src/bit.rs | 22 +-- crates/arbolta/src/cell/test_macros.rs | 10 +- crates/arbolta/src/yosys.rs | 3 + crates/arbolta/tests/test_bitvec.rs | 2 +- crates/fault/src/lib.rs | 7 +- crates/fault/src/main.rs | 205 ++++++++++++++--------- crates/fault/src/utils.rs | 159 +++++++++++------- crates/python_bindings/src/conversion.rs | 2 +- 8 files changed, 255 insertions(+), 155 deletions(-) diff --git a/crates/arbolta/src/bit.rs b/crates/arbolta/src/bit.rs index f5129ba..e618c95 100644 --- a/crates/arbolta/src/bit.rs +++ b/crates/arbolta/src/bit.rs @@ -4,18 +4,17 @@ use anyhow::Result; use bincode::{Decode, Encode}; use core::fmt; -use derive_more::{BitAnd, BitOr, BitXor, IntoIterator, Not}; +use derive_more::{BitAnd, BitOr, BitXor, Debug, IntoIterator, Not}; use num_traits::{PrimInt, WrappingAdd, WrappingShl, WrappingSub}; use serde::{Deserialize, Serialize}; use std::convert::{From, Into}; -use std::fmt::Debug; use std::str::FromStr; use thiserror::Error; /// Primitive signal value #[repr(transparent)] #[derive( - derive_more::Debug, + Debug, Clone, Eq, Copy, @@ -147,22 +146,17 @@ impl From for String { } } -impl BitVec { - /// Create from iterator of bools. - /// - /// # Arguments - /// * `vals` - Bools to convert. - pub fn from_bools(vals: I) -> Self - where - I: IntoIterator, - { - vals +impl FromIterator for BitVec { + fn from_iter>(iter: I) -> Self { + iter .into_iter() .map(|b| b.into()) .collect::>() .into() } +} +impl BitVec { /// Create from int. /// /// # Arguments @@ -240,7 +234,7 @@ impl BitVec { pub fn to_ints( &self, elem_size: Option, - ) -> impl Iterator { + ) -> impl Iterator + '_ { // let elem_size = elem_size.unwrap_or(std::mem::size_of::() * 8); let elem_size = elem_size.unwrap_or(self.shape[1]); self diff --git a/crates/arbolta/src/cell/test_macros.rs b/crates/arbolta/src/cell/test_macros.rs index 6177e39..043fe34 100644 --- a/crates/arbolta/src/cell/test_macros.rs +++ b/crates/arbolta/src/cell/test_macros.rs @@ -1,8 +1,8 @@ #[macro_export] macro_rules! make_unary_wires { ($a:expr, $y:expr) => {{ - let a_size = $a.shape[1]; - let y_size = $y.shape[1]; + let a_size = $a.bits.len(); + let y_size = $y.bits.len(); let a_nets: Vec = (0..a_size).collect(); let y_nets: Vec = (a_size..a_size + y_size).collect(); (a_nets, y_nets) @@ -15,9 +15,9 @@ pub(crate) use make_unary_wires; #[macro_export] macro_rules! make_binary_wires { ($a:expr, $b:expr, $y:expr) => {{ - let a_size = $a.shape[1]; - let b_size = $b.shape[1]; - let y_size = $y.shape[1]; + let a_size = $a.bits.len(); + let b_size = $b.bits.len(); + let y_size = $y.bits.len(); let a_nets: Vec = (0..a_size).collect(); let b_nets: Vec = (a_size..a_size + b_size).collect(); let y_nets: Vec = (a_size + b_size..a_size + b_size + y_size).collect(); diff --git a/crates/arbolta/src/yosys.rs b/crates/arbolta/src/yosys.rs index 23f3024..451ef3d 100644 --- a/crates/arbolta/src/yosys.rs +++ b/crates/arbolta/src/yosys.rs @@ -196,3 +196,6 @@ pub fn parse_torder(raw: &str) -> HashMap> { torder } + +// Re-export +pub use yosys_json::Netlist; diff --git a/crates/arbolta/tests/test_bitvec.rs b/crates/arbolta/tests/test_bitvec.rs index 03074db..5e63fe6 100644 --- a/crates/arbolta/tests/test_bitvec.rs +++ b/crates/arbolta/tests/test_bitvec.rs @@ -50,7 +50,7 @@ fn test_str_to_bits(#[case] val: String, #[case] expected: Vec) { ], "00100101" )] fn test_bools_to_bits(#[case] vals: Vec, #[case] expected: BitVec) { - assert_eq!(BitVec::from_bools(vals), expected) + assert_eq!(BitVec::from_iter(vals), expected) } #[rstest] diff --git a/crates/fault/src/lib.rs b/crates/fault/src/lib.rs index 62a69e3..5ee948e 100644 --- a/crates/fault/src/lib.rs +++ b/crates/fault/src/lib.rs @@ -1 +1,6 @@ -// pub mod utils; +pub mod utils; + +// #[inline(always)] +// pub fn div_ceil(num: usize, denom: usize) -> usize { +// (num + denom - 1) / denom +// } diff --git a/crates/fault/src/main.rs b/crates/fault/src/main.rs index e977cae..37fe485 100644 --- a/crates/fault/src/main.rs +++ b/crates/fault/src/main.rs @@ -1,85 +1,136 @@ -use anyhow::Result; -// use arbolta::bit::Bit; -// use arbolta::hardware_module::HardwareModule; -// use clap::Parser; -// use fault::utils::worker; +use anyhow::{Result, anyhow, ensure}; +use arbolta::{ + bit::Bit, + hardware_module::HardwareModule, + yosys::{Netlist, parse_torder}, +}; +use clap::Parser; +use fault::utils::matmul; // use indicatif::ParallelProgressIterator; -// use ndarray::parallel::prelude::*; -// use ndarray::{Array2, Array3}; -// use ndarray_npy::read_npy; -// use std::path::PathBuf; +use ndarray::prelude::*; //parallel::prelude::* +use ndarray_npy::read_npy; +use std::path::PathBuf; // use std::sync::Mutex; -// #[derive(Parser, Debug)] -// struct Args { -// /// Name of top module in netlist. -// #[clap(long)] -// top_module: String, -// /// Path to Yosys netlist JSON file. -// #[clap(long)] -// netlist: String, -// /// Path to NumPy inputs file. -// #[clap(long)] -// inputs: PathBuf, -// /// Path to NumPy weights file. -// #[clap(long)] -// weights: PathBuf, -// /// Path to NumPy targets file. -// // #[clap(long)] -// // targets: PathBuf, -// /// Path to list of nets for fault injection campaign. -// #[clap(long)] -// nets: PathBuf, -// #[clap(long)] -// rows: usize, -// #[clap(long)] -// cols: usize, -// #[clap(long)] -// iterations: usize, -// #[clap(long)] -// sx_size: usize, -// #[clap(long)] -// sk_size: usize, -// #[clap(long)] -// m_size: usize, -// } +#[derive(Parser, Debug)] +struct Args { + /// Name of top module in netlist. + #[clap(long)] + top_module: String, + /// Path to topological cell order file. + #[clap(long)] + torder_path: String, + /// Path to Yosys netlist JSON file. + #[clap(long)] + netlist_path: String, + /// Path to NumPy file for matrix A. + #[clap(long)] + a_path: PathBuf, + /// Path to NumPy file for matrix B. + #[clap(long)] + b_path: PathBuf, + /// Path to NumPy targets file. + // #[clap(long)] + // targets: PathBuf, + /// Path to list of nets for fault injection campaign. + // #[clap(long)] + // nets: PathBuf, + #[clap(long)] + rows: usize, + #[clap(long)] + cols: usize, + // #[clap(long)] + // iterations: usize, + #[clap(long)] + x_width: usize, + #[clap(long)] + k_width: usize, + #[clap(long)] + m_width: usize, +} +// TODO: Move setup to generic function and do dynamic dispatch here... +#[allow(non_snake_case)] fn main() -> Result<()> { - // let flags = Args::parse(); - - // // Load everything - // let mut design = HardwareModule::new_from_path(&flags.netlist, &flags.top_module)?; - // let inputs: Array3 = read_npy(&flags.inputs) - // .with_context(|| format!("Failed to read NumPy array from {:?}", flags.inputs))?; // (N, 28, 28) - // let weights: Array2 = read_npy(&flags.weights) - // .with_context(|| format!("Failed to read NumPy array from {:?}", flags.weights))?; // (10, 784) - // // let targets: Array2 = read_npy(&flags.targets) - // // .with_context(|| format!("Failed to read NumPy array from {:?}", flags.targets))?; - - // let nets: Vec = std::fs::read_to_string(&flags.nets)? - // .split_whitespace() - // .map(|x| x.parse().unwrap()) - // .collect(); - - // // Setup designs - // design.set_clock(2, Bit::ONE)?; - // design.set_reset(3, Bit::ZERO)?; - // design.set_port_shape("sx_data_i", &[flags.rows, flags.sx_size])?; - // design.set_port_shape("sk_data_i", &[flags.cols, flags.sk_size])?; - // design.set_port_shape("m_data_o", &[1, flags.m_size])?; - - // let designs: Vec> = (0..rayon::current_num_threads()) - // .map(|_| Mutex::new(design.clone())) - // .collect(); - - // // Start simulation - // nets.into_par_iter().progress().for_each(|n| { - // for stuck_val in [Bit::ZERO, Bit::ONE] { - // let thread_idx = rayon::current_thread_index().unwrap(); - // let design = &mut designs[thread_idx].lock().unwrap(); - // worker(design, &inputs.view(), &weights.view(), n, stuck_val).unwrap(); - // } - // }); + let flags = Args::parse(); + + // Load and parse netlist and topological cell order + let raw_netlist = std::fs::read(flags.netlist_path)?; + let raw_torder = std::fs::read_to_string(flags.torder_path)?; + let netlist = Netlist::from_slice(&raw_netlist)?; + let torder = parse_torder(&raw_torder); + + // Setup design + let (ROWS, COLS) = (flags.rows, flags.cols); // Copy for easier use + + let mut design = HardwareModule::new(&netlist, &torder, &flags.top_module)?; + design.set_clock(2, Bit::ONE)?; + design.set_reset(3, Bit::ZERO)?; + design.set_port_shape("sx_data_i", &[ROWS, flags.x_width])?; + design.set_port_shape("sk_data_i", &[COLS, flags.k_width])?; + design.set_port_shape("m_data_o", &[ROWS, flags.m_width])?; + + // Load and process inputs + let a: Array2 = read_npy(flags.a_path)?; + let b: Array2 = read_npy(flags.b_path)?; + + // a: (X, K), b: (K, Z) + let (X, K, Z) = (a.nrows(), a.ncols(), b.ncols()); + ensure!(K == b.nrows(), anyhow!("{} != {}", K, b.nrows())); + + // Pad `a` and `b` for alignment with systolic array + let (out_x, out_z) = (X.div_ceil(COLS) * COLS, (Z.div_ceil(ROWS) * ROWS)); + + // TODO: May have to adjust for Array3 + let mut a_pad = Array2::::zeros((out_x, K)); + let mut b_pad = Array2::::zeros((K, out_z)); + a_pad.slice_mut(s![0..X, 0..K]).assign(&a.view()); + b_pad.slice_mut(s![0..K, 0..Z]).assign(&b.view()); + + // Setup padded output array (must crop later) + let mut out = Array2::::zeros((out_x, out_z)); + matmul( + &mut design, + COLS, + ROWS, + a_pad.view(), + b_pad.view(), + out.view_mut(), + )?; + + let temp = out.slice(s![0..X, 0..Z]).to_owned(); + println!("{temp}"); + + /* + // Load everything + let mut design = HardwareModule::new_from_path(&flags.netlist, &flags.top_module)?; + let inputs: Array3 = read_npy(&flags.inputs) + .with_context(|| format!("Failed to read NumPy array from {:?}", flags.inputs))?; // (N, 28, 28) + let weights: Array2 = read_npy(&flags.weights) + .with_context(|| format!("Failed to read NumPy array from {:?}", flags.weights))?; // (10, 784) + // let targets: Array2 = read_npy(&flags.targets) + // .with_context(|| format!("Failed to read NumPy array from {:?}", flags.targets))?; + + let nets: Vec = std::fs::read_to_string(&flags.nets)? + .split_whitespace() + .map(|x| x.parse().unwrap()) + .collect(); + + + + let designs: Vec> = (0..rayon::current_num_threads()) + .map(|_| Mutex::new(design.clone())) + .collect(); + + // Start simulation + nets.into_par_iter().progress().for_each(|n| { + for stuck_val in [Bit::ZERO, Bit::ONE] { + let thread_idx = rayon::current_thread_index().unwrap(); + let design = &mut designs[thread_idx].lock().unwrap(); + worker(design, &inputs.view(), &weights.view(), n, stuck_val).unwrap(); + } + }); + */ Ok(()) } diff --git a/crates/fault/src/utils.rs b/crates/fault/src/utils.rs index be99355..faabc81 100644 --- a/crates/fault/src/utils.rs +++ b/crates/fault/src/utils.rs @@ -1,10 +1,12 @@ -// use anyhow::{Ok, Result}; -// use arbolta::{bit::Bit, hardware_module::HardwareModule}; -// use ndarray::{Array1, Array2, ArrayView2, ArrayView3, Axis}; -// use ndarray_npy::write_npy; -// use ndarray_stats::QuantileExt; -// use num_traits::PrimInt; -// use std::fmt::Debug; +use anyhow::Result; +use arbolta::{ + bit::{Bit, BitVec}, + hardware_module::HardwareModule, +}; +use ndarray::prelude::*; +use ndarray_npy::write_npy; +use ndarray_stats::QuantileExt; +use num_traits::{PrimInt, WrappingAdd, WrappingShl, WrappingSub}; // pub fn worker( // design: &mut HardwareModule, @@ -36,53 +38,98 @@ // Ok(()) // } -// /// Run inputs through systolic array. -// /// Expects x: (K,R), k: (K,C) -> y: (C,R) -// pub fn run_sa< -// A: PrimInt + std::ops::BitXorAssign, -// B: PrimInt + std::ops::BitXorAssign, -// C: PrimInt + std::ops::BitXorAssign + Debug, -// >( -// design: &mut HardwareModule, -// x: &ArrayView2, -// k: &ArrayView2, -// y: &mut Array2, -// ) -> Result<()> { -// let iterations: usize = x.shape()[0]; // K - -// design.eval_reset_clocked(Some(1))?; -// for i in 0..iterations { -// while design.get_port_int::("s_ready_o")? == 0 { -// design.eval_clocked(Some(1))?; -// } -// design.set_port_int("s_valid_i", 1_u32)?; -// design.set_port_ndarray("sx_data_i", x.row(i))?; -// design.set_port_ndarray("sk_data_i", k.row(i))?; - -// if i == iterations - 1 { -// design.set_port_int("s_last_i", 1_u32)?; -// } - -// design.eval_clocked(Some(1))?; -// } - -// design.set_port_int("m_ready_i", 1_u32)?; -// design.set_port_int("s_valid_i", 0_u32)?; -// design.set_port_int("s_last_i", 0_u32)?; - -// let mut idx = 0; -// loop { -// if design.get_port_int::("m_valid_o")? == 1 { -// let m_data = design.get_port_ndarray::("m_data_o")?; -// m_data.assign_to(y.row_mut(idx)); -// idx += 1; -// } - -// if design.get_port_int::("m_last_o")? == 1 { -// break; -// } -// design.eval_clocked(Some(1))?; -// } +/// Does a: (X_pad, K) @ b: (K, Z_pad) -> (X_pad, Z_pad) +pub fn matmul( + design: &mut HardwareModule, + cols: usize, + rows: usize, + a_pad: ArrayView2, // (X_pad, K) + b_pad: ArrayView2, // (K, Z_pad) + mut out: ArrayViewMut2, // (X_pad, Z_pad) +) -> Result<()> { + // Number of tiles in each dimension + let tiles_x = out.nrows() / cols; // along rows of a/out + let tiles_z = out.ncols() / rows; // along cols of b/out -// Ok(()) -// } + for i in 0..tiles_z { + let x_block = b_pad.slice(s![.., i * rows..(i + 1) * rows]); + + for j in 0..tiles_x { + let k_block = a_pad.slice(s![j * cols..(j + 1) * cols, ..]); + let mut out_block = out.slice_mut(s![j * cols..(j + 1) * cols, i * rows..(i + 1) * rows]); + run_sa( + design, + x_block.view(), + k_block.t().view(), + out_block.view_mut(), + )?; + } + } + + Ok(()) +} + +/// Run inputs through systolic array. +/// Expects x: (K,R), k: (K,C) -> y: (C,R) +#[inline] +pub fn run_sa( + design: &mut HardwareModule, + x: ArrayView2, // (K, R) + k: ArrayView2, // (K, C) + mut y: ArrayViewMut2, // (C, R) +) -> Result<()> { + let iterations: usize = x.shape()[0]; // K + // TODO: CHECK + let sx_size = design.get_port_shape("sx_data_i")?[1]; + let sz_size = design.get_port_shape("sk_data_i")?[1]; + + design.eval_reset_clocked(Some(1))?; + + // TODO: Check iterations, report failed... + for i in 0..iterations { + // SA not ready for inputs + while design.get_port("s_ready_o")?.bits[0] == Bit::ZERO { + // TODO: Check some cycle limit + design.eval_clocked(Some(1))?; + } + + design.set_port("s_valid_i", [Bit::ONE])?; + design.set_port( + "sx_data_i", + BitVec::from_ints(x.row(i).iter().copied(), Some(sx_size)), + )?; + design.set_port( + "sk_data_i", + BitVec::from_ints(k.row(i).iter().copied(), Some(sz_size)), + )?; + + if i == iterations - 1 { + design.set_port("s_last_i", [Bit::ONE])?; + } + + design.eval_clocked(Some(1))?; + } + + design.set_port("m_ready_i", [Bit::ONE])?; + design.set_port("s_valid_i", [Bit::ZERO])?; + design.set_port("s_last_i", [Bit::ZERO])?; + + let mut idx = 0; + loop { + if design.get_port("m_valid_o")?.bits[0] == Bit::ONE { + let m_data = design.get_port("m_data_o")?; + let m_data = m_data.to_ints::(None); // Module should set size + y.row_mut(idx).assign(&Array1::from_iter(m_data)); + + idx += 1; + } + + if design.get_port("m_last_o")?.bits[0] == Bit::ONE { + break; + } + + design.eval_clocked(Some(1))?; + } + + Ok(()) +} diff --git a/crates/python_bindings/src/conversion.rs b/crates/python_bindings/src/conversion.rs index 412477b..e97b117 100644 --- a/crates/python_bindings/src/conversion.rs +++ b/crates/python_bindings/src/conversion.rs @@ -20,7 +20,7 @@ pub fn bits_to_bool_numpy(bits: &BitVec, numpy_array: &Bound<'_, PyAny>) -> PyRe pub fn bool_numpy_to_bits(numpy_array: &Bound<'_, PyAny>) -> PyResult { let buffer = numpy_array.extract::>()?; - Ok(BitVec::from_bools(buffer.to_owned_array())) + Ok(BitVec::from_iter(buffer.to_owned_array())) } pub fn bits_to_int_numpy( From 12891749ca382458919c2b7c8cef167927a54106 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Sat, 30 Aug 2025 01:04:33 +0000 Subject: [PATCH 27/64] Initial commit for CIFAR10 --- experiments/cifar10/train.ipynb | 115 ++++++++++ experiments/cifar10/utils/dataset.py | 91 ++++++++ experiments/cifar10/utils/minifloat.py | 160 +++++++++++++ experiments/cifar10/utils/resnet.py | 303 +++++++++++++++++++++++++ experiments/cifar10/utils/train.py | 45 ++++ 5 files changed, 714 insertions(+) create mode 100644 experiments/cifar10/train.ipynb create mode 100644 experiments/cifar10/utils/dataset.py create mode 100644 experiments/cifar10/utils/minifloat.py create mode 100644 experiments/cifar10/utils/resnet.py create mode 100644 experiments/cifar10/utils/train.py diff --git a/experiments/cifar10/train.ipynb b/experiments/cifar10/train.ipynb new file mode 100644 index 0000000..6083d5e --- /dev/null +++ b/experiments/cifar10/train.ipynb @@ -0,0 +1,115 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "62d18188", + "metadata": {}, + "outputs": [], + "source": [ + "# import numpy as np\n", + "import torch\n", + "import torch.nn as nn\n", + "\n", + "# import torch.nn.functional as F\n", + "import torch.optim as optim\n", + "import torch.optim.lr_scheduler as lrs\n", + "\n", + "from utils.dataset import get_cifar10_dataloaders, create_calibration_dataloader\n", + "from utils.resnet import quant_resnet18, filter_params\n", + "from utils.train import train_for_epoch\n", + "\n", + "DATASET_PATH = \"./data\" # Change me\n", + "EXPORT_PATH = \"./model_outputs\" # Change me" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "05c65f1a", + "metadata": {}, + "outputs": [], + "source": [ + "device = (\n", + " \"xpu\"\n", + " if torch.xpu.is_available() # For the only person who has an Intel GPU\n", + " else \"cuda\"\n", + " if torch.cuda.is_available()\n", + " else \"cpu\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "115d90f7", + "metadata": {}, + "outputs": [], + "source": [ + "# Training parameters\n", + "learning_rate_init = 0.1\n", + "learning_rate_step_size = 30\n", + "learning_rate_gamma = 0.1\n", + "momentum = 0.5\n", + "weight_decay = 1e-5\n", + "\n", + "# Setup model\n", + "model = quant_resnet18(weight_bit_width=4, act_bit_width=4, num_classes=10)\n", + "model = model.to(device)\n", + "criterion = nn.CrossEntropyLoss()\n", + "optimizer = optim.SGD(\n", + " filter_params(model.named_parameters(), weight_decay),\n", + " lr=learning_rate_init,\n", + " weight_decay=weight_decay,\n", + ")\n", + "scheduler = lrs.StepLR(\n", + " optimizer, step_size=learning_rate_step_size, gamma=learning_rate_gamma\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1ca65d52", + "metadata": {}, + "outputs": [], + "source": [ + "train_loader, test_loader = get_cifar10_dataloaders(\n", + " DATASET_PATH, pin_memory=True, pin_memory_device=device\n", + ")\n", + "calib_loader = create_calibration_dataloader(dataset=train_loader.dataset)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cc5b590a", + "metadata": {}, + "outputs": [], + "source": [ + "for epoch in range(20): # 90 default\n", + " train_loss = train_for_epoch(model, device, train_loader, criterion, optimizer)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "arbolta", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/experiments/cifar10/utils/dataset.py b/experiments/cifar10/utils/dataset.py new file mode 100644 index 0000000..55ea0f3 --- /dev/null +++ b/experiments/cifar10/utils/dataset.py @@ -0,0 +1,91 @@ +# Based on https://github.com/Xilinx/brevitas/blob/dev/src/brevitas_examples/imagenet_classification/a2q/utils.py +import numpy as np +import torchvision +import torchvision.transforms as transforms +from torch.utils.data import DataLoader, Dataset, Subset + + +def get_cifar10_dataloaders( + data_root: str, + batch_size_train: int = 128, + batch_size_test: int = 100, + num_workers: int = 0, + pin_memory: bool = True, + pin_memory_device: str = "cpu", + download: bool = False, +) -> tuple[DataLoader, DataLoader]: + mean, std = [0.491, 0.482, 0.447], [0.247, 0.243, 0.262] + + # Transformations + transform_train = transforms.Compose( + [ + transforms.RandomCrop(32, padding=4), + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ] + ) + + transform_test = transforms.Compose( + [ + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ] + ) + + train_dataset = torchvision.datasets.CIFAR10( + root=data_root, + train=True, + download=download, + transform=transform_train, + ) + + test_dataset = torchvision.datasets.CIFAR10( + root=data_root, + train=False, + download=download, + transform=transform_test, + ) + + train_loader = DataLoader( + train_dataset, + batch_size=batch_size_train, + shuffle=True, + num_workers=num_workers, + pin_memory=pin_memory, + pin_memory_device=pin_memory_device, + persistent_workers=num_workers > 0, # Remove? + ) + + test_loader = DataLoader( + test_dataset, + batch_size=batch_size_test, + shuffle=False, + num_workers=num_workers, + pin_memory=pin_memory, + pin_memory_device=pin_memory_device, + persistent_workers=num_workers > 0, # Remove? + ) + + return train_loader, test_loader + + +def create_calibration_dataloader( + dataset: Dataset, + batch_size: int = 256, + num_workers: int = 0, + subset_size: int = 1000, + pin_memory: bool = True, + pin_memory_device: str = "cpu", +) -> DataLoader: + all_indices = np.arange(len(dataset)) + cur_indices = np.random.choice(all_indices, size=subset_size) + subset = Subset(dataset, cur_indices) + loader = DataLoader( + subset, + batch_size=batch_size, + num_workers=num_workers, + pin_memory=pin_memory, + pin_memory_device=pin_memory_device, + ) + return loader diff --git a/experiments/cifar10/utils/minifloat.py b/experiments/cifar10/utils/minifloat.py new file mode 100644 index 0000000..888ab44 --- /dev/null +++ b/experiments/cifar10/utils/minifloat.py @@ -0,0 +1,160 @@ +from typing import Optional, cast + +import array_api_compat +import brevitas.nn as qnn +import numpy as np +import torch.nn as nn +from brevitas.inject import ExtendedInjector +from brevitas.quant.experimental.float_base import ( + FloatActBase, + FloatBase, + FloatWeightBase, +) +from numpy.typing import NDArray +from torch import FloatTensor, IntTensor + +__all__ = ["mf_to_raw", "raw_to_mf", "fp_mixin_factory", "MinifloatQuantizer"] + + +# Helper for converting quantized floats to raw binary +def mf_to_raw( + x: FloatTensor | NDArray[np.float32], + exponent_bit_width: int, + mantissa_bit_width: int, + exponent_bias: int, + eps: float, +) -> IntTensor | NDArray[np.int_]: + xp = array_api_compat.array_namespace(x) + + emin = -exponent_bias + 1 + + sign = xp.astype(xp.signbit(x), int) + exp = xp.astype(xp.floor(xp.log2(xp.abs(x) + eps)), int) + exp = xp.clip(exp, min=emin) + man = xp.abs(x) / xp.exp2(exp) + + exp_bits = exp - xp.astype((man < 1), int) + exponent_bias # Denorm + man_bits = xp.astype((man * (1 << mantissa_bit_width)), int) + man_bits = man_bits & ((1 << mantissa_bit_width) - 1) + + out = ( + (sign << (exponent_bit_width + mantissa_bit_width)) + | (exp_bits << mantissa_bit_width) + | man_bits + ) + + format_width = 1 + exponent_bit_width + mantissa_bit_width + # Cast to unsigned int for interfacing w/ Arbolta + out_dtype = ( + xp.uint8 + if format_width <= 8 + else xp.uint16 + if format_width <= 16 + else xp.uint32 + if format_width <= 32 + else xp.uint64 + ) + + return xp.astype(out, out_dtype) + + +# Helper for converting raw binary minifloats to floats +def raw_to_mf( + x: IntTensor | NDArray[np.int_], + exponent_bit_width: int, + mantissa_bit_width: int, + exponent_bias: int, +) -> FloatTensor | NDArray[np.float32]: + xp = array_api_compat.array_namespace(x) + x = xp.astype(x, int) # Cast to signed int to allow for bit shifting + + emin = -exponent_bias + 1 + + sign_mask = 1 << (exponent_bit_width + mantissa_bit_width) + exp_mask = ((1 << exponent_bit_width) - 1) << mantissa_bit_width + man_mask = (1 << mantissa_bit_width) - 1 + + sign = xp.where((x & sign_mask) == 0, 1.0, -1.0) + exp_denorm = xp.astype((x & exp_mask) >> mantissa_bit_width, xp.float32) + man_denorm = xp.astype(x & man_mask, xp.float32) / (2**mantissa_bit_width) + + exp = xp.where(exp_denorm == 0, emin, exp_denorm - exponent_bias) + man = xp.where(exp_denorm == 0, man_denorm, 1.0 + man_denorm) + + return sign * man * xp.exp2(exp) + + +# Helper for creating custom minifloat quantizers +def fp_mixin_factory( + exponent_bit_width: int, mantissa_bit_width: int, base_class: FloatBase +): + bit_width = 1 + exponent_bit_width + mantissa_bit_width + name = f"Fp{bit_width}e{exponent_bit_width}m{mantissa_bit_width}" + mixin = type( + name + "Mixin", + (ExtendedInjector,), + { + # Add sign bit + "bit_width": bit_width, + "exponent_bit_width": exponent_bit_width, + "mantissa_bit_width": mantissa_bit_width, + "saturating": True, + }, + ) + + if base_class is FloatActBase: + class_name = name + "Act" + elif base_class is FloatWeightBase: + class_name = name + "Weight" + else: + raise TypeError("Unsupported Float Base") + + return type(class_name, (mixin, base_class), {}) + + +class MinifloatQuantizer(nn.Module): + def __init__( + self, + exponent_bit_width: int, + mantissa_bit_width: int, + exponent_bias: Optional[int] = None, + eps: float = 1e-9, + ): + super(MinifloatQuantizer, self).__init__() + if exponent_bias is None: + exponent_bias = (2 ** (exponent_bit_width - 1)) - 1 + + self.exponent_bit_width = exponent_bit_width + self.mantissa_bit_width = mantissa_bit_width + self.exponent_bias = cast(int, exponent_bias) + self.eps = eps + self.ident = qnn.QuantIdentity( + act_quant=fp_mixin_factory( + exponent_bit_width, + mantissa_bit_width, + FloatActBase, # pyright: ignore[reportArgumentType] + ) + ) + + def forward( + self, x: FloatTensor, return_raw: bool = False + ) -> IntTensor | FloatTensor: + out = self.ident(x) + + if return_raw: + out = mf_to_raw( + out, + self.exponent_bit_width, + self.mantissa_bit_width, + self.exponent_bias, + self.eps, + ) + out = cast(IntTensor, out) + + return out + + def dequantize_raw(self, x: IntTensor) -> FloatTensor: + out = raw_to_mf( + x, self.exponent_bit_width, self.mantissa_bit_width, self.exponent_bias + ) + return cast(FloatTensor, out) diff --git a/experiments/cifar10/utils/resnet.py b/experiments/cifar10/utils/resnet.py new file mode 100644 index 0000000..b0f7227 --- /dev/null +++ b/experiments/cifar10/utils/resnet.py @@ -0,0 +1,303 @@ +# Based on https://github.com/Xilinx/brevitas/blob/master/src/brevitas_examples/bnn_pynq/models/resnet.py +# from abc import ABCMeta, abstractmethod + +import brevitas.nn as qnn + +# import torch +import torch.nn as nn + +# import torch.nn.functional as F +# from brevitas.quant.experimental.float_base import ( +# FloatActBase, +# FloatWeightBase, +# ) +# from brevitas.quant_tensor.float_quant_tensor import FloatQuantTensor +# from brevitas.quant_tensor.int_quant_tensor import IntQuantTensor +from torch import Tensor + +# from .minifloat import fp_mixin_factory, mf_to_raw + +# DELETE +from brevitas.quant import Int8WeightPerChannelFloat +from brevitas.quant import Int8WeightPerTensorFloat +from brevitas.quant import Int32Bias +from brevitas.quant import TruncTo8bit +from brevitas.quant_tensor import QuantTensor + + +def make_quant_conv2d( + in_channels, + out_channels, + kernel_size, + weight_bit_width, + weight_quant, + stride=1, + padding=0, + bias=False, +): + return qnn.QuantConv2d( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + bias=bias, + weight_quant=weight_quant, + weight_bit_width=weight_bit_width, + ) + + +# No input quantizer? +class QuantBasicBlock(nn.Module): + expansion = 1 + + def __init__( + self, + in_planes, # num input filters + planes, # num output filters + stride=1, + bias=False, + shared_quant_act=None, + act_bit_width=8, # Assumes input is uint8 + weight_bit_width=8, + weight_quant=Int8WeightPerChannelFloat, + ): + super(QuantBasicBlock, self).__init__() + self.conv1 = make_quant_conv2d( + in_planes, + planes, + kernel_size=3, + stride=stride, + padding=1, + bias=bias, + weight_bit_width=weight_bit_width, + weight_quant=weight_quant, + ) + self.bn1 = nn.BatchNorm2d(planes) + self.relu1 = qnn.QuantReLU(bit_width=act_bit_width, return_quant_tensor=True) + self.conv2 = make_quant_conv2d( + planes, + planes, + kernel_size=3, + stride=1, + padding=1, + bias=bias, + weight_bit_width=weight_bit_width, + weight_quant=weight_quant, + ) + self.bn2 = nn.BatchNorm2d(planes) + self.downsample = nn.Sequential() + if stride != 1 or in_planes != self.expansion * planes: + self.downsample = nn.Sequential( + make_quant_conv2d( + in_planes, + self.expansion * planes, + kernel_size=1, + stride=stride, + padding=0, + bias=bias, + weight_bit_width=weight_bit_width, + weight_quant=weight_quant, + ), + nn.BatchNorm2d(self.expansion * planes), + # We add a ReLU activation here because FINN requires the same sign along residual adds + qnn.QuantReLU(bit_width=act_bit_width, return_quant_tensor=True), + ) + # Redefine shared_quant_act whenever shortcut is performing downsampling + shared_quant_act = self.downsample[-1] + if shared_quant_act is None: + shared_quant_act = qnn.QuantReLU( + bit_width=act_bit_width, return_quant_tensor=True + ) + # We add a ReLU activation here because FINN requires the same sign along residual adds + self.relu2 = shared_quant_act + self.relu_out = qnn.QuantReLU(return_quant_tensor=True, bit_width=act_bit_width) + + def forward(self, x): + out = self.relu1(self.bn1(self.conv1(x))) + out = self.relu2(self.bn2(self.conv2(out))) + if len(self.downsample): + x = self.downsample(x) + # Check that the addition is made explicitly among QuantTensor structures + assert isinstance(out, QuantTensor), "Perform add among QuantTensors" + assert isinstance(x, QuantTensor), "Perform add among QuantTensors" + out = out + x + out = self.relu_out(out) + return out + + +class QuantResNet(nn.Module): + def __init__( + self, + block_impl, + num_blocks: list[int], + first_maxpool=False, + zero_init_residual=False, + num_classes=10, + act_bit_width=8, + weight_bit_width=8, + round_average_pool=False, + last_layer_bias_quant=Int32Bias, + weight_quant=Int8WeightPerChannelFloat, + first_layer_weight_quant=Int8WeightPerChannelFloat, + last_layer_weight_quant=Int8WeightPerTensorFloat, + ): + super(QuantResNet, self).__init__() + self.in_planes = 64 + self.conv1 = make_quant_conv2d( + 3, + 64, + kernel_size=3, + stride=1, + padding=1, + weight_bit_width=8, + weight_quant=first_layer_weight_quant, + ) + self.bn1 = nn.BatchNorm2d(64) + shared_quant_act = qnn.QuantReLU( + bit_width=act_bit_width, return_quant_tensor=True + ) + self.relu = shared_quant_act + # MaxPool is typically present for ImageNet but not for CIFAR10 + if first_maxpool: + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + else: + self.maxpool = nn.Identity() + + self.layer1, shared_quant_act = self._make_layer( + block_impl, + 64, + num_blocks[0], + 1, + shared_quant_act, + weight_bit_width, + act_bit_width, + weight_quant, + ) + self.layer2, shared_quant_act = self._make_layer( + block_impl, + 128, + num_blocks[1], + 2, + shared_quant_act, + weight_bit_width, + act_bit_width, + weight_quant, + ) + self.layer3, shared_quant_act = self._make_layer( + block_impl, + 256, + num_blocks[2], + 2, + shared_quant_act, + weight_bit_width, + act_bit_width, + weight_quant, + ) + self.layer4, _ = self._make_layer( + block_impl, + 512, + num_blocks[3], + 2, + shared_quant_act, + weight_bit_width, + act_bit_width, + weight_quant, + ) + + # Performs truncation to 8b (without rounding), which is supported in FINN + avgpool_float_to_int_impl_type = "ROUND" if round_average_pool else "FLOOR" + self.final_pool = qnn.TruncAvgPool2d( + kernel_size=4, + trunc_quant=TruncTo8bit, + float_to_int_impl_type=avgpool_float_to_int_impl_type, + ) + # Keep last layer at 8b + self.linear = qnn.QuantLinear( + 512 * block_impl.expansion, + num_classes, + weight_bit_width=8, + bias=True, + bias_quant=last_layer_bias_quant, + weight_quant=last_layer_weight_quant, + ) + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu") + elif isinstance(m, nn.BatchNorm2d): + nn.init.constant_(m.weight, 1) + nn.init.constant_(m.bias, 0) + + # Zero-initialize the last BN in each residual branch + if zero_init_residual: + for m in self.modules(): + if isinstance(m, QuantBasicBlock) and m.bn2.weight is not None: + nn.init.constant_(m.bn2.weight, 0) + + def _make_layer( + self, + block_impl, + planes, + num_blocks, + stride, + shared_quant_act, + weight_bit_width, + act_bit_width, + weight_quant, + ): + strides = [stride] + [1] * (num_blocks - 1) + layers = [] + for stride in strides: + block = block_impl( + in_planes=self.in_planes, + planes=planes, + stride=stride, + bias=False, + shared_quant_act=shared_quant_act, + act_bit_width=act_bit_width, + weight_bit_width=weight_bit_width, + weight_quant=weight_quant, + ) + layers.append(block) + shared_quant_act = layers[-1].relu_out + self.in_planes = planes * block_impl.expansion + return nn.Sequential(*layers), shared_quant_act + + def forward(self, x: Tensor): + # There is no input quantizer, we assume the input is already 8b RGB + out = self.relu(self.bn1(self.conv1(x))) + out = self.maxpool(out) + out = self.layer1(out) + out = self.layer2(out) + out = self.layer3(out) + out = self.layer4(out) + out = self.final_pool(out) + out = out.view(out.size(0), -1) + out = self.linear(out) + return out + + +def quant_resnet18(weight_bit_width, act_bit_width, num_classes) -> QuantResNet: + model = QuantResNet( + block_impl=QuantBasicBlock, + num_blocks=[2, 2, 2, 2], + num_classes=num_classes, + weight_bit_width=weight_bit_width, + act_bit_width=act_bit_width, + ) + return model + + +def filter_params(named_params, decay): + decay_params, no_decay_params = [], [] + for name, param in named_params: + # Do not apply weight decay to the bias or any scaling parameters + if "scaling" in name or name.endswith(".bias"): + no_decay_params.append(param) + else: + decay_params.append(param) + return [ + {"params": no_decay_params, "weight_decay": 0.0}, + {"params": decay_params, "weight_decay": decay}, + ] diff --git a/experiments/cifar10/utils/train.py b/experiments/cifar10/utils/train.py new file mode 100644 index 0000000..a4f3ab8 --- /dev/null +++ b/experiments/cifar10/utils/train.py @@ -0,0 +1,45 @@ +# from typing import Optional + +# import torch +import torch.nn as nn +from torch import Tensor +from torch.nn.modules.loss import _Loss +from torch.optim.optimizer import Optimizer +from torch.utils.data import DataLoader +from tqdm.notebook import tqdm + +__all__ = [ + "train_for_epoch", +] + + +def train_for_epoch( + model: nn.Module, + device: str, + train_loader: DataLoader, + criterion: _Loss, + optimizer: Optimizer, +) -> None: + model.train() + + correct_total = 0 + size_total = 0 + with tqdm(train_loader) as tepoch: + tepoch.set_description("Train") + for images, targets in tepoch: + images, targets = images.to(device), targets.to(device) + + optimizer.zero_grad() + logits: Tensor = model(images) + loss: Tensor = criterion(logits, targets) + loss.backward() + optimizer.step() + + # Compute metrics + preds = logits.argmax(dim=1, keepdim=True).squeeze() + num_correct = (preds == targets).sum().item() + correct_total += num_correct + size_total += len(targets) + accuracy = correct_total / size_total + + tepoch.set_postfix(loss=loss.item(), acc=format(accuracy, "3.2%")) From de2e22cc1fc5f3507d0d076049e9ff110e22f69c Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Mon, 15 Sep 2025 19:09:46 +0000 Subject: [PATCH 28/64] Fixed set_net bug --- crates/arbolta/src/signal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/arbolta/src/signal.rs b/crates/arbolta/src/signal.rs index 6d60bea..394aa03 100644 --- a/crates/arbolta/src/signal.rs +++ b/crates/arbolta/src/signal.rs @@ -38,7 +38,7 @@ impl Signals { #[inline] pub fn set_net(&mut self, net: usize, val: Bit) { // Constant or unchanged, do nothing - if !self.constant[net] && self.nets[net] == val { + if self.constant[net] || self.nets[net] == val { return; } From efa07c05c8911a62511e91c3e8e87c8bfc9acae5 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 22 Oct 2025 00:07:26 +0000 Subject: [PATCH 29/64] Forgot to remove old benchmarking stuff --- crates/arbolta/Cargo.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index 11c7311..36ffe2a 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -4,10 +4,6 @@ version = "0.1.0" authors = ["AMD Research & Advanced Development"] edition = "2024" -[[bench]] -name = "benchmark" -harness = false - [dependencies] anyhow = { workspace = true } bincode = { workspace = true } From 56e900f25af79c6fde05eb505ddcddb9ab29e451 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 26 Nov 2025 21:45:48 +0000 Subject: [PATCH 30/64] Large graph revamp --- crates/arbolta/Cargo.toml | 1 + crates/arbolta/src/cell/mod.rs | 434 +- crates/arbolta/src/hardware_module.rs | 356 +- crates/arbolta/src/lib.rs | 1 + crates/arbolta/src/port.rs | 71 +- crates/arbolta/src/temp2.rs | 377 + crates/arbolta/src/yosys.rs | 84 + .../arbolta/tests/deps/simcells_wrappers.json | 11968 +--------------- crates/arbolta/tests/generate_deps.py | 63 + crates/arbolta/tests/test_module.rs | 37 +- crates/fault/src/main.rs | 10 +- crates/python_bindings/Cargo.toml | 2 +- crates/python_bindings/src/design.rs | 60 +- 13 files changed, 1163 insertions(+), 12301 deletions(-) create mode 100644 crates/arbolta/src/temp2.rs create mode 100644 crates/arbolta/tests/generate_deps.py diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index 36ffe2a..deebd16 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -28,3 +28,4 @@ yosys_server = { path = "../yosys_server" } criterion = "0.5" pprof = { version = "0.15", features = ["criterion", "flamegraph"] } once_cell = { workspace = true } +petgraph = "0.8.3" diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index bd6e375..ec6363c 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -6,11 +6,10 @@ mod test_macros; pub use simcells::*; pub use simlib::*; -use crate::{bit::Bit, signal::Signals}; +use crate::{bit::Bit, signal::Signals, temp2::TopoCell}; use bincode::{Decode, Encode}; use enum_dispatch::enum_dispatch; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; use thiserror::Error; use yosys_netlist_json as yosys_json; @@ -81,25 +80,270 @@ pub enum CellError { Direction(String), #[error("Bad parameter `{0}` = `{1:?}`")] Parameter(String, yosys_json::AttributeVal), + #[error("Bad attribute `{0}` = `{1:?}`")] + Attribute(String, yosys_json::AttributeVal), } -/// Parse global net from `BitVal`. -/// Errors if bit direction is not supported. -fn parse_bit(bit: &yosys_json::BitVal) -> Result { - match bit { - yosys_json::BitVal::N(net) => Ok(*net), - yosys_json::BitVal::S(constant) => match constant { - yosys_json::SpecialBit::_0 => Ok(0), // Global 0 - yosys_json::SpecialBit::_1 => Ok(1), // Global 1 - yosys_json::SpecialBit::X => Err(CellError::Direction("X".to_string())), - yosys_json::SpecialBit::Z => Err(CellError::Direction("Z".to_string())), - }, - } -} - +// TODO: Make this take topo cell AND move into try_from +// TODO: Move matching to hashmap of function pointers +// will allow for different cell libraries... and reuse of cells in new ways /// Generate a cell given its Yosys netlist description /// # Arguments /// * `cell` - Yosys cell +pub fn create_cell(cell: &TopoCell) -> Result { + // TODO: Error handling.. + let Some(connections) = &cell.connections else { + todo!() + }; + let Some(parameters) = &cell.parameters else { + todo!() + }; + + let new_cell: Cell = match cell.cell_type.as_str() { + // Sim cells + "BUF" | "$_BUF_" => Cell::Buffer(Buffer::new(connections["A"][0], connections["Y"][0])), + "NOT" | "$_NOT_" => Cell::Inverter(Inverter::new(connections["A"][0], connections["Y"][0])), + "AND" | "$_AND_" => Cell::And(And::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "NAND" | "$_NAND_" => Cell::Nand(Nand::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "OR" | "$_OR_" => Cell::Or(Or::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "NOR" | "$_NOR_" => Cell::Nor(Nor::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "XOR" | "$_XOR_" => Cell::Xor(Xor::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "XNOR" | "$_XNOR_" => Cell::Xnor(Xnor::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "ANDNOT" | "$_ANDNOT_" => Cell::AndNot(AndNot::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "ORNOT" | "$_ORNOT_" => Cell::OrNot(OrNot::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + )), + "$_MUX_" => Cell::Mux2(Mux2::new( + connections["A"][0], + connections["B"][0], + connections["S"][0], + connections["Y"][0], + )), + "$_NMUX_" => Cell::NMux2(NMux2::new( + connections["A"][0], + connections["B"][0], + connections["S"][0], + connections["Y"][0], + )), + "$_AOI3_" => Cell::AndOrInvert(AndOrInvert::new( + connections["A"][0], + connections["B"][0], + connections["C"][0], + connections["Y"][0], + )), + "$_OAI3_ " => Cell::OrAndInvert(OrAndInvert::new( + connections["A"][0], + connections["B"][0], + connections["C"][0], + connections["Y"][0], + )), + "DFF" | "$_DFF_P_" => Cell::Dff(Dff::new( + Bit::ONE, + connections["C"][0], + connections["D"][0], + connections["Q"][0], + )), + "$_SDFF_PP0_ " => Cell::DffReset(DffReset::new( + Bit::ONE, + Bit::ONE, + Bit::ZERO, + connections["C"][0], + connections["R"][0], + connections["D"][0], + connections["Q"][0], + )), + // Sim lib + "$not" => Cell::Not(Not::new( + parameters["A_SIGNED"] != 0, + connections["A"].clone(), + connections["Y"].clone(), + )), + "$pos" => Cell::Pos(Pos::new( + parameters["A_SIGNED"] != 0, + connections["A"].clone(), + connections["Y"].clone(), + )), + "$neg" => Cell::Neg(Neg::new( + parameters["A_SIGNED"] != 0, + connections["A"].clone(), + connections["Y"].clone(), + )), + "$add" => Cell::Add(Add::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$sub" => Cell::Sub(Sub::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$mul" => Cell::Mul(Mul::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$div" => Cell::Div(Div::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$mod" => Cell::Modulus(Modulus::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$le" => Cell::Le(Le::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$ge" => Cell::Ge(Ge::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$gt" => Cell::Gt(Gt::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$shl" => Cell::Shl(Shl::new( + parameters["A_SIGNED"] != 0, + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$shr" => Cell::Shr(Shr::new( + parameters["A_SIGNED"] != 0, + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$dff" => Cell::Reg(Reg::new( + (parameters["CLK_POLARITY"] != 0).into(), + connections["CLK"][0], + connections["D"].clone(), + connections["Q"].clone(), + )), + "$aldff" => Cell::ALDff(ALDff::new( + (parameters["CLK_POLARITY"] != 0).into(), + (parameters["ALOAD_POLARITY"] != 0).into(), + connections["CLK"][0], + connections["ALOAD"][0], + connections["AD"].clone(), + connections["D"].clone(), + connections["Q"].clone(), + )), + "$mux" => Cell::Mux(Mux::new( + connections["S"][0], + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$bmux" => Cell::BMux(BMux::new( + connections["S"].clone(), + connections["A"].clone(), + connections["Y"].clone(), + )), + "$pmux" => Cell::PMux(PMux::new( + connections["S"].clone(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$logic_and" => Cell::LogicAnd(LogicAnd::new( + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$logic_not" => Cell::LogicNot(LogicNot::new( + connections["A"].clone(), + connections["Y"].clone(), + )), + "$reduce_or" | "$reduce_bool" => Cell::ReduceOr(ReduceOr::new( + connections["A"].clone(), + connections["Y"].clone(), + )), + "$reduce_and" => Cell::ReduceAnd(ReduceAnd::new( + connections["A"].clone(), + connections["Y"].clone(), + )), + "$and" => Cell::ProcAnd(ProcAnd::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$or" => Cell::ProcOr(ProcOr::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$xor" => Cell::ProcXor(ProcXor::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$eq" => Cell::Eq(Eq::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + "$ne" => Cell::Ne(Ne::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + )), + _ => return Err(CellError::Unsupported(cell.cell_type.to_string())), + }; + + Ok(new_cell) +} + +/* pub fn create_cell(cell: &yosys_json::Cell) -> Result { // Port name -> net let mut connections: HashMap> = HashMap::new(); @@ -207,160 +451,162 @@ pub fn create_cell(cell: &yosys_json::Cell) -> Result { // Sim lib "$not" => Cell::Not(Not::new( parameters["A_SIGNED"] != 0, - connections["A"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["Y"].clone(), )), "$pos" => Cell::Pos(Pos::new( parameters["A_SIGNED"] != 0, - connections["A"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["Y"].clone(), )), "$neg" => Cell::Neg(Neg::new( parameters["A_SIGNED"] != 0, - connections["A"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["Y"].clone(), )), "$add" => Cell::Add(Add::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$sub" => Cell::Sub(Sub::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$mul" => Cell::Mul(Mul::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$div" => Cell::Div(Div::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$mod" => Cell::Modulus(Modulus::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$le" => Cell::Le(Le::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$ge" => Cell::Ge(Ge::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$gt" => Cell::Gt(Gt::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$shl" => Cell::Shl(Shl::new( parameters["A_SIGNED"] != 0, - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$shr" => Cell::Shr(Shr::new( parameters["A_SIGNED"] != 0, - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$dff" => Cell::Reg(Reg::new( - (parameters["CLK_POLARITY"] != 0).into(), + (parameters["CLK_POLARITY"] != 0), connections["CLK"][0], - connections["D"].clone().into(), - connections["Q"].clone().into(), + connections["D"].clone(), + connections["Q"].clone(), )), "$aldff" => Cell::ALDff(ALDff::new( - (parameters["CLK_POLARITY"] != 0).into(), - (parameters["ALOAD_POLARITY"] != 0).into(), + (parameters["CLK_POLARITY"] != 0), + (parameters["ALOAD_POLARITY"] != 0), connections["CLK"][0], connections["ALOAD"][0], - connections["AD"].clone().into(), - connections["D"].clone().into(), - connections["Q"].clone().into(), + connections["AD"].clone(), + connections["D"].clone(), + connections["Q"].clone(), )), "$mux" => Cell::Mux(Mux::new( connections["S"][0], - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$bmux" => Cell::BMux(BMux::new( - connections["S"].clone().into(), - connections["A"].clone().into(), - connections["Y"].clone().into(), + connections["S"].clone(), + connections["A"].clone(), + connections["Y"].clone(), )), "$pmux" => Cell::PMux(PMux::new( - connections["S"].clone().into(), - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["S"].clone(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$logic_and" => Cell::LogicAnd(LogicAnd::new( - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$logic_not" => Cell::LogicNot(LogicNot::new( - connections["A"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["Y"].clone(), )), "$reduce_or" | "$reduce_bool" => Cell::ReduceOr(ReduceOr::new( - connections["A"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["Y"].clone(), )), "$reduce_and" => Cell::ReduceAnd(ReduceAnd::new( - connections["A"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["Y"].clone(), )), "$and" => Cell::ProcAnd(ProcAnd::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$or" => Cell::ProcOr(ProcOr::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$xor" => Cell::ProcXor(ProcXor::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$eq" => Cell::Eq(Eq::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), "$ne" => Cell::Ne(Ne::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone().into(), - connections["B"].clone().into(), - connections["Y"].clone().into(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), )), _ => return Err(CellError::Unsupported(cell.cell_type.to_string())), }; Ok(new_cell) } + +*/ diff --git a/crates/arbolta/src/hardware_module.rs b/crates/arbolta/src/hardware_module.rs index b9be375..ca18bb6 100644 --- a/crates/arbolta/src/hardware_module.rs +++ b/crates/arbolta/src/hardware_module.rs @@ -7,26 +7,39 @@ use crate::{ port::PortError, port::{Port, PortDirection, parse_bit}, signal::Signals, + temp2::*, + yosys::Netlist, }; use bincode::{Decode, Encode}; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fmt::Debug}; use thiserror::Error; -use yosys_netlist_json as yosys_json; -pub type PortMap = HashMap; -pub type SignalMap = HashMap>; -pub type SubmoduleMap = HashMap>; // TODO: Change to box? +#[derive(Clone, Debug, Deserialize, Serialize, Decode, Encode)] +pub enum CellType { + Submodule(String), + Primitive(String), +} + +#[derive(Default, Clone, Debug, Deserialize, Serialize, Decode, Encode)] +pub struct SubModule { + ports: HashMap, + nets: HashMap>, + // TODO: make pointer to cells in hardware module? + // TODO: make pointer to cells in hardware module? + cells: HashMap, // cell, cell type? +} + +// Names +pub type SubmoduleMap = HashMap, SubModule>; #[derive(Default, Clone, Debug, Deserialize, Serialize, Decode, Encode)] pub struct HardwareModule { - pub name: String, - pub ports: PortMap, - pub signal_map: SignalMap, + pub top_module: String, pub signals: Signals, pub cell_info: HashMap, pub cells: Box<[Cell]>, - pub submodules: SubmoduleMap, // (name, netnames) + pub submodules: SubmoduleMap, pub clock_net: Option<(usize, Bit)>, // (net, polarity) pub reset_net: Option<(usize, Bit)>, // (net, polarity) } @@ -43,6 +56,10 @@ pub enum ModuleError { Cell(#[from] CellError), #[error("Port error `{0}`")] Port(#[from] PortError), + #[error("No reset configured")] + MissingReset, + #[error("No clock configured")] + MissingClock, #[error("Signal `{0}` doesn't exist")] MissingSignal(String), #[error("Net `{0}` doesn't exist")] @@ -59,103 +76,115 @@ pub enum ToggleCount { Falling, Total, } - -impl HardwareModule { - // TODO: Fix - #[allow(unused)] - pub fn load(path: &str) -> anyhow::Result { - todo!() - } - - #[allow(unused)] - pub fn save(&self, path: &str) -> anyhow::Result<()> { - todo!() - } - - pub fn new( - netlist: &yosys_json::Netlist, - topo_order: &HashMap>, - top_module: &str, - ) -> Result { - // Check that top module exists - let Some(synth_module) = netlist.modules.get(top_module) else { - return Err(ModuleError::TopModule(top_module.to_string())); - }; - - let mut cells = vec![]; - let mut cell_info: HashMap = HashMap::new(); - let Some(module_torder) = topo_order.get(top_module) else { - return Err(ModuleError::TopoOrder(top_module.to_string())); - }; - - for cell_name in module_torder.iter().rev() { - // $scopeinfo not in torder - - let Some(synth_cell) = synth_module.cells.get(cell_name) else { - return Err(CellError::NotFound(cell_name.to_string()).into()); +fn get_submodules( + parents: &[TopoCellParent], + hierarchy: &TopoHierarchy, + global_nets: &TopoNetMap, + netlist: &Netlist, +) -> Result { + let name = parents + .iter() + .map(|p| p.name.clone()) + .collect::>(); + let top_parent = parents.last().unwrap(); // Assume top is last parent... + + let mut submodules = SubmoduleMap::new(); + + // Create new submodule + let mut submodule = SubModule::default(); + if let Some(synth_module) = netlist.modules.get(&top_parent.cell_type.to_string()) { + // TODO: Error checking? + for (cell_name, cell_info) in &synth_module.cells { + // TODO: Make pointer to cell in HardwareModule cells + // TODO: Use hierarchy to tell if cell is submodule or not + let cell_type = if netlist.modules.contains_key(&cell_info.cell_type) { + CellType::Submodule(cell_info.cell_type.to_owned()) + } else { + CellType::Primitive(cell_info.cell_type.to_owned()) }; - - let cell = create_cell(synth_cell)?; - cells.push(cell); - cell_info.insert(cell_name.to_string(), synth_cell.cell_type.to_string()); + submodule.cells.insert(cell_name.to_owned(), cell_type); } - let mut signal_map = SignalMap::new(); - let mut max_signal = 0; - for (name, netname) in &synth_module.netnames { - let mut nets = vec![]; - for bit in &netname.bits { - let net = parse_bit(bit)?; - max_signal = max_signal.max(net); - nets.push(net); + // Add nets + for (net_name, net_info) in &synth_module.netnames { + let nets: Vec = net_info + .bits + .iter() + .map(parse_bit) + .collect::>()?; + // Translate + let nets: Vec = nets.iter().map(|n| global_nets[top_parent][n]).collect(); + + // Add ports + if let Some(port_info) = synth_module.ports.get(net_name) { + let port = Port { + direction: PortDirection::try_from(&port_info.direction)?, + shape: [1, nets.len()], + }; + + submodule.ports.insert(net_name.to_owned(), port); } - signal_map.insert(name.to_string(), nets.into_boxed_slice()); - } - let mut ports = PortMap::new(); - for (name, port) in &synth_module.ports { - ports.insert(name.clone(), Port::new(port)?); + submodule + .nets + .insert(net_name.to_owned(), nets.into_boxed_slice()); } - // Temp? - let mut submodules = SubmoduleMap::default(); - for (name, netname) in &synth_module.netnames { - if ports.contains_key(name) { - continue; - } + } - let port = yosys_json::Port { - direction: yosys_json::PortDirection::Output, - bits: netname.bits.clone(), - offset: Default::default(), - upto: Default::default(), - signed: Default::default(), - }; + submodules.insert(name.into(), submodule); - ports.insert(name.clone(), Port::new(&port)?); - - // Check if net belongs to scopeinfo cell - let split = name.split(".").collect::>(); - if let Some(&scope_name) = split.first() - && let Some(cell) = synth_module.cells.get(scope_name) - && cell.cell_type == "$scopeinfo" - { - submodules - .entry(scope_name.to_string()) - .or_default() - .push(name.to_string()); - } + if let Some(entry) = hierarchy.get(top_parent) { + for child in entry { + let mut parents = parents.to_vec(); + parents.push(child.clone()); + + submodules.extend(get_submodules(&parents, hierarchy, global_nets, netlist)?); } + } - let mut signals = Signals::new(max_signal + 1); + Ok(submodules) +} + +impl HardwareModule { + pub fn new(top_module: &str, netlist: &Netlist) -> Result { + let top_module_parent = TopoCellParent::new(top_module.to_string(), top_module.to_string()); + let mut topo_cells = collect_cells( + top_module, + std::slice::from_ref(&top_module_parent), + netlist, + )?; + let hierarchy = get_cell_hierarchy(&topo_cells); + + let mut global_nets = TopoNetMap::new(); + let mut global_net_max = 0; + collect_nets( + &top_module_parent, + &hierarchy, + netlist, + &mut global_nets, + &mut global_net_max, + )?; + + update_cells(topo_cells.as_mut_slice(), &global_nets, netlist)?; + + let graph = build_graph(&top_module_parent, &topo_cells, &global_nets, netlist)?; + let topo_order = get_topo_cell_order(&graph); + + let submodules = get_submodules(&[top_module_parent], &hierarchy, &global_nets, netlist)?; + + let cells: Vec = topo_order + .into_iter() + .map(create_cell) + .collect::, _>>()?; + + let mut signals = Signals::new(global_net_max + 1); signals.set_constant(0, Bit::ZERO); signals.set_constant(1, Bit::ONE); Ok(Self { - name: top_module.to_string(), - ports, - signal_map, + top_module: top_module.to_owned(), signals, - cell_info, + cell_info: HashMap::new(), cells: cells.into_boxed_slice(), submodules, clock_net: None, @@ -163,11 +192,9 @@ impl HardwareModule { }) } - pub fn get_signal_nets(&self, name: &str) -> Result, ModuleError> { - match self.signal_map.get(name) { - Some(net) => Ok(net.clone()), - None => Err(ModuleError::MissingSignal(name.to_string())), - } + pub fn get_signal_nets(&self, name: &str) -> Option> { + let submodule = &self.submodules[std::slice::from_ref(&self.top_module)]; + submodule.nets.get(name).cloned() } pub fn stick_signal(&mut self, net: usize, value: Bit) -> Result<(), ModuleError> { @@ -217,9 +244,7 @@ impl HardwareModule { .iter_mut() .for_each(|cell| cell.eval(&mut self.signals)); - let after = self.signals.nets.clone(); - - if before == after { + if before == self.signals.nets { break; } } @@ -227,7 +252,7 @@ impl HardwareModule { pub fn eval_clocked(&mut self, cycles: Option) -> Result<(), ModuleError> { let Some((clock_net, polarity)) = self.clock_net else { - return Err(ModuleError::MissingSignal("clock".to_string())); + return Err(ModuleError::MissingClock); }; let cycles = cycles.unwrap_or(1); @@ -245,7 +270,7 @@ impl HardwareModule { pub fn eval_reset_clocked(&mut self, cycles: Option) -> Result<(), ModuleError> { let Some((reset_net, polarity)) = self.reset_net else { - return Err(ModuleError::MissingSignal("reset".to_string())); + return Err(ModuleError::MissingReset); }; self.signals.set_net(reset_net, polarity); @@ -264,33 +289,52 @@ impl HardwareModule { } pub fn set_port_shape(&mut self, name: &str, shape: &[usize; 2]) -> Result<(), ModuleError> { - match self.ports.get_mut(name) { + let submodule = self + .submodules + .get_mut(std::slice::from_ref(&self.top_module)) + .unwrap(); + + match submodule.ports.get_mut(name) { Some(port) => Ok(port.set_shape(shape)?), None => Err(ModuleError::MissingPort(name.to_string())), } } pub fn get_port_shape(&self, name: &str) -> Result<[usize; 2], ModuleError> { - match self.ports.get(name) { + let submodule = self + .submodules + .get(std::slice::from_ref(&self.top_module)) + .unwrap(); + + match submodule.ports.get(name) { Some(port) => Ok(port.get_shape()), None => Err(ModuleError::MissingPort(name.to_string())), } } pub fn get_port_direction(&self, name: &str) -> Result { - match self.ports.get(name) { + let submodule = self + .submodules + .get(std::slice::from_ref(&self.top_module)) + .unwrap(); + + match submodule.ports.get(name) { Some(port) => Ok(port.direction.clone()), None => Err(ModuleError::MissingPort(name.to_string())), } } pub fn get_port(&self, name: &str) -> Result { - let Some(port) = self.ports.get(name) else { + let submodule = self + .submodules + .get(std::slice::from_ref(&self.top_module)) + .unwrap(); + + let (Some(port), Some(nets)) = (submodule.ports.get(name), submodule.nets.get(name)) else { return Err(ModuleError::MissingPort(name.to_string())); }; - let mut bits: BitVec = port - .nets + let mut bits: BitVec = nets .iter() .map(|idx| self.signals.get_net(*idx)) .collect::>() @@ -305,12 +349,16 @@ impl HardwareModule { I: IntoIterator, B: Into, { - let Some(port) = self.ports.get(name) else { + let submodule = self + .submodules + .get_mut(std::slice::from_ref(&self.top_module)) + .unwrap(); + + let (Some(_port), Some(nets)) = (submodule.ports.get(name), submodule.nets.get(name)) else { return Err(ModuleError::MissingPort(name.to_string())); }; - port - .nets + nets .iter() .zip(vals.into_iter().map(Into::into)) .for_each(|(net, b)| self.signals.set_net(*net, b)); @@ -318,6 +366,42 @@ impl HardwareModule { Ok(()) } + pub fn get_all_signal_nets(&self) -> HashMap> { + let mut all_nets = HashMap::new(); + for (parents, submodule) in &self.submodules { + let parent_name = parents.join("."); + + for (net_name, nets) in &submodule.nets { + let name = format!("{parent_name}.{net_name}"); + + all_nets.insert(name, nets.to_vec()); + } + } + + all_nets + } + + pub fn get_all_signal_values(&self) -> HashMap { + let mut all_nets = HashMap::new(); + for (parents, submodule) in &self.submodules { + let parent_name = parents.join("."); + + for (net_name, nets) in &submodule.nets { + let name = format!("{parent_name}.{net_name}"); + + let bits: BitVec = nets + .iter() + .map(|idx| self.signals.get_net(*idx)) + .collect::>() + .into(); + + all_nets.insert(name, bits); + } + } + + all_nets + } + // pub fn get_cell_breakdown(&self) -> HashMap { // todo!() // } @@ -344,31 +428,41 @@ impl HardwareModule { total } - pub fn get_submodule_toggles( - &self, - name: &str, - category: ToggleCount, - ) -> Result { - let Some(net_names) = self.submodules.get(name) else { - return Err(ModuleError::MissingSubmodule(name.to_string())); - }; - - let mut total: u64 = 0; - for name in net_names { - self.ports[name].nets.iter().for_each(|&n| { - total += match category { - ToggleCount::Falling => self.signals.get_toggles_falling(n), - ToggleCount::Rising => self.signals.get_toggles_rising(n), - ToggleCount::Total => self.signals.get_total_toggles(n), - } - }); - } - - Ok(total) - } + // pub fn get_submodule_toggles( + // &self, + // name: &str, + // category: ToggleCount, + // ) -> Result { + // let Some(net_names) = self.submodules.get(name) else { + // return Err(ModuleError::MissingSubmodule(name.to_string())); + // }; + + // let mut total: u64 = 0; + // for name in net_names { + // self.ports[name].nets.iter().for_each(|&n| { + // total += match category { + // ToggleCount::Falling => self.signals.get_toggles_falling(n), + // ToggleCount::Rising => self.signals.get_toggles_rising(n), + // ToggleCount::Total => self.signals.get_total_toggles(n), + // } + // }); + // } + + // Ok(total) + // } // #[allow(unused_variables)] // pub fn search_module_total_toggle_count(&self, name: &str) -> Result { // todo!() // } + // TODO: Fix + #[allow(unused)] + pub fn load(path: &str) -> anyhow::Result { + todo!() + } + + #[allow(unused)] + pub fn save(&self, path: &str) -> anyhow::Result<()> { + todo!() + } } diff --git a/crates/arbolta/src/lib.rs b/crates/arbolta/src/lib.rs index 24bc5b9..6049c64 100644 --- a/crates/arbolta/src/lib.rs +++ b/crates/arbolta/src/lib.rs @@ -6,4 +6,5 @@ pub mod cell; pub mod hardware_module; pub mod port; pub mod signal; +pub mod temp2; pub mod yosys; diff --git a/crates/arbolta/src/port.rs b/crates/arbolta/src/port.rs index 1bdf58b..b8eb13a 100644 --- a/crates/arbolta/src/port.rs +++ b/crates/arbolta/src/port.rs @@ -7,7 +7,7 @@ use std::fmt::Debug; use thiserror::Error; use yosys_netlist_json as yosys_json; -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, Encode, Decode)] pub enum PortDirection { Input, Output, @@ -16,8 +16,7 @@ pub enum PortDirection { #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Encode, Decode)] pub struct Port { pub direction: PortDirection, - pub nets: Box<[usize]>, - pub shape: [usize; 2], + pub shape: [usize; 2], // TODO: Change this to option? } /// Parse global net from `BitVal`. @@ -47,35 +46,57 @@ pub enum PortError { }, } -impl Port { - pub fn new(port: &yosys_json::Port) -> Result { - let direction = match port.direction { - yosys_json::PortDirection::InOut => Err(PortError::Direction("inout".to_string()))?, - yosys_json::PortDirection::Input => PortDirection::Input, - yosys_json::PortDirection::Output => PortDirection::Output, - }; +impl TryFrom<&yosys_json::PortDirection> for PortDirection { + type Error = PortError; + fn try_from(direction: &yosys_json::PortDirection) -> Result { + match direction { + yosys_json::PortDirection::InOut => Err(PortError::Direction("inout".to_string())), + yosys_json::PortDirection::Input => Ok(PortDirection::Input), + yosys_json::PortDirection::Output => Ok(PortDirection::Output), + } + } +} - let nets: Vec = port.bits.iter().map(parse_bit).collect::>()?; - let shape = [1, nets.len()]; +// impl TryFrom<&yosys_json::Port> for Port { +// type Error = PortError; +// fn try_from(port: &yosys_json::Port) -> Result { +// let direction = PortDirection::try_from(&port.direction)?; +// let nets: Vec = port.bits.iter().map(parse_bit).collect::>()?; +// let shape = [1, nets.len()]; - Ok(Self { - direction, - nets: nets.into(), - shape, - }) - } +// Ok(Self { +// direction, +// nets: nets.into(), +// shape, +// }) +// } +// } + +impl Port { + // TODO: Remove and use try_from + // pub fn new(port: &yosys_json::Port) -> Result { + // let direction = PortDirection::try_from(&port.direction)?; + // let nets: Vec = port.bits.iter().map(parse_bit).collect::>()?; + // let shape = [1, nets.len()]; + + // Ok(Self { + // direction, + // nets: nets.into(), + // shape, + // }) + // } pub fn set_shape(&mut self, shape: &[usize; 2]) -> Result<(), PortError> { - if shape[0] * shape[1] != self.nets.len() { - return Err(PortError::Shape { + if shape[0] * shape[1] != self.shape[0] * self.shape[1] { + Err(PortError::Shape { requested: *shape, actual: self.shape, - }); - } + }) + } else { + (self.shape[0], self.shape[1]) = (shape[0], shape[1]); - (self.shape[0], self.shape[1]) = (shape[0], shape[1]); - - Ok(()) + Ok(()) + } } pub fn get_shape(&self) -> [usize; 2] { diff --git a/crates/arbolta/src/temp2.rs b/crates/arbolta/src/temp2.rs new file mode 100644 index 0000000..a22aa6e --- /dev/null +++ b/crates/arbolta/src/temp2.rs @@ -0,0 +1,377 @@ +use crate::{ + hardware_module::ModuleError, + port::{PortDirection, parse_bit}, +}; +use petgraph::visit::IntoNodeReferences; +use petgraph::{prelude::*, visit::DfsPostOrder}; // dot::Dot +use std::collections::{BTreeMap, HashMap, HashSet}; +// use std::io::Write; +use yosys_netlist_json as yosys_json; + +#[derive(Debug, Clone, Eq, PartialEq, Hash, Default, derive_more::Constructor)] +pub struct TopoCellParent { + pub name: String, + pub cell_type: String, +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash, Default, derive_more::Display)] +#[display("name: {name}")] +pub struct TopoCell { + pub parents: Vec, + pub name: String, + pub cell_type: String, + // Only after getting global nets... + // BTreeMap is hashable! :) + pub connections: Option>>, + pub port_directions: Option>, + pub parameters: Option>, +} + +pub fn collect_cells( + top_module: &str, + parents: &[TopoCellParent], + netlist: &yosys_json::Netlist, +) -> Result, ModuleError> { + let synth_module = netlist + .modules + .get(top_module) + .ok_or(ModuleError::TopModule(top_module.to_string()))?; + + let mut flattened_cells = vec![]; + for (cell_name, cell_info) in &synth_module.cells { + let cell_type = &cell_info.cell_type; + + // Submodule + if netlist.modules.contains_key(cell_type) { + let mut parents = parents.to_vec(); + parents.push(TopoCellParent { + name: cell_name.to_owned(), + cell_type: cell_type.to_owned(), + }); + + flattened_cells.append(&mut collect_cells(cell_type, &parents, netlist)?); + // Primitive cell + } else { + flattened_cells.push(TopoCell { + parents: parents.to_vec(), + name: cell_name.to_owned(), + cell_type: cell_type.to_owned(), + ..Default::default() + }); + } + } + + Ok(flattened_cells) +} + +pub type TopoHierarchy = HashMap>; + +pub fn get_cell_hierarchy(cells: &[TopoCell]) -> TopoHierarchy { + let mut hierarchy = TopoHierarchy::new(); + cells.iter().map(|c| &c.parents).for_each(|parents| { + // For each topo cell's parents + (0..parents.len() - 1).for_each(|i| { + hierarchy + .entry(parents[i].clone()) // Previous parent + .or_default() + .insert(parents[i + 1].clone()); // Current parent + }); + }); + + hierarchy +} + +pub type TopoNetMap = HashMap>; + +pub fn collect_nets( + parent: &TopoCellParent, + hierarchy: &TopoHierarchy, + netlist: &yosys_json::Netlist, + global_nets: &mut TopoNetMap, + global_net_max: &mut usize, +) -> Result<(), ModuleError> { + let synth_module = netlist + .modules + .get(&parent.cell_type) + .ok_or(ModuleError::TopModule(parent.cell_type.to_string()))?; + + let nets = synth_module + .netnames + .values() + .flat_map(|net_info| &net_info.bits) + .map(parse_bit) + .collect::, _>>()?; + + if !global_nets.contains_key(parent) { + global_nets.insert(parent.clone(), HashMap::from([(0, 0), (1, 1)])); + + nets.iter().for_each(|&n| { + global_nets.get_mut(parent).unwrap().insert(n, n); + *global_net_max = std::cmp::max(*global_net_max, n); + }); + } + + // println!("GLOBAL NETS"); + // for (key, val) in global_nets.iter() { + // println!("{key:?}"); + // let mut vals: Vec<(&usize, &usize)> = val.iter().collect(); + // vals.sort(); + // for v in vals { + // println!("{v:?}"); + // } + // } + + // Add rest of nets + for n in &nets { + if !global_nets[parent].contains_key(n) { + *global_net_max += 1; + global_nets + .get_mut(parent) + .unwrap() + .insert(*n, *global_net_max); + } + } + + // Add children + if let Some(children) = hierarchy.get(parent) { + for child in children { + global_nets.insert(child.clone(), HashMap::from([(0, 0), (1, 1)])); + let synth_cell = &synth_module.cells[&child.name.to_string()]; + let child_module = &netlist.modules[&child.cell_type.to_string()]; + + for (port_name, port_info) in &child_module.ports { + let conn_bits = &synth_cell.connections[port_name]; + for (net_bit, conn_bit) in port_info.bits.iter().zip(conn_bits) { + let net = parse_bit(net_bit)?; + // Translate connection + let conn = global_nets[parent][&parse_bit(conn_bit)?]; + + global_nets.get_mut(child).unwrap().insert(net, conn); + } + } + + collect_nets(child, hierarchy, netlist, global_nets, global_net_max)?; + } + } + + Ok(()) +} + +pub fn update_cells( + cells: &mut [TopoCell], + global_nets: &TopoNetMap, + netlist: &yosys_json::Netlist, +) -> Result<(), ModuleError> { + for cell in cells.iter_mut() { + // TODO: error handling + let parent = cell.parents.last().unwrap(); + let synth_cell = &netlist.modules[&parent.cell_type.to_string()].cells[&cell.name.to_string()]; + + let mut connections = BTreeMap::new(); + let mut port_directions = BTreeMap::new(); + for (port_name, bits) in &synth_cell.connections { + let direction = PortDirection::try_from(&synth_cell.port_directions[port_name])?; + let mut nets: Vec = bits.iter().map(parse_bit).collect::, _>>()?; + // Actual mapping + nets = nets.iter().map(|n| global_nets[parent][n]).collect(); + + port_directions.insert(port_name.to_string(), direction); + connections.insert(port_name.to_string(), nets.into_boxed_slice()); + } + + // Add parameters + let mut parameters = BTreeMap::new(); + for (param_name, param) in &synth_cell.parameters { + // TODO: Error handling + if let Some(param) = param.to_number() { + parameters.insert(param_name.to_string(), param); + } else { + println!("Couldn't convert parameter `{param_name}={param:?}`"); + } + } + + cell.port_directions = Some(port_directions); + cell.connections = Some(connections); + cell.parameters = Some(parameters); + } + + Ok(()) +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash, derive_more::Display)] +pub enum TopoNode { + Cell(TopoCell), + Net(usize), +} +// TODO: Change net node or graph edge to some enum/struct that has netname... +// enum for multi bit port +// enum::single net(name, b) +// enum::multi net(name, index, b) merge later... +pub type NetlistGraph = DiGraph; + +pub fn build_graph( + top_module: &TopoCellParent, + cells: &[TopoCell], + global_nets: &TopoNetMap, + netlist: &yosys_json::Netlist, +) -> Result { + let synth_module = netlist + .modules + .get(&top_module.cell_type.to_string()) + .ok_or(ModuleError::TopModule(top_module.cell_type.to_string()))?; + + let mut cell_nodes = HashMap::::new(); + let mut net_nodes = HashMap::::new(); + let mut graph = NetlistGraph::new(); + + // All top module port inputs driven by "input" cell for ordering + let input_cell = TopoCell { + parents: vec![top_module.clone()], + name: "input".to_string(), + cell_type: "input".to_string(), + ..Default::default() + }; + + // Create all cell nodes (ensure input cell is first) + std::slice::from_ref(&input_cell) + .iter() + .chain(cells) + .for_each(|c| { + cell_nodes.insert(c.clone(), graph.add_node(TopoNode::Cell(c.clone()))); + }); + + // Create all net nodes + for net in global_nets.values().flat_map(|c| c.values()) { + // Avoid duplicates + if !net_nodes.contains_key(net) { + net_nodes.insert(*net, graph.add_node(TopoNode::Net(*net))); + } + } + + // Connect input cell to top module input nets + for port_info in synth_module.ports.values() { + if port_info.direction == yosys_json::PortDirection::Input { + for net in port_info + .bits + .iter() + .map(parse_bit) + .collect::, _>>()? + { + // Avoid duplicates + if !graph.contains_edge(cell_nodes[&input_cell], net_nodes[&net]) { + graph.add_edge(cell_nodes[&input_cell], net_nodes[&net], net); + } + } + } + } + + // Connect all other cells/nets + for cell in cells { + let cell_node = &cell_nodes[cell]; + + // TODO: Error handling + for (port_name, nets) in cell.connections.as_ref().unwrap() { + // TODO: Error handling + let direction = &cell.port_directions.as_ref().unwrap()[port_name]; + + for net in nets { + let net_node = &net_nodes[net]; + + match direction { + PortDirection::Input if !graph.contains_edge(*net_node, *cell_node) => { + graph.add_edge(*net_node, *cell_node, *net); + } + PortDirection::Output if !graph.contains_edge(*cell_node, *net_node) => { + graph.add_edge(*cell_node, *net_node, *net); + } + _ => continue, // Edge exists, do nothing + } + } + } + } + + Ok(graph) +} + +pub fn get_topo_cell_order(graph: &NetlistGraph) -> Vec<&TopoCell> { + let mut cell_graph: DiGraph<&TopoCell, usize> = DiGraph::new(); + let mut orig_cell_nodes = HashMap::<&TopoCell, NodeIndex>::new(); + let mut cell_nodes = HashMap::<&TopoCell, NodeIndex>::new(); + + // Create new graph with only cell nodes + // Get node indices for cells in original graph + for (node_index, node) in graph.node_references() { + if let TopoNode::Cell(cell) = node { + orig_cell_nodes.insert(cell, node_index); + cell_nodes.insert(cell, cell_graph.add_node(cell)); + } + } + + // Create connections + for (cell, orig_node_index) in orig_cell_nodes { + // All neighbors should be net nodes + for net_index in graph.neighbors(orig_node_index) { + let TopoNode::Net(n) = &graph[net_index] else { + todo!() + }; + + // All neighbors should be cell nodes + for driven_node in graph.neighbors(net_index) { + let driver_node = &cell_nodes[cell]; + let TopoNode::Cell(driven_cell) = graph.node_weight(driven_node).unwrap() else { + todo!() + }; + let driven_node = &cell_nodes[driven_cell]; + + cell_graph.add_edge(*driver_node, *driven_node, *n); + } + } + } + + // TODO: check weight that node is == input + let mut topo_search = DfsPostOrder::new(&cell_graph, NodeIndex::from(0)); + let mut found = vec![]; + + while let Some(visited) = topo_search.next(&cell_graph) { + let cell = cell_graph.node_weight(visited).unwrap(); + if cell.cell_type != "input" { + // Don't include input cell/node + found.push(*cell); + } + } + + found.reverse(); + found +} + +pub fn parse_module( + top_module: &str, + netlist: &yosys_json::Netlist, +) -> Result<(NetlistGraph, TopoNetMap), ModuleError> { + let top_cell_parent = TopoCellParent { + name: top_module.to_string(), + cell_type: top_module.to_string(), + }; + + let flattened_cells = collect_cells(top_module, std::slice::from_ref(&top_cell_parent), netlist)?; + let hierarchy = get_cell_hierarchy(&flattened_cells); + + let mut global_nets = TopoNetMap::new(); + collect_nets( + &top_cell_parent, + &hierarchy, + netlist, + &mut global_nets, + &mut 0, + )?; + + let graph = build_graph(&top_cell_parent, &flattened_cells, &global_nets, netlist)?; + + // let dot_output = format!("{}", Dot::new(&graph)); + // let mut file = std::fs::File::create("graph.dot").expect("Could not create file"); + // file + // .write_all(dot_output.as_bytes()) + // .expect("Could not write to file"); + + Ok((graph, global_nets)) +} diff --git a/crates/arbolta/src/yosys.rs b/crates/arbolta/src/yosys.rs index 451ef3d..090e71c 100644 --- a/crates/arbolta/src/yosys.rs +++ b/crates/arbolta/src/yosys.rs @@ -1,3 +1,87 @@ +// cell::CellError, +// use crate::{ +// cell::CellError, +// hardware_module::ModuleError, +// port::{PortDirection, parse_bit}, +// temp2::{TopoCell, TopoNetMap}, +// }; +// use std::collections::HashMap; +// use yosys_netlist_json as yosys_json; + +// #[derive(Debug, Clone, Eq, PartialEq, Default)] +// pub struct SynthCell<'a> { +// cell_type: &'a str, +// parameters: HashMap<&'a str, usize>, +// // attributes: HashMap<&'a str, &'a str>, +// port_directions: HashMap<&'a str, PortDirection>, +// connections: HashMap<&'a str, Box<[usize]>>, +// } + +// impl<'a> SynthCell<'a> { +// pub fn from_yosys( +// topo_cell: &TopoCell<'a>, +// global_nets: &TopoNetMap<'a>, +// netlist: &'a yosys_json::Netlist, +// ) -> Result { +// let mut cell = Self { +// cell_type: &topo_cell.cell_type, +// ..Default::default() +// }; + +// // TODO: error handling +// let mut synth_module = &netlist.modules[topo_cell.parents.last().unwrap().cell_type]; +// let mut synth_cell = synth_module.cells[topo_cell.name]; + +// todo!() +// } +// } + +// impl TryFrom<&yosys_json::Cell> for SynthCell { +// type Error = ModuleError; +// fn try_from(cell: &yosys_json::Cell) -> Result { +// let mut synth_cell = SynthCell { +// cell_type: cell.cell_type.to_string(), +// ..Default::default() +// }; + +// // Add connections and directions +// for (port_name, conn_bits) in cell.connections.iter() { +// let direction = PortDirection::try_from(&cell.port_directions[port_name])?; +// let conn_nets: Vec = conn_bits +// .iter() +// .map(parse_bit) +// .collect::, _>>()?; + +// synth_cell +// .port_directions +// .insert(port_name.clone(), direction); +// synth_cell.connections.insert(port_name.clone(), conn_nets); +// } + +// // Add parameters +// for (param_name, param) in cell.parameters.iter() { +// let Some(param) = param.to_number() else { +// // TODO: Clean this up +// return Err(CellError::Parameter(param_name.to_string(), param.clone()).into()); +// }; +// synth_cell.parameters.insert(param_name.to_string(), param); +// } + +// // Add attributes +// for (attr_name, attr) in cell.attributes.iter() { +// let Some(attr) = attr.to_string_if_string() else { +// // TODO: Clean this up +// return Err(CellError::Attribute(attr_name.to_string(), attr.clone()).into()); +// }; +// synth_cell +// .attributes +// .insert(attr_name.to_string(), attr.to_string()); +// } + +// Ok(synth_cell) +// } +// } + use std::{ collections::HashMap, net::{IpAddr, Ipv6Addr}, diff --git a/crates/arbolta/tests/deps/simcells_wrappers.json b/crates/arbolta/tests/deps/simcells_wrappers.json index ecab684..381c74c 100644 --- a/crates/arbolta/tests/deps/simcells_wrappers.json +++ b/crates/arbolta/tests/deps/simcells_wrappers.json @@ -1,11967 +1 @@ -{ - "creator": "Yosys 0.50+7 (git sha1 38f858374, clang++ 18.1.8 -fPIC -O3)", - "modules": { - "$_ALDFFE_NNN_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "L": { - "direction": "input", - "bits": [ - 4 - ] - }, - "AD": { - "direction": "input", - "bits": [ - 5 - ] - }, - "E": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_ALDFFE_NNN_": { - "type": "$_ALDFFE_NNN_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "L": [ - 4 - ], - "AD": [ - 5 - ], - "E": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "AD": { - "bits": [ - 5 - ] - }, - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 6 - ] - }, - "L": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 7 - ] - } - } - }, - "$_ALDFFE_NNP_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "L": { - "direction": "input", - "bits": [ - 4 - ] - }, - "AD": { - "direction": "input", - "bits": [ - 5 - ] - }, - "E": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_ALDFFE_NNP_": { - "type": "$_ALDFFE_NNP_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "L": [ - 4 - ], - "AD": [ - 5 - ], - "E": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "AD": { - "bits": [ - 5 - ] - }, - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 6 - ] - }, - "L": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 7 - ] - } - } - }, - "$_ALDFFE_NPN_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "L": { - "direction": "input", - "bits": [ - 4 - ] - }, - "AD": { - "direction": "input", - "bits": [ - 5 - ] - }, - "E": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_ALDFFE_NPN_": { - "type": "$_ALDFFE_NPN_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "L": [ - 4 - ], - "AD": [ - 5 - ], - "E": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "AD": { - "bits": [ - 5 - ] - }, - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 6 - ] - }, - "L": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 7 - ] - } - } - }, - "$_ALDFFE_NPP_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "L": { - "direction": "input", - "bits": [ - 4 - ] - }, - "AD": { - "direction": "input", - "bits": [ - 5 - ] - }, - "E": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_ALDFFE_NPP_": { - "type": "$_ALDFFE_NPP_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "L": [ - 4 - ], - "AD": [ - 5 - ], - "E": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "AD": { - "bits": [ - 5 - ] - }, - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 6 - ] - }, - "L": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 7 - ] - } - } - }, - "$_ALDFFE_PNN_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "L": { - "direction": "input", - "bits": [ - 4 - ] - }, - "AD": { - "direction": "input", - "bits": [ - 5 - ] - }, - "E": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_ALDFFE_PNN_": { - "type": "$_ALDFFE_PNN_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "L": [ - 4 - ], - "AD": [ - 5 - ], - "E": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "AD": { - "bits": [ - 5 - ] - }, - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 6 - ] - }, - "L": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 7 - ] - } - } - }, - "$_ALDFFE_PNP_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "L": { - "direction": "input", - "bits": [ - 4 - ] - }, - "AD": { - "direction": "input", - "bits": [ - 5 - ] - }, - "E": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_ALDFFE_PNP_": { - "type": "$_ALDFFE_PNP_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "L": [ - 4 - ], - "AD": [ - 5 - ], - "E": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "AD": { - "bits": [ - 5 - ] - }, - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 6 - ] - }, - "L": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 7 - ] - } - } - }, - "$_ALDFFE_PPN_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "L": { - "direction": "input", - "bits": [ - 4 - ] - }, - "AD": { - "direction": "input", - "bits": [ - 5 - ] - }, - "E": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_ALDFFE_PPN_": { - "type": "$_ALDFFE_PPN_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "L": [ - 4 - ], - "AD": [ - 5 - ], - "E": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "AD": { - "bits": [ - 5 - ] - }, - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 6 - ] - }, - "L": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 7 - ] - } - } - }, - "$_ALDFFE_PPP_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "L": { - "direction": "input", - "bits": [ - 4 - ] - }, - "AD": { - "direction": "input", - "bits": [ - 5 - ] - }, - "E": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_ALDFFE_PPP_": { - "type": "$_ALDFFE_PPP_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "L": [ - 4 - ], - "AD": [ - 5 - ], - "E": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "AD": { - "bits": [ - 5 - ] - }, - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 6 - ] - }, - "L": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 7 - ] - } - } - }, - "$_ALDFF_NN_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "L": { - "direction": "input", - "bits": [ - 4 - ] - }, - "AD": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_ALDFF_NN_": { - "type": "$_ALDFF_NN_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "L": [ - 4 - ], - "AD": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "AD": { - "bits": [ - 5 - ] - }, - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "L": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 6 - ] - } - } - }, - "$_ALDFF_NP_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "L": { - "direction": "input", - "bits": [ - 4 - ] - }, - "AD": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_ALDFF_NP_": { - "type": "$_ALDFF_NP_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "L": [ - 4 - ], - "AD": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "AD": { - "bits": [ - 5 - ] - }, - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "L": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 6 - ] - } - } - }, - "$_ALDFF_PN_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "L": { - "direction": "input", - "bits": [ - 4 - ] - }, - "AD": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_ALDFF_PN_": { - "type": "$_ALDFF_PN_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "L": [ - 4 - ], - "AD": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "AD": { - "bits": [ - 5 - ] - }, - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "L": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 6 - ] - } - } - }, - "$_ALDFF_PP_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "L": { - "direction": "input", - "bits": [ - 4 - ] - }, - "AD": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_ALDFF_PP_": { - "type": "$_ALDFF_PP_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "L": [ - 4 - ], - "AD": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "AD": { - "bits": [ - 5 - ] - }, - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "L": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 6 - ] - } - } - }, - "$_ANDNOT_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_ANDNOT_": { - "type": "$_ANDNOT_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "Y": [ - 4 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "Y": { - "bits": [ - 4 - ] - } - } - }, - "$_AND_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_AND_": { - "type": "$_AND_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "Y": [ - 4 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "Y": { - "bits": [ - 4 - ] - } - } - }, - "$_AOI3_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "C": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_AOI3_": { - "type": "$_AOI3_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "C": [ - 4 - ], - "Y": [ - 5 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "C": { - "bits": [ - 4 - ] - }, - "Y": { - "bits": [ - 5 - ] - } - } - }, - "$_AOI4_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "C": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_AOI4_": { - "type": "$_AOI4_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "C": [ - 4 - ], - "D": [ - 5 - ], - "Y": [ - 6 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "C": { - "bits": [ - 4 - ] - }, - "D": { - "bits": [ - 5 - ] - }, - "Y": { - "bits": [ - 6 - ] - } - } - }, - "$_BUF_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 3 - ] - } - }, - "cells": { - "$_BUF_": { - "type": "$_BUF_", - "connections": { - "A": [ - 2 - ], - "Y": [ - 3 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "Y": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFE_NN0N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_NN0N_": { - "type": "$_DFFE_NN0N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_NN0P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_NN0P_": { - "type": "$_DFFE_NN0P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_NN1N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_NN1N_": { - "type": "$_DFFE_NN1N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_NN1P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_NN1P_": { - "type": "$_DFFE_NN1P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_NN_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "E": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DFFE_NN_": { - "type": "$_DFFE_NN_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "E": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 5 - ] - } - } - }, - "$_DFFE_NP0N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_NP0N_": { - "type": "$_DFFE_NP0N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_NP0P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_NP0P_": { - "type": "$_DFFE_NP0P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_NP1N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_NP1N_": { - "type": "$_DFFE_NP1N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_NP1P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_NP1P_": { - "type": "$_DFFE_NP1P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_NP_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "E": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DFFE_NP_": { - "type": "$_DFFE_NP_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "E": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 5 - ] - } - } - }, - "$_DFFE_PN0N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_PN0N_": { - "type": "$_DFFE_PN0N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_PN0P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_PN0P_": { - "type": "$_DFFE_PN0P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_PN1N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_PN1N_": { - "type": "$_DFFE_PN1N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_PN1P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_PN1P_": { - "type": "$_DFFE_PN1P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_PN_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "E": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DFFE_PN_": { - "type": "$_DFFE_PN_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "E": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 5 - ] - } - } - }, - "$_DFFE_PP0N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_PP0N_": { - "type": "$_DFFE_PP0N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_PP0P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_PP0P_": { - "type": "$_DFFE_PP0P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_PP1N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_PP1N_": { - "type": "$_DFFE_PP1N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_PP1P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFE_PP1P_": { - "type": "$_DFFE_PP1P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFFE_PP_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "E": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DFFE_PP_": { - "type": "$_DFFE_PP_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "E": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 4 - ] - }, - "Q": { - "bits": [ - 5 - ] - } - } - }, - "$_DFFSRE_NNNN_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_NNNN_": { - "type": "$_DFFSRE_NNNN_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSRE_NNNP_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_NNNP_": { - "type": "$_DFFSRE_NNNP_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSRE_NNPN_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_NNPN_": { - "type": "$_DFFSRE_NNPN_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSRE_NNPP_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_NNPP_": { - "type": "$_DFFSRE_NNPP_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSRE_NPNN_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_NPNN_": { - "type": "$_DFFSRE_NPNN_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSRE_NPNP_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_NPNP_": { - "type": "$_DFFSRE_NPNP_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSRE_NPPN_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_NPPN_": { - "type": "$_DFFSRE_NPPN_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSRE_NPPP_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_NPPP_": { - "type": "$_DFFSRE_NPPP_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSRE_PNNN_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_PNNN_": { - "type": "$_DFFSRE_PNNN_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSRE_PNNP_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_PNNP_": { - "type": "$_DFFSRE_PNNP_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSRE_PNPN_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_PNPN_": { - "type": "$_DFFSRE_PNPN_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSRE_PNPP_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_PNPP_": { - "type": "$_DFFSRE_PNPP_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSRE_PPNN_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_PPNN_": { - "type": "$_DFFSRE_PPNN_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSRE_PPNP_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_PPNP_": { - "type": "$_DFFSRE_PPNP_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSRE_PPPN_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_PPPN_": { - "type": "$_DFFSRE_PPPN_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSRE_PPPP_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "D": { - "direction": "input", - "bits": [ - 6 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 7 - ] - } - }, - "cells": { - "$_DFFSRE_PPPP_": { - "type": "$_DFFSRE_PPPP_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "D": [ - 6 - ], - "Q": [ - 7 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 6 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 7 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSR_NNN_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFSR_NNN_": { - "type": "$_DFFSR_NNN_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSR_NNP_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFSR_NNP_": { - "type": "$_DFFSR_NNP_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSR_NPN_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFSR_NPN_": { - "type": "$_DFFSR_NPN_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSR_NPP_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFSR_NPP_": { - "type": "$_DFFSR_NPP_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSR_PNN_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFSR_PNN_": { - "type": "$_DFFSR_PNN_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSR_PNP_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFSR_PNP_": { - "type": "$_DFFSR_PNP_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSR_PPN_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFSR_PPN_": { - "type": "$_DFFSR_PPN_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFFSR_PPP_": { - "ports": { - "C": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DFFSR_PPP_": { - "type": "$_DFFSR_PPP_", - "connections": { - "C": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 2 - ] - }, - "D": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DFF_NN0_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DFF_NN0_": { - "type": "$_DFF_NN0_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFF_NN1_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DFF_NN1_": { - "type": "$_DFF_NN1_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFF_NP0_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DFF_NP0_": { - "type": "$_DFF_NP0_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFF_NP1_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DFF_NP1_": { - "type": "$_DFF_NP1_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFF_N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_DFF_N_": { - "type": "$_DFF_N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "Q": [ - 4 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 4 - ] - } - } - }, - "$_DFF_PN0_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DFF_PN0_": { - "type": "$_DFF_PN0_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFF_PN1_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DFF_PN1_": { - "type": "$_DFF_PN1_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFF_PP0_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DFF_PP0_": { - "type": "$_DFF_PP0_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFF_PP1_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DFF_PP1_": { - "type": "$_DFF_PP1_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_DFF_P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_DFF_P_": { - "type": "$_DFF_P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "Q": [ - 4 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 4 - ] - } - } - }, - "$_DLATCHSR_NNN_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DLATCHSR_NNN_": { - "type": "$_DLATCHSR_NNN_", - "connections": { - "E": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 5 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCHSR_NNP_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DLATCHSR_NNP_": { - "type": "$_DLATCHSR_NNP_", - "connections": { - "E": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 5 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCHSR_NPN_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DLATCHSR_NPN_": { - "type": "$_DLATCHSR_NPN_", - "connections": { - "E": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 5 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCHSR_NPP_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DLATCHSR_NPP_": { - "type": "$_DLATCHSR_NPP_", - "connections": { - "E": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 5 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCHSR_PNN_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DLATCHSR_PNN_": { - "type": "$_DLATCHSR_PNN_", - "connections": { - "E": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 5 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCHSR_PNP_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DLATCHSR_PNP_": { - "type": "$_DLATCHSR_PNP_", - "connections": { - "E": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 5 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCHSR_PPN_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DLATCHSR_PPN_": { - "type": "$_DLATCHSR_PPN_", - "connections": { - "E": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 5 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCHSR_PPP_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "S": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_DLATCHSR_PPP_": { - "type": "$_DLATCHSR_PPP_", - "connections": { - "E": [ - 2 - ], - "S": [ - 3 - ], - "R": [ - 4 - ], - "D": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 5 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - }, - "S": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCH_NN0_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "R": { - "direction": "input", - "bits": [ - 3 - ] - }, - "D": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DLATCH_NN0_": { - "type": "$_DLATCH_NN0_", - "connections": { - "E": [ - 2 - ], - "R": [ - 3 - ], - "D": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 4 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCH_NN1_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "R": { - "direction": "input", - "bits": [ - 3 - ] - }, - "D": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DLATCH_NN1_": { - "type": "$_DLATCH_NN1_", - "connections": { - "E": [ - 2 - ], - "R": [ - 3 - ], - "D": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 4 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCH_NP0_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "R": { - "direction": "input", - "bits": [ - 3 - ] - }, - "D": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DLATCH_NP0_": { - "type": "$_DLATCH_NP0_", - "connections": { - "E": [ - 2 - ], - "R": [ - 3 - ], - "D": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 4 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCH_NP1_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "R": { - "direction": "input", - "bits": [ - 3 - ] - }, - "D": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DLATCH_NP1_": { - "type": "$_DLATCH_NP1_", - "connections": { - "E": [ - 2 - ], - "R": [ - 3 - ], - "D": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 4 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCH_N_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "D": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_DLATCH_N_": { - "type": "$_DLATCH_N_", - "connections": { - "E": [ - 2 - ], - "D": [ - 3 - ], - "Q": [ - 4 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 3 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 4 - ] - } - } - }, - "$_DLATCH_PN0_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "R": { - "direction": "input", - "bits": [ - 3 - ] - }, - "D": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DLATCH_PN0_": { - "type": "$_DLATCH_PN0_", - "connections": { - "E": [ - 2 - ], - "R": [ - 3 - ], - "D": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 4 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCH_PN1_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "R": { - "direction": "input", - "bits": [ - 3 - ] - }, - "D": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DLATCH_PN1_": { - "type": "$_DLATCH_PN1_", - "connections": { - "E": [ - 2 - ], - "R": [ - 3 - ], - "D": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 4 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCH_PP0_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "R": { - "direction": "input", - "bits": [ - 3 - ] - }, - "D": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DLATCH_PP0_": { - "type": "$_DLATCH_PP0_", - "connections": { - "E": [ - 2 - ], - "R": [ - 3 - ], - "D": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 4 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCH_PP1_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "R": { - "direction": "input", - "bits": [ - 3 - ] - }, - "D": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_DLATCH_PP1_": { - "type": "$_DLATCH_PP1_", - "connections": { - "E": [ - 2 - ], - "R": [ - 3 - ], - "D": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 4 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 3 - ] - } - } - }, - "$_DLATCH_P_": { - "ports": { - "E": { - "direction": "input", - "bits": [ - 2 - ] - }, - "D": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_DLATCH_P_": { - "type": "$_DLATCH_P_", - "connections": { - "E": [ - 2 - ], - "D": [ - 3 - ], - "Q": [ - 4 - ] - } - } - }, - "netnames": { - "D": { - "bits": [ - 3 - ] - }, - "E": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 4 - ] - } - } - }, - "$_MUX16_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "C": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "E": { - "direction": "input", - "bits": [ - 6 - ] - }, - "F": { - "direction": "input", - "bits": [ - 7 - ] - }, - "G": { - "direction": "input", - "bits": [ - 8 - ] - }, - "H": { - "direction": "input", - "bits": [ - 9 - ] - }, - "I": { - "direction": "input", - "bits": [ - 10 - ] - }, - "J": { - "direction": "input", - "bits": [ - 11 - ] - }, - "K": { - "direction": "input", - "bits": [ - 12 - ] - }, - "L": { - "direction": "input", - "bits": [ - 13 - ] - }, - "M": { - "direction": "input", - "bits": [ - 14 - ] - }, - "N": { - "direction": "input", - "bits": [ - 15 - ] - }, - "O": { - "direction": "input", - "bits": [ - 16 - ] - }, - "P": { - "direction": "input", - "bits": [ - 17 - ] - }, - "S": { - "direction": "input", - "bits": [ - 18 - ] - }, - "T": { - "direction": "input", - "bits": [ - 19 - ] - }, - "U": { - "direction": "input", - "bits": [ - 20 - ] - }, - "V": { - "direction": "input", - "bits": [ - 21 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 22 - ] - } - }, - "cells": { - "$_MUX16_": { - "type": "$_MUX16_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "C": [ - 4 - ], - "D": [ - 5 - ], - "E": [ - 6 - ], - "F": [ - 7 - ], - "G": [ - 8 - ], - "H": [ - 9 - ], - "I": [ - 10 - ], - "J": [ - 11 - ], - "K": [ - 12 - ], - "L": [ - 13 - ], - "M": [ - 14 - ], - "N": [ - 15 - ], - "O": [ - 16 - ], - "P": [ - 17 - ], - "S": [ - 18 - ], - "T": [ - 19 - ], - "U": [ - 20 - ], - "V": [ - 21 - ], - "Y": [ - 22 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "C": { - "bits": [ - 4 - ] - }, - "D": { - "bits": [ - 5 - ] - }, - "E": { - "bits": [ - 6 - ] - }, - "F": { - "bits": [ - 7 - ] - }, - "G": { - "bits": [ - 8 - ] - }, - "H": { - "bits": [ - 9 - ] - }, - "I": { - "bits": [ - 10 - ] - }, - "J": { - "bits": [ - 11 - ] - }, - "K": { - "bits": [ - 12 - ] - }, - "L": { - "bits": [ - 13 - ] - }, - "M": { - "bits": [ - 14 - ] - }, - "N": { - "bits": [ - 15 - ] - }, - "O": { - "bits": [ - 16 - ] - }, - "P": { - "bits": [ - 17 - ] - }, - "S": { - "bits": [ - 18 - ] - }, - "T": { - "bits": [ - 19 - ] - }, - "U": { - "bits": [ - 20 - ] - }, - "V": { - "bits": [ - 21 - ] - }, - "Y": { - "bits": [ - 22 - ] - } - } - }, - "$_MUX4_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "C": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "S": { - "direction": "input", - "bits": [ - 6 - ] - }, - "T": { - "direction": "input", - "bits": [ - 7 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 8 - ] - } - }, - "cells": { - "$_MUX4_": { - "type": "$_MUX4_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "C": [ - 4 - ], - "D": [ - 5 - ], - "S": [ - 6 - ], - "T": [ - 7 - ], - "Y": [ - 8 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "C": { - "bits": [ - 4 - ] - }, - "D": { - "bits": [ - 5 - ] - }, - "S": { - "bits": [ - 6 - ] - }, - "T": { - "bits": [ - 7 - ] - }, - "Y": { - "bits": [ - 8 - ] - } - } - }, - "$_MUX8_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "C": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "E": { - "direction": "input", - "bits": [ - 6 - ] - }, - "F": { - "direction": "input", - "bits": [ - 7 - ] - }, - "G": { - "direction": "input", - "bits": [ - 8 - ] - }, - "H": { - "direction": "input", - "bits": [ - 9 - ] - }, - "S": { - "direction": "input", - "bits": [ - 10 - ] - }, - "T": { - "direction": "input", - "bits": [ - 11 - ] - }, - "U": { - "direction": "input", - "bits": [ - 12 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 13 - ] - } - }, - "cells": { - "$_MUX8_": { - "type": "$_MUX8_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "C": [ - 4 - ], - "D": [ - 5 - ], - "E": [ - 6 - ], - "F": [ - 7 - ], - "G": [ - 8 - ], - "H": [ - 9 - ], - "S": [ - 10 - ], - "T": [ - 11 - ], - "U": [ - 12 - ], - "Y": [ - 13 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "C": { - "bits": [ - 4 - ] - }, - "D": { - "bits": [ - 5 - ] - }, - "E": { - "bits": [ - 6 - ] - }, - "F": { - "bits": [ - 7 - ] - }, - "G": { - "bits": [ - 8 - ] - }, - "H": { - "bits": [ - 9 - ] - }, - "S": { - "bits": [ - 10 - ] - }, - "T": { - "bits": [ - 11 - ] - }, - "U": { - "bits": [ - 12 - ] - }, - "Y": { - "bits": [ - 13 - ] - } - } - }, - "$_MUX_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "S": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_MUX_": { - "type": "$_MUX_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "S": [ - 4 - ], - "Y": [ - 5 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "S": { - "bits": [ - 4 - ] - }, - "Y": { - "bits": [ - 5 - ] - } - } - }, - "$_NAND_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_NAND_": { - "type": "$_NAND_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "Y": [ - 4 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "Y": { - "bits": [ - 4 - ] - } - } - }, - "$_NMUX_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "S": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_NMUX_": { - "type": "$_NMUX_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "S": [ - 4 - ], - "Y": [ - 5 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "S": { - "bits": [ - 4 - ] - }, - "Y": { - "bits": [ - 5 - ] - } - } - }, - "$_NOR_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_NOR_": { - "type": "$_NOR_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "Y": [ - 4 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "Y": { - "bits": [ - 4 - ] - } - } - }, - "$_NOT_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 3 - ] - } - }, - "cells": { - "$_NOT_": { - "type": "$_NOT_", - "connections": { - "A": [ - 2 - ], - "Y": [ - 3 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "Y": { - "bits": [ - 3 - ] - } - } - }, - "$_OAI3_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "C": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_OAI3_": { - "type": "$_OAI3_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "C": [ - 4 - ], - "Y": [ - 5 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "C": { - "bits": [ - 4 - ] - }, - "Y": { - "bits": [ - 5 - ] - } - } - }, - "$_OAI4_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "C": { - "direction": "input", - "bits": [ - 4 - ] - }, - "D": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_OAI4_": { - "type": "$_OAI4_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "C": [ - 4 - ], - "D": [ - 5 - ], - "Y": [ - 6 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "C": { - "bits": [ - 4 - ] - }, - "D": { - "bits": [ - 5 - ] - }, - "Y": { - "bits": [ - 6 - ] - } - } - }, - "$_ORNOT_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_ORNOT_": { - "type": "$_ORNOT_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "Y": [ - 4 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "Y": { - "bits": [ - 4 - ] - } - } - }, - "$_OR_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_OR_": { - "type": "$_OR_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "Y": [ - 4 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "Y": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_NN0N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_NN0N_": { - "type": "$_SDFFCE_NN0N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_NN0P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_NN0P_": { - "type": "$_SDFFCE_NN0P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_NN1N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_NN1N_": { - "type": "$_SDFFCE_NN1N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_NN1P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_NN1P_": { - "type": "$_SDFFCE_NN1P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_NP0N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_NP0N_": { - "type": "$_SDFFCE_NP0N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_NP0P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_NP0P_": { - "type": "$_SDFFCE_NP0P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_NP1N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_NP1N_": { - "type": "$_SDFFCE_NP1N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_NP1P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_NP1P_": { - "type": "$_SDFFCE_NP1P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_PN0N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_PN0N_": { - "type": "$_SDFFCE_PN0N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_PN0P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_PN0P_": { - "type": "$_SDFFCE_PN0P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_PN1N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_PN1N_": { - "type": "$_SDFFCE_PN1N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_PN1P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_PN1P_": { - "type": "$_SDFFCE_PN1P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_PP0N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_PP0N_": { - "type": "$_SDFFCE_PP0N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_PP0P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_PP0P_": { - "type": "$_SDFFCE_PP0P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_PP1N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_PP1N_": { - "type": "$_SDFFCE_PP1N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFCE_PP1P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFCE_PP1P_": { - "type": "$_SDFFCE_PP1P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_NN0N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_NN0N_": { - "type": "$_SDFFE_NN0N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_NN0P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_NN0P_": { - "type": "$_SDFFE_NN0P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_NN1N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_NN1N_": { - "type": "$_SDFFE_NN1N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_NN1P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_NN1P_": { - "type": "$_SDFFE_NN1P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_NP0N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_NP0N_": { - "type": "$_SDFFE_NP0N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_NP0P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_NP0P_": { - "type": "$_SDFFE_NP0P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_NP1N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_NP1N_": { - "type": "$_SDFFE_NP1N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_NP1P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_NP1P_": { - "type": "$_SDFFE_NP1P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_PN0N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_PN0N_": { - "type": "$_SDFFE_PN0N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_PN0P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_PN0P_": { - "type": "$_SDFFE_PN0P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_PN1N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_PN1N_": { - "type": "$_SDFFE_PN1N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_PN1P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_PN1P_": { - "type": "$_SDFFE_PN1P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_PP0N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_PP0N_": { - "type": "$_SDFFE_PP0N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_PP0P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_PP0P_": { - "type": "$_SDFFE_PP0P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_PP1N_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_PP1N_": { - "type": "$_SDFFE_PP1N_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFFE_PP1P_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "E": { - "direction": "input", - "bits": [ - 5 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 6 - ] - } - }, - "cells": { - "$_SDFFE_PP1P_": { - "type": "$_SDFFE_PP1P_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "E": [ - 5 - ], - "Q": [ - 6 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 5 - ] - }, - "Q": { - "bits": [ - 6 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFF_NN0_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_SDFF_NN0_": { - "type": "$_SDFF_NN0_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFF_NN1_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_SDFF_NN1_": { - "type": "$_SDFF_NN1_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFF_NP0_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_SDFF_NP0_": { - "type": "$_SDFF_NP0_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFF_NP1_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_SDFF_NP1_": { - "type": "$_SDFF_NP1_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFF_PN0_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_SDFF_PN0_": { - "type": "$_SDFF_PN0_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFF_PN1_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_SDFF_PN1_": { - "type": "$_SDFF_PN1_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFF_PP0_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_SDFF_PP0_": { - "type": "$_SDFF_PP0_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SDFF_PP1_": { - "ports": { - "D": { - "direction": "input", - "bits": [ - 2 - ] - }, - "C": { - "direction": "input", - "bits": [ - 3 - ] - }, - "R": { - "direction": "input", - "bits": [ - 4 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 5 - ] - } - }, - "cells": { - "$_SDFF_PP1_": { - "type": "$_SDFF_PP1_", - "connections": { - "D": [ - 2 - ], - "C": [ - 3 - ], - "R": [ - 4 - ], - "Q": [ - 5 - ] - } - } - }, - "netnames": { - "C": { - "bits": [ - 3 - ] - }, - "D": { - "bits": [ - 2 - ] - }, - "Q": { - "bits": [ - 5 - ] - }, - "R": { - "bits": [ - 4 - ] - } - } - }, - "$_SR_NN_": { - "ports": { - "S": { - "direction": "input", - "bits": [ - 2 - ] - }, - "R": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_SR_NN_": { - "type": "$_SR_NN_", - "connections": { - "S": [ - 2 - ], - "R": [ - 3 - ], - "Q": [ - 4 - ] - } - } - }, - "netnames": { - "Q": { - "bits": [ - 4 - ] - }, - "R": { - "bits": [ - 3 - ] - }, - "S": { - "bits": [ - 2 - ] - } - } - }, - "$_SR_NP_": { - "ports": { - "S": { - "direction": "input", - "bits": [ - 2 - ] - }, - "R": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_SR_NP_": { - "type": "$_SR_NP_", - "connections": { - "S": [ - 2 - ], - "R": [ - 3 - ], - "Q": [ - 4 - ] - } - } - }, - "netnames": { - "Q": { - "bits": [ - 4 - ] - }, - "R": { - "bits": [ - 3 - ] - }, - "S": { - "bits": [ - 2 - ] - } - } - }, - "$_SR_PN_": { - "ports": { - "S": { - "direction": "input", - "bits": [ - 2 - ] - }, - "R": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_SR_PN_": { - "type": "$_SR_PN_", - "connections": { - "S": [ - 2 - ], - "R": [ - 3 - ], - "Q": [ - 4 - ] - } - } - }, - "netnames": { - "Q": { - "bits": [ - 4 - ] - }, - "R": { - "bits": [ - 3 - ] - }, - "S": { - "bits": [ - 2 - ] - } - } - }, - "$_SR_PP_": { - "ports": { - "S": { - "direction": "input", - "bits": [ - 2 - ] - }, - "R": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Q": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_SR_PP_": { - "type": "$_SR_PP_", - "connections": { - "S": [ - 2 - ], - "R": [ - 3 - ], - "Q": [ - 4 - ] - } - } - }, - "netnames": { - "Q": { - "bits": [ - 4 - ] - }, - "R": { - "bits": [ - 3 - ] - }, - "S": { - "bits": [ - 2 - ] - } - } - }, - "$_TBUF_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "E": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_TBUF_": { - "type": "$_TBUF_", - "connections": { - "A": [ - 2 - ], - "E": [ - 3 - ], - "Y": [ - 4 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "E": { - "bits": [ - 3 - ] - }, - "Y": { - "bits": [ - 4 - ] - } - } - }, - "$_XNOR_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_XNOR_": { - "type": "$_XNOR_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "Y": [ - 4 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "Y": { - "bits": [ - 4 - ] - } - } - }, - "$_XOR_": { - "ports": { - "A": { - "direction": "input", - "bits": [ - 2 - ] - }, - "B": { - "direction": "input", - "bits": [ - 3 - ] - }, - "Y": { - "direction": "output", - "bits": [ - 4 - ] - } - }, - "cells": { - "$_XOR_": { - "type": "$_XOR_", - "connections": { - "A": [ - 2 - ], - "B": [ - 3 - ], - "Y": [ - 4 - ] - } - } - }, - "netnames": { - "A": { - "bits": [ - 2 - ] - }, - "B": { - "bits": [ - 3 - ] - }, - "Y": { - "bits": [ - 4 - ] - } - } - } - } -} +{"creator": "Yosys 0.56+186 (git sha1 83d953e95, clang++ 18.1.8 -fPIC -O3)", "modules": {"$_ALDFFE_NNN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1420.1-1429.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "L": {"direction": "input", "bits": [4]}, "AD": {"direction": "input", "bits": [5]}, "E": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_ALDFFE_NNN_": {"type": "$_ALDFFE_NNN_", "port_directions": {"D": "input", "C": "input", "L": "input", "AD": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "L": [4], "AD": [5], "E": [6], "Q": [7]}}}, "netnames": {"AD": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1421.16-1421.18"}}, "C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1421.10-1421.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1421.7-1421.8"}}, "E": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1421.20-1421.21"}}, "L": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1421.13-1421.14"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1422.12-1422.13"}}}}, "$_ALDFFE_NNP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1445.1-1454.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "L": {"direction": "input", "bits": [4]}, "AD": {"direction": "input", "bits": [5]}, "E": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_ALDFFE_NNP_": {"type": "$_ALDFFE_NNP_", "port_directions": {"D": "input", "C": "input", "L": "input", "AD": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "L": [4], "AD": [5], "E": [6], "Q": [7]}}}, "netnames": {"AD": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1446.16-1446.18"}}, "C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1446.10-1446.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1446.7-1446.8"}}, "E": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1446.20-1446.21"}}, "L": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1446.13-1446.14"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1447.12-1447.13"}}}}, "$_ALDFFE_NPN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1470.1-1479.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "L": {"direction": "input", "bits": [4]}, "AD": {"direction": "input", "bits": [5]}, "E": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_ALDFFE_NPN_": {"type": "$_ALDFFE_NPN_", "port_directions": {"D": "input", "C": "input", "L": "input", "AD": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "L": [4], "AD": [5], "E": [6], "Q": [7]}}}, "netnames": {"AD": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1471.16-1471.18"}}, "C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1471.10-1471.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1471.7-1471.8"}}, "E": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1471.20-1471.21"}}, "L": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1471.13-1471.14"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1472.12-1472.13"}}}}, "$_ALDFFE_NPP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1495.1-1504.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "L": {"direction": "input", "bits": [4]}, "AD": {"direction": "input", "bits": [5]}, "E": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_ALDFFE_NPP_": {"type": "$_ALDFFE_NPP_", "port_directions": {"D": "input", "C": "input", "L": "input", "AD": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "L": [4], "AD": [5], "E": [6], "Q": [7]}}}, "netnames": {"AD": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1496.16-1496.18"}}, "C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1496.10-1496.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1496.7-1496.8"}}, "E": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1496.20-1496.21"}}, "L": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1496.13-1496.14"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1497.12-1497.13"}}}}, "$_ALDFFE_PNN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1520.1-1529.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "L": {"direction": "input", "bits": [4]}, "AD": {"direction": "input", "bits": [5]}, "E": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_ALDFFE_PNN_": {"type": "$_ALDFFE_PNN_", "port_directions": {"D": "input", "C": "input", "L": "input", "AD": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "L": [4], "AD": [5], "E": [6], "Q": [7]}}}, "netnames": {"AD": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1521.16-1521.18"}}, "C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1521.10-1521.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1521.7-1521.8"}}, "E": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1521.20-1521.21"}}, "L": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1521.13-1521.14"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1522.12-1522.13"}}}}, "$_ALDFFE_PNP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1545.1-1554.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "L": {"direction": "input", "bits": [4]}, "AD": {"direction": "input", "bits": [5]}, "E": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_ALDFFE_PNP_": {"type": "$_ALDFFE_PNP_", "port_directions": {"D": "input", "C": "input", "L": "input", "AD": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "L": [4], "AD": [5], "E": [6], "Q": [7]}}}, "netnames": {"AD": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1546.16-1546.18"}}, "C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1546.10-1546.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1546.7-1546.8"}}, "E": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1546.20-1546.21"}}, "L": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1546.13-1546.14"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1547.12-1547.13"}}}}, "$_ALDFFE_PPN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1570.1-1579.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "L": {"direction": "input", "bits": [4]}, "AD": {"direction": "input", "bits": [5]}, "E": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_ALDFFE_PPN_": {"type": "$_ALDFFE_PPN_", "port_directions": {"D": "input", "C": "input", "L": "input", "AD": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "L": [4], "AD": [5], "E": [6], "Q": [7]}}}, "netnames": {"AD": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1571.16-1571.18"}}, "C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1571.10-1571.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1571.7-1571.8"}}, "E": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1571.20-1571.21"}}, "L": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1571.13-1571.14"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1572.12-1572.13"}}}}, "$_ALDFFE_PPP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1595.1-1604.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "L": {"direction": "input", "bits": [4]}, "AD": {"direction": "input", "bits": [5]}, "E": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_ALDFFE_PPP_": {"type": "$_ALDFFE_PPP_", "port_directions": {"D": "input", "C": "input", "L": "input", "AD": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "L": [4], "AD": [5], "E": [6], "Q": [7]}}}, "netnames": {"AD": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1596.16-1596.18"}}, "C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1596.10-1596.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1596.7-1596.8"}}, "E": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1596.20-1596.21"}}, "L": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1596.13-1596.14"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1597.12-1597.13"}}}}, "$_ALDFF_NN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1323.1-1332.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "L": {"direction": "input", "bits": [4]}, "AD": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_ALDFF_NN_": {"type": "$_ALDFF_NN_", "port_directions": {"D": "input", "C": "input", "L": "input", "AD": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "L": [4], "AD": [5], "Q": [6]}}}, "netnames": {"AD": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1324.16-1324.18"}}, "C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1324.10-1324.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1324.7-1324.8"}}, "L": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1324.13-1324.14"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1325.12-1325.13"}}}}, "$_ALDFF_NP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1347.1-1356.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "L": {"direction": "input", "bits": [4]}, "AD": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_ALDFF_NP_": {"type": "$_ALDFF_NP_", "port_directions": {"D": "input", "C": "input", "L": "input", "AD": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "L": [4], "AD": [5], "Q": [6]}}}, "netnames": {"AD": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1348.16-1348.18"}}, "C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1348.10-1348.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1348.7-1348.8"}}, "L": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1348.13-1348.14"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1349.12-1349.13"}}}}, "$_ALDFF_PN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1371.1-1380.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "L": {"direction": "input", "bits": [4]}, "AD": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_ALDFF_PN_": {"type": "$_ALDFF_PN_", "port_directions": {"D": "input", "C": "input", "L": "input", "AD": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "L": [4], "AD": [5], "Q": [6]}}}, "netnames": {"AD": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1372.16-1372.18"}}, "C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1372.10-1372.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1372.7-1372.8"}}, "L": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1372.13-1372.14"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1373.12-1373.13"}}}}, "$_ALDFF_PP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1395.1-1404.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "L": {"direction": "input", "bits": [4]}, "AD": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_ALDFF_PP_": {"type": "$_ALDFF_PP_", "port_directions": {"D": "input", "C": "input", "L": "input", "AD": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "L": [4], "AD": [5], "Q": [6]}}}, "netnames": {"AD": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1396.16-1396.18"}}, "C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1396.10-1396.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1396.7-1396.8"}}, "L": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1396.13-1396.14"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1397.12-1397.13"}}}}, "$_ANDNOT__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:198.1-202.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "Y": {"direction": "output", "bits": [4]}}, "cells": {"$_ANDNOT_": {"type": "$_ANDNOT_", "port_directions": {"A": "input", "B": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "Y": [4]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:199.7-199.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:199.10-199.11"}}, "Y": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:200.8-200.9"}}}}, "$_AND__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:78.1-82.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "Y": {"direction": "output", "bits": [4]}}, "cells": {"$_AND_": {"type": "$_AND_", "port_directions": {"A": "input", "B": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "Y": [4]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:79.7-79.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:79.10-79.11"}}, "Y": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:80.8-80.9"}}}}, "$_AOI3__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:367.1-371.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "C": {"direction": "input", "bits": [4]}, "Y": {"direction": "output", "bits": [5]}}, "cells": {"$_AOI3_": {"type": "$_AOI3_", "port_directions": {"A": "input", "B": "input", "C": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "C": [4], "Y": [5]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:368.7-368.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:368.10-368.11"}}, "C": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:368.13-368.14"}}, "Y": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:369.8-369.9"}}}}, "$_AOI4__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:423.1-427.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "C": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Y": {"direction": "output", "bits": [6]}}, "cells": {"$_AOI4_": {"type": "$_AOI4_", "port_directions": {"A": "input", "B": "input", "C": "input", "D": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "C": [4], "D": [5], "Y": [6]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:424.7-424.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:424.10-424.11"}}, "C": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:424.13-424.14"}}, "D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:424.16-424.17"}}, "Y": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:425.8-425.9"}}}}, "$_BUF__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:40.1-44.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "Y": {"direction": "output", "bits": [3]}}, "cells": {"$_BUF_": {"type": "$_BUF_", "port_directions": {"A": "input", "Y": "output"}, "connections": {"A": [2], "Y": [3]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:41.7-41.8"}}, "Y": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:42.8-42.9"}}}}, "$_DFFE_NN0N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:924.1-933.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_NN0N_": {"type": "$_DFFE_NN0N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:925.10-925.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:925.7-925.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:925.16-925.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:926.12-926.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:925.13-925.14"}}}}, "$_DFFE_NN0P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:949.1-958.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_NN0P_": {"type": "$_DFFE_NN0P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:950.10-950.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:950.7-950.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:950.16-950.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:951.12-951.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:950.13-950.14"}}}}, "$_DFFE_NN1N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:974.1-983.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_NN1N_": {"type": "$_DFFE_NN1N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:975.10-975.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:975.7-975.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:975.16-975.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:976.12-976.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:975.13-975.14"}}}}, "$_DFFE_NN1P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:999.1-1008.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_NN1P_": {"type": "$_DFFE_NN1P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1000.10-1000.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1000.7-1000.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1000.16-1000.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1001.12-1001.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1000.13-1000.14"}}}}, "$_DFFE_NN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:650.1-656.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "E": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DFFE_NN_": {"type": "$_DFFE_NN_", "port_directions": {"D": "input", "C": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "E": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:651.10-651.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:651.7-651.8"}}, "E": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:651.13-651.14"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:652.12-652.13"}}}}, "$_DFFE_NP0N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1024.1-1033.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_NP0N_": {"type": "$_DFFE_NP0N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1025.10-1025.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1025.7-1025.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1025.16-1025.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1026.12-1026.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1025.13-1025.14"}}}}, "$_DFFE_NP0P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1049.1-1058.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_NP0P_": {"type": "$_DFFE_NP0P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1050.10-1050.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1050.7-1050.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1050.16-1050.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1051.12-1051.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1050.13-1050.14"}}}}, "$_DFFE_NP1N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1074.1-1083.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_NP1N_": {"type": "$_DFFE_NP1N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1075.10-1075.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1075.7-1075.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1075.16-1075.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1076.12-1076.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1075.13-1075.14"}}}}, "$_DFFE_NP1P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1099.1-1108.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_NP1P_": {"type": "$_DFFE_NP1P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1100.10-1100.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1100.7-1100.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1100.16-1100.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1101.12-1101.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1100.13-1100.14"}}}}, "$_DFFE_NP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:670.1-676.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "E": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DFFE_NP_": {"type": "$_DFFE_NP_", "port_directions": {"D": "input", "C": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "E": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:671.10-671.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:671.7-671.8"}}, "E": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:671.13-671.14"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:672.12-672.13"}}}}, "$_DFFE_PN0N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1124.1-1133.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_PN0N_": {"type": "$_DFFE_PN0N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1125.10-1125.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1125.7-1125.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1125.16-1125.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1126.12-1126.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1125.13-1125.14"}}}}, "$_DFFE_PN0P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1149.1-1158.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_PN0P_": {"type": "$_DFFE_PN0P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1150.10-1150.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1150.7-1150.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1150.16-1150.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1151.12-1151.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1150.13-1150.14"}}}}, "$_DFFE_PN1N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1174.1-1183.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_PN1N_": {"type": "$_DFFE_PN1N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1175.10-1175.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1175.7-1175.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1175.16-1175.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1176.12-1176.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1175.13-1175.14"}}}}, "$_DFFE_PN1P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1199.1-1208.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_PN1P_": {"type": "$_DFFE_PN1P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1200.10-1200.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1200.7-1200.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1200.16-1200.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1201.12-1201.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1200.13-1200.14"}}}}, "$_DFFE_PN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:690.1-696.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "E": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DFFE_PN_": {"type": "$_DFFE_PN_", "port_directions": {"D": "input", "C": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "E": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:691.10-691.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:691.7-691.8"}}, "E": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:691.13-691.14"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:692.12-692.13"}}}}, "$_DFFE_PP0N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1224.1-1233.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_PP0N_": {"type": "$_DFFE_PP0N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1225.10-1225.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1225.7-1225.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1225.16-1225.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1226.12-1226.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1225.13-1225.14"}}}}, "$_DFFE_PP0P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1249.1-1258.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_PP0P_": {"type": "$_DFFE_PP0P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1250.10-1250.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1250.7-1250.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1250.16-1250.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1251.12-1251.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1250.13-1250.14"}}}}, "$_DFFE_PP1N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1274.1-1283.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_PP1N_": {"type": "$_DFFE_PP1N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1275.10-1275.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1275.7-1275.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1275.16-1275.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1276.12-1276.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1275.13-1275.14"}}}}, "$_DFFE_PP1P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1299.1-1308.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFE_PP1P_": {"type": "$_DFFE_PP1P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1300.10-1300.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1300.7-1300.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1300.16-1300.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1301.12-1301.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1300.13-1300.14"}}}}, "$_DFFE_PP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:710.1-716.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "E": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DFFE_PP_": {"type": "$_DFFE_PP_", "port_directions": {"D": "input", "C": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "E": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:711.10-711.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:711.7-711.8"}}, "E": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:711.13-711.14"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:712.12-712.13"}}}}, "$_DFFSRE_NNNN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1845.1-1856.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_NNNN_": {"type": "$_DFFSRE_NNNN_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1846.7-1846.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1846.19-1846.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1846.16-1846.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1847.12-1847.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1846.13-1846.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1846.10-1846.11"}}}}, "$_DFFSRE_NNNP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1873.1-1884.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_NNNP_": {"type": "$_DFFSRE_NNNP_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1874.7-1874.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1874.19-1874.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1874.16-1874.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1875.12-1875.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1874.13-1874.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1874.10-1874.11"}}}}, "$_DFFSRE_NNPN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1901.1-1912.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_NNPN_": {"type": "$_DFFSRE_NNPN_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1902.7-1902.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1902.19-1902.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1902.16-1902.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1903.12-1903.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1902.13-1902.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1902.10-1902.11"}}}}, "$_DFFSRE_NNPP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1929.1-1940.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_NNPP_": {"type": "$_DFFSRE_NNPP_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1930.7-1930.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1930.19-1930.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1930.16-1930.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1931.12-1931.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1930.13-1930.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1930.10-1930.11"}}}}, "$_DFFSRE_NPNN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1957.1-1968.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_NPNN_": {"type": "$_DFFSRE_NPNN_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1958.7-1958.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1958.19-1958.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1958.16-1958.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1959.12-1959.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1958.13-1958.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1958.10-1958.11"}}}}, "$_DFFSRE_NPNP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1985.1-1996.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_NPNP_": {"type": "$_DFFSRE_NPNP_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1986.7-1986.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1986.19-1986.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1986.16-1986.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1987.12-1987.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1986.13-1986.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1986.10-1986.11"}}}}, "$_DFFSRE_NPPN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2013.1-2024.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_NPPN_": {"type": "$_DFFSRE_NPPN_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2014.7-2014.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2014.19-2014.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2014.16-2014.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2015.12-2015.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2014.13-2014.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2014.10-2014.11"}}}}, "$_DFFSRE_NPPP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2041.1-2052.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_NPPP_": {"type": "$_DFFSRE_NPPP_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2042.7-2042.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2042.19-2042.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2042.16-2042.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2043.12-2043.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2042.13-2042.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2042.10-2042.11"}}}}, "$_DFFSRE_PNNN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2069.1-2080.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_PNNN_": {"type": "$_DFFSRE_PNNN_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2070.7-2070.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2070.19-2070.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2070.16-2070.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2071.12-2071.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2070.13-2070.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2070.10-2070.11"}}}}, "$_DFFSRE_PNNP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2097.1-2108.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_PNNP_": {"type": "$_DFFSRE_PNNP_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2098.7-2098.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2098.19-2098.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2098.16-2098.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2099.12-2099.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2098.13-2098.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2098.10-2098.11"}}}}, "$_DFFSRE_PNPN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2125.1-2136.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_PNPN_": {"type": "$_DFFSRE_PNPN_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2126.7-2126.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2126.19-2126.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2126.16-2126.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2127.12-2127.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2126.13-2126.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2126.10-2126.11"}}}}, "$_DFFSRE_PNPP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2153.1-2164.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_PNPP_": {"type": "$_DFFSRE_PNPP_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2154.7-2154.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2154.19-2154.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2154.16-2154.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2155.12-2155.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2154.13-2154.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2154.10-2154.11"}}}}, "$_DFFSRE_PPNN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2181.1-2192.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_PPNN_": {"type": "$_DFFSRE_PPNN_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2182.7-2182.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2182.19-2182.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2182.16-2182.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2183.12-2183.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2182.13-2182.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2182.10-2182.11"}}}}, "$_DFFSRE_PPNP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2209.1-2220.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_PPNP_": {"type": "$_DFFSRE_PPNP_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2210.7-2210.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2210.19-2210.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2210.16-2210.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2211.12-2211.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2210.13-2210.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2210.10-2210.11"}}}}, "$_DFFSRE_PPPN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2237.1-2248.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_PPPN_": {"type": "$_DFFSRE_PPPN_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2238.7-2238.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2238.19-2238.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2238.16-2238.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2239.12-2239.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2238.13-2238.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2238.10-2238.11"}}}}, "$_DFFSRE_PPPP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2265.1-2276.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "D": {"direction": "input", "bits": [6]}, "Q": {"direction": "output", "bits": [7]}}, "cells": {"$_DFFSRE_PPPP_": {"type": "$_DFFSRE_PPPP_", "port_directions": {"C": "input", "S": "input", "R": "input", "E": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "E": [5], "D": [6], "Q": [7]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2266.7-2266.8"}}, "D": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2266.19-2266.20"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2266.16-2266.17"}}, "Q": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2267.12-2267.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2266.13-2266.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2266.10-2266.11"}}}}, "$_DFFSR_NNN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1621.1-1632.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFSR_NNN_": {"type": "$_DFFSR_NNN_", "port_directions": {"C": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1622.7-1622.8"}}, "D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1622.16-1622.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1623.12-1623.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1622.13-1622.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1622.10-1622.11"}}}}, "$_DFFSR_NNP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1649.1-1660.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFSR_NNP_": {"type": "$_DFFSR_NNP_", "port_directions": {"C": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1650.7-1650.8"}}, "D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1650.16-1650.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1651.12-1651.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1650.13-1650.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1650.10-1650.11"}}}}, "$_DFFSR_NPN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1677.1-1688.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFSR_NPN_": {"type": "$_DFFSR_NPN_", "port_directions": {"C": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1678.7-1678.8"}}, "D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1678.16-1678.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1679.12-1679.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1678.13-1678.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1678.10-1678.11"}}}}, "$_DFFSR_NPP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1705.1-1716.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFSR_NPP_": {"type": "$_DFFSR_NPP_", "port_directions": {"C": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1706.7-1706.8"}}, "D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1706.16-1706.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1707.12-1707.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1706.13-1706.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1706.10-1706.11"}}}}, "$_DFFSR_PNN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1733.1-1744.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFSR_PNN_": {"type": "$_DFFSR_PNN_", "port_directions": {"C": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1734.7-1734.8"}}, "D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1734.16-1734.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1735.12-1735.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1734.13-1734.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1734.10-1734.11"}}}}, "$_DFFSR_PNP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1761.1-1772.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFSR_PNP_": {"type": "$_DFFSR_PNP_", "port_directions": {"C": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1762.7-1762.8"}}, "D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1762.16-1762.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1763.12-1763.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1762.13-1762.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1762.10-1762.11"}}}}, "$_DFFSR_PPN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1789.1-1800.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFSR_PPN_": {"type": "$_DFFSR_PPN_", "port_directions": {"C": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1790.7-1790.8"}}, "D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1790.16-1790.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1791.12-1791.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1790.13-1790.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1790.10-1790.11"}}}}, "$_DFFSR_PPP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1817.1-1828.10"}, "ports": {"C": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DFFSR_PPP_": {"type": "$_DFFSR_PPP_", "port_directions": {"C": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"C": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1818.7-1818.8"}}, "D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1818.16-1818.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1819.12-1819.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1818.13-1818.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:1818.10-1818.11"}}}}, "$_DFF_NN0__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:731.1-740.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DFF_NN0_": {"type": "$_DFF_NN0_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:732.10-732.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:732.7-732.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:733.12-733.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:732.13-732.14"}}}}, "$_DFF_NN1__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:755.1-764.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DFF_NN1_": {"type": "$_DFF_NN1_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:756.10-756.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:756.7-756.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:757.12-757.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:756.13-756.14"}}}}, "$_DFF_NP0__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:779.1-788.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DFF_NP0_": {"type": "$_DFF_NP0_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:780.10-780.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:780.7-780.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:781.12-781.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:780.13-780.14"}}}}, "$_DFF_NP1__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:803.1-812.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DFF_NP1_": {"type": "$_DFF_NP1_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:804.10-804.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:804.7-804.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:805.12-805.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:804.13-804.14"}}}}, "$_DFF_N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:610.1-616.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "Q": {"direction": "output", "bits": [4]}}, "cells": {"$_DFF_N_": {"type": "$_DFF_N_", "port_directions": {"D": "input", "C": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "Q": [4]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:611.10-611.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:611.7-611.8"}}, "Q": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:612.12-612.13"}}}}, "$_DFF_PN0__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:827.1-836.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DFF_PN0_": {"type": "$_DFF_PN0_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:828.10-828.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:828.7-828.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:829.12-829.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:828.13-828.14"}}}}, "$_DFF_PN1__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:851.1-860.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DFF_PN1_": {"type": "$_DFF_PN1_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:852.10-852.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:852.7-852.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:853.12-853.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:852.13-852.14"}}}}, "$_DFF_PP0__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:875.1-884.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DFF_PP0_": {"type": "$_DFF_PP0_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:876.10-876.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:876.7-876.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:877.12-877.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:876.13-876.14"}}}}, "$_DFF_PP1__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:899.1-908.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DFF_PP1_": {"type": "$_DFF_PP1_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:900.10-900.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:900.7-900.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:901.12-901.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:900.13-900.14"}}}}, "$_DFF_P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:630.1-636.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "Q": {"direction": "output", "bits": [4]}}, "cells": {"$_DFF_P_": {"type": "$_DFF_P_", "port_directions": {"D": "input", "C": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "Q": [4]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:631.10-631.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:631.7-631.8"}}, "Q": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:632.12-632.13"}}}}, "$_DLATCHSR_NNN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3551.1-3562.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DLATCHSR_NNN_": {"type": "$_DLATCHSR_NNN_", "port_directions": {"E": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3552.16-3552.17"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3552.7-3552.8"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3553.12-3553.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3552.13-3552.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3552.10-3552.11"}}}}, "$_DLATCHSR_NNP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3579.1-3590.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DLATCHSR_NNP_": {"type": "$_DLATCHSR_NNP_", "port_directions": {"E": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3580.16-3580.17"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3580.7-3580.8"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3581.12-3581.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3580.13-3580.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3580.10-3580.11"}}}}, "$_DLATCHSR_NPN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3607.1-3618.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DLATCHSR_NPN_": {"type": "$_DLATCHSR_NPN_", "port_directions": {"E": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3608.16-3608.17"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3608.7-3608.8"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3609.12-3609.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3608.13-3608.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3608.10-3608.11"}}}}, "$_DLATCHSR_NPP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3635.1-3646.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DLATCHSR_NPP_": {"type": "$_DLATCHSR_NPP_", "port_directions": {"E": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3636.16-3636.17"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3636.7-3636.8"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3637.12-3637.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3636.13-3636.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3636.10-3636.11"}}}}, "$_DLATCHSR_PNN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3663.1-3674.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DLATCHSR_PNN_": {"type": "$_DLATCHSR_PNN_", "port_directions": {"E": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3664.16-3664.17"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3664.7-3664.8"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3665.12-3665.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3664.13-3664.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3664.10-3664.11"}}}}, "$_DLATCHSR_PNP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3691.1-3702.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DLATCHSR_PNP_": {"type": "$_DLATCHSR_PNP_", "port_directions": {"E": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3692.16-3692.17"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3692.7-3692.8"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3693.12-3693.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3692.13-3692.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3692.10-3692.11"}}}}, "$_DLATCHSR_PPN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3719.1-3730.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DLATCHSR_PPN_": {"type": "$_DLATCHSR_PPN_", "port_directions": {"E": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3720.16-3720.17"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3720.7-3720.8"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3721.12-3721.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3720.13-3720.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3720.10-3720.11"}}}}, "$_DLATCHSR_PPP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3747.1-3758.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "S": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_DLATCHSR_PPP_": {"type": "$_DLATCHSR_PPP_", "port_directions": {"E": "input", "S": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "S": [3], "R": [4], "D": [5], "Q": [6]}}}, "netnames": {"D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3748.16-3748.17"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3748.7-3748.8"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3749.12-3749.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3748.13-3748.14"}}, "S": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3748.10-3748.11"}}}}, "$_DLATCH_NN0__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3357.1-3366.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "R": {"direction": "input", "bits": [3]}, "D": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DLATCH_NN0_": {"type": "$_DLATCH_NN0_", "port_directions": {"E": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "R": [3], "D": [4], "Q": [5]}}}, "netnames": {"D": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3358.13-3358.14"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3358.7-3358.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3359.12-3359.13"}}, "R": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3358.10-3358.11"}}}}, "$_DLATCH_NN1__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3381.1-3390.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "R": {"direction": "input", "bits": [3]}, "D": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DLATCH_NN1_": {"type": "$_DLATCH_NN1_", "port_directions": {"E": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "R": [3], "D": [4], "Q": [5]}}}, "netnames": {"D": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3382.13-3382.14"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3382.7-3382.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3383.12-3383.13"}}, "R": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3382.10-3382.11"}}}}, "$_DLATCH_NP0__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3405.1-3414.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "R": {"direction": "input", "bits": [3]}, "D": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DLATCH_NP0_": {"type": "$_DLATCH_NP0_", "port_directions": {"E": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "R": [3], "D": [4], "Q": [5]}}}, "netnames": {"D": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3406.13-3406.14"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3406.7-3406.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3407.12-3407.13"}}, "R": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3406.10-3406.11"}}}}, "$_DLATCH_NP1__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3429.1-3438.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "R": {"direction": "input", "bits": [3]}, "D": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DLATCH_NP1_": {"type": "$_DLATCH_NP1_", "port_directions": {"E": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "R": [3], "D": [4], "Q": [5]}}}, "netnames": {"D": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3430.13-3430.14"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3430.7-3430.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3431.12-3431.13"}}, "R": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3430.10-3430.11"}}}}, "$_DLATCH_N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3314.1-3321.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "D": {"direction": "input", "bits": [3]}, "Q": {"direction": "output", "bits": [4]}}, "cells": {"$_DLATCH_N_": {"type": "$_DLATCH_N_", "port_directions": {"E": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "D": [3], "Q": [4]}}}, "netnames": {"D": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3315.10-3315.11"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3315.7-3315.8"}}, "Q": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3316.12-3316.13"}}}}, "$_DLATCH_PN0__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3453.1-3462.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "R": {"direction": "input", "bits": [3]}, "D": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DLATCH_PN0_": {"type": "$_DLATCH_PN0_", "port_directions": {"E": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "R": [3], "D": [4], "Q": [5]}}}, "netnames": {"D": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3454.13-3454.14"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3454.7-3454.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3455.12-3455.13"}}, "R": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3454.10-3454.11"}}}}, "$_DLATCH_PN1__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3477.1-3486.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "R": {"direction": "input", "bits": [3]}, "D": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DLATCH_PN1_": {"type": "$_DLATCH_PN1_", "port_directions": {"E": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "R": [3], "D": [4], "Q": [5]}}}, "netnames": {"D": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3478.13-3478.14"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3478.7-3478.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3479.12-3479.13"}}, "R": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3478.10-3478.11"}}}}, "$_DLATCH_PP0__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3501.1-3510.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "R": {"direction": "input", "bits": [3]}, "D": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DLATCH_PP0_": {"type": "$_DLATCH_PP0_", "port_directions": {"E": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "R": [3], "D": [4], "Q": [5]}}}, "netnames": {"D": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3502.13-3502.14"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3502.7-3502.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3503.12-3503.13"}}, "R": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3502.10-3502.11"}}}}, "$_DLATCH_PP1__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3525.1-3534.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "R": {"direction": "input", "bits": [3]}, "D": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_DLATCH_PP1_": {"type": "$_DLATCH_PP1_", "port_directions": {"E": "input", "R": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "R": [3], "D": [4], "Q": [5]}}}, "netnames": {"D": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3526.13-3526.14"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3526.7-3526.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3527.12-3527.13"}}, "R": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3526.10-3526.11"}}}}, "$_DLATCH_P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3335.1-3342.10"}, "ports": {"E": {"direction": "input", "bits": [2]}, "D": {"direction": "input", "bits": [3]}, "Q": {"direction": "output", "bits": [4]}}, "cells": {"$_DLATCH_P_": {"type": "$_DLATCH_P_", "port_directions": {"E": "input", "D": "input", "Q": "output"}, "connections": {"E": [2], "D": [3], "Q": [4]}}}, "netnames": {"D": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3336.10-3336.11"}}, "E": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3336.7-3336.8"}}, "Q": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3337.12-3337.13"}}}}, "$_MUX16__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:336.1-347.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "C": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "E": {"direction": "input", "bits": [6]}, "F": {"direction": "input", "bits": [7]}, "G": {"direction": "input", "bits": [8]}, "H": {"direction": "input", "bits": [9]}, "I": {"direction": "input", "bits": [10]}, "J": {"direction": "input", "bits": [11]}, "K": {"direction": "input", "bits": [12]}, "L": {"direction": "input", "bits": [13]}, "M": {"direction": "input", "bits": [14]}, "N": {"direction": "input", "bits": [15]}, "O": {"direction": "input", "bits": [16]}, "P": {"direction": "input", "bits": [17]}, "S": {"direction": "input", "bits": [18]}, "T": {"direction": "input", "bits": [19]}, "U": {"direction": "input", "bits": [20]}, "V": {"direction": "input", "bits": [21]}, "Y": {"direction": "output", "bits": [22]}}, "cells": {"$_MUX16_": {"type": "$_MUX16_", "port_directions": {"A": "input", "B": "input", "C": "input", "D": "input", "E": "input", "F": "input", "G": "input", "H": "input", "I": "input", "J": "input", "K": "input", "L": "input", "M": "input", "N": "input", "O": "input", "P": "input", "S": "input", "T": "input", "U": "input", "V": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "C": [4], "D": [5], "E": [6], "F": [7], "G": [8], "H": [9], "I": [10], "J": [11], "K": [12], "L": [13], "M": [14], "N": [15], "O": [16], "P": [17], "S": [18], "T": [19], "U": [20], "V": [21], "Y": [22]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.7-337.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.10-337.11"}}, "C": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.13-337.14"}}, "D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.16-337.17"}}, "E": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.19-337.20"}}, "F": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.22-337.23"}}, "G": {"hide_name": 0, "bits": [8], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.25-337.26"}}, "H": {"hide_name": 0, "bits": [9], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.28-337.29"}}, "I": {"hide_name": 0, "bits": [10], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.31-337.32"}}, "J": {"hide_name": 0, "bits": [11], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.34-337.35"}}, "K": {"hide_name": 0, "bits": [12], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.37-337.38"}}, "L": {"hide_name": 0, "bits": [13], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.40-337.41"}}, "M": {"hide_name": 0, "bits": [14], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.43-337.44"}}, "N": {"hide_name": 0, "bits": [15], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.46-337.47"}}, "O": {"hide_name": 0, "bits": [16], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.49-337.50"}}, "P": {"hide_name": 0, "bits": [17], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.52-337.53"}}, "S": {"hide_name": 0, "bits": [18], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.55-337.56"}}, "T": {"hide_name": 0, "bits": [19], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.58-337.59"}}, "U": {"hide_name": 0, "bits": [20], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.61-337.62"}}, "V": {"hide_name": 0, "bits": [21], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:337.64-337.65"}}, "Y": {"hide_name": 0, "bits": [22], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:338.8-338.9"}}}}, "$_MUX4__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:276.1-281.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "C": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "S": {"direction": "input", "bits": [6]}, "T": {"direction": "input", "bits": [7]}, "Y": {"direction": "output", "bits": [8]}}, "cells": {"$_MUX4_": {"type": "$_MUX4_", "port_directions": {"A": "input", "B": "input", "C": "input", "D": "input", "S": "input", "T": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "C": [4], "D": [5], "S": [6], "T": [7], "Y": [8]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:277.7-277.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:277.10-277.11"}}, "C": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:277.13-277.14"}}, "D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:277.16-277.17"}}, "S": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:277.19-277.20"}}, "T": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:277.22-277.23"}}, "Y": {"hide_name": 0, "bits": [8], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:278.8-278.9"}}}}, "$_MUX8__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:301.1-308.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "C": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "E": {"direction": "input", "bits": [6]}, "F": {"direction": "input", "bits": [7]}, "G": {"direction": "input", "bits": [8]}, "H": {"direction": "input", "bits": [9]}, "S": {"direction": "input", "bits": [10]}, "T": {"direction": "input", "bits": [11]}, "U": {"direction": "input", "bits": [12]}, "Y": {"direction": "output", "bits": [13]}}, "cells": {"$_MUX8_": {"type": "$_MUX8_", "port_directions": {"A": "input", "B": "input", "C": "input", "D": "input", "E": "input", "F": "input", "G": "input", "H": "input", "S": "input", "T": "input", "U": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "C": [4], "D": [5], "E": [6], "F": [7], "G": [8], "H": [9], "S": [10], "T": [11], "U": [12], "Y": [13]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:302.7-302.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:302.10-302.11"}}, "C": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:302.13-302.14"}}, "D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:302.16-302.17"}}, "E": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:302.19-302.20"}}, "F": {"hide_name": 0, "bits": [7], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:302.22-302.23"}}, "G": {"hide_name": 0, "bits": [8], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:302.25-302.26"}}, "H": {"hide_name": 0, "bits": [9], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:302.28-302.29"}}, "S": {"hide_name": 0, "bits": [10], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:302.31-302.32"}}, "T": {"hide_name": 0, "bits": [11], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:302.34-302.35"}}, "U": {"hide_name": 0, "bits": [12], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:302.37-302.38"}}, "Y": {"hide_name": 0, "bits": [13], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:303.8-303.9"}}}}, "$_MUX__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:236.1-240.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "S": {"direction": "input", "bits": [4]}, "Y": {"direction": "output", "bits": [5]}}, "cells": {"$_MUX_": {"type": "$_MUX_", "port_directions": {"A": "input", "B": "input", "S": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "S": [4], "Y": [5]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:237.7-237.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:237.10-237.11"}}, "S": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:237.13-237.14"}}, "Y": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:238.8-238.9"}}}}, "$_NAND__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:98.1-102.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "Y": {"direction": "output", "bits": [4]}}, "cells": {"$_NAND_": {"type": "$_NAND_", "port_directions": {"A": "input", "B": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "Y": [4]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:99.7-99.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:99.10-99.11"}}, "Y": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:100.8-100.9"}}}}, "$_NMUX__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:256.1-260.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "S": {"direction": "input", "bits": [4]}, "Y": {"direction": "output", "bits": [5]}}, "cells": {"$_NMUX_": {"type": "$_NMUX_", "port_directions": {"A": "input", "B": "input", "S": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "S": [4], "Y": [5]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:257.7-257.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:257.10-257.11"}}, "S": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:257.13-257.14"}}, "Y": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:258.8-258.9"}}}}, "$_NOR__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:138.1-142.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "Y": {"direction": "output", "bits": [4]}}, "cells": {"$_NOR_": {"type": "$_NOR_", "port_directions": {"A": "input", "B": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "Y": [4]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:139.7-139.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:139.10-139.11"}}, "Y": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:140.8-140.9"}}}}, "$_NOT__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:58.1-62.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "Y": {"direction": "output", "bits": [3]}}, "cells": {"$_NOT_": {"type": "$_NOT_", "port_directions": {"A": "input", "Y": "output"}, "connections": {"A": [2], "Y": [3]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:59.7-59.8"}}, "Y": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:60.8-60.9"}}}}, "$_OAI3__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:391.1-395.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "C": {"direction": "input", "bits": [4]}, "Y": {"direction": "output", "bits": [5]}}, "cells": {"$_OAI3_": {"type": "$_OAI3_", "port_directions": {"A": "input", "B": "input", "C": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "C": [4], "Y": [5]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:392.7-392.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:392.10-392.11"}}, "C": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:392.13-392.14"}}, "Y": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:393.8-393.9"}}}}, "$_OAI4__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:455.1-459.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "C": {"direction": "input", "bits": [4]}, "D": {"direction": "input", "bits": [5]}, "Y": {"direction": "output", "bits": [6]}}, "cells": {"$_OAI4_": {"type": "$_OAI4_", "port_directions": {"A": "input", "B": "input", "C": "input", "D": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "C": [4], "D": [5], "Y": [6]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:456.7-456.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:456.10-456.11"}}, "C": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:456.13-456.14"}}, "D": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:456.16-456.17"}}, "Y": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:457.8-457.9"}}}}, "$_ORNOT__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:218.1-222.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "Y": {"direction": "output", "bits": [4]}}, "cells": {"$_ORNOT_": {"type": "$_ORNOT_", "port_directions": {"A": "input", "B": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "Y": [4]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:219.7-219.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:219.10-219.11"}}, "Y": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:220.8-220.9"}}}}, "$_OR__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:118.1-122.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "Y": {"direction": "output", "bits": [4]}}, "cells": {"$_OR_": {"type": "$_OR_", "port_directions": {"A": "input", "B": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "Y": [4]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:119.7-119.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:119.10-119.11"}}, "Y": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:120.8-120.9"}}}}, "$_SDFFCE_NN0N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2884.1-2895.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_NN0N_": {"type": "$_SDFFCE_NN0N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2885.10-2885.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2885.7-2885.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2885.16-2885.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2886.12-2886.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2885.13-2885.14"}}}}, "$_SDFFCE_NN0P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2911.1-2922.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_NN0P_": {"type": "$_SDFFCE_NN0P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2912.10-2912.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2912.7-2912.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2912.16-2912.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2913.12-2913.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2912.13-2912.14"}}}}, "$_SDFFCE_NN1N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2938.1-2949.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_NN1N_": {"type": "$_SDFFCE_NN1N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2939.10-2939.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2939.7-2939.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2939.16-2939.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2940.12-2940.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2939.13-2939.14"}}}}, "$_SDFFCE_NN1P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2965.1-2976.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_NN1P_": {"type": "$_SDFFCE_NN1P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2966.10-2966.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2966.7-2966.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2966.16-2966.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2967.12-2967.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2966.13-2966.14"}}}}, "$_SDFFCE_NP0N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2992.1-3003.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_NP0N_": {"type": "$_SDFFCE_NP0N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2993.10-2993.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2993.7-2993.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2993.16-2993.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2994.12-2994.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2993.13-2993.14"}}}}, "$_SDFFCE_NP0P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3019.1-3030.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_NP0P_": {"type": "$_SDFFCE_NP0P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3020.10-3020.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3020.7-3020.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3020.16-3020.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3021.12-3021.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3020.13-3020.14"}}}}, "$_SDFFCE_NP1N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3046.1-3057.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_NP1N_": {"type": "$_SDFFCE_NP1N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3047.10-3047.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3047.7-3047.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3047.16-3047.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3048.12-3048.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3047.13-3047.14"}}}}, "$_SDFFCE_NP1P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3073.1-3084.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_NP1P_": {"type": "$_SDFFCE_NP1P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3074.10-3074.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3074.7-3074.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3074.16-3074.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3075.12-3075.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3074.13-3074.14"}}}}, "$_SDFFCE_PN0N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3100.1-3111.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_PN0N_": {"type": "$_SDFFCE_PN0N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3101.10-3101.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3101.7-3101.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3101.16-3101.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3102.12-3102.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3101.13-3101.14"}}}}, "$_SDFFCE_PN0P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3127.1-3138.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_PN0P_": {"type": "$_SDFFCE_PN0P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3128.10-3128.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3128.7-3128.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3128.16-3128.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3129.12-3129.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3128.13-3128.14"}}}}, "$_SDFFCE_PN1N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3154.1-3165.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_PN1N_": {"type": "$_SDFFCE_PN1N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3155.10-3155.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3155.7-3155.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3155.16-3155.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3156.12-3156.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3155.13-3155.14"}}}}, "$_SDFFCE_PN1P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3181.1-3192.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_PN1P_": {"type": "$_SDFFCE_PN1P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3182.10-3182.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3182.7-3182.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3182.16-3182.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3183.12-3183.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3182.13-3182.14"}}}}, "$_SDFFCE_PP0N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3208.1-3219.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_PP0N_": {"type": "$_SDFFCE_PP0N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3209.10-3209.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3209.7-3209.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3209.16-3209.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3210.12-3210.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3209.13-3209.14"}}}}, "$_SDFFCE_PP0P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3235.1-3246.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_PP0P_": {"type": "$_SDFFCE_PP0P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3236.10-3236.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3236.7-3236.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3236.16-3236.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3237.12-3237.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3236.13-3236.14"}}}}, "$_SDFFCE_PP1N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3262.1-3273.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_PP1N_": {"type": "$_SDFFCE_PP1N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3263.10-3263.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3263.7-3263.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3263.16-3263.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3264.12-3264.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3263.13-3263.14"}}}}, "$_SDFFCE_PP1P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3289.1-3300.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFCE_PP1P_": {"type": "$_SDFFCE_PP1P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3290.10-3290.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3290.7-3290.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3290.16-3290.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3291.12-3291.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:3290.13-3290.14"}}}}, "$_SDFFE_NN0N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2484.1-2493.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_NN0N_": {"type": "$_SDFFE_NN0N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2485.10-2485.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2485.7-2485.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2485.16-2485.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2486.12-2486.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2485.13-2485.14"}}}}, "$_SDFFE_NN0P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2509.1-2518.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_NN0P_": {"type": "$_SDFFE_NN0P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2510.10-2510.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2510.7-2510.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2510.16-2510.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2511.12-2511.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2510.13-2510.14"}}}}, "$_SDFFE_NN1N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2534.1-2543.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_NN1N_": {"type": "$_SDFFE_NN1N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2535.10-2535.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2535.7-2535.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2535.16-2535.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2536.12-2536.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2535.13-2535.14"}}}}, "$_SDFFE_NN1P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2559.1-2568.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_NN1P_": {"type": "$_SDFFE_NN1P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2560.10-2560.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2560.7-2560.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2560.16-2560.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2561.12-2561.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2560.13-2560.14"}}}}, "$_SDFFE_NP0N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2584.1-2593.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_NP0N_": {"type": "$_SDFFE_NP0N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2585.10-2585.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2585.7-2585.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2585.16-2585.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2586.12-2586.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2585.13-2585.14"}}}}, "$_SDFFE_NP0P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2609.1-2618.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_NP0P_": {"type": "$_SDFFE_NP0P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2610.10-2610.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2610.7-2610.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2610.16-2610.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2611.12-2611.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2610.13-2610.14"}}}}, "$_SDFFE_NP1N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2634.1-2643.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_NP1N_": {"type": "$_SDFFE_NP1N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2635.10-2635.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2635.7-2635.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2635.16-2635.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2636.12-2636.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2635.13-2635.14"}}}}, "$_SDFFE_NP1P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2659.1-2668.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_NP1P_": {"type": "$_SDFFE_NP1P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2660.10-2660.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2660.7-2660.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2660.16-2660.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2661.12-2661.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2660.13-2660.14"}}}}, "$_SDFFE_PN0N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2684.1-2693.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_PN0N_": {"type": "$_SDFFE_PN0N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2685.10-2685.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2685.7-2685.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2685.16-2685.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2686.12-2686.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2685.13-2685.14"}}}}, "$_SDFFE_PN0P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2709.1-2718.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_PN0P_": {"type": "$_SDFFE_PN0P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2710.10-2710.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2710.7-2710.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2710.16-2710.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2711.12-2711.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2710.13-2710.14"}}}}, "$_SDFFE_PN1N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2734.1-2743.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_PN1N_": {"type": "$_SDFFE_PN1N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2735.10-2735.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2735.7-2735.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2735.16-2735.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2736.12-2736.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2735.13-2735.14"}}}}, "$_SDFFE_PN1P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2759.1-2768.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_PN1P_": {"type": "$_SDFFE_PN1P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2760.10-2760.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2760.7-2760.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2760.16-2760.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2761.12-2761.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2760.13-2760.14"}}}}, "$_SDFFE_PP0N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2784.1-2793.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_PP0N_": {"type": "$_SDFFE_PP0N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2785.10-2785.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2785.7-2785.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2785.16-2785.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2786.12-2786.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2785.13-2785.14"}}}}, "$_SDFFE_PP0P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2809.1-2818.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_PP0P_": {"type": "$_SDFFE_PP0P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2810.10-2810.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2810.7-2810.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2810.16-2810.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2811.12-2811.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2810.13-2810.14"}}}}, "$_SDFFE_PP1N__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2834.1-2843.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_PP1N_": {"type": "$_SDFFE_PP1N_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2835.10-2835.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2835.7-2835.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2835.16-2835.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2836.12-2836.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2835.13-2835.14"}}}}, "$_SDFFE_PP1P__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2859.1-2868.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "E": {"direction": "input", "bits": [5]}, "Q": {"direction": "output", "bits": [6]}}, "cells": {"$_SDFFE_PP1P_": {"type": "$_SDFFE_PP1P_", "port_directions": {"D": "input", "C": "input", "R": "input", "E": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "E": [5], "Q": [6]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2860.10-2860.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2860.7-2860.8"}}, "E": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2860.16-2860.17"}}, "Q": {"hide_name": 0, "bits": [6], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2861.12-2861.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2860.13-2860.14"}}}}, "$_SDFF_NN0__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2291.1-2300.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_SDFF_NN0_": {"type": "$_SDFF_NN0_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2292.10-2292.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2292.7-2292.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2293.12-2293.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2292.13-2292.14"}}}}, "$_SDFF_NN1__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2315.1-2324.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_SDFF_NN1_": {"type": "$_SDFF_NN1_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2316.10-2316.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2316.7-2316.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2317.12-2317.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2316.13-2316.14"}}}}, "$_SDFF_NP0__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2339.1-2348.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_SDFF_NP0_": {"type": "$_SDFF_NP0_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2340.10-2340.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2340.7-2340.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2341.12-2341.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2340.13-2340.14"}}}}, "$_SDFF_NP1__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2363.1-2372.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_SDFF_NP1_": {"type": "$_SDFF_NP1_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2364.10-2364.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2364.7-2364.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2365.12-2365.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2364.13-2364.14"}}}}, "$_SDFF_PN0__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2387.1-2396.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_SDFF_PN0_": {"type": "$_SDFF_PN0_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2388.10-2388.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2388.7-2388.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2389.12-2389.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2388.13-2388.14"}}}}, "$_SDFF_PN1__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2411.1-2420.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_SDFF_PN1_": {"type": "$_SDFF_PN1_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2412.10-2412.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2412.7-2412.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2413.12-2413.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2412.13-2412.14"}}}}, "$_SDFF_PP0__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2435.1-2444.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_SDFF_PP0_": {"type": "$_SDFF_PP0_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2436.10-2436.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2436.7-2436.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2437.12-2437.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2436.13-2436.14"}}}}, "$_SDFF_PP1__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2459.1-2468.10"}, "ports": {"D": {"direction": "input", "bits": [2]}, "C": {"direction": "input", "bits": [3]}, "R": {"direction": "input", "bits": [4]}, "Q": {"direction": "output", "bits": [5]}}, "cells": {"$_SDFF_PP1_": {"type": "$_SDFF_PP1_", "port_directions": {"D": "input", "C": "input", "R": "input", "Q": "output"}, "connections": {"D": [2], "C": [3], "R": [4], "Q": [5]}}}, "netnames": {"C": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2460.10-2460.11"}}, "D": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2460.7-2460.8"}}, "Q": {"hide_name": 0, "bits": [5], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2461.12-2461.13"}}, "R": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:2460.13-2460.14"}}}}, "$_SR_NN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:497.1-506.10"}, "ports": {"S": {"direction": "input", "bits": [2]}, "R": {"direction": "input", "bits": [3]}, "Q": {"direction": "output", "bits": [4]}}, "cells": {"$_SR_NN_": {"type": "$_SR_NN_", "port_directions": {"S": "input", "R": "input", "Q": "output"}, "connections": {"S": [2], "R": [3], "Q": [4]}}}, "netnames": {"Q": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:499.12-499.13"}}, "R": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:498.10-498.11"}}, "S": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:498.7-498.8"}}}}, "$_SR_NP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:521.1-530.10"}, "ports": {"S": {"direction": "input", "bits": [2]}, "R": {"direction": "input", "bits": [3]}, "Q": {"direction": "output", "bits": [4]}}, "cells": {"$_SR_NP_": {"type": "$_SR_NP_", "port_directions": {"S": "input", "R": "input", "Q": "output"}, "connections": {"S": [2], "R": [3], "Q": [4]}}}, "netnames": {"Q": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:523.12-523.13"}}, "R": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:522.10-522.11"}}, "S": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:522.7-522.8"}}}}, "$_SR_PN__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:545.1-554.10"}, "ports": {"S": {"direction": "input", "bits": [2]}, "R": {"direction": "input", "bits": [3]}, "Q": {"direction": "output", "bits": [4]}}, "cells": {"$_SR_PN_": {"type": "$_SR_PN_", "port_directions": {"S": "input", "R": "input", "Q": "output"}, "connections": {"S": [2], "R": [3], "Q": [4]}}}, "netnames": {"Q": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:547.12-547.13"}}, "R": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:546.10-546.11"}}, "S": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:546.7-546.8"}}}}, "$_SR_PP__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:569.1-578.10"}, "ports": {"S": {"direction": "input", "bits": [2]}, "R": {"direction": "input", "bits": [3]}, "Q": {"direction": "output", "bits": [4]}}, "cells": {"$_SR_PP_": {"type": "$_SR_PP_", "port_directions": {"S": "input", "R": "input", "Q": "output"}, "connections": {"S": [2], "R": [3], "Q": [4]}}}, "netnames": {"Q": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:571.12-571.13"}}, "R": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:570.10-570.11"}}, "S": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:570.7-570.8"}}}}, "$_TBUF__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:473.1-477.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "E": {"direction": "input", "bits": [3]}, "Y": {"direction": "output", "bits": [4]}}, "cells": {"$_TBUF_": {"type": "$_TBUF_", "port_directions": {"A": "input", "E": "input", "Y": "output"}, "connections": {"A": [2], "E": [3], "Y": [4]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:474.7-474.8"}}, "E": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:474.10-474.11"}}, "Y": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:475.8-475.9"}}}}, "$_XNOR__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:178.1-182.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "Y": {"direction": "output", "bits": [4]}}, "cells": {"$_XNOR_": {"type": "$_XNOR_", "port_directions": {"A": "input", "B": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "Y": [4]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:179.7-179.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:179.10-179.11"}}, "Y": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:180.8-180.9"}}}}, "$_XOR__WRAPPER": {"attributes": {"blackbox": "00000000000000000000000000000001", "cells_not_processed": "00000000000000000000000000000001", "src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:158.1-162.10"}, "ports": {"A": {"direction": "input", "bits": [2]}, "B": {"direction": "input", "bits": [3]}, "Y": {"direction": "output", "bits": [4]}}, "cells": {"$_XOR_": {"type": "$_XOR_", "port_directions": {"A": "input", "B": "input", "Y": "output"}, "connections": {"A": [2], "B": [3], "Y": [4]}}}, "netnames": {"A": {"hide_name": 0, "bits": [2], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:159.7-159.8"}}, "B": {"hide_name": 0, "bits": [3], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:159.10-159.11"}}, "Y": {"hide_name": 0, "bits": [4], "attributes": {"src": "/opt/oss-cad-suite/latest/share/yosys/simcells.v:160.8-160.9"}}}}}} diff --git a/crates/arbolta/tests/generate_deps.py b/crates/arbolta/tests/generate_deps.py new file mode 100644 index 0000000..82fcfc7 --- /dev/null +++ b/crates/arbolta/tests/generate_deps.py @@ -0,0 +1,63 @@ +import copy +import json +import subprocess +from pathlib import Path + + +def generate_simcells_netlist(simcells_verilog: Path, netlist: Path): + passes = f""" + read_verilog -sv -icells -lib {simcells_verilog}; + proc; + clean; + autoname; + setundef -zero; + flatten -scopename; + write_json {netlist}; + """ + args = ["yosys", "-p", passes] + + result = subprocess.run( + args, capture_output=True, timeout=60, text=True, encoding="utf-8" + ) + assert result.returncode == 0, result.stderr + + +def clean_simcells_netlist(netlist_path: Path, new_netlist_path: Path): + with open(netlist_path, "r") as f: + netlist = json.load(f) + + new_netlist = {k: v for k, v in netlist.items() if k != "modules"} + new_netlist["modules"] = {} + + for module_name, module in netlist["modules"].items(): + new_module_name = f"{module_name}_WRAPPER" + new_netlist["modules"][new_module_name] = copy.deepcopy(module) + + if len(module["cells"]) > 0: + continue + + module_cell = { + "type": module_name, + "port_directions": {}, + "connections": {}, + } + + for port_name, port_info in module["ports"].items(): + # print(port_info) + module_cell["port_directions"][port_name] = port_info["direction"] + module_cell["connections"][port_name] = port_info["bits"] + + new_netlist["modules"][new_module_name]["cells"] = {module_name: module_cell} + + with open(new_netlist_path, "w") as f: + json.dump(new_netlist, f) + + # print(netlist) + + +if __name__ == "__main__": + rtl_path = Path("/opt/oss-cad-suite/latest/share/yosys/simcells.v") + netlist_path = Path("./deps/simcells_wrappers.json") + + generate_simcells_netlist(rtl_path, netlist_path) + clean_simcells_netlist(netlist_path, netlist_path) diff --git a/crates/arbolta/tests/test_module.rs b/crates/arbolta/tests/test_module.rs index c7f5497..e28a072 100644 --- a/crates/arbolta/tests/test_module.rs +++ b/crates/arbolta/tests/test_module.rs @@ -4,30 +4,22 @@ use arbolta::{bit::BitVec, hardware_module::HardwareModule}; use once_cell::sync::Lazy; use rstest::rstest; -use std::collections::HashMap; use yosys_netlist_json::Netlist; -const CELL_WRAPPER_NETLIST: &str = include_str!("deps/simcells_wrappers.json"); - -static CELL_WRAPPER: Lazy = - Lazy::new(|| Netlist::from_slice(CELL_WRAPPER_NETLIST.as_bytes()).unwrap()); +static CELL_WRAPPER_NETLIST: Lazy = + Lazy::new(|| Netlist::from_slice(include_bytes!("deps/simcells_wrappers.json")).unwrap()); #[rstest] -#[case::buffer("$_BUF_", [ +#[case::buffer("$_BUF__WRAPPER", [ (0, 0), (1, 1), ])] -#[case::inverter("$_NOT_", [ +#[case::inverter("$_NOT__WRAPPER", [ (0, 1), (1, 0), ])] fn test_module_unary_cell(#[case] cell: &str, #[case] cases: [(u8, u8); 2]) { - let mut module = HardwareModule::new( - &CELL_WRAPPER, - &HashMap::from([(cell.to_string(), vec![cell.to_string()])]), - cell, - ) - .unwrap(); + let mut module = HardwareModule::new(cell, &CELL_WRAPPER_NETLIST).unwrap(); for (a, expected) in cases { module.set_port("A", BitVec::from_int(a, None)).unwrap(); @@ -38,49 +30,44 @@ fn test_module_unary_cell(#[case] cell: &str, #[case] cases: [(u8, u8); 2]) { } #[rstest] -#[case("$_AND_", [ +#[case("$_AND__WRAPPER", [ (0, 0, 0), (0, 1, 0), (1, 0, 0), (1, 1, 1), ])] -#[case("$_NOR_", [ +#[case("$_NOR__WRAPPER", [ (0, 0, 1), (0, 1, 0), (1, 0, 0), (1, 1, 0), ])] -#[case("$_NAND_", [ +#[case("$_NAND__WRAPPER", [ (0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 0), ])] -#[case("$_OR_", [ +#[case("$_OR__WRAPPER", [ (0, 0, 0), (0, 1, 1), (1, 0, 1), (1, 1, 1), ])] -#[case("$_XOR_", [ +#[case("$_XOR__WRAPPER", [ (0, 0, 0), (0, 1, 1), (1, 0, 1), (1, 1, 0), ])] -#[case("$_XNOR_", [ +#[case("$_XNOR__WRAPPER", [ (0, 0, 1), (0, 1, 0), (1, 0, 0), (1, 1, 1), ])] fn test_module_binary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8); 4]) { - let mut module = HardwareModule::new( - &CELL_WRAPPER, - &HashMap::from([(cell.to_string(), vec![cell.to_string()])]), - cell, - ) - .unwrap(); + let mut module = HardwareModule::new(cell, &CELL_WRAPPER_NETLIST).unwrap(); for (a, b, expected) in cases { module.set_port("A", BitVec::from_int(a, None)).unwrap(); diff --git a/crates/fault/src/main.rs b/crates/fault/src/main.rs index 37fe485..5f937b4 100644 --- a/crates/fault/src/main.rs +++ b/crates/fault/src/main.rs @@ -1,9 +1,5 @@ use anyhow::{Result, anyhow, ensure}; -use arbolta::{ - bit::Bit, - hardware_module::HardwareModule, - yosys::{Netlist, parse_torder}, -}; +use arbolta::{bit::Bit, hardware_module::HardwareModule, yosys::Netlist}; use clap::Parser; use fault::utils::matmul; // use indicatif::ParallelProgressIterator; @@ -56,14 +52,12 @@ fn main() -> Result<()> { // Load and parse netlist and topological cell order let raw_netlist = std::fs::read(flags.netlist_path)?; - let raw_torder = std::fs::read_to_string(flags.torder_path)?; let netlist = Netlist::from_slice(&raw_netlist)?; - let torder = parse_torder(&raw_torder); // Setup design let (ROWS, COLS) = (flags.rows, flags.cols); // Copy for easier use - let mut design = HardwareModule::new(&netlist, &torder, &flags.top_module)?; + let mut design = HardwareModule::new(&flags.top_module, &netlist)?; design.set_clock(2, Bit::ONE)?; design.set_reset(3, Bit::ZERO)?; design.set_port_shape("sx_data_i", &[ROWS, flags.x_width])?; diff --git a/crates/python_bindings/Cargo.toml b/crates/python_bindings/Cargo.toml index 576d01b..b064585 100644 --- a/crates/python_bindings/Cargo.toml +++ b/crates/python_bindings/Cargo.toml @@ -13,7 +13,7 @@ pyo3 = "0.24.2" numpy = "0.24.0" num-traits = "0.2" serde = { version = "1.0", features = ["derive"] } -bincode = {version = "2.0.1", features = ["serde"] } +bincode = { workspace = true } yosys-netlist-json = { workspace = true } tokio = { workspace = true } diff --git a/crates/python_bindings/src/design.rs b/crates/python_bindings/src/design.rs index d773cae..226cb3d 100644 --- a/crates/python_bindings/src/design.rs +++ b/crates/python_bindings/src/design.rs @@ -6,7 +6,7 @@ use crate::conversion::{ }; use arbol::hardware_module::HardwareModule; use arbol::port::PortDirection; -use arbol::yosys::{YosysClient, parse_torder}; +use arbol::yosys::Netlist; use bincode::{Decode, Encode}; use pyo3::exceptions::{PyAttributeError, PyException, PyValueError}; use pyo3::prelude::*; @@ -14,7 +14,6 @@ use pyo3::types::{PyBytes, PyDict}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::path::PathBuf; -use yosys_netlist_json::Netlist; #[pyclass(dict, module = "arbolta", name = "Design")] #[derive(Deserialize, Serialize, Decode, Encode)] @@ -28,48 +27,14 @@ pub struct PyDesign { #[pymethods] impl PyDesign { #[new] - #[pyo3(signature = (top_module, netlist_path, torder_path=None, yosys_path="yosys", yosys_server_path="yosys_server"))] - fn __new__( - top_module: &str, - netlist_path: &str, - torder_path: Option<&str>, - yosys_path: Option<&str>, - yosys_server_path: Option<&str>, - ) -> PyResult { - // Setup Yosys client - let mut client = YosysClient::default(); - - if let Some(yosys_path) = yosys_path { - client.yosys_path = PathBuf::from(yosys_path) - } - - if let Some(yosys_server_path) = yosys_server_path { - client.yosys_server_path = PathBuf::from(yosys_server_path) - } - + #[pyo3(signature = (top_module, netlist_path))] + fn __new__(top_module: &str, netlist_path: &str) -> PyResult { // Read raw JSON netlist let raw_netlist = std::fs::read(netlist_path)?; - let original_netlist = + let netlist = Netlist::from_slice(&raw_netlist).map_err(|e| PyValueError::new_err(format!("{e}")))?; - // Get topological cell order and final netlist - let netlist: Netlist; - let torder: HashMap>; - - if let Some(torder_path) = torder_path { - let raw_torder = - std::fs::read_to_string(torder_path).map_err(|e| PyValueError::new_err(format!("{e}")))?; - torder = parse_torder(&raw_torder); - netlist = original_netlist; - } else { - // TODO: Probably don't flatten here, just get topological ordering - let rt = tokio::runtime::Runtime::new()?; - (netlist, torder) = rt - .block_on(client.flatten_netlist(top_module, original_netlist)) - .map_err(|e| PyValueError::new_err(format!("{e}")))?; - } - - let design = HardwareModule::new(&netlist, &torder, top_module) + let design = HardwareModule::new(top_module, &netlist) .map_err(|e| PyValueError::new_err(format!("{e}")))?; Ok(Self { @@ -110,7 +75,7 @@ impl PyDesign { Err(err) => return Err(PyValueError::new_err(format!("{err}"))), }; - let top_module = design.name.clone(); + let top_module = design.top_module.clone(); Ok(Self { top_module, @@ -142,16 +107,11 @@ impl PyDesign { } fn get_module_names(&self) -> Vec { - self.design.submodules.keys().cloned().collect() + self.design.submodules.keys().map(|p| p.join(".")).collect() } fn get_signal_map(&self) -> HashMap> { - self - .design - .signal_map - .iter() - .map(|(name, nets)| (name.to_string(), nets.to_vec())) - .collect() + self.design.get_all_signal_nets() } fn get_cell_info(&self, py: Python<'_>) -> PyResult { @@ -182,7 +142,7 @@ impl PyDesign { } fn set_clock(&mut self, name: &str, polarity: bool) -> PyResult<()> { - let Some(nets) = self.design.signal_map.get(name) else { + let Some(nets) = self.design.get_signal_nets(name) else { return Err(PyException::new_err(format!("No signal `{name}`"))); }; @@ -196,7 +156,7 @@ impl PyDesign { } fn set_reset(&mut self, name: &str, polarity: bool) -> PyResult<()> { - let Some(nets) = self.design.signal_map.get(name) else { + let Some(nets) = self.design.get_signal_nets(name) else { return Err(PyException::new_err(format!("No signal `{name}`"))); }; From cd9c7246c205b5db3d283ae713f172ede8a81417 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Fri, 5 Dec 2025 22:22:29 +0000 Subject: [PATCH 31/64] Improving nested submodule and netlist graph --- .cargo/config.toml | 2 + Cargo.toml | 8 +- crates/arbolta/Cargo.toml | 24 +- crates/arbolta/src/bit.rs | 12 +- crates/arbolta/src/cell/mod.rs | 552 +----------------- crates/arbolta/src/cell/simcells.rs | 219 ++++++- crates/arbolta/src/cell/simlib/mod.rs | 361 +++++++++++- crates/arbolta/src/{temp2.rs => graph.rs} | 251 +++++--- crates/arbolta/src/hardware_module.rs | 233 +++++--- crates/arbolta/src/lib.rs | 2 +- crates/arbolta/src/port.rs | 4 +- crates/arbolta/src/signal.rs | 17 +- crates/arbolta/src/yosys.rs | 287 +-------- crates/arbolta/tests/test_module.rs | 70 +++ crates/arbolta/tests/test_signal.rs | 18 +- crates/arbolta/tests/test_simcell.rs | 42 +- crates/fault/Cargo.toml | 4 +- crates/python_bindings/Cargo.toml | 3 +- .../python_bindings/py_src/arbolta/design.py | 46 +- crates/python_bindings/src/design.rs | 186 ++++-- 20 files changed, 1254 insertions(+), 1087 deletions(-) create mode 100644 .cargo/config.toml rename crates/arbolta/src/{temp2.rs => graph.rs} (60%) diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..03f4879 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +rustflags = ["-Ctarget-cpu=native"] diff --git a/Cargo.toml b/Cargo.toml index 19eaa80..e4d2a73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,21 +8,21 @@ bincode = { version = "2", features = ["serde"] } clap = { version = "4.5", features = ["derive"] } derive_more = { version = "2", features = ["full"] } enum_dispatch = "0.3" -flexbuffers = "25.2" +flexbuffers = "25.9" futures = "0.3" -indexmap = "2.9" +indexmap = "2.12" ndarray = "0.16.1" num-traits = "0.2" +once_cell = "1.21" rstest = "0.26" serde = { version = "1", features = ["derive"] } serde-error = "0.1" smallvec = { version = "1", features = ["serde", "impl_bincode"] } tarpc = { version = "0.36", features = ["full"] } -tempfile = "3.19" +tempfile = "3.23" thiserror = "2.0" tokio = { version = "1", features = ["full"] } yosys-netlist-json = { git = "https://github.com/alexredd99/yosys-netlist-json.git" } -once_cell = "1.21" [profile.release] lto = true diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index deebd16..7b20d5b 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -7,25 +7,23 @@ edition = "2024" [dependencies] anyhow = { workspace = true } bincode = { workspace = true } +derive-new = "0.7" derive_more = { workspace = true } enum_dispatch = { workspace = true } -flexbuffers = { workspace = true } -futures = { workspace = true } +# flexbuffers = { workspace = true } +# futures = { workspace = true } indexmap = { workspace = true } +inventory = "0.3.21" ndarray = { workspace = true } num-traits = { workspace = true } +# criterion = "0.5" +# pprof = { version = "0.15", features = ["criterion", "flamegraph"] } +once_cell = { workspace = true } +petgraph = { version = "0.8.3", features = ["serde-1"] } rstest = { workspace = true } serde = { workspace = true } -serde-error = { workspace = true } -smallvec = { workspace = true } -tarpc = { workspace = true } -tempfile = { workspace = true } +# serde-error = { workspace = true } +# tarpc = { workspace = true } +# tempfile = { workspace = true }smal thiserror = { workspace = true } -tokio = { workspace = true } yosys-netlist-json = { workspace = true } -derive-new = "0.7" -yosys_server = { path = "../yosys_server" } -criterion = "0.5" -pprof = { version = "0.15", features = ["criterion", "flamegraph"] } -once_cell = { workspace = true } -petgraph = "0.8.3" diff --git a/crates/arbolta/src/bit.rs b/crates/arbolta/src/bit.rs index e618c95..e6443d5 100644 --- a/crates/arbolta/src/bit.rs +++ b/crates/arbolta/src/bit.rs @@ -7,8 +7,10 @@ use core::fmt; use derive_more::{BitAnd, BitOr, BitXor, Debug, IntoIterator, Not}; use num_traits::{PrimInt, WrappingAdd, WrappingShl, WrappingSub}; use serde::{Deserialize, Serialize}; -use std::convert::{From, Into}; -use std::str::FromStr; +use std::{ + convert::{From, Into}, + str::FromStr, +}; use thiserror::Error; /// Primitive signal value @@ -102,6 +104,12 @@ impl From> for BitVec { } } +impl From for Vec { + fn from(value: BitVec) -> Self { + value.bits.iter().map(|&b| b.into()).collect() + } +} + impl fmt::Display for BitVec { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let bit_string: String = self diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index ec6363c..a06e873 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -3,15 +3,15 @@ mod simlib; mod test_macros; // Re-export -pub use simcells::*; -pub use simlib::*; - -use crate::{bit::Bit, signal::Signals, temp2::TopoCell}; +use crate::{graph::TopoCell, signal::Signals}; use bincode::{Decode, Encode}; use enum_dispatch::enum_dispatch; +use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; +pub use simcells::*; +pub use simlib::*; +use std::collections::{BTreeMap, HashMap}; use thiserror::Error; -use yosys_netlist_json as yosys_json; #[enum_dispatch] pub trait CellFn { @@ -19,6 +19,27 @@ pub trait CellFn { fn reset(&mut self); } +pub type CellCtor = fn(&BTreeMap>, &BTreeMap) -> Cell; +pub type CellDispatchMap = HashMap<&'static str, CellCtor>; + +#[derive(derive_more::Constructor)] +pub struct CellRegistration { + pub aliases: &'static [&'static str], + pub ctor: CellCtor, +} + +inventory::collect!(CellRegistration); + +pub static CELL_DISPATCH: Lazy> = Lazy::new(|| { + let mut cell_map = HashMap::new(); + for registration in inventory::iter:: { + for name in registration.aliases { + cell_map.insert(*name, registration.ctor); + } + } + cell_map +}); + #[enum_dispatch(CellFn)] #[derive(Debug, Serialize, Deserialize, Clone, Decode, Encode)] /// Proxy for a standard-cell and basic unit of 'compute'. @@ -78,10 +99,6 @@ pub enum CellError { NotFound(String), #[error("Direction `{0}` not supported")] Direction(String), - #[error("Bad parameter `{0}` = `{1:?}`")] - Parameter(String, yosys_json::AttributeVal), - #[error("Bad attribute `{0}` = `{1:?}`")] - Attribute(String, yosys_json::AttributeVal), } // TODO: Make this take topo cell AND move into try_from @@ -91,7 +108,11 @@ pub enum CellError { /// # Arguments /// * `cell` - Yosys cell pub fn create_cell(cell: &TopoCell) -> Result { - // TODO: Error handling.. + let cell_type = cell.cell_type.as_str(); + let ctor = CELL_DISPATCH + .get(cell_type) + .ok_or_else(|| CellError::Unsupported(cell_type.to_string()))?; + let Some(connections) = &cell.connections else { todo!() }; @@ -99,514 +120,5 @@ pub fn create_cell(cell: &TopoCell) -> Result { todo!() }; - let new_cell: Cell = match cell.cell_type.as_str() { - // Sim cells - "BUF" | "$_BUF_" => Cell::Buffer(Buffer::new(connections["A"][0], connections["Y"][0])), - "NOT" | "$_NOT_" => Cell::Inverter(Inverter::new(connections["A"][0], connections["Y"][0])), - "AND" | "$_AND_" => Cell::And(And::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "NAND" | "$_NAND_" => Cell::Nand(Nand::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "OR" | "$_OR_" => Cell::Or(Or::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "NOR" | "$_NOR_" => Cell::Nor(Nor::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "XOR" | "$_XOR_" => Cell::Xor(Xor::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "XNOR" | "$_XNOR_" => Cell::Xnor(Xnor::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "ANDNOT" | "$_ANDNOT_" => Cell::AndNot(AndNot::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "ORNOT" | "$_ORNOT_" => Cell::OrNot(OrNot::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "$_MUX_" => Cell::Mux2(Mux2::new( - connections["A"][0], - connections["B"][0], - connections["S"][0], - connections["Y"][0], - )), - "$_NMUX_" => Cell::NMux2(NMux2::new( - connections["A"][0], - connections["B"][0], - connections["S"][0], - connections["Y"][0], - )), - "$_AOI3_" => Cell::AndOrInvert(AndOrInvert::new( - connections["A"][0], - connections["B"][0], - connections["C"][0], - connections["Y"][0], - )), - "$_OAI3_ " => Cell::OrAndInvert(OrAndInvert::new( - connections["A"][0], - connections["B"][0], - connections["C"][0], - connections["Y"][0], - )), - "DFF" | "$_DFF_P_" => Cell::Dff(Dff::new( - Bit::ONE, - connections["C"][0], - connections["D"][0], - connections["Q"][0], - )), - "$_SDFF_PP0_ " => Cell::DffReset(DffReset::new( - Bit::ONE, - Bit::ONE, - Bit::ZERO, - connections["C"][0], - connections["R"][0], - connections["D"][0], - connections["Q"][0], - )), - // Sim lib - "$not" => Cell::Not(Not::new( - parameters["A_SIGNED"] != 0, - connections["A"].clone(), - connections["Y"].clone(), - )), - "$pos" => Cell::Pos(Pos::new( - parameters["A_SIGNED"] != 0, - connections["A"].clone(), - connections["Y"].clone(), - )), - "$neg" => Cell::Neg(Neg::new( - parameters["A_SIGNED"] != 0, - connections["A"].clone(), - connections["Y"].clone(), - )), - "$add" => Cell::Add(Add::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$sub" => Cell::Sub(Sub::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$mul" => Cell::Mul(Mul::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$div" => Cell::Div(Div::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$mod" => Cell::Modulus(Modulus::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$le" => Cell::Le(Le::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$ge" => Cell::Ge(Ge::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$gt" => Cell::Gt(Gt::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$shl" => Cell::Shl(Shl::new( - parameters["A_SIGNED"] != 0, - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$shr" => Cell::Shr(Shr::new( - parameters["A_SIGNED"] != 0, - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$dff" => Cell::Reg(Reg::new( - (parameters["CLK_POLARITY"] != 0).into(), - connections["CLK"][0], - connections["D"].clone(), - connections["Q"].clone(), - )), - "$aldff" => Cell::ALDff(ALDff::new( - (parameters["CLK_POLARITY"] != 0).into(), - (parameters["ALOAD_POLARITY"] != 0).into(), - connections["CLK"][0], - connections["ALOAD"][0], - connections["AD"].clone(), - connections["D"].clone(), - connections["Q"].clone(), - )), - "$mux" => Cell::Mux(Mux::new( - connections["S"][0], - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$bmux" => Cell::BMux(BMux::new( - connections["S"].clone(), - connections["A"].clone(), - connections["Y"].clone(), - )), - "$pmux" => Cell::PMux(PMux::new( - connections["S"].clone(), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$logic_and" => Cell::LogicAnd(LogicAnd::new( - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$logic_not" => Cell::LogicNot(LogicNot::new( - connections["A"].clone(), - connections["Y"].clone(), - )), - "$reduce_or" | "$reduce_bool" => Cell::ReduceOr(ReduceOr::new( - connections["A"].clone(), - connections["Y"].clone(), - )), - "$reduce_and" => Cell::ReduceAnd(ReduceAnd::new( - connections["A"].clone(), - connections["Y"].clone(), - )), - "$and" => Cell::ProcAnd(ProcAnd::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$or" => Cell::ProcOr(ProcOr::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$xor" => Cell::ProcXor(ProcXor::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$eq" => Cell::Eq(Eq::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$ne" => Cell::Ne(Ne::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - _ => return Err(CellError::Unsupported(cell.cell_type.to_string())), - }; - - Ok(new_cell) -} - -/* -pub fn create_cell(cell: &yosys_json::Cell) -> Result { - // Port name -> net - let mut connections: HashMap> = HashMap::new(); - for (port_name, port_bits) in cell.connections.iter() { - for bit in port_bits { - let net = parse_bit(bit)?; - connections - .entry(port_name.to_string()) - .or_default() - .push(net); - } - } - - let mut parameters: HashMap = HashMap::new(); - for (param_name, param) in cell.parameters.iter() { - let Some(param) = param.to_number() else { - return Err(CellError::Parameter(param_name.to_string(), param.clone())); - }; - parameters.insert(param_name.to_string(), param); - } - - let new_cell: Cell = match cell.cell_type.as_str() { - // Sim cells - "BUF" | "$_BUF_" => Cell::Buffer(Buffer::new(connections["A"][0], connections["Y"][0])), - "NOT" | "$_NOT_" => Cell::Inverter(Inverter::new(connections["A"][0], connections["Y"][0])), - "AND" | "$_AND_" => Cell::And(And::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "NAND" | "$_NAND_" => Cell::Nand(Nand::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "OR" | "$_OR_" => Cell::Or(Or::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "NOR" | "$_NOR_" => Cell::Nor(Nor::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "XOR" | "$_XOR_" => Cell::Xor(Xor::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "XNOR" | "$_XNOR_" => Cell::Xnor(Xnor::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "ANDNOT" | "$_ANDNOT_" => Cell::AndNot(AndNot::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "ORNOT" | "$_ORNOT_" => Cell::OrNot(OrNot::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - )), - "$_MUX_" => Cell::Mux2(Mux2::new( - connections["A"][0], - connections["B"][0], - connections["S"][0], - connections["Y"][0], - )), - "$_NMUX_" => Cell::NMux2(NMux2::new( - connections["A"][0], - connections["B"][0], - connections["S"][0], - connections["Y"][0], - )), - "$_AOI3_" => Cell::AndOrInvert(AndOrInvert::new( - connections["A"][0], - connections["B"][0], - connections["C"][0], - connections["Y"][0], - )), - "$_OAI3_ " => Cell::OrAndInvert(OrAndInvert::new( - connections["A"][0], - connections["B"][0], - connections["C"][0], - connections["Y"][0], - )), - "DFF" | "$_DFF_P_" => Cell::Dff(Dff::new( - Bit::ONE, - connections["C"][0], - connections["D"][0], - connections["Q"][0], - )), - "$_SDFF_PP0_ " => Cell::DffReset(DffReset::new( - Bit::ONE, - Bit::ONE, - Bit::ZERO, - connections["C"][0], - connections["R"][0], - connections["D"][0], - connections["Q"][0], - )), - // Sim lib - "$not" => Cell::Not(Not::new( - parameters["A_SIGNED"] != 0, - connections["A"].clone(), - connections["Y"].clone(), - )), - "$pos" => Cell::Pos(Pos::new( - parameters["A_SIGNED"] != 0, - connections["A"].clone(), - connections["Y"].clone(), - )), - "$neg" => Cell::Neg(Neg::new( - parameters["A_SIGNED"] != 0, - connections["A"].clone(), - connections["Y"].clone(), - )), - "$add" => Cell::Add(Add::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$sub" => Cell::Sub(Sub::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$mul" => Cell::Mul(Mul::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$div" => Cell::Div(Div::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$mod" => Cell::Modulus(Modulus::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$le" => Cell::Le(Le::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$ge" => Cell::Ge(Ge::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$gt" => Cell::Gt(Gt::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$shl" => Cell::Shl(Shl::new( - parameters["A_SIGNED"] != 0, - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$shr" => Cell::Shr(Shr::new( - parameters["A_SIGNED"] != 0, - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$dff" => Cell::Reg(Reg::new( - (parameters["CLK_POLARITY"] != 0), - connections["CLK"][0], - connections["D"].clone(), - connections["Q"].clone(), - )), - "$aldff" => Cell::ALDff(ALDff::new( - (parameters["CLK_POLARITY"] != 0), - (parameters["ALOAD_POLARITY"] != 0), - connections["CLK"][0], - connections["ALOAD"][0], - connections["AD"].clone(), - connections["D"].clone(), - connections["Q"].clone(), - )), - "$mux" => Cell::Mux(Mux::new( - connections["S"][0], - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$bmux" => Cell::BMux(BMux::new( - connections["S"].clone(), - connections["A"].clone(), - connections["Y"].clone(), - )), - "$pmux" => Cell::PMux(PMux::new( - connections["S"].clone(), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$logic_and" => Cell::LogicAnd(LogicAnd::new( - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$logic_not" => Cell::LogicNot(LogicNot::new( - connections["A"].clone(), - connections["Y"].clone(), - )), - "$reduce_or" | "$reduce_bool" => Cell::ReduceOr(ReduceOr::new( - connections["A"].clone(), - connections["Y"].clone(), - )), - "$reduce_and" => Cell::ReduceAnd(ReduceAnd::new( - connections["A"].clone(), - connections["Y"].clone(), - )), - "$and" => Cell::ProcAnd(ProcAnd::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$or" => Cell::ProcOr(ProcOr::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$xor" => Cell::ProcXor(ProcXor::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$eq" => Cell::Eq(Eq::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - "$ne" => Cell::Ne(Ne::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - )), - _ => return Err(CellError::Unsupported(cell.cell_type.to_string())), - }; - - Ok(new_cell) + Ok(ctor(connections, parameters)) } - -*/ diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index 014ab15..e547451 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -1,8 +1,9 @@ -use super::CellFn; -use crate::{bit::Bit, signal::Signals}; +use super::{Cell, CellFn}; +use crate::{bit::Bit, cell::CellRegistration, signal::Signals}; use bincode::{Decode, Encode}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; macro_rules! define_unary_cell { ($name:ident, $body:expr) => { @@ -157,9 +158,7 @@ impl CellFn for DffReset { // Rising edge if clock == Bit::ONE && self.last_clock == Bit::ZERO { // Check if reset active for any polarity - let reset = !(signals.get_net(self.reset_net) & self.reset_polarity); - - if reset == Bit::ONE { + if signals.get_net(self.reset_net) == self.reset_polarity { signals.set_net(self.data_out_net, self.reset_val); } else { let data_in = signals.get_net(self.data_in_net); @@ -174,3 +173,213 @@ impl CellFn for DffReset { self.last_clock = Bit::ZERO; } } + +// TODO: Create with macro... +// Cell Constructor functions +fn make_buf( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + Buffer::new(connections["A"][0], connections["Y"][0]).into() +} + +fn make_not( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + Inverter::new(connections["A"][0], connections["Y"][0]).into() +} + +fn make_and( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + And::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + ) + .into() +} + +fn make_nand( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + Nand::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + ) + .into() +} + +fn make_or( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + Or::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + ) + .into() +} + +fn make_nor( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + Nor::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + ) + .into() +} + +fn make_xor( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + Xor::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + ) + .into() +} + +fn make_xnor( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + Xnor::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + ) + .into() +} + +fn make_andnot( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + AndNot::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + ) + .into() +} + +fn make_ornot( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + OrNot::new( + connections["A"][0], + connections["B"][0], + connections["Y"][0], + ) + .into() +} + +fn make_mux( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + Mux2::new( + connections["A"][0], + connections["B"][0], + connections["S"][0], + connections["Y"][0], + ) + .into() +} + +fn make_nmux( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + NMux2::new( + connections["A"][0], + connections["B"][0], + connections["S"][0], + connections["Y"][0], + ) + .into() +} + +fn make_andorinvert( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + AndOrInvert::new( + connections["A"][0], + connections["B"][0], + connections["C"][0], + connections["Y"][0], + ) + .into() +} + +fn make_orandinvert( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + OrAndInvert::new( + connections["A"][0], + connections["B"][0], + connections["C"][0], + connections["Y"][0], + ) + .into() +} + +fn make_dff( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + Dff::new( + Bit::ONE, + connections["C"][0], + connections["D"][0], + connections["Q"][0], + ) + .into() +} + +fn make_dffreset( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + DffReset::new( + Bit::ONE, + Bit::ONE, + Bit::ZERO, + connections["C"][0], + connections["R"][0], + connections["D"][0], + connections["Q"][0], + ) + .into() +} + +inventory::submit! {CellRegistration::new(&["BUF", "$_BUF_"], make_buf)} +inventory::submit! {CellRegistration::new(&["NOT", "$_NOT_"], make_not)} +inventory::submit! {CellRegistration::new(&["AND", "$_AND_"], make_and)} +inventory::submit! {CellRegistration::new(&["NAND", "$_NAND_"], make_nand)} +inventory::submit! {CellRegistration::new(&["OR", "$_OR_"], make_or)} +inventory::submit! {CellRegistration::new(&["NOR", "$_NOR_"], make_nor)} +inventory::submit! {CellRegistration::new(&["XOR", "$_XOR_"], make_xor)} +inventory::submit! {CellRegistration::new(&["XNOR", "$_XNOR_"], make_xnor)} +inventory::submit! {CellRegistration::new(&["ANDNOT", "$_ANDNOT_"], make_andnot)} +inventory::submit! {CellRegistration::new(&["ORNOT", "$_ORNOT_"], make_ornot)} +inventory::submit! {CellRegistration::new(&["$_MUX_"], make_mux)} +inventory::submit! {CellRegistration::new(&["$_NMUX_"], make_nmux)} +inventory::submit! {CellRegistration::new(&["$_AOI3_"], make_andorinvert)} +inventory::submit! {CellRegistration::new(&["$_OAI3_"], make_orandinvert)} +inventory::submit! {CellRegistration::new(&["DFF", "$_DFF_P_"], make_dff)} +inventory::submit! {CellRegistration::new(&["$_SDFF_PP0_"], make_dffreset)} diff --git a/crates/arbolta/src/cell/simlib/mod.rs b/crates/arbolta/src/cell/simlib/mod.rs index c91b47a..6173328 100644 --- a/crates/arbolta/src/cell/simlib/mod.rs +++ b/crates/arbolta/src/cell/simlib/mod.rs @@ -1,5 +1,6 @@ -use super::CellFn; +use super::{Cell, CellFn, CellRegistration}; use crate::{bit::Bit, signal::Signals}; +use std::collections::BTreeMap; mod arithmetic; mod bool_ops; @@ -147,3 +148,361 @@ pub use logic_reduce_ops::*; pub use registers::*; pub use shift_ops::*; pub use various::*; + +// TODO: clean up temp functions +fn make_not( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Not::new( + parameters["A_SIGNED"] != 0, + connections["A"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_pos( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Pos::new( + parameters["A_SIGNED"] != 0, + connections["A"].clone(), + connections["Y"].clone(), + ) + .into() +} +fn make_neg( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Neg::new( + parameters["A_SIGNED"] != 0, + connections["A"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_add( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Add::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_sub( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Sub::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_mul( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Mul::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_div( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Div::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_mod( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Modulus::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_le( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Le::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_ge( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Ge::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_gt( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Gt::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_shl( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Shl::new( + parameters["A_SIGNED"] != 0, + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_shr( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Shr::new( + parameters["A_SIGNED"] != 0, + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_dff( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Reg::new( + (parameters["CLK_POLARITY"] != 0).into(), + connections["CLK"][0], + connections["D"].clone(), + connections["Q"].clone(), + ) + .into() +} + +fn make_aldff( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + ALDff::new( + (parameters["CLK_POLARITY"] != 0).into(), + (parameters["ALOAD_POLARITY"] != 0).into(), + connections["CLK"][0], + connections["ALOAD"][0], + connections["AD"].clone(), + connections["D"].clone(), + connections["Q"].clone(), + ) + .into() +} + +fn make_mux( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + Mux::new( + connections["S"][0], + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_bmux( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + BMux::new( + connections["S"].clone(), + connections["A"].clone(), + connections["Y"].clone(), + ) + .into() +} +fn make_pmux( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + PMux::new( + connections["S"].clone(), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_logic_and( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + LogicAnd::new( + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_logic_not( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + LogicNot::new(connections["A"].clone(), connections["Y"].clone()).into() +} + +fn make_reduce_or( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + ReduceOr::new(connections["A"].clone(), connections["Y"].clone()).into() +} + +fn make_reduce_and( + connections: &BTreeMap>, + _parameters: &BTreeMap, +) -> Cell { + ReduceAnd::new(connections["A"].clone(), connections["Y"].clone()).into() +} + +fn make_and( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + ProcAnd::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_or( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + ProcOr::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_xor( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + ProcXor::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_eq( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Eq::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +fn make_ne( + connections: &BTreeMap>, + parameters: &BTreeMap, +) -> Cell { + Ne::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + +inventory::submit! {CellRegistration::new(&["$not"], make_not)} +inventory::submit! {CellRegistration::new(&["$pos"], make_pos)} +inventory::submit! {CellRegistration::new(&["$neg"], make_neg)} +inventory::submit! {CellRegistration::new(&["$add"], make_add)} +inventory::submit! {CellRegistration::new(&["$sub"], make_sub)} +inventory::submit! {CellRegistration::new(&["$mul"], make_mul)} +inventory::submit! {CellRegistration::new(&["$div"], make_div)} +inventory::submit! {CellRegistration::new(&["$mod"], make_mod)} +inventory::submit! {CellRegistration::new(&["$le"], make_le)} +inventory::submit! {CellRegistration::new(&["$ge"], make_ge)} +inventory::submit! {CellRegistration::new(&["$gt"], make_gt)} +inventory::submit! {CellRegistration::new(&["$shl"], make_shl)} +inventory::submit! {CellRegistration::new(&["$shr"], make_shr)} +inventory::submit! {CellRegistration::new(&["$dff"], make_dff)} +inventory::submit! {CellRegistration::new(&["$aldff"], make_aldff)} +inventory::submit! {CellRegistration::new(&["$mux"], make_mux)} +inventory::submit! {CellRegistration::new(&["$bmux"], make_bmux)} +inventory::submit! {CellRegistration::new(&["$pmux"], make_pmux)} +inventory::submit! {CellRegistration::new(&["$logic_and"], make_logic_and)} +inventory::submit! {CellRegistration::new(&["$logic_not"], make_logic_not)} +inventory::submit! {CellRegistration::new(&["$reduce_or", "$reduce_bool"], make_reduce_or)} +inventory::submit! {CellRegistration::new(&["$reduce_and"], make_reduce_and)} +inventory::submit! {CellRegistration::new(&["$and"], make_and)} +inventory::submit! {CellRegistration::new(&["$or"], make_or)} +inventory::submit! {CellRegistration::new(&["$xor"], make_xor)} +inventory::submit! {CellRegistration::new(&["$eq"], make_eq)} +inventory::submit! {CellRegistration::new(&["$ne"], make_ne)} diff --git a/crates/arbolta/src/temp2.rs b/crates/arbolta/src/graph.rs similarity index 60% rename from crates/arbolta/src/temp2.rs rename to crates/arbolta/src/graph.rs index a22aa6e..737478a 100644 --- a/crates/arbolta/src/temp2.rs +++ b/crates/arbolta/src/graph.rs @@ -1,21 +1,23 @@ use crate::{ + cell::CELL_DISPATCH, hardware_module::ModuleError, port::{PortDirection, parse_bit}, }; -use petgraph::visit::IntoNodeReferences; -use petgraph::{prelude::*, visit::DfsPostOrder}; // dot::Dot -use std::collections::{BTreeMap, HashMap, HashSet}; -// use std::io::Write; +use derive_more::{Constructor, Display}; +use petgraph::{dot::Dot, prelude::*, visit::Topo}; +use std::{ + collections::{BTreeMap, HashMap, HashSet}, + fmt, +}; use yosys_netlist_json as yosys_json; -#[derive(Debug, Clone, Eq, PartialEq, Hash, Default, derive_more::Constructor)] +#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Default, Constructor)] pub struct TopoCellParent { pub name: String, pub cell_type: String, } -#[derive(Debug, Clone, Eq, PartialEq, Hash, Default, derive_more::Display)] -#[display("name: {name}")] +#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Default)] pub struct TopoCell { pub parents: Vec, pub name: String, @@ -27,6 +29,21 @@ pub struct TopoCell { pub parameters: Option>, } +impl fmt::Display for TopoCell { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let parent_names = self + .parents + .iter() + .map(|p| p.name.as_str()) + .collect::>() + .join("."); + + let name = format!("{parent_names}.{}", self.name); + + write!(f, "{name}, {}", self.cell_type) + } +} + pub fn collect_cells( top_module: &str, parents: &[TopoCellParent], @@ -41,8 +58,8 @@ pub fn collect_cells( for (cell_name, cell_info) in &synth_module.cells { let cell_type = &cell_info.cell_type; - // Submodule - if netlist.modules.contains_key(cell_type) { + // Submodule (and NOT a primitive cell) + if netlist.modules.contains_key(cell_type) && !CELL_DISPATCH.contains_key(cell_type.as_str()) { let mut parents = parents.to_vec(); parents.push(TopoCellParent { name: cell_name.to_owned(), @@ -111,16 +128,6 @@ pub fn collect_nets( }); } - // println!("GLOBAL NETS"); - // for (key, val) in global_nets.iter() { - // println!("{key:?}"); - // let mut vals: Vec<(&usize, &usize)> = val.iter().collect(); - // vals.sort(); - // for v in vals { - // println!("{v:?}"); - // } - // } - // Add rest of nets for n in &nets { if !global_nets[parent].contains_key(n) { @@ -186,7 +193,8 @@ pub fn update_cells( if let Some(param) = param.to_number() { parameters.insert(param_name.to_string(), param); } else { - println!("Couldn't convert parameter `{param_name}={param:?}`"); + // TODO: Handle later + println!("Ignoring parameter `{param_name}={param:?}`"); } } @@ -198,17 +206,22 @@ pub fn update_cells( Ok(()) } -#[derive(Debug, Clone, Eq, PartialEq, Hash, derive_more::Display)] -pub enum TopoNode { - Cell(TopoCell), - Net(usize), -} +// #[derive(Debug, Clone, Eq, PartialEq, Hash, Display)] +// pub enum TopoNode { +// Cell(TopoCell), +// #[display("{name}[{offset}]")] +// Port { +// name: String, +// offset: usize, +// }, // Net(usize), +// } // TODO: Change net node or graph edge to some enum/struct that has netname... // enum for multi bit port // enum::single net(name, b) // enum::multi net(name, index, b) merge later... -pub type NetlistGraph = DiGraph; +// pub type NetlistGraph = DiGraph; +/* pub fn build_graph( top_module: &TopoCellParent, cells: &[TopoCell], @@ -292,86 +305,148 @@ pub fn build_graph( Ok(graph) } +*/ + +// TODO: rename +fn build_port_net_map(cell: &TopoCell) -> HashMap { + let mut nets = HashMap::new(); + for (port_name, conns) in cell.connections.as_ref().unwrap() { + for (i, &n) in conns.iter().enumerate() { + nets.insert(n, TopoPort::new(port_name.clone(), i)); + } + } + + nets +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash, Display, Constructor)] +#[display("{port}[{offset}]")] +pub struct TopoPort { + port: String, + offset: usize, +} -pub fn get_topo_cell_order(graph: &NetlistGraph) -> Vec<&TopoCell> { - let mut cell_graph: DiGraph<&TopoCell, usize> = DiGraph::new(); - let mut orig_cell_nodes = HashMap::<&TopoCell, NodeIndex>::new(); +#[derive(Debug, Clone, Eq, PartialEq, Hash, Display, Constructor)] +#[display("({net}, {src}, {dst})")] +pub struct TopoEdge { + net: usize, + src: TopoPort, + dst: TopoPort, +} + +pub type NetlistGraph<'a> = DiGraph<&'a TopoCell, TopoEdge>; + +// TODO: Merge with get_topo_cell_order... +// pub fn build_graph(cells: &[TopoCell]) -> Result { +pub fn build_graph(cells: &[TopoCell]) -> Result { + let mut graph = NetlistGraph::new(); let mut cell_nodes = HashMap::<&TopoCell, NodeIndex>::new(); - // Create new graph with only cell nodes - // Get node indices for cells in original graph - for (node_index, node) in graph.node_references() { - if let TopoNode::Cell(cell) = node { - orig_cell_nodes.insert(cell, node_index); - cell_nodes.insert(cell, cell_graph.add_node(cell)); + let mut bit_drivers = HashMap::>::new(); + let mut bit_users = HashMap::>::new(); + + for cell in cells { + // Check if primitive cell + if !CELL_DISPATCH.contains_key(cell.cell_type.as_str()) { + continue; } - } - // Create connections - for (cell, orig_node_index) in orig_cell_nodes { - // All neighbors should be net nodes - for net_index in graph.neighbors(orig_node_index) { - let TopoNode::Net(n) = &graph[net_index] else { - todo!() - }; - - // All neighbors should be cell nodes - for driven_node in graph.neighbors(net_index) { - let driver_node = &cell_nodes[cell]; - let TopoNode::Cell(driven_cell) = graph.node_weight(driven_node).unwrap() else { - todo!() - }; - let driven_node = &cell_nodes[driven_cell]; - - cell_graph.add_edge(*driver_node, *driven_node, *n); + for (port_name, nets) in cell.connections.as_ref().unwrap() { + // TODO: Potentially filter here + if let Some(direction) = cell.port_directions.as_ref().unwrap().get(port_name) { + for &n in nets { + match direction { + // TODO: Do we need to clone? + PortDirection::Input => { + bit_users.entry(n).or_default().insert(cell); + } + PortDirection::Output => { + bit_drivers.entry(n).or_default().insert(cell); + } + } + } } } + cell_nodes.insert(cell, graph.add_node(cell)); } - // TODO: check weight that node is == input - let mut topo_search = DfsPostOrder::new(&cell_graph, NodeIndex::from(0)); - let mut found = vec![]; + for (net, user_cells) in &bit_users { + if let Some(drivers) = bit_drivers.get(net) { + for driver_cell in drivers { + let driver_nets = build_port_net_map(driver_cell); + + for user_cell in user_cells { + let user_nets = build_port_net_map(user_cell); + + let driver_node = &cell_nodes[driver_cell]; + let user_node = &cell_nodes[user_cell]; + let edge = TopoEdge::new(*net, driver_nets[net].to_owned(), user_nets[net].to_owned()); - while let Some(visited) = topo_search.next(&cell_graph) { - let cell = cell_graph.node_weight(visited).unwrap(); - if cell.cell_type != "input" { - // Don't include input cell/node - found.push(*cell); + graph.add_edge(*driver_node, *user_node, edge); + } + } } } - found.reverse(); - found + Ok(Dot::new(&graph).to_string()) + // Ok(graph) } -pub fn parse_module( - top_module: &str, - netlist: &yosys_json::Netlist, -) -> Result<(NetlistGraph, TopoNetMap), ModuleError> { - let top_cell_parent = TopoCellParent { - name: top_module.to_string(), - cell_type: top_module.to_string(), - }; +// Based on https://github.com/YosysHQ/yosys/blob/main/passes/cmds/torder.cc +pub fn get_topo_cell_order(cells: &[TopoCell]) -> Vec<&TopoCell> { + let mut graph: DiGraph<&TopoCell, usize> = DiGraph::new(); + let mut cell_nodes = HashMap::<&TopoCell, NodeIndex>::new(); + + let mut bit_drivers = HashMap::>::new(); + let mut bit_users = HashMap::>::new(); + + for cell in cells { + // Check if primitive cell + if !CELL_DISPATCH.contains_key(cell.cell_type.as_str()) { + continue; + } + + for (port_name, nets) in cell.connections.as_ref().unwrap() { + if ["Q", "CTRL_OUT", "RD_DATA"].contains(&port_name.as_str()) { + // TODO: Add case for memrd + continue; + } - let flattened_cells = collect_cells(top_module, std::slice::from_ref(&top_cell_parent), netlist)?; - let hierarchy = get_cell_hierarchy(&flattened_cells); + if let Some(direction) = cell.port_directions.as_ref().unwrap().get(port_name) { + for &n in nets { + match direction { + PortDirection::Input => { + bit_users.entry(n).or_default().insert(cell); + } + PortDirection::Output => { + bit_drivers.entry(n).or_default().insert(cell); + } + } + } + } + } + cell_nodes.insert(cell, graph.add_node(cell)); + } - let mut global_nets = TopoNetMap::new(); - collect_nets( - &top_cell_parent, - &hierarchy, - netlist, - &mut global_nets, - &mut 0, - )?; + for (net, user_cells) in &bit_users { + if let Some(drivers) = bit_drivers.get(net) { + for driver_cell in drivers { + for user_cell in user_cells { + let driver_node = &cell_nodes[driver_cell]; + let user_node = &cell_nodes[user_cell]; - let graph = build_graph(&top_cell_parent, &flattened_cells, &global_nets, netlist)?; + graph.add_edge(*driver_node, *user_node, *net); + } + } + } + } - // let dot_output = format!("{}", Dot::new(&graph)); - // let mut file = std::fs::File::create("graph.dot").expect("Could not create file"); - // file - // .write_all(dot_output.as_bytes()) - // .expect("Could not write to file"); + let mut topo_search = Topo::new(&graph); + let mut found = vec![]; + while let Some(visited) = topo_search.next(&graph) { + let cell = graph.node_weight(visited).unwrap(); + found.push(*cell); + } - Ok((graph, global_nets)) + found } diff --git a/crates/arbolta/src/hardware_module.rs b/crates/arbolta/src/hardware_module.rs index ca18bb6..471d722 100644 --- a/crates/arbolta/src/hardware_module.rs +++ b/crates/arbolta/src/hardware_module.rs @@ -3,31 +3,33 @@ use crate::{ bit::{Bit, BitVec}, - cell::{Cell, CellError, CellFn, create_cell}, + cell::{CELL_DISPATCH, Cell, CellError, CellFn, create_cell}, + graph::*, port::PortError, port::{Port, PortDirection, parse_bit}, signal::Signals, - temp2::*, yosys::Netlist, }; use bincode::{Decode, Encode}; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fmt::Debug}; +use std::{ + collections::{HashMap, HashSet}, + fmt::Debug, +}; use thiserror::Error; #[derive(Clone, Debug, Deserialize, Serialize, Decode, Encode)] pub enum CellType { Submodule(String), + Scopeinfo(String), // TODO: Convert this to submodule Primitive(String), } #[derive(Default, Clone, Debug, Deserialize, Serialize, Decode, Encode)] pub struct SubModule { - ports: HashMap, - nets: HashMap>, - // TODO: make pointer to cells in hardware module? - // TODO: make pointer to cells in hardware module? - cells: HashMap, // cell, cell type? + pub ports: HashMap, + pub nets: HashMap>, + pub cells: HashMap, } // Names @@ -40,6 +42,8 @@ pub struct HardwareModule { pub cell_info: HashMap, pub cells: Box<[Cell]>, pub submodules: SubmoduleMap, + pub graph: String, // TODO: Don't store this as a dot string... + // pub topo_cells: Vec, pub clock_net: Option<(usize, Bit)>, // (net, polarity) pub reset_net: Option<(usize, Bit)>, // (net, polarity) } @@ -70,12 +74,13 @@ pub enum ModuleError { MissingSubmodule(String), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Copy)] pub enum ToggleCount { Rising, Falling, Total, } + fn get_submodules( parents: &[TopoCellParent], hierarchy: &TopoHierarchy, @@ -93,15 +98,17 @@ fn get_submodules( // Create new submodule let mut submodule = SubModule::default(); if let Some(synth_module) = netlist.modules.get(&top_parent.cell_type.to_string()) { - // TODO: Error checking? for (cell_name, cell_info) in &synth_module.cells { - // TODO: Make pointer to cell in HardwareModule cells - // TODO: Use hierarchy to tell if cell is submodule or not - let cell_type = if netlist.modules.contains_key(&cell_info.cell_type) { - CellType::Submodule(cell_info.cell_type.to_owned()) + let cell_type = cell_info.cell_type.to_owned(); + + let cell_type = if CELL_DISPATCH.contains_key(&cell_type.as_str()) { + CellType::Primitive(cell_type) + } else if cell_type == "$scopeinfo" { + CellType::Scopeinfo(cell_type) } else { - CellType::Primitive(cell_info.cell_type.to_owned()) + CellType::Submodule(cell_type) }; + submodule.cells.insert(cell_name.to_owned(), cell_type); } @@ -167,11 +174,11 @@ impl HardwareModule { update_cells(topo_cells.as_mut_slice(), &global_nets, netlist)?; - let graph = build_graph(&top_module_parent, &topo_cells, &global_nets, netlist)?; - let topo_order = get_topo_cell_order(&graph); + // let graph = build_graph(&top_module_parent, &topo_cells, &global_nets, netlist)?; + let graph = build_graph(&topo_cells)?; + let topo_order = get_topo_cell_order(&topo_cells); let submodules = get_submodules(&[top_module_parent], &hierarchy, &global_nets, netlist)?; - let cells: Vec = topo_order .into_iter() .map(create_cell) @@ -187,14 +194,16 @@ impl HardwareModule { cell_info: HashMap::new(), cells: cells.into_boxed_slice(), submodules, + graph, clock_net: None, reset_net: None, }) } - pub fn get_signal_nets(&self, name: &str) -> Option> { - let submodule = &self.submodules[std::slice::from_ref(&self.top_module)]; - submodule.nets.get(name).cloned() + pub fn get_nets(&self, net_name: &str, parents: Option<&[String]>) -> Option> { + let parents = parents.unwrap_or(std::slice::from_ref(&self.top_module)); + let submodule = &self.submodules[parents]; + submodule.nets.get(net_name).cloned() } pub fn stick_signal(&mut self, net: usize, value: Bit) -> Result<(), ModuleError> { @@ -234,17 +243,15 @@ impl HardwareModule { } // Eval until all signals have settled - // TODO: Make this more efficient pub fn eval(&mut self) { loop { - let before = self.signals.nets.clone(); - + self.signals.clear_dirty(); self .cells .iter_mut() .for_each(|cell| cell.eval(&mut self.signals)); - if before == self.signals.nets { + if !self.signals.is_dirty() { break; } } @@ -402,59 +409,155 @@ impl HardwareModule { all_nets } - // pub fn get_cell_breakdown(&self) -> HashMap { - // todo!() - // } + // TODO: Add tests for these + pub fn get_toggles(&self, category: ToggleCount) -> u64 { + let toggle_fn = match category { + ToggleCount::Falling => Signals::get_toggles_falling, + ToggleCount::Rising => Signals::get_toggles_rising, + ToggleCount::Total => Signals::get_toggles_total, + }; - // #[allow(unused_variables)] - // pub fn search_module_cell_breakdown( - // &self, - // name: &str, - // ) -> Result, ModuleError> { - // todo!() - // } + (0..self.signals.size).fold(0, |acc, i| acc + toggle_fn(&self.signals, i)) + } - // TODO: Add tests for these + // Returns all child_nets + fn find_nets_helper( + &self, + parents: Option<&[String]>, + filter_inputs: Option, + global_nets: &mut HashMap, HashSet>, + ) -> Result, ModuleError> { + // First time calling, parse top module + let parents = parents.unwrap_or(std::slice::from_ref(&self.top_module)); + + let Some(submodule) = self.submodules.get(parents) else { + return Err(ModuleError::MissingSubmodule(parents.join("."))); + }; - pub fn get_toggles(&self, category: ToggleCount) -> u64 { - let mut total: u64 = 0; - (0..self.signals.size).for_each(|i| { - total += match category { - ToggleCount::Falling => self.signals.get_toggles_falling(i), - ToggleCount::Rising => self.signals.get_toggles_rising(i), - ToggleCount::Total => self.signals.get_total_toggles(i), + // Collect all nets in submodule + let mut submodule_nets = + HashSet::::from_iter(submodule.nets.values().flatten().copied()); + + // First time calling, don't filter input nets + if filter_inputs.unwrap_or(false) { + // Collect all input nets + let mut input_nets = HashSet::::new(); + for (port_name, port_info) in &submodule.ports { + if port_info.direction == PortDirection::Input + && let Some(nets) = submodule.nets.get(port_name) + { + input_nets.extend(nets); + } + } + + submodule_nets = submodule_nets.difference(&input_nets).copied().collect(); + } + + // Parse children, collect all nets + let mut total_nets = HashSet::::new(); + for (cell_name, cell_type) in &submodule.cells { + if let CellType::Submodule(_) = cell_type { + // Submodule child + let child_parents = [parents, &[cell_name.to_owned()]].concat(); + let child_nets = self.find_nets_helper(Some(&child_parents), Some(true), global_nets)?; + + total_nets.extend(child_nets); + } + } + // Filter out child nets + submodule_nets = submodule_nets.difference(&total_nets).copied().collect(); + + global_nets.insert(parents.to_vec(), submodule_nets.clone()); + total_nets.extend(submodule_nets); // Add current submodule nets to total + + Ok(total_nets) + } + + pub fn get_submodule_nets(&self) -> Result, HashSet>, ModuleError> { + let mut submodule_nets = HashMap::new(); + self.find_nets_helper(None, None, &mut submodule_nets)?; + Ok(submodule_nets) + } + + pub fn get_submodule_toggles( + &self, + category: ToggleCount, + ) -> Result, HashMap>, ModuleError> { + let mut total_toggles = HashMap::new(); + let submodule_nets = self.get_submodule_nets()?; + + let toggle_fn = match category { + ToggleCount::Falling => Signals::get_toggles_falling, + ToggleCount::Rising => Signals::get_toggles_rising, + ToggleCount::Total => Signals::get_toggles_total, + }; + + for (submodule_name, nets) in submodule_nets { + let toggles = nets + .into_iter() + .map(|n| (n, toggle_fn(&self.signals, n))) + .collect::>(); + + total_toggles.insert(submodule_name, HashMap::from_iter(toggles)); + } + + Ok(total_toggles) + } + + pub fn get_submodule_net_map(&self) -> HashMap, HashMap>> { + HashMap::from_iter(self.submodules.iter().map(|(sub_name, sub)| { + ( + sub_name.to_vec(), + HashMap::from_iter( + sub + .nets + .iter() + .map(|(net_name, nets)| (net_name.to_owned(), nets.to_vec())), + ), + ) + })) + } + + pub fn get_all_signal_nets_reverse(&self) -> HashMap> { + let signal_net_map = self.get_all_signal_nets(); + + let mut reverse_map = HashMap::>::new(); + for (net_name, nets) in &signal_net_map { + if nets.len() > 1 { + for (i, &n) in nets.iter().enumerate() { + let entry = reverse_map.entry(n).or_default(); + entry.push(format!("{net_name}[{i}]")); + } + } else { + let entry = reverse_map.entry(nets[0]).or_default(); + entry.push(net_name.to_owned()) } - }); - total + } + + reverse_map } - // pub fn get_submodule_toggles( + // fn find_cells_helper( // &self, - // name: &str, - // category: ToggleCount, - // ) -> Result { - // let Some(net_names) = self.submodules.get(name) else { - // return Err(ModuleError::MissingSubmodule(name.to_string())); + // parents: Option<&[String]>, + // global_cells: &mut HashMap, HashSet>, + // ) -> Result, ModuleError> { + // let parents = parents.unwrap_or(std::slice::from_ref(&self.top_module)); + + // let Some(submodule) = self.submodules.get(parents) else { + // return Err(ModuleError::MissingSubmodule(parents.join("."))); // }; - // let mut total: u64 = 0; - // for name in net_names { - // self.ports[name].nets.iter().for_each(|&n| { - // total += match category { - // ToggleCount::Falling => self.signals.get_toggles_falling(n), - // ToggleCount::Rising => self.signals.get_toggles_rising(n), - // ToggleCount::Total => self.signals.get_total_toggles(n), - // } - // }); + // // let submodule_cells = vec![] + // for (cell_name, cell_type) in &submodule.cells { + // if let CellType::Primitive(cell_type) = cell_type { + // todo!() + // } // } - // Ok(total) - // } - - // #[allow(unused_variables)] - // pub fn search_module_total_toggle_count(&self, name: &str) -> Result { // todo!() // } + // TODO: Fix #[allow(unused)] pub fn load(path: &str) -> anyhow::Result { diff --git a/crates/arbolta/src/lib.rs b/crates/arbolta/src/lib.rs index 6049c64..b3dd00c 100644 --- a/crates/arbolta/src/lib.rs +++ b/crates/arbolta/src/lib.rs @@ -3,8 +3,8 @@ pub mod bit; pub mod cell; +pub mod graph; pub mod hardware_module; pub mod port; pub mod signal; -pub mod temp2; pub mod yosys; diff --git a/crates/arbolta/src/port.rs b/crates/arbolta/src/port.rs index b8eb13a..988e813 100644 --- a/crates/arbolta/src/port.rs +++ b/crates/arbolta/src/port.rs @@ -7,7 +7,9 @@ use std::fmt::Debug; use thiserror::Error; use yosys_netlist_json as yosys_json; -#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, Encode, Decode)] +#[derive( + Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, Deserialize, Serialize, Encode, Decode, +)] pub enum PortDirection { Input, Output, diff --git a/crates/arbolta/src/signal.rs b/crates/arbolta/src/signal.rs index 394aa03..4a28d75 100644 --- a/crates/arbolta/src/signal.rs +++ b/crates/arbolta/src/signal.rs @@ -12,6 +12,8 @@ pub struct Signals { pub size: usize, /// Value of nets pub nets: Box<[Bit]>, // TODO: Make this private + /// Signals have been modified + dirty: bool, // Is net constant constant: Box<[bool]>, /// Number of times net has transitioned from 0 -> 1 @@ -24,6 +26,7 @@ impl Signals { pub fn new(size: usize) -> Self { Self { size, + dirty: false, nets: vec![Bit::ZERO; size].into(), constant: vec![false; size].into(), toggles_rising: vec![0; size].into(), @@ -47,6 +50,7 @@ impl Signals { Bit::ZERO => self.toggles_falling[net] += 1, } + self.dirty = true; self.nets[net] = val; } @@ -79,8 +83,19 @@ impl Signals { self.constant[net] = false; } + #[inline] + pub fn is_dirty(&self) -> bool { + self.dirty + } + + #[inline] + pub fn clear_dirty(&mut self) { + self.dirty = false + } + /// Reset all nets to `Bit::ZERO` and clear statistics. pub fn reset(&mut self) { + self.dirty = false; self.nets.iter_mut().for_each(|n| *n = Bit::ZERO); self.constant.iter_mut().for_each(|c| *c = false); self.toggles_rising.iter_mut().for_each(|t| *t = 0); @@ -93,7 +108,7 @@ impl Signals { /// # Arguments /// * `net` - Selected signal net. #[inline] - pub fn get_total_toggles(&self, net: usize) -> u64 { + pub fn get_toggles_total(&self, net: usize) -> u64 { self.toggles_falling[net] + self.toggles_rising[net] } diff --git a/crates/arbolta/src/yosys.rs b/crates/arbolta/src/yosys.rs index 090e71c..a404ef0 100644 --- a/crates/arbolta/src/yosys.rs +++ b/crates/arbolta/src/yosys.rs @@ -1,285 +1,4 @@ -// cell::CellError, -// use crate::{ -// cell::CellError, -// hardware_module::ModuleError, -// port::{PortDirection, parse_bit}, -// temp2::{TopoCell, TopoNetMap}, -// }; -// use std::collections::HashMap; -// use yosys_netlist_json as yosys_json; - -// #[derive(Debug, Clone, Eq, PartialEq, Default)] -// pub struct SynthCell<'a> { -// cell_type: &'a str, -// parameters: HashMap<&'a str, usize>, -// // attributes: HashMap<&'a str, &'a str>, -// port_directions: HashMap<&'a str, PortDirection>, -// connections: HashMap<&'a str, Box<[usize]>>, -// } - -// impl<'a> SynthCell<'a> { -// pub fn from_yosys( -// topo_cell: &TopoCell<'a>, -// global_nets: &TopoNetMap<'a>, -// netlist: &'a yosys_json::Netlist, -// ) -> Result { -// let mut cell = Self { -// cell_type: &topo_cell.cell_type, -// ..Default::default() -// }; - -// // TODO: error handling -// let mut synth_module = &netlist.modules[topo_cell.parents.last().unwrap().cell_type]; -// let mut synth_cell = synth_module.cells[topo_cell.name]; - -// todo!() -// } -// } - -// impl TryFrom<&yosys_json::Cell> for SynthCell { -// type Error = ModuleError; -// fn try_from(cell: &yosys_json::Cell) -> Result { -// let mut synth_cell = SynthCell { -// cell_type: cell.cell_type.to_string(), -// ..Default::default() -// }; - -// // Add connections and directions -// for (port_name, conn_bits) in cell.connections.iter() { -// let direction = PortDirection::try_from(&cell.port_directions[port_name])?; -// let conn_nets: Vec = conn_bits -// .iter() -// .map(parse_bit) -// .collect::, _>>()?; - -// synth_cell -// .port_directions -// .insert(port_name.clone(), direction); -// synth_cell.connections.insert(port_name.clone(), conn_nets); -// } - -// // Add parameters -// for (param_name, param) in cell.parameters.iter() { -// let Some(param) = param.to_number() else { -// // TODO: Clean this up -// return Err(CellError::Parameter(param_name.to_string(), param.clone()).into()); -// }; -// synth_cell.parameters.insert(param_name.to_string(), param); -// } - -// // Add attributes -// for (attr_name, attr) in cell.attributes.iter() { -// let Some(attr) = attr.to_string_if_string() else { -// // TODO: Clean this up -// return Err(CellError::Attribute(attr_name.to_string(), attr.clone()).into()); -// }; -// synth_cell -// .attributes -// .insert(attr_name.to_string(), attr.to_string()); -// } - -// Ok(synth_cell) -// } -// } - -use std::{ - collections::HashMap, - net::{IpAddr, Ipv6Addr}, - time::Duration, - {path::PathBuf, process::Command}, -}; -use tarpc::{client, context, tokio_serde::formats::Json}; -use thiserror::Error; -use tokio::time; -use yosys_netlist_json as yosys_json; -pub use yosys_service::*; - -#[derive(Debug)] -pub struct YosysClient { - pub port: u16, - pub yosys_server_path: PathBuf, - pub yosys_path: PathBuf, -} - -impl Default for YosysClient { - fn default() -> Self { - Self { - port: 8080, - yosys_server_path: "yosys_server".into(), - yosys_path: "yosys".into(), - } - } -} - -#[derive(Debug, Error)] -pub enum YosysError { - #[error("Server error: {0}")] - Server(#[from] serde_error::Error), - - #[error("RPC error: {0}")] - Rpc(#[from] tarpc::client::RpcError), - - #[error("IO error: {0}")] - Io(#[from] std::io::Error), - - #[error("Yosys error: {0}")] - Yosys(String), -} - -const MAX_RETRIES: u32 = 4; -const RETRY_DELAY_MS: u64 = 1; - -impl YosysClient { - pub async fn flatten_netlist( - &self, - top_module: &str, - netlist: yosys_json::Netlist, - ) -> Result<(yosys_json::Netlist, HashMap>), YosysError> { - // Start Yosys server - let mut yosys = Command::new(&self.yosys_server_path) - .arg("--port") - .arg(format!("{}", self.port)) - .arg("--yosys-path") - .arg(&self.yosys_path) - .spawn()?; - - let server_addr = (IpAddr::V6(Ipv6Addr::LOCALHOST), self.port); - - // Keep trying to connect to server - let mut retries = 0; - let transport = loop { - let mut transport = tarpc::serde_transport::tcp::connect(server_addr, Json::default); - transport.config_mut().max_frame_length(usize::MAX); - - match transport.await { - Ok(t) => break t, - Err(e) => { - retries += 1; - eprintln!("Yosys client failed to connect (attempt {retries}): {e}"); - - if retries >= MAX_RETRIES { - return Err(e.into()); - } - - time::sleep(Duration::from_millis(RETRY_DELAY_MS)).await; - } - } - }; - - let request = FlattenRequest { - top_module: top_module.into(), - netlist, - }; - let client = SynthesisClient::new(client::Config::default(), transport).spawn(); - - let response = async move { - tokio::select! { - response1 = client.flatten(context::current(), request) => {response1} - } - } - .await??; - - // Check Yosys error log - if !response.error_log.is_empty() { - return Err(YosysError::Yosys(response.error_log)); - } - - yosys.kill()?; // Kill Yosys server - - Ok((response.netlist.unwrap(), response.topo_order.unwrap())) - } - - // TODO: De-duplicate code - pub async fn simple_synth( - &self, - verilog_path: &PathBuf, - top_module: Option, - config: SynthConfig, - ) -> Result<(yosys_json::Netlist, HashMap>), YosysError> { - let verilog_source = std::fs::read_to_string(verilog_path)?; - - // Try to start Yosys server, should fail if already started - // TODO: Clean this up - let mut _yosys = Command::new(&self.yosys_server_path) - .arg("--port") - .arg(format!("{}", self.port)) - .arg("--yosys-path") - .arg(&self.yosys_path) - .spawn(); - - let server_addr = (IpAddr::V6(Ipv6Addr::LOCALHOST), self.port); - - // Keep trying to connect to server - let mut retries = 0; - let transport = loop { - let mut transport = tarpc::serde_transport::tcp::connect(server_addr, Json::default); - transport.config_mut().max_frame_length(usize::MAX); - - match transport.await { - Ok(t) => break t, - Err(e) => { - retries += 1; - eprintln!("Yosys client failed to connect (attempt {retries}): {e}",); - - if retries >= MAX_RETRIES { - return Err(e.into()); - } - - time::sleep(Duration::from_millis(RETRY_DELAY_MS)).await; - } - } - }; - - let request = SimpleSynthRequest { - verilog_source, - top_module, - config, - }; - - let client = SynthesisClient::new(client::Config::default(), transport).spawn(); - - let response = async move { - tokio::select! { - response1 = client.simple_synth(context::current(), request) => {response1} - } - } - .await??; - - // Check Yosys error log - if !response.error_log.is_empty() { - return Err(YosysError::Yosys(response.error_log)); - } - - // yosys.kill()?; // Kill Yosys server - - Ok((response.netlist.unwrap(), response.topo_order.unwrap())) - } -} - -// TODO: De-duplicate with yosys_server -/// Parse raw Yosys topological order output -pub fn parse_torder(raw: &str) -> HashMap> { - let mut torder: HashMap> = HashMap::new(); - let mut current_module: Option<&str> = None; // If Some, save cells - - for line in raw.lines() { - let line = line.trim(); - - // Start new module - if let Some(module_name) = line.strip_prefix("module ") { - current_module = Some(module_name) - } else if let Some(module_name) = current_module - && let Some(cell_name) = line.strip_prefix("cell ") - { - torder - .entry(module_name.to_string()) - .or_default() - .push(cell_name.to_string()); - } - } - - torder -} - // Re-export -pub use yosys_json::Netlist; +pub use yosys_netlist_json::Netlist; + +// TODO: Maybe re-add Yosys bindings... diff --git a/crates/arbolta/tests/test_module.rs b/crates/arbolta/tests/test_module.rs index e28a072..ff41afd 100644 --- a/crates/arbolta/tests/test_module.rs +++ b/crates/arbolta/tests/test_module.rs @@ -36,6 +36,12 @@ fn test_module_unary_cell(#[case] cell: &str, #[case] cases: [(u8, u8); 2]) { (1, 0, 0), (1, 1, 1), ])] +#[case("$_ANDNOT__WRAPPER", [ + (0, 0, 0), + (0, 1, 0), + (1, 0, 1), + (1, 1, 0), +])] #[case("$_NOR__WRAPPER", [ (0, 0, 1), (0, 1, 0), @@ -78,4 +84,68 @@ fn test_module_binary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8); 4]) } } +#[rstest] +#[case::andorinvert("$_AOI3__WRAPPER", [ + (0, 0, 0, 1), + (0, 0, 1, 0), + (0, 1, 0, 1), + (0, 1, 1, 0), + (1, 0, 0, 1), + (1, 0, 1, 0), + (1, 1, 0, 0), + (1, 1, 1, 0), +])] +#[case::orandinvert("$_OAI3__WRAPPER", [ + (0, 0, 0, 1), + (0, 0, 1, 1), + (0, 1, 0, 1), + (0, 1, 1, 0), + (1, 0, 0, 1), + (1, 0, 1, 0), + (1, 1, 0, 1), + (1, 1, 1, 0), +])] +fn test_module_ternary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8, u8); 8]) { + let mut module = HardwareModule::new(cell, &CELL_WRAPPER_NETLIST).unwrap(); + for (a, b, c, expected) in cases { + module.set_port("A", BitVec::from_int(a, None)).unwrap(); + module.set_port("B", BitVec::from_int(b, None)).unwrap(); + module.set_port("C", BitVec::from_int(c, None)).unwrap(); + module.eval(); + let actual: u8 = module.get_port("Y").unwrap().to_int(); + assert_eq!(actual, expected, "inputs: `{a}`, `{b}`, `{c}`"); + } +} +#[rstest] +#[case::mux2("$_MUX__WRAPPER", [ + (0, 0, 0, 0), + (0, 0, 1, 0), + (0, 1, 0, 0), + (0, 1, 1, 1), + (1, 0, 0, 1), + (1, 0, 1, 0), + (1, 1, 0, 1), + (1, 1, 1, 1), +])] +#[case::nmux2("$_NMUX__WRAPPER", [ + (0, 0, 0, 1), + (0, 0, 1, 1), + (0, 1, 0, 1), + (0, 1, 1, 0), + (1, 0, 0, 0), + (1, 0, 1, 1), + (1, 1, 0, 0), + (1, 1, 1, 0), +])] +fn test_module_mux_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8, u8); 8]) { + let mut module = HardwareModule::new(cell, &CELL_WRAPPER_NETLIST).unwrap(); + for (a, b, s, expected) in cases { + module.set_port("A", BitVec::from_int(a, None)).unwrap(); + module.set_port("B", BitVec::from_int(b, None)).unwrap(); + module.set_port("S", BitVec::from_int(s, None)).unwrap(); + module.eval(); + let actual: u8 = module.get_port("Y").unwrap().to_int(); + assert_eq!(actual, expected, "inputs: `{a}`, `{b}`, `{s}`"); + } +} // TODO: Expand testing diff --git a/crates/arbolta/tests/test_signal.rs b/crates/arbolta/tests/test_signal.rs index 5ac84fd..97b38e2 100644 --- a/crates/arbolta/tests/test_signal.rs +++ b/crates/arbolta/tests/test_signal.rs @@ -11,7 +11,7 @@ fn test_signal_net_init() { assert_eq!(x.get_net(0), Bit::ZERO); assert_eq!(x.get_toggles_falling(0), 0); assert_eq!(x.get_toggles_rising(0), 0); - assert_eq!(x.get_total_toggles(0), 0); + assert_eq!(x.get_toggles_total(0), 0); } #[test] @@ -27,13 +27,13 @@ fn test_signal_net_set_value() { fn test_signal_net_toggle_rising() { let mut x = Signals::new(1); - assert_eq!(x.get_total_toggles(0), 0); + assert_eq!(x.get_toggles_total(0), 0); assert_eq!(x.get_toggles_falling(0), 0); assert_eq!(x.get_toggles_rising(0), 0); x.set_net(0, Bit::ONE); - assert_eq!(x.get_total_toggles(0), 1); + assert_eq!(x.get_toggles_total(0), 1); assert_eq!(x.get_toggles_falling(0), 0); assert_eq!(x.get_toggles_rising(0), 1); } @@ -43,13 +43,13 @@ fn test_signal_net_toggle_falling() { let mut x = Signals::new(1); x.nets[0] = Bit::ONE; - assert_eq!(x.get_total_toggles(0), 0); + assert_eq!(x.get_toggles_total(0), 0); assert_eq!(x.get_toggles_falling(0), 0); assert_eq!(x.get_toggles_rising(0), 0); x.set_net(0, Bit::ZERO); - assert_eq!(x.get_total_toggles(0), 1); + assert_eq!(x.get_toggles_total(0), 1); assert_eq!(x.get_toggles_falling(0), 1); assert_eq!(x.get_toggles_rising(0), 0); } @@ -58,13 +58,13 @@ fn test_signal_net_toggle_falling() { fn test_signal_net_toggle_same_zero() { let mut x = Signals::new(1); - assert_eq!(x.get_total_toggles(0), 0); + assert_eq!(x.get_toggles_total(0), 0); assert_eq!(x.get_toggles_falling(0), 0); assert_eq!(x.get_toggles_rising(0), 0); x.set_net(0, Bit::ZERO); - assert_eq!(x.get_total_toggles(0), 0); + assert_eq!(x.get_toggles_total(0), 0); assert_eq!(x.get_toggles_falling(0), 0); assert_eq!(x.get_toggles_rising(0), 0); } @@ -74,13 +74,13 @@ fn test_signal_net_toggle_same_one() { let mut x = Signals::new(1); x.nets[0] = Bit::ONE; - assert_eq!(x.get_total_toggles(0), 0); + assert_eq!(x.get_toggles_total(0), 0); assert_eq!(x.get_toggles_falling(0), 0); assert_eq!(x.get_toggles_rising(0), 0); x.set_net(0, Bit::ONE); - assert_eq!(x.get_total_toggles(0), 0); + assert_eq!(x.get_toggles_total(0), 0); assert_eq!(x.get_toggles_falling(0), 0); assert_eq!(x.get_toggles_rising(0), 0); } diff --git a/crates/arbolta/tests/test_simcell.rs b/crates/arbolta/tests/test_simcell.rs index ec0f31c..00eb7e9 100644 --- a/crates/arbolta/tests/test_simcell.rs +++ b/crates/arbolta/tests/test_simcell.rs @@ -138,8 +138,7 @@ fn test_cell_ternary(#[case] mut cell: Box, #[case] cases: [(Bit, Bi #[rstest] fn test_cell_dff_posedge() { - // D, C, Q - let (data_in, clock, data_out) = (0, 1, 2); + let (clock, data_in, data_out) = (0, 1, 2); let mut cell = Dff::new(Bit::ONE, clock, data_in, data_out); let mut signals = Signals::new(3); @@ -167,35 +166,40 @@ fn test_cell_dff_posedge() { assert_eq!(signals.get_net(data_out), Bit::ZERO); } -/* #[rstest] fn test_cell_sdff_pp() { - // D, C, R, Q - let (data_in, clock, reset, data_out) = (0, 1, 2, 3); - let mut cell = DffReset::new(Bit::ONE, data_in, clock, reset, data_out); - let mut signals = vec![Signal::default(); 4].into_boxed_slice(); + let (clock, reset, data_in, data_out) = (0, 1, 2, 3); + let mut cell = DffReset::new( + Bit::ONE, + Bit::ONE, + Bit::ZERO, + clock, + reset, + data_in, + data_out, + ); + let mut signals = Signals::new(4); cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ZERO); + assert_eq!(signals.get_net(data_out), Bit::ZERO); - signals[data_in].set_value(Bit::ONE); + signals.set_net(data_in, Bit::ONE); cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ZERO); + assert_eq!(signals.get_net(data_out), Bit::ZERO); - signals[clock].set_value(Bit::ONE); // Rising edge + signals.set_net(clock, Bit::ONE); // Rising edge cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ONE); + assert_eq!(signals.get_net(data_out), Bit::ONE); - signals[clock].set_value(Bit::ZERO); // Falling edge + signals.set_net(clock, Bit::ZERO); // Falling edge cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ONE); + assert_eq!(signals.get_net(data_out), Bit::ONE); - signals[reset].set_value(Bit::ONE); + signals.set_net(reset, Bit::ONE); cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ONE); + assert_eq!(signals.get_net(data_out), Bit::ONE); - signals[clock].set_value(Bit::ONE); // Rising edge + signals.set_net(clock, Bit::ONE); // Rising edge cell.eval(&mut signals); - assert_eq!(signals[data_out].get_value(), Bit::ZERO); + assert_eq!(signals.get_net(data_out), Bit::ZERO); } -*/ diff --git a/crates/fault/Cargo.toml b/crates/fault/Cargo.toml index 78258a0..fbf3a5f 100644 --- a/crates/fault/Cargo.toml +++ b/crates/fault/Cargo.toml @@ -8,9 +8,9 @@ ndarray-npy = "0.9.1" ndarray = { version = "0.16.1", features = ["rayon"] } ndarray-stats = "0.6.0" num-traits = "0.2" -rayon = "1.10.0" +rayon = "1.11.0" arbolta = { path = "../arbolta" } once_cell = "1.21.3" -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.53", features = ["derive"] } anyhow = "1.0" indicatif = { version = "0.17.11", features = ["rayon"] } diff --git a/crates/python_bindings/Cargo.toml b/crates/python_bindings/Cargo.toml index b064585..4d38b4e 100644 --- a/crates/python_bindings/Cargo.toml +++ b/crates/python_bindings/Cargo.toml @@ -14,8 +14,7 @@ numpy = "0.24.0" num-traits = "0.2" serde = { version = "1.0", features = ["derive"] } bincode = { workspace = true } -yosys-netlist-json = { workspace = true } -tokio = { workspace = true } +thiserror = { workspace = true } [dependencies.arbolta] path = "../arbolta" diff --git a/crates/python_bindings/py_src/arbolta/design.py b/crates/python_bindings/py_src/arbolta/design.py index 0ad9f19..a5cad20 100644 --- a/crates/python_bindings/py_src/arbolta/design.py +++ b/crates/python_bindings/py_src/arbolta/design.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: MIT from dataclasses import dataclass -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict, List, Literal, Optional, Tuple, Union import numpy as np @@ -31,10 +31,10 @@ class PortConfig: """ shape: Tuple[int, int] = (1, 1) - dtype: Union[np.dtype, type] = np.uint32 + dtype: Union[np.dtype, type] = np.uint clock: bool = False reset: bool = False - polarity: Optional[int] = None + polarity: Optional[Literal[0, 1]] = None @dataclass @@ -52,9 +52,17 @@ def __init__(self, config: dict[str, PortConfig], design: Design): for port_name, port_config in config.items(): if port_config.reset and port_config.clock: raise AttributeError(f"Port `{port_name}` cannot be a reset and clock") + if port_config.reset: + if port_config.polarity not in [0, 1]: + raise ValueError(f"Unsupported polarity: `{port_config.polarity}`") + design.set_reset(port_name, bool(port_config.polarity)) + elif port_config.clock: + if port_config.polarity not in [0, 1]: + raise ValueError(f"Unsupported polarity: `{port_config.polarity}`") + design.set_clock(port_name, bool(port_config.polarity)) design.set_port_shape(port_name, port_config.shape) @@ -98,13 +106,7 @@ def __setattr__(self, name: str, value: Any) -> None: class HardwareDesign: def __init__( # TODO: de-duplicate defaults - self, - top_module: str, - netlist_path: str, - config: dict[str, PortConfig], - torder_path: Optional[str] = None, - yosys_path: Optional[str] = "yosys", - yosys_server_path: Optional[str] = "yosys_server", + self, top_module: str, netlist_path: str, config: dict[str, PortConfig] ): """ Parameters @@ -117,9 +119,7 @@ def __init__( # TODO: de-duplicate defaults Configuration for design ports. """ self.top_module = top_module - self.design = Design( - top_module, netlist_path, torder_path, yosys_path, yosys_server_path - ) + self.design = Design(top_module, netlist_path) self.ports = HardwarePorts(config, self.design) def reset(self): @@ -148,7 +148,6 @@ def eval(self): if port.updated: self.design.set_port_numpy(port_name, port.data) port.updated = False - # if self.design.is_port_input(port_name): self.design.eval() @@ -162,11 +161,9 @@ def eval_clocked(self, cycles: Optional[int] = 1): """ port: Port for port_name, port in self.ports._ports.items(): - # if self.design.is_port_input(port_name): if port.updated: self.design.set_port_numpy(port_name, port.data) port.updated = False - # self.design.set_port_numpy(port_name, port_array) self.design.eval_clocked(cycles) @@ -240,6 +237,17 @@ def total_toggle_count(self, module_name: Optional[str] = None) -> int: else: return self.design.get_module_total_toggle_count(module_name) + def submodule_toggles( + self, category: Literal["falling", "rising", "total"] + ) -> Dict[str, Dict[int, int]]: + return self.design.get_submodule_toggles(category) + + def submodule_nets(self) -> Dict[str, int]: + return self.design.get_submodule_nets() + + def submodule_net_map(self) -> Dict[str, Dict[str, list[int]]]: + return self.design.get_submodule_net_map() + def module_names(self) -> List[str]: """ Get names of modules in top-level design module. @@ -251,9 +259,15 @@ def module_names(self) -> List[str]: """ return self.design.get_module_names() + def get_graph(self) -> str: + return self.design.get_graph() + def signal_map(self) -> Dict[str, List[int]]: return self.design.get_signal_map() + def signal_nets_reverse(self) -> Dict[int, list[str]]: + return self.design.get_all_signal_nets_reverse() + def stick_signal(self, net: int, val: Union[int, bool]) -> None: return self.design.stick_signal(net, bool(val)) diff --git a/crates/python_bindings/src/design.rs b/crates/python_bindings/src/design.rs index 226cb3d..83ecd65 100644 --- a/crates/python_bindings/src/design.rs +++ b/crates/python_bindings/src/design.rs @@ -4,16 +4,19 @@ use crate::conversion::{ bits_to_bool_numpy, bits_to_int_numpy, bool_numpy_to_bits, int_numpy_to_bits, }; -use arbol::hardware_module::HardwareModule; -use arbol::port::PortDirection; -use arbol::yosys::Netlist; +use arbol::{ + hardware_module::{HardwareModule, ToggleCount}, + port::PortDirection, + yosys::Netlist, +}; use bincode::{Decode, Encode}; -use pyo3::exceptions::{PyAttributeError, PyException, PyValueError}; -use pyo3::prelude::*; -use pyo3::types::{PyBytes, PyDict}; +use pyo3::{ + exceptions::{PyAttributeError, PyException, PyValueError}, + prelude::*, + types::{PyBytes, PyDict}, +}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use std::path::PathBuf; #[pyclass(dict, module = "arbolta", name = "Design")] #[derive(Deserialize, Serialize, Decode, Encode)] @@ -62,19 +65,15 @@ impl PyDesign { } fn save(&self, path: &str) -> PyResult<()> { - match self.design.save(path) { - Ok(()) => Ok(()), - Err(err) => Err(PyValueError::new_err(format!("{err}"))), - } + self + .design + .save(path) + .map_err(|e| PyValueError::new_err(format!("{e}"))) } #[staticmethod] fn load(path: &str) -> PyResult { - let design = match HardwareModule::load(path) { - Ok(module) => module, - Err(err) => return Err(PyValueError::new_err(format!("{err}"))), - }; - + let design = HardwareModule::load(path).map_err(|e| PyValueError::new_err(format!("{e}")))?; let top_module = design.top_module.clone(); Ok(Self { @@ -100,10 +99,14 @@ impl PyDesign { let internal_shape = self.get_port_shape(name)?; let (num_elems, elem_size) = (shape[1], internal_shape[1] / shape[1]); - match self.design.set_port_shape(name, &[num_elems, elem_size]) { - Ok(()) => Ok(()), - Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), - } + self + .design + .set_port_shape(name, &[num_elems, elem_size]) + .map_err(|e| PyValueError::new_err(format!("{e}"))) + } + + fn get_graph(&self) -> String { + self.design.graph.clone() } fn get_module_names(&self) -> Vec { @@ -114,6 +117,77 @@ impl PyDesign { self.design.get_all_signal_nets() } + fn get_all_signal_nets(&self) -> HashMap> { + self.design.get_all_signal_nets() + } + + fn get_all_signal_nets_reverse(&self) -> HashMap> { + self.design.get_all_signal_nets_reverse() + } + + fn get_all_signal_values(&self) -> HashMap> { + let signal_values = self.design.get_all_signal_values(); + HashMap::from_iter( + signal_values + .into_iter() + .map(|(name, bits)| (name, bits.into())), + ) + } + + fn get_submodule_nets(&self) -> PyResult>> { + let submodule_nets = self + .design + .get_submodule_nets() + .map_err(|e| PyAttributeError::new_err(format!("{e}")))?; + + let submodule_nets = HashMap::from_iter( + submodule_nets + .into_iter() + .map(|(name, nets)| (name.join("."), nets.into_iter().collect())), + ); + + Ok(submodule_nets) + } + + fn get_submodule_toggles( + &self, + category: &str, + ) -> PyResult>> { + let category = match category { + "falling" => ToggleCount::Falling, + "rising" => ToggleCount::Rising, + "total" => ToggleCount::Total, + _ => { + return Err(PyValueError::new_err(format!( + "Unsupported category `{category}`" + ))); + } + }; + + let submodule_toggles = self + .design + .get_submodule_toggles(category) + .map_err(|e| PyValueError::new_err(format!("{e}")))?; + + let submodule_toggles = HashMap::from_iter( + submodule_toggles + .into_iter() + .map(|(name, toggles)| (name.join("."), toggles)), + ); + + Ok(submodule_toggles) + } + + fn get_submodule_net_map(&self) -> HashMap>> { + HashMap::from_iter( + self + .design + .get_submodule_net_map() + .into_iter() + .map(|(name, nets)| (name.join("."), nets)), + ) + } + fn get_cell_info(&self, py: Python<'_>) -> PyResult { let all_cell_info = PyDict::new(py); for (name, cell_type) in self.design.cell_info.iter() { @@ -128,68 +202,72 @@ impl PyDesign { } pub fn stick_signal(&mut self, net: usize, val: bool) -> PyResult<()> { - match self.design.stick_signal(net, val.into()) { - Ok(()) => Ok(()), - Err(err) => Err(PyException::new_err(format!("{err}"))), - } + self + .design + .stick_signal(net, val.into()) + .map_err(|e| PyException::new_err(format!("{e}"))) } pub fn unstick_signal(&mut self, net: usize) -> PyResult<()> { - match self.design.unstick_signal(net) { - Ok(()) => Ok(()), - Err(err) => Err(PyException::new_err(format!("{err}"))), - } + self + .design + .unstick_signal(net) + .map_err(|e| PyException::new_err(format!("{e}"))) } fn set_clock(&mut self, name: &str, polarity: bool) -> PyResult<()> { - let Some(nets) = self.design.get_signal_nets(name) else { - return Err(PyException::new_err(format!("No signal `{name}`"))); - }; + let nets = self + .design + .get_nets(name, None) + .ok_or(PyException::new_err(format!("No signal `{name}`")))?; if nets.len() != 1 { return Err(PyException::new_err("Clock net ambiguous".to_string())); } - self.design.set_clock(nets[0], polarity.into()).unwrap(); - - Ok(()) + self + .design + .set_clock(nets[0], polarity.into()) + .map_err(|e| PyException::new_err(format!("{e}"))) } fn set_reset(&mut self, name: &str, polarity: bool) -> PyResult<()> { - let Some(nets) = self.design.get_signal_nets(name) else { - return Err(PyException::new_err(format!("No signal `{name}`"))); - }; + let nets = self + .design + .get_nets(name, None) + .ok_or(PyException::new_err(format!("No signal `{name}`")))?; if nets.len() != 1 { return Err(PyException::new_err("Reset net ambiguous".to_string())); } - self.design.set_reset(nets[0], polarity.into()).unwrap(); - - Ok(()) + self + .design + .set_reset(nets[0], polarity.into()) + .map_err(|e| PyException::new_err(format!("{e}"))) } fn reset(&mut self) { - self.design.reset(); + self.design.reset() } fn eval(&mut self) { - self.design.eval(); + self.design.eval() } // TODO: Fix this fn eval_clocked(&mut self, cycles: u32) -> PyResult<()> { - match self.design.eval_clocked(Some(cycles)) { - Ok(()) => Ok(()), - Err(err) => Err(PyException::new_err(format!("{err}"))), - } + self + .design + .eval_clocked(Some(cycles)) + .map_err(|e| PyException::new_err(format!("{e}"))) } fn eval_reset_clocked(&mut self, cycles: u32) -> PyResult<()> { - match self.design.eval_reset_clocked(Some(cycles)) { - Ok(()) => Ok(()), - Err(err) => Err(PyException::new_err(format!("{err}"))), - } + self + .design + .eval_reset_clocked(Some(cycles)) + .map_err(|e| PyException::new_err(format!("{e}"))) } #[allow(unused_variables)] @@ -208,10 +286,10 @@ impl PyDesign { } fn is_port_input(&self, name: &str) -> PyResult { - let direction = match self.design.get_port_direction(name) { - Ok(direction) => direction, - Err(err) => return Err(PyAttributeError::new_err(format!("{err}"))), - }; + let direction = self + .design + .get_port_direction(name) + .map_err(|e| PyException::new_err(format!("{e}")))?; Ok(direction == PortDirection::Input) } From 09371977242a896dd2d9cd4afe36625aee124847 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Thu, 18 Dec 2025 21:13:04 +0000 Subject: [PATCH 32/64] Switched to RTLID representation and going back to yosys topological ordering --- Cargo.toml | 2 +- crates/arbolta/Cargo.toml | 9 +- crates/arbolta/src/bit.rs | 11 +- crates/arbolta/src/cell/mod.rs | 30 +- crates/arbolta/src/cell/simcells.rs | 75 ++- crates/arbolta/src/cell/simlib/arithmetic.rs | 13 +- crates/arbolta/src/cell/simlib/bool_ops.rs | 7 +- crates/arbolta/src/cell/simlib/compare_ops.rs | 1 - .../src/cell/simlib/logic_reduce_ops.rs | 51 +- crates/arbolta/src/cell/simlib/mod.rs | 139 +++--- crates/arbolta/src/cell/simlib/registers.rs | 84 +++- crates/arbolta/src/cell/simlib/shift_ops.rs | 9 +- crates/arbolta/src/cell/simlib/various.rs | 51 +- .../cell/{test_macros.rs => test_helpers.rs} | 56 ++- crates/arbolta/src/graph.rs | 452 ------------------ crates/arbolta/src/hardware_module.rs | 428 ++++------------- crates/arbolta/src/lib.rs | 2 + crates/arbolta/src/netlist_wrapper.rs | 315 ++++++++++++ crates/arbolta/src/port.rs | 54 +-- crates/arbolta/src/signal.rs | 3 +- crates/arbolta/src/yosys.rs | 80 +++- .../arbolta/tests/deps/simlib_wrappers.json | 48 ++ crates/arbolta/tests/deps/simlib_wrappers.sv | 48 +- crates/arbolta/tests/helpers.rs | 97 ++++ ...{test_module.rs => test_simcell_module.rs} | 20 +- crates/arbolta/tests/test_simlib_module.rs | 236 +++++++++ crates/fault/src/main.rs | 260 +++++----- crates/python_bindings/Cargo.toml | 4 +- .../python_bindings/py_src/arbolta/design.py | 10 +- crates/python_bindings/src/design.rs | 287 +++++------ 30 files changed, 1551 insertions(+), 1331 deletions(-) rename crates/arbolta/src/cell/{test_macros.rs => test_helpers.rs} (71%) delete mode 100644 crates/arbolta/src/graph.rs create mode 100644 crates/arbolta/src/netlist_wrapper.rs create mode 100644 crates/arbolta/tests/deps/simlib_wrappers.json create mode 100644 crates/arbolta/tests/helpers.rs rename crates/arbolta/tests/{test_module.rs => test_simcell_module.rs} (80%) create mode 100644 crates/arbolta/tests/test_simlib_module.rs diff --git a/Cargo.toml b/Cargo.toml index e4d2a73..d2469e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,6 @@ resolver = "2" [workspace.dependencies] anyhow = "1" -bincode = { version = "2", features = ["serde"] } clap = { version = "4.5", features = ["derive"] } derive_more = { version = "2", features = ["full"] } enum_dispatch = "0.3" @@ -14,6 +13,7 @@ indexmap = "2.12" ndarray = "0.16.1" num-traits = "0.2" once_cell = "1.21" +postcard = "1" rstest = "0.26" serde = { version = "1", features = ["derive"] } serde-error = "0.1" diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index 7b20d5b..c7b7c96 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -6,24 +6,17 @@ edition = "2024" [dependencies] anyhow = { workspace = true } -bincode = { workspace = true } derive-new = "0.7" derive_more = { workspace = true } enum_dispatch = { workspace = true } -# flexbuffers = { workspace = true } -# futures = { workspace = true } indexmap = { workspace = true } inventory = "0.3.21" ndarray = { workspace = true } num-traits = { workspace = true } -# criterion = "0.5" -# pprof = { version = "0.15", features = ["criterion", "flamegraph"] } once_cell = { workspace = true } petgraph = { version = "0.8.3", features = ["serde-1"] } +postcard = { workspace = true } rstest = { workspace = true } serde = { workspace = true } -# serde-error = { workspace = true } -# tarpc = { workspace = true } -# tempfile = { workspace = true }smal thiserror = { workspace = true } yosys-netlist-json = { workspace = true } diff --git a/crates/arbolta/src/bit.rs b/crates/arbolta/src/bit.rs index e6443d5..c881001 100644 --- a/crates/arbolta/src/bit.rs +++ b/crates/arbolta/src/bit.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: MIT use anyhow::Result; -use bincode::{Decode, Encode}; use core::fmt; use derive_more::{BitAnd, BitOr, BitXor, Debug, IntoIterator, Not}; use num_traits::{PrimInt, WrappingAdd, WrappingShl, WrappingSub}; @@ -26,8 +25,6 @@ use thiserror::Error; derive_more::Into, Serialize, Default, - Encode, - Decode, BitAnd, BitOr, BitXor, @@ -165,6 +162,14 @@ impl FromIterator for BitVec { } impl BitVec { + pub fn len(&self) -> usize { + self.bits.len() + } + + pub fn is_empty(&self) -> bool { + self.bits.is_empty() + } + /// Create from int. /// /// # Arguments diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index a06e873..20a44f6 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -1,10 +1,9 @@ mod simcells; mod simlib; -mod test_macros; +mod test_helpers; // Re-export -use crate::{graph::TopoCell, signal::Signals}; -use bincode::{Decode, Encode}; +use crate::signal::Signals; use enum_dispatch::enum_dispatch; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; @@ -19,7 +18,8 @@ pub trait CellFn { fn reset(&mut self); } -pub type CellCtor = fn(&BTreeMap>, &BTreeMap) -> Cell; +// connections, parameters +pub type CellCtor = fn(&BTreeMap<&str, Box<[usize]>>, &BTreeMap<&str, usize>) -> Cell; pub type CellDispatchMap = HashMap<&'static str, CellCtor>; #[derive(derive_more::Constructor)] @@ -41,7 +41,7 @@ pub static CELL_DISPATCH: Lazy> = Lazy::new(|| { }); #[enum_dispatch(CellFn)] -#[derive(Debug, Serialize, Deserialize, Clone, Decode, Encode)] +#[derive(Debug, Serialize, Deserialize, Clone)] /// Proxy for a standard-cell and basic unit of 'compute'. pub enum Cell { // Sim Cells @@ -101,24 +101,14 @@ pub enum CellError { Direction(String), } -// TODO: Make this take topo cell AND move into try_from -// TODO: Move matching to hashmap of function pointers -// will allow for different cell libraries... and reuse of cells in new ways -/// Generate a cell given its Yosys netlist description -/// # Arguments -/// * `cell` - Yosys cell -pub fn create_cell(cell: &TopoCell) -> Result { - let cell_type = cell.cell_type.as_str(); +pub fn create_cell( + cell_type: &str, + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, +) -> Result { let ctor = CELL_DISPATCH .get(cell_type) .ok_or_else(|| CellError::Unsupported(cell_type.to_string()))?; - let Some(connections) = &cell.connections else { - todo!() - }; - let Some(parameters) = &cell.parameters else { - todo!() - }; - Ok(ctor(connections, parameters)) } diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index e547451..e9768d9 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -1,13 +1,12 @@ use super::{Cell, CellFn}; use crate::{bit::Bit, cell::CellRegistration, signal::Signals}; -use bincode::{Decode, Encode}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; macro_rules! define_unary_cell { ($name:ident, $body:expr) => { - #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] + #[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct $name { a_net: usize, y_net: usize, @@ -30,7 +29,7 @@ define_unary_cell!(Inverter, |x: Bit| !x); macro_rules! define_binary_cell { ($name:ident, $body:expr) => { - #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] + #[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct $name { a_net: usize, b_net: usize, @@ -60,7 +59,7 @@ define_binary_cell!(OrNot, |x: [Bit; 2]| x[0] | !x[1]); macro_rules! define_ternary_cell { ($name:ident, $op0_net:ident, $op1_net:ident, $op2_net:ident, $body:expr) => { - #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] + #[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct $name { $op0_net: usize, $op1_net: usize, @@ -106,7 +105,7 @@ define_ternary_cell!( |x: [Bit; 3]| if x[2].into() { !x[1] } else { !x[0] } ); -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, derive_new::new)] +#[derive(Debug, Clone, Serialize, Deserialize, derive_new::new)] pub struct Dff { polarity: Bit, clock_net: usize, @@ -136,7 +135,7 @@ impl CellFn for Dff { } } -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, derive_new::new)] +#[derive(Debug, Clone, Serialize, Deserialize, derive_new::new)] pub struct DffReset { clock_polarity: Bit, reset_polarity: Bit, @@ -177,22 +176,22 @@ impl CellFn for DffReset { // TODO: Create with macro... // Cell Constructor functions fn make_buf( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { Buffer::new(connections["A"][0], connections["Y"][0]).into() } fn make_not( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { Inverter::new(connections["A"][0], connections["Y"][0]).into() } fn make_and( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { And::new( connections["A"][0], @@ -203,8 +202,8 @@ fn make_and( } fn make_nand( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { Nand::new( connections["A"][0], @@ -215,8 +214,8 @@ fn make_nand( } fn make_or( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { Or::new( connections["A"][0], @@ -227,8 +226,8 @@ fn make_or( } fn make_nor( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { Nor::new( connections["A"][0], @@ -239,8 +238,8 @@ fn make_nor( } fn make_xor( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { Xor::new( connections["A"][0], @@ -251,8 +250,8 @@ fn make_xor( } fn make_xnor( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { Xnor::new( connections["A"][0], @@ -263,8 +262,8 @@ fn make_xnor( } fn make_andnot( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { AndNot::new( connections["A"][0], @@ -275,8 +274,8 @@ fn make_andnot( } fn make_ornot( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { OrNot::new( connections["A"][0], @@ -287,8 +286,8 @@ fn make_ornot( } fn make_mux( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { Mux2::new( connections["A"][0], @@ -300,8 +299,8 @@ fn make_mux( } fn make_nmux( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { NMux2::new( connections["A"][0], @@ -313,8 +312,8 @@ fn make_nmux( } fn make_andorinvert( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { AndOrInvert::new( connections["A"][0], @@ -326,8 +325,8 @@ fn make_andorinvert( } fn make_orandinvert( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { OrAndInvert::new( connections["A"][0], @@ -339,8 +338,8 @@ fn make_orandinvert( } fn make_dff( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { Dff::new( Bit::ONE, @@ -352,8 +351,8 @@ fn make_dff( } fn make_dffreset( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { DffReset::new( Bit::ONE, diff --git a/crates/arbolta/src/cell/simlib/arithmetic.rs b/crates/arbolta/src/cell/simlib/arithmetic.rs index d241706..1b4c516 100644 --- a/crates/arbolta/src/cell/simlib/arithmetic.rs +++ b/crates/arbolta/src/cell/simlib/arithmetic.rs @@ -1,6 +1,5 @@ use super::*; use crate::{bit::BitVec, signal::Signals}; -use bincode::{Decode, Encode}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; use std::ops::Rem; @@ -14,7 +13,7 @@ define_arithmetic_cell!(Le, <); define_arithmetic_cell!(Gt, >); define_arithmetic_cell!(Ge, &ge); -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct Neg { signed: bool, a_nets: Box<[usize]>, @@ -43,7 +42,7 @@ impl CellFn for Neg { } }; - copy_bits(signals, &self.y_nets, y); + copy_bits(signals, &self.y_nets, &y); } fn reset(&mut self) {} @@ -52,7 +51,7 @@ impl CellFn for Neg { #[cfg(test)] mod tests { use super::*; - use crate::cell::test_macros::*; + use crate::cell::test_helpers::*; use rstest::rstest; #[rstest] @@ -78,7 +77,7 @@ mod tests { #[case(true, "1", "1", "111111111110")] // -1 + -1 = -2 #[case(true, "01", "1", "000000000000")] // 1 + -1 = 0 fn add(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { - run_binary_cell_case!(Add, signed, a, b, expected); + run_binary_cell_case_signed!(Add, signed, a, b, expected); } #[rstest] @@ -101,7 +100,7 @@ mod tests { // 37738 + 4365 = 99938 #[case::unsigned_overflow(false, "1001001101101010", "0001000100001101", "00011000011001100010")] fn mul(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { - run_binary_cell_case!(Mul, signed, a, b, expected); + run_binary_cell_case_signed!(Mul, signed, a, b, expected); } #[rstest] @@ -119,7 +118,7 @@ mod tests { #[case::unsigned_normal(false, "1001001101101010", "0001000100001101", "0")] #[case::signed_normal(true, "1001001101101010", "0001000100001101", "1")] fn le(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { - run_binary_cell_case!(Le, signed, a, b, expected); + run_binary_cell_case_signed!(Le, signed, a, b, expected); } #[rstest] diff --git a/crates/arbolta/src/cell/simlib/bool_ops.rs b/crates/arbolta/src/cell/simlib/bool_ops.rs index 2c1957f..6810738 100644 --- a/crates/arbolta/src/cell/simlib/bool_ops.rs +++ b/crates/arbolta/src/cell/simlib/bool_ops.rs @@ -2,11 +2,10 @@ use std::ops::{BitAnd, BitOr, BitXor}; use super::*; use crate::{bit::BitVec, signal::Signals}; -use bincode::{Decode, Encode}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct Not { signed: bool, a_nets: Box<[usize]>, @@ -35,7 +34,7 @@ define_arithmetic_cell!(ProcXor, bitxor); #[cfg(test)] mod tests { use super::*; - use crate::cell::test_macros::*; + use crate::cell::test_helpers::*; use rstest::rstest; #[rstest] @@ -65,7 +64,7 @@ mod tests { #[case(false, "1111", "1111", "1111")] #[case(false, "0000", "1111", "0000")] fn and(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { - run_binary_cell_case!(ProcAnd, signed, a, b, expected); + run_binary_cell_case_signed!(ProcAnd, signed, a, b, expected); } #[rstest] diff --git a/crates/arbolta/src/cell/simlib/compare_ops.rs b/crates/arbolta/src/cell/simlib/compare_ops.rs index d503da0..67fe431 100644 --- a/crates/arbolta/src/cell/simlib/compare_ops.rs +++ b/crates/arbolta/src/cell/simlib/compare_ops.rs @@ -1,6 +1,5 @@ use super::*; use crate::{bit::BitVec, signal::Signals}; -use bincode::{Decode, Encode}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; diff --git a/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs b/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs index adc6a8e..ff7d785 100644 --- a/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs +++ b/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs @@ -1,6 +1,5 @@ use super::CellFn; use crate::{bit::Bit, signal::Signals}; -use bincode::{Decode, Encode}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; @@ -12,7 +11,7 @@ macro_rules! reduce_nets { }; } -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Constructor)] +#[derive(Debug, Clone, Serialize, Deserialize, Constructor)] pub struct ReduceAnd { a_nets: Box<[usize]>, y_nets: Box<[usize]>, @@ -30,10 +29,10 @@ impl CellFn for ReduceAnd { fn reset(&mut self) {} } -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Constructor)] +#[derive(Debug, Clone, Serialize, Deserialize, Constructor)] pub struct ReduceOr { - a_nets: Box<[usize]>, - y_nets: Box<[usize]>, + pub a_nets: Box<[usize]>, + pub y_nets: Box<[usize]>, } impl CellFn for ReduceOr { @@ -48,11 +47,11 @@ impl CellFn for ReduceOr { fn reset(&mut self) {} } -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Constructor)] +#[derive(Debug, Clone, Serialize, Deserialize, Constructor)] pub struct LogicAnd { - a_nets: Box<[usize]>, - b_nets: Box<[usize]>, - y_nets: Box<[usize]>, + pub a_nets: Box<[usize]>, + pub b_nets: Box<[usize]>, + pub y_nets: Box<[usize]>, } impl CellFn for LogicAnd { @@ -66,10 +65,10 @@ impl CellFn for LogicAnd { fn reset(&mut self) {} } -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Constructor)] +#[derive(Debug, Clone, Serialize, Deserialize, Constructor)] pub struct LogicNot { - a_nets: Box<[usize]>, - y_nets: Box<[usize]>, + pub a_nets: Box<[usize]>, + pub y_nets: Box<[usize]>, } impl CellFn for LogicNot { @@ -82,7 +81,7 @@ impl CellFn for LogicNot { fn reset(&mut self) {} } -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct LogicOr { a_nets: Box<[usize]>, b_nets: Box<[usize]>, @@ -104,7 +103,7 @@ impl CellFn for LogicOr { mod tests { use super::*; use crate::bit::BitVec; - use crate::cell::test_macros::*; + use crate::cell::test_helpers::*; use rstest::rstest; #[rstest] @@ -124,13 +123,29 @@ mod tests { } #[rstest] - fn logic_and() { - println!("TODO") + #[case("0", "0", "0")] + #[case("0", "1", "0")] + #[case("1", "0", "0")] + #[case("1", "1", "1")] + #[case("00000000", "0000000", "0000")] + #[case("10000000", "0000001", "0001")] + #[case("1", "0000001", "01")] + #[case("1111111", "01", "01")] + #[case("1010100", "0000", "00000")] + fn logic_and(#[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { + run_binary_cell_case!(LogicAnd, a, b, expected); } #[rstest] - fn logic_not() { - println!("TODO") + #[case("0", "1")] + #[case("1", "0")] + #[case("00000000", "0000001")] + #[case("10000000", "0000000")] + #[case("1", "0000000")] + #[case("1111111", "00")] + #[case("1010100", "00")] + fn logic_not(#[case] a: BitVec, #[case] expected: BitVec) { + run_unary_cell_case!(LogicNot, a, expected); } #[rstest] diff --git a/crates/arbolta/src/cell/simlib/mod.rs b/crates/arbolta/src/cell/simlib/mod.rs index 6173328..dd08e61 100644 --- a/crates/arbolta/src/cell/simlib/mod.rs +++ b/crates/arbolta/src/cell/simlib/mod.rs @@ -42,23 +42,27 @@ fn copy_nets(signals: &mut Signals, src_nets: &[usize], dst_nets: &[usize]) { } #[inline(always)] -fn copy_bits(signals: &mut Signals, dst_nets: &[usize], bits: impl IntoIterator) { +fn copy_bits<'a>( + signals: &mut Signals, + dst_nets: &[usize], + bits: impl IntoIterator, +) { dst_nets .iter() .zip(bits) - .for_each(|(&n, b)| signals.set_net(n, b)); + .for_each(|(&n, b)| signals.set_net(n, *b)); } #[macro_export] macro_rules! define_arithmetic_cell { // Takes `b` by value ($name:ident, $op:ident) => { - #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] + #[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct $name { - signed: bool, - a_nets: Box<[usize]>, - b_nets: Box<[usize]>, - y_nets: Box<[usize]>, + pub signed: bool, + pub a_nets: Box<[usize]>, + pub b_nets: Box<[usize]>, + pub y_nets: Box<[usize]>, } impl CellFn for $name { @@ -87,7 +91,7 @@ macro_rules! define_arithmetic_cell { } }; - copy_bits(signals, &self.y_nets, y); + copy_bits(signals, &self.y_nets, &y); } fn reset(&mut self) {} @@ -95,7 +99,7 @@ macro_rules! define_arithmetic_cell { }; // Takes `b` by reference ($name:ident, & $op:ident) => { - #[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] + #[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct $name { signed: bool, a_nets: Box<[usize]>, @@ -129,7 +133,7 @@ macro_rules! define_arithmetic_cell { } }; - copy_bits(signals, &self.y_nets, y); + copy_bits(signals, &self.y_nets, &y); } fn reset(&mut self) {} @@ -151,8 +155,8 @@ pub use various::*; // TODO: clean up temp functions fn make_not( - connections: &BTreeMap>, - parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, ) -> Cell { Not::new( parameters["A_SIGNED"] != 0, @@ -163,8 +167,8 @@ fn make_not( } fn make_pos( - connections: &BTreeMap>, - parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, ) -> Cell { Pos::new( parameters["A_SIGNED"] != 0, @@ -174,8 +178,8 @@ fn make_pos( .into() } fn make_neg( - connections: &BTreeMap>, - parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, ) -> Cell { Neg::new( parameters["A_SIGNED"] != 0, @@ -186,8 +190,8 @@ fn make_neg( } fn make_add( - connections: &BTreeMap>, - parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, ) -> Cell { Add::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), @@ -199,8 +203,8 @@ fn make_add( } fn make_sub( - connections: &BTreeMap>, - parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, ) -> Cell { Sub::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), @@ -212,8 +216,8 @@ fn make_sub( } fn make_mul( - connections: &BTreeMap>, - parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, ) -> Cell { Mul::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), @@ -225,8 +229,8 @@ fn make_mul( } fn make_div( - connections: &BTreeMap>, - parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, ) -> Cell { Div::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), @@ -238,8 +242,8 @@ fn make_div( } fn make_mod( - connections: &BTreeMap>, - parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, ) -> Cell { Modulus::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), @@ -250,10 +254,7 @@ fn make_mod( .into() } -fn make_le( - connections: &BTreeMap>, - parameters: &BTreeMap, -) -> Cell { +fn make_le(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>) -> Cell { Le::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), connections["A"].clone(), @@ -263,10 +264,7 @@ fn make_le( .into() } -fn make_ge( - connections: &BTreeMap>, - parameters: &BTreeMap, -) -> Cell { +fn make_ge(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>) -> Cell { Ge::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), connections["A"].clone(), @@ -276,10 +274,7 @@ fn make_ge( .into() } -fn make_gt( - connections: &BTreeMap>, - parameters: &BTreeMap, -) -> Cell { +fn make_gt(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>) -> Cell { Gt::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), connections["A"].clone(), @@ -290,8 +285,8 @@ fn make_gt( } fn make_shl( - connections: &BTreeMap>, - parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, ) -> Cell { Shl::new( parameters["A_SIGNED"] != 0, @@ -303,8 +298,8 @@ fn make_shl( } fn make_shr( - connections: &BTreeMap>, - parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, ) -> Cell { Shr::new( parameters["A_SIGNED"] != 0, @@ -316,8 +311,8 @@ fn make_shr( } fn make_dff( - connections: &BTreeMap>, - parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, ) -> Cell { Reg::new( (parameters["CLK_POLARITY"] != 0).into(), @@ -329,8 +324,8 @@ fn make_dff( } fn make_aldff( - connections: &BTreeMap>, - parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, ) -> Cell { ALDff::new( (parameters["CLK_POLARITY"] != 0).into(), @@ -345,8 +340,8 @@ fn make_aldff( } fn make_mux( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { Mux::new( connections["S"][0], @@ -358,8 +353,8 @@ fn make_mux( } fn make_bmux( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { BMux::new( connections["S"].clone(), @@ -368,9 +363,10 @@ fn make_bmux( ) .into() } + fn make_pmux( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { PMux::new( connections["S"].clone(), @@ -382,8 +378,8 @@ fn make_pmux( } fn make_logic_and( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { LogicAnd::new( connections["A"].clone(), @@ -394,29 +390,29 @@ fn make_logic_and( } fn make_logic_not( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { LogicNot::new(connections["A"].clone(), connections["Y"].clone()).into() } fn make_reduce_or( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { ReduceOr::new(connections["A"].clone(), connections["Y"].clone()).into() } fn make_reduce_and( - connections: &BTreeMap>, - _parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, ) -> Cell { ReduceAnd::new(connections["A"].clone(), connections["Y"].clone()).into() } fn make_and( - connections: &BTreeMap>, - parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, ) -> Cell { ProcAnd::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), @@ -427,10 +423,7 @@ fn make_and( .into() } -fn make_or( - connections: &BTreeMap>, - parameters: &BTreeMap, -) -> Cell { +fn make_or(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>) -> Cell { ProcOr::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), connections["A"].clone(), @@ -441,8 +434,8 @@ fn make_or( } fn make_xor( - connections: &BTreeMap>, - parameters: &BTreeMap, + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, ) -> Cell { ProcXor::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), @@ -453,10 +446,7 @@ fn make_xor( .into() } -fn make_eq( - connections: &BTreeMap>, - parameters: &BTreeMap, -) -> Cell { +fn make_eq(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>) -> Cell { Eq::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), connections["A"].clone(), @@ -466,10 +456,7 @@ fn make_eq( .into() } -fn make_ne( - connections: &BTreeMap>, - parameters: &BTreeMap, -) -> Cell { +fn make_ne(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>) -> Cell { Ne::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), connections["A"].clone(), diff --git a/crates/arbolta/src/cell/simlib/registers.rs b/crates/arbolta/src/cell/simlib/registers.rs index 5f8d89e..268e10b 100644 --- a/crates/arbolta/src/cell/simlib/registers.rs +++ b/crates/arbolta/src/cell/simlib/registers.rs @@ -1,26 +1,23 @@ use super::{CellFn, copy_nets}; use crate::{bit::Bit, signal::Signals}; -use bincode::{Decode, Encode}; use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, derive_new::new)] +#[derive(Debug, Clone, Serialize, Deserialize, derive_new::new)] pub struct Reg { - polarity: Bit, - clock_net: usize, - data_in_nets: Box<[usize]>, - data_out_nets: Box<[usize]>, + pub polarity: Bit, + pub clock_net: usize, + pub data_in_nets: Box<[usize]>, + pub data_out_nets: Box<[usize]>, #[new(default)] - last_clock: Bit, + pub last_clock: Bit, } impl CellFn for Reg { #[inline] fn eval(&mut self, signals: &mut Signals) { - // Check if clock active for any polarity - let clock = !(signals.get_net(self.clock_net) ^ self.polarity); + let clock = signals.get_net(self.clock_net); - // Rising edge - if clock == Bit::ONE && self.last_clock == Bit::ZERO { + if clock == self.polarity && self.last_clock == !self.polarity { copy_nets(signals, &self.data_in_nets, &self.data_out_nets); } @@ -28,11 +25,11 @@ impl CellFn for Reg { } fn reset(&mut self) { - self.last_clock = Bit::ZERO; + self.last_clock = !self.polarity; } } -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, derive_new::new)] +#[derive(Debug, Clone, Serialize, Deserialize, derive_new::new)] pub struct ALDff { clock_polarity: Bit, al_polarity: Bit, @@ -73,11 +70,68 @@ impl CellFn for ALDff { #[cfg(test)] mod tests { + use super::super::{bits_from_nets, copy_bits}; + use super::*; + use crate::{bit::BitVec, cell::test_helpers::*}; use rstest::rstest; #[rstest] - fn reg() { - println!("TODO") + #[case::zero(Bit::ONE, "000000000000")] // 0 + #[case::one(Bit::ONE, "000000000001")] // 1 + #[case(Bit::ONE, "1101111001101100")] // 56940 + #[case(Bit::ZERO, "000000000000")] // 0 + #[case(Bit::ZERO, "000000000001")] // 1 + #[case(Bit::ZERO, "1101111001101100")] // 56940 + fn reg(#[case] polarity: Bit, #[case] data_in: BitVec) { + let reg_size = data_in.len(); + let nets = allocate_nets(Some(1), &[&data_in, &data_in]); + + let clock_net: usize = 0; + let (data_in_nets, data_out_nets) = (&nets[0], &nets[1]); + + let mut signals = Signals::new(data_out_nets.last().unwrap() + 1); + let mut cell = Reg::new( + polarity, + clock_net, + data_in_nets.clone(), + data_out_nets.clone(), + ); + + signals.set_net(clock_net, !polarity); // Reset + cell.eval(&mut signals); + let mut actual = BitVec::from(bits_from_nets(&mut signals, data_out_nets)); + assert_eq!(actual.to_int::(), 0); + assert_eq!(cell.last_clock, !polarity); + + copy_bits(&mut signals, data_in_nets, &data_in); + cell.eval(&mut signals); + actual = BitVec::from(bits_from_nets(&mut signals, data_out_nets)); + assert_eq!(actual.to_int::(), 0); + assert_eq!(cell.last_clock, !polarity); + + signals.set_net(clock_net, polarity); // Rising edge + cell.eval(&mut signals); + actual = BitVec::from(bits_from_nets(&mut signals, data_out_nets)); + assert_eq!(actual, data_in); + assert_eq!(cell.last_clock, polarity); + + signals.set_net(clock_net, !polarity); // Falling edge + cell.eval(&mut signals); + actual = BitVec::from(bits_from_nets(&mut signals, data_out_nets)); + assert_eq!(actual, data_in); + assert_eq!(cell.last_clock, !polarity); + + copy_bits(&mut signals, data_in_nets, &vec![Bit::ZERO; reg_size]); // Zero + cell.eval(&mut signals); + actual = BitVec::from(bits_from_nets(&mut signals, data_out_nets)); + assert_eq!(actual, data_in); + assert_eq!(cell.last_clock, !polarity); + + signals.set_net(clock_net, polarity); // Rising edge + cell.eval(&mut signals); + actual = BitVec::from(bits_from_nets(&mut signals, data_out_nets)); + assert_eq!(actual, BitVec::from(vec![Bit::ZERO; reg_size])); + assert_eq!(cell.last_clock, polarity); } #[rstest] diff --git a/crates/arbolta/src/cell/simlib/shift_ops.rs b/crates/arbolta/src/cell/simlib/shift_ops.rs index aa1a590..dc9a461 100644 --- a/crates/arbolta/src/cell/simlib/shift_ops.rs +++ b/crates/arbolta/src/cell/simlib/shift_ops.rs @@ -1,11 +1,10 @@ use super::*; use crate::{bit::BitVec, signal::Signals}; -use bincode::{Decode, Encode}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; // define_arithmetic_cell!(Shl, <<); -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct Shl { signed: bool, a_nets: Box<[usize]>, @@ -44,13 +43,13 @@ impl CellFn for Shl { ) }; - copy_bits(signals, &self.y_nets, y); + copy_bits(signals, &self.y_nets, &y); } fn reset(&mut self) {} } -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct Shr { signed: bool, a_nets: Box<[usize]>, @@ -89,7 +88,7 @@ impl CellFn for Shr { ) }; - copy_bits(signals, &self.y_nets, y); + copy_bits(signals, &self.y_nets, &y); } fn reset(&mut self) {} diff --git a/crates/arbolta/src/cell/simlib/various.rs b/crates/arbolta/src/cell/simlib/various.rs index 7d38ec2..9034707 100644 --- a/crates/arbolta/src/cell/simlib/various.rs +++ b/crates/arbolta/src/cell/simlib/various.rs @@ -4,11 +4,10 @@ use crate::{ cell::simlib::bits_from_nets, signal::Signals, }; -use bincode::{Decode, Encode}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Constructor, Serialize, Deserialize, Encode, Decode)] +#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct Pos { signed: bool, a_nets: Box<[usize]>, @@ -20,32 +19,34 @@ impl CellFn for Pos { fn eval(&mut self, signals: &mut Signals) { // Passthrough with padding let a = bits_from_nets_pad(self.signed, signals, &self.a_nets, self.y_nets.len()); - copy_bits(signals, &self.y_nets, a); + copy_bits(signals, &self.y_nets, &a); } fn reset(&mut self) {} } -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Constructor)] +#[derive(Debug, Clone, Serialize, Deserialize, Constructor)] pub struct Mux { - select_net: usize, - a_nets: Box<[usize]>, - b_nets: Box<[usize]>, - y_nets: Box<[usize]>, + pub select_net: usize, + pub a_nets: Box<[usize]>, + pub b_nets: Box<[usize]>, + pub y_nets: Box<[usize]>, } impl CellFn for Mux { #[inline] fn eval(&mut self, signals: &mut Signals) { - let select = signals.get_net(self.select_net) == Bit::ONE; - let src_nets = if select { &self.b_nets } else { &self.a_nets }; + let src_nets = match signals.get_net(self.select_net) { + Bit::ZERO => &self.a_nets, + Bit::ONE => &self.b_nets, + }; copy_nets(signals, src_nets, &self.y_nets); } fn reset(&mut self) {} } -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Constructor)] +#[derive(Debug, Clone, Serialize, Deserialize, Constructor)] pub struct BMux { select_nets: Box<[usize]>, a_nets: Box<[usize]>, @@ -71,7 +72,7 @@ impl CellFn for BMux { } // "Selects between 'slices' of B where each slice corresponds to a single bit // of S. Outputs A when all bits of S are low." -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Constructor)] +#[derive(Debug, Clone, Serialize, Deserialize, Constructor)] pub struct PMux { select_nets: Box<[usize]>, a_nets: Box<[usize]>, // output when S all low @@ -88,7 +89,7 @@ impl CellFn for PMux { // let src_nets = &self.a_nets; if select == 0 { let a = bits_from_nets(signals, &self.a_nets); - copy_bits(signals, &self.y_nets, a); + copy_bits(signals, &self.y_nets, &a); } else { let start_net = (select.ilog2() as usize) * self.y_nets.len(); let end_net = start_net + self.y_nets.len(); @@ -106,6 +107,8 @@ impl CellFn for PMux { #[cfg(test)] mod tests { + use super::*; + use crate::cell::test_helpers::*; use rstest::rstest; #[rstest] @@ -114,8 +117,26 @@ mod tests { } #[rstest] - fn mux() { - println!("TODO"); + #[case(Bit::ZERO, "000", "001", "000")] + #[case(Bit::ONE, "000", "001", "001")] + #[case(Bit::ZERO, "111", "000", "111")] + #[case(Bit::ONE, "111", "000", "000")] + fn mux(#[case] select: Bit, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { + let nets = allocate_nets(Some(1), &[&a, &b, &expected]); + + let select_net: usize = 0; + let (a_nets, b_nets, y_nets) = (&nets[0], &nets[1], &nets[2]); + let mut signals = Signals::new(y_nets.last().unwrap() + 1); + let mut cell = Mux::new(select_net, a_nets.clone(), b_nets.clone(), y_nets.clone()); + + signals.set_net(select_net, select); + copy_bits(&mut signals, a_nets, &a); + copy_bits(&mut signals, b_nets, &b); + + cell.eval(&mut signals); + let actual = BitVec::from(bits_from_nets(&mut signals, y_nets)); + + assert_eq!(actual, expected); } #[rstest] diff --git a/crates/arbolta/src/cell/test_macros.rs b/crates/arbolta/src/cell/test_helpers.rs similarity index 71% rename from crates/arbolta/src/cell/test_macros.rs rename to crates/arbolta/src/cell/test_helpers.rs index 043fe34..db82e23 100644 --- a/crates/arbolta/src/cell/test_macros.rs +++ b/crates/arbolta/src/cell/test_helpers.rs @@ -1,3 +1,5 @@ +use crate::bit::BitVec; + #[macro_export] macro_rules! make_unary_wires { ($a:expr, $y:expr) => {{ @@ -90,6 +92,41 @@ pub(crate) use run_unary_cell_case; #[macro_export] macro_rules! run_binary_cell_case { + ($cell:ty, $a:expr, $b:expr, $expected:expr) => {{ + let (a_nets, b_nets, y_nets) = make_binary_wires!($a, $b, $expected); + let mut signals = Signals::new(y_nets.last().unwrap() + 1); + + // Set inputs + a_nets + .iter() + .enumerate() + .for_each(|(i, n)| signals.set_net(*n, $a.bits[i])); + + b_nets + .iter() + .enumerate() + .for_each(|(i, n)| signals.set_net(*n, $b.bits[i])); + + // Create cell and evaluate + let mut cell: $cell = <$cell>::new(a_nets.into(), b_nets.into(), y_nets.clone().into()); + cell.eval(&mut signals); + + // Get outputs + let actual: $crate::bit::BitVec = y_nets + .iter() + .map(|&i| signals.get_net(i)) + .collect::>() + .into(); + + assert_eq!(actual, $expected); + }}; +} + +#[allow(unused)] +pub(crate) use run_binary_cell_case; + +#[macro_export] +macro_rules! run_binary_cell_case_signed { ($cell:ty, $signed:expr, $a:expr, $b:expr, $expected:expr) => {{ let (a_nets, b_nets, y_nets) = make_binary_wires!($a, $b, $expected); let mut signals = Signals::new(y_nets.last().unwrap() + 1); @@ -122,4 +159,21 @@ macro_rules! run_binary_cell_case { } #[allow(unused)] -pub(crate) use run_binary_cell_case; +pub(crate) use run_binary_cell_case_signed; + +#[allow(unused)] +pub fn allocate_nets(offset: Option, data: &[&BitVec]) -> Vec> { + let mut nets: Vec> = vec![]; + let offset = offset.unwrap_or(0); + + for bits in data { + let start: usize = match nets.last() { + Some(last) => last[last.len() - 1] + 1, + None => offset, + }; + + nets.push((start..start + bits.len()).collect()); + } + + nets +} diff --git a/crates/arbolta/src/graph.rs b/crates/arbolta/src/graph.rs deleted file mode 100644 index 737478a..0000000 --- a/crates/arbolta/src/graph.rs +++ /dev/null @@ -1,452 +0,0 @@ -use crate::{ - cell::CELL_DISPATCH, - hardware_module::ModuleError, - port::{PortDirection, parse_bit}, -}; -use derive_more::{Constructor, Display}; -use petgraph::{dot::Dot, prelude::*, visit::Topo}; -use std::{ - collections::{BTreeMap, HashMap, HashSet}, - fmt, -}; -use yosys_netlist_json as yosys_json; - -#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Default, Constructor)] -pub struct TopoCellParent { - pub name: String, - pub cell_type: String, -} - -#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Default)] -pub struct TopoCell { - pub parents: Vec, - pub name: String, - pub cell_type: String, - // Only after getting global nets... - // BTreeMap is hashable! :) - pub connections: Option>>, - pub port_directions: Option>, - pub parameters: Option>, -} - -impl fmt::Display for TopoCell { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let parent_names = self - .parents - .iter() - .map(|p| p.name.as_str()) - .collect::>() - .join("."); - - let name = format!("{parent_names}.{}", self.name); - - write!(f, "{name}, {}", self.cell_type) - } -} - -pub fn collect_cells( - top_module: &str, - parents: &[TopoCellParent], - netlist: &yosys_json::Netlist, -) -> Result, ModuleError> { - let synth_module = netlist - .modules - .get(top_module) - .ok_or(ModuleError::TopModule(top_module.to_string()))?; - - let mut flattened_cells = vec![]; - for (cell_name, cell_info) in &synth_module.cells { - let cell_type = &cell_info.cell_type; - - // Submodule (and NOT a primitive cell) - if netlist.modules.contains_key(cell_type) && !CELL_DISPATCH.contains_key(cell_type.as_str()) { - let mut parents = parents.to_vec(); - parents.push(TopoCellParent { - name: cell_name.to_owned(), - cell_type: cell_type.to_owned(), - }); - - flattened_cells.append(&mut collect_cells(cell_type, &parents, netlist)?); - // Primitive cell - } else { - flattened_cells.push(TopoCell { - parents: parents.to_vec(), - name: cell_name.to_owned(), - cell_type: cell_type.to_owned(), - ..Default::default() - }); - } - } - - Ok(flattened_cells) -} - -pub type TopoHierarchy = HashMap>; - -pub fn get_cell_hierarchy(cells: &[TopoCell]) -> TopoHierarchy { - let mut hierarchy = TopoHierarchy::new(); - cells.iter().map(|c| &c.parents).for_each(|parents| { - // For each topo cell's parents - (0..parents.len() - 1).for_each(|i| { - hierarchy - .entry(parents[i].clone()) // Previous parent - .or_default() - .insert(parents[i + 1].clone()); // Current parent - }); - }); - - hierarchy -} - -pub type TopoNetMap = HashMap>; - -pub fn collect_nets( - parent: &TopoCellParent, - hierarchy: &TopoHierarchy, - netlist: &yosys_json::Netlist, - global_nets: &mut TopoNetMap, - global_net_max: &mut usize, -) -> Result<(), ModuleError> { - let synth_module = netlist - .modules - .get(&parent.cell_type) - .ok_or(ModuleError::TopModule(parent.cell_type.to_string()))?; - - let nets = synth_module - .netnames - .values() - .flat_map(|net_info| &net_info.bits) - .map(parse_bit) - .collect::, _>>()?; - - if !global_nets.contains_key(parent) { - global_nets.insert(parent.clone(), HashMap::from([(0, 0), (1, 1)])); - - nets.iter().for_each(|&n| { - global_nets.get_mut(parent).unwrap().insert(n, n); - *global_net_max = std::cmp::max(*global_net_max, n); - }); - } - - // Add rest of nets - for n in &nets { - if !global_nets[parent].contains_key(n) { - *global_net_max += 1; - global_nets - .get_mut(parent) - .unwrap() - .insert(*n, *global_net_max); - } - } - - // Add children - if let Some(children) = hierarchy.get(parent) { - for child in children { - global_nets.insert(child.clone(), HashMap::from([(0, 0), (1, 1)])); - let synth_cell = &synth_module.cells[&child.name.to_string()]; - let child_module = &netlist.modules[&child.cell_type.to_string()]; - - for (port_name, port_info) in &child_module.ports { - let conn_bits = &synth_cell.connections[port_name]; - for (net_bit, conn_bit) in port_info.bits.iter().zip(conn_bits) { - let net = parse_bit(net_bit)?; - // Translate connection - let conn = global_nets[parent][&parse_bit(conn_bit)?]; - - global_nets.get_mut(child).unwrap().insert(net, conn); - } - } - - collect_nets(child, hierarchy, netlist, global_nets, global_net_max)?; - } - } - - Ok(()) -} - -pub fn update_cells( - cells: &mut [TopoCell], - global_nets: &TopoNetMap, - netlist: &yosys_json::Netlist, -) -> Result<(), ModuleError> { - for cell in cells.iter_mut() { - // TODO: error handling - let parent = cell.parents.last().unwrap(); - let synth_cell = &netlist.modules[&parent.cell_type.to_string()].cells[&cell.name.to_string()]; - - let mut connections = BTreeMap::new(); - let mut port_directions = BTreeMap::new(); - for (port_name, bits) in &synth_cell.connections { - let direction = PortDirection::try_from(&synth_cell.port_directions[port_name])?; - let mut nets: Vec = bits.iter().map(parse_bit).collect::, _>>()?; - // Actual mapping - nets = nets.iter().map(|n| global_nets[parent][n]).collect(); - - port_directions.insert(port_name.to_string(), direction); - connections.insert(port_name.to_string(), nets.into_boxed_slice()); - } - - // Add parameters - let mut parameters = BTreeMap::new(); - for (param_name, param) in &synth_cell.parameters { - // TODO: Error handling - if let Some(param) = param.to_number() { - parameters.insert(param_name.to_string(), param); - } else { - // TODO: Handle later - println!("Ignoring parameter `{param_name}={param:?}`"); - } - } - - cell.port_directions = Some(port_directions); - cell.connections = Some(connections); - cell.parameters = Some(parameters); - } - - Ok(()) -} - -// #[derive(Debug, Clone, Eq, PartialEq, Hash, Display)] -// pub enum TopoNode { -// Cell(TopoCell), -// #[display("{name}[{offset}]")] -// Port { -// name: String, -// offset: usize, -// }, // Net(usize), -// } -// TODO: Change net node or graph edge to some enum/struct that has netname... -// enum for multi bit port -// enum::single net(name, b) -// enum::multi net(name, index, b) merge later... -// pub type NetlistGraph = DiGraph; - -/* -pub fn build_graph( - top_module: &TopoCellParent, - cells: &[TopoCell], - global_nets: &TopoNetMap, - netlist: &yosys_json::Netlist, -) -> Result { - let synth_module = netlist - .modules - .get(&top_module.cell_type.to_string()) - .ok_or(ModuleError::TopModule(top_module.cell_type.to_string()))?; - - let mut cell_nodes = HashMap::::new(); - let mut net_nodes = HashMap::::new(); - let mut graph = NetlistGraph::new(); - - // All top module port inputs driven by "input" cell for ordering - let input_cell = TopoCell { - parents: vec![top_module.clone()], - name: "input".to_string(), - cell_type: "input".to_string(), - ..Default::default() - }; - - // Create all cell nodes (ensure input cell is first) - std::slice::from_ref(&input_cell) - .iter() - .chain(cells) - .for_each(|c| { - cell_nodes.insert(c.clone(), graph.add_node(TopoNode::Cell(c.clone()))); - }); - - // Create all net nodes - for net in global_nets.values().flat_map(|c| c.values()) { - // Avoid duplicates - if !net_nodes.contains_key(net) { - net_nodes.insert(*net, graph.add_node(TopoNode::Net(*net))); - } - } - - // Connect input cell to top module input nets - for port_info in synth_module.ports.values() { - if port_info.direction == yosys_json::PortDirection::Input { - for net in port_info - .bits - .iter() - .map(parse_bit) - .collect::, _>>()? - { - // Avoid duplicates - if !graph.contains_edge(cell_nodes[&input_cell], net_nodes[&net]) { - graph.add_edge(cell_nodes[&input_cell], net_nodes[&net], net); - } - } - } - } - - // Connect all other cells/nets - for cell in cells { - let cell_node = &cell_nodes[cell]; - - // TODO: Error handling - for (port_name, nets) in cell.connections.as_ref().unwrap() { - // TODO: Error handling - let direction = &cell.port_directions.as_ref().unwrap()[port_name]; - - for net in nets { - let net_node = &net_nodes[net]; - - match direction { - PortDirection::Input if !graph.contains_edge(*net_node, *cell_node) => { - graph.add_edge(*net_node, *cell_node, *net); - } - PortDirection::Output if !graph.contains_edge(*cell_node, *net_node) => { - graph.add_edge(*cell_node, *net_node, *net); - } - _ => continue, // Edge exists, do nothing - } - } - } - } - - Ok(graph) -} -*/ - -// TODO: rename -fn build_port_net_map(cell: &TopoCell) -> HashMap { - let mut nets = HashMap::new(); - for (port_name, conns) in cell.connections.as_ref().unwrap() { - for (i, &n) in conns.iter().enumerate() { - nets.insert(n, TopoPort::new(port_name.clone(), i)); - } - } - - nets -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash, Display, Constructor)] -#[display("{port}[{offset}]")] -pub struct TopoPort { - port: String, - offset: usize, -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash, Display, Constructor)] -#[display("({net}, {src}, {dst})")] -pub struct TopoEdge { - net: usize, - src: TopoPort, - dst: TopoPort, -} - -pub type NetlistGraph<'a> = DiGraph<&'a TopoCell, TopoEdge>; - -// TODO: Merge with get_topo_cell_order... -// pub fn build_graph(cells: &[TopoCell]) -> Result { -pub fn build_graph(cells: &[TopoCell]) -> Result { - let mut graph = NetlistGraph::new(); - let mut cell_nodes = HashMap::<&TopoCell, NodeIndex>::new(); - - let mut bit_drivers = HashMap::>::new(); - let mut bit_users = HashMap::>::new(); - - for cell in cells { - // Check if primitive cell - if !CELL_DISPATCH.contains_key(cell.cell_type.as_str()) { - continue; - } - - for (port_name, nets) in cell.connections.as_ref().unwrap() { - // TODO: Potentially filter here - if let Some(direction) = cell.port_directions.as_ref().unwrap().get(port_name) { - for &n in nets { - match direction { - // TODO: Do we need to clone? - PortDirection::Input => { - bit_users.entry(n).or_default().insert(cell); - } - PortDirection::Output => { - bit_drivers.entry(n).or_default().insert(cell); - } - } - } - } - } - cell_nodes.insert(cell, graph.add_node(cell)); - } - - for (net, user_cells) in &bit_users { - if let Some(drivers) = bit_drivers.get(net) { - for driver_cell in drivers { - let driver_nets = build_port_net_map(driver_cell); - - for user_cell in user_cells { - let user_nets = build_port_net_map(user_cell); - - let driver_node = &cell_nodes[driver_cell]; - let user_node = &cell_nodes[user_cell]; - let edge = TopoEdge::new(*net, driver_nets[net].to_owned(), user_nets[net].to_owned()); - - graph.add_edge(*driver_node, *user_node, edge); - } - } - } - } - - Ok(Dot::new(&graph).to_string()) - // Ok(graph) -} - -// Based on https://github.com/YosysHQ/yosys/blob/main/passes/cmds/torder.cc -pub fn get_topo_cell_order(cells: &[TopoCell]) -> Vec<&TopoCell> { - let mut graph: DiGraph<&TopoCell, usize> = DiGraph::new(); - let mut cell_nodes = HashMap::<&TopoCell, NodeIndex>::new(); - - let mut bit_drivers = HashMap::>::new(); - let mut bit_users = HashMap::>::new(); - - for cell in cells { - // Check if primitive cell - if !CELL_DISPATCH.contains_key(cell.cell_type.as_str()) { - continue; - } - - for (port_name, nets) in cell.connections.as_ref().unwrap() { - if ["Q", "CTRL_OUT", "RD_DATA"].contains(&port_name.as_str()) { - // TODO: Add case for memrd - continue; - } - - if let Some(direction) = cell.port_directions.as_ref().unwrap().get(port_name) { - for &n in nets { - match direction { - PortDirection::Input => { - bit_users.entry(n).or_default().insert(cell); - } - PortDirection::Output => { - bit_drivers.entry(n).or_default().insert(cell); - } - } - } - } - } - cell_nodes.insert(cell, graph.add_node(cell)); - } - - for (net, user_cells) in &bit_users { - if let Some(drivers) = bit_drivers.get(net) { - for driver_cell in drivers { - for user_cell in user_cells { - let driver_node = &cell_nodes[driver_cell]; - let user_node = &cell_nodes[user_cell]; - - graph.add_edge(*driver_node, *user_node, *net); - } - } - } - } - - let mut topo_search = Topo::new(&graph); - let mut found = vec![]; - while let Some(visited) = topo_search.next(&graph) { - let cell = graph.node_weight(visited).unwrap(); - found.push(*cell); - } - - found -} diff --git a/crates/arbolta/src/hardware_module.rs b/crates/arbolta/src/hardware_module.rs index 471d722..da6935f 100644 --- a/crates/arbolta/src/hardware_module.rs +++ b/crates/arbolta/src/hardware_module.rs @@ -3,57 +3,37 @@ use crate::{ bit::{Bit, BitVec}, - cell::{CELL_DISPATCH, Cell, CellError, CellFn, create_cell}, - graph::*, - port::PortError, - port::{Port, PortDirection, parse_bit}, + cell::{Cell, CellError, CellFn}, + netlist_wrapper::NetlistWrapper, + port::{Port, PortDirection, PortError}, signal::Signals, - yosys::Netlist, + yosys::{Netlist, TopoOrder}, }; -use bincode::{Decode, Encode}; use serde::{Deserialize, Serialize}; -use std::{ - collections::{HashMap, HashSet}, - fmt::Debug, -}; +use std::{collections::HashMap, fmt::Debug}; use thiserror::Error; -#[derive(Clone, Debug, Deserialize, Serialize, Decode, Encode)] -pub enum CellType { - Submodule(String), - Scopeinfo(String), // TODO: Convert this to submodule - Primitive(String), -} - -#[derive(Default, Clone, Debug, Deserialize, Serialize, Decode, Encode)] -pub struct SubModule { - pub ports: HashMap, - pub nets: HashMap>, - pub cells: HashMap, -} - -// Names -pub type SubmoduleMap = HashMap, SubModule>; - -#[derive(Default, Clone, Debug, Deserialize, Serialize, Decode, Encode)] +#[derive(Default, Clone, Debug, Deserialize, Serialize)] //Decode, Encode pub struct HardwareModule { - pub top_module: String, + pub netlist: NetlistWrapper, pub signals: Signals, - pub cell_info: HashMap, pub cells: Box<[Cell]>, - pub submodules: SubmoduleMap, - pub graph: String, // TODO: Don't store this as a dot string... - // pub topo_cells: Vec, + pub ports: HashMap, pub clock_net: Option<(usize, Bit)>, // (net, polarity) pub reset_net: Option<(usize, Bit)>, // (net, polarity) } +// TODO: Refactor these #[derive(Debug, Error)] pub enum ModuleError { #[error("Module is not flattened or empty")] UnFlattened, - #[error("Missing top module `{0}`")] - TopModule(String), + #[error("Missing top module")] + TopModule, + #[error("Cell `{0}` doesn't exist")] + MissingCell(String), + #[error("No modules given")] + MissingModule, #[error("Module `{0}` missing from topological order")] TopoOrder(String), #[error("Cell error `{0}`")] @@ -81,129 +61,55 @@ pub enum ToggleCount { Total, } -fn get_submodules( - parents: &[TopoCellParent], - hierarchy: &TopoHierarchy, - global_nets: &TopoNetMap, - netlist: &Netlist, -) -> Result { - let name = parents - .iter() - .map(|p| p.name.clone()) - .collect::>(); - let top_parent = parents.last().unwrap(); // Assume top is last parent... - - let mut submodules = SubmoduleMap::new(); - - // Create new submodule - let mut submodule = SubModule::default(); - if let Some(synth_module) = netlist.modules.get(&top_parent.cell_type.to_string()) { - for (cell_name, cell_info) in &synth_module.cells { - let cell_type = cell_info.cell_type.to_owned(); - - let cell_type = if CELL_DISPATCH.contains_key(&cell_type.as_str()) { - CellType::Primitive(cell_type) - } else if cell_type == "$scopeinfo" { - CellType::Scopeinfo(cell_type) - } else { - CellType::Submodule(cell_type) - }; - - submodule.cells.insert(cell_name.to_owned(), cell_type); - } - - // Add nets - for (net_name, net_info) in &synth_module.netnames { - let nets: Vec = net_info - .bits - .iter() - .map(parse_bit) - .collect::>()?; - // Translate - let nets: Vec = nets.iter().map(|n| global_nets[top_parent][n]).collect(); - - // Add ports - if let Some(port_info) = synth_module.ports.get(net_name) { - let port = Port { - direction: PortDirection::try_from(&port_info.direction)?, - shape: [1, nets.len()], - }; - - submodule.ports.insert(net_name.to_owned(), port); - } - - submodule - .nets - .insert(net_name.to_owned(), nets.into_boxed_slice()); - } - } - - submodules.insert(name.into(), submodule); - - if let Some(entry) = hierarchy.get(top_parent) { - for child in entry { - let mut parents = parents.to_vec(); - parents.push(child.clone()); - - submodules.extend(get_submodules(&parents, hierarchy, global_nets, netlist)?); - } - } - - Ok(submodules) -} - impl HardwareModule { - pub fn new(top_module: &str, netlist: &Netlist) -> Result { - let top_module_parent = TopoCellParent::new(top_module.to_string(), top_module.to_string()); - let mut topo_cells = collect_cells( - top_module, - std::slice::from_ref(&top_module_parent), - netlist, - )?; - let hierarchy = get_cell_hierarchy(&topo_cells); - - let mut global_nets = TopoNetMap::new(); - let mut global_net_max = 0; - collect_nets( - &top_module_parent, - &hierarchy, - netlist, - &mut global_nets, - &mut global_net_max, - )?; + pub fn new( + netlist: Netlist, + top_module: Option<&str>, + torder: TopoOrder, + ) -> Result { + let netlist = NetlistWrapper::new(netlist, top_module, torder)?; - update_cells(topo_cells.as_mut_slice(), &global_nets, netlist)?; + let cells = netlist.build_cells()?; - // let graph = build_graph(&top_module_parent, &topo_cells, &global_nets, netlist)?; - let graph = build_graph(&topo_cells)?; - let topo_order = get_topo_cell_order(&topo_cells); - - let submodules = get_submodules(&[top_module_parent], &hierarchy, &global_nets, netlist)?; - let cells: Vec = topo_order - .into_iter() - .map(create_cell) - .collect::, _>>()?; + let global_net_max: usize = netlist + .nets + .values() + .flatten() + .fold(0, |max, &x| std::cmp::max(max, x)); let mut signals = Signals::new(global_net_max + 1); signals.set_constant(0, Bit::ZERO); signals.set_constant(1, Bit::ONE); + // type doesn't matter here... + let ports = netlist.find_module_ports::<&str>(None)?; + Ok(Self { - top_module: top_module.to_owned(), + netlist, signals, - cell_info: HashMap::new(), - cells: cells.into_boxed_slice(), - submodules, - graph, - clock_net: None, - reset_net: None, + cells: cells.into(), + ports, + ..Default::default() }) } - pub fn get_nets(&self, net_name: &str, parents: Option<&[String]>) -> Option> { - let parents = parents.unwrap_or(std::slice::from_ref(&self.top_module)); - let submodule = &self.submodules[parents]; - submodule.nets.get(net_name).cloned() + // TODO: Fix + pub fn get_net_bits(&self, name: &str) -> Result { + let Some(nets) = self.get_net(name) else { + return Err(ModuleError::MissingPort(name.to_string())); + }; + + let bits: BitVec = nets + .iter() + .map(|idx| self.signals.get_net(*idx)) + .collect::>() + .into(); + + Ok(bits) + } + + pub fn get_net(&self, name: &str) -> Option<&[usize]> { + self.netlist.names_to_nets.get(name).map(|v| &**v) } pub fn stick_signal(&mut self, net: usize, value: Bit) -> Result<(), ModuleError> { @@ -296,48 +202,28 @@ impl HardwareModule { } pub fn set_port_shape(&mut self, name: &str, shape: &[usize; 2]) -> Result<(), ModuleError> { - let submodule = self - .submodules - .get_mut(std::slice::from_ref(&self.top_module)) - .unwrap(); - - match submodule.ports.get_mut(name) { + match self.ports.get_mut(name) { Some(port) => Ok(port.set_shape(shape)?), None => Err(ModuleError::MissingPort(name.to_string())), } } pub fn get_port_shape(&self, name: &str) -> Result<[usize; 2], ModuleError> { - let submodule = self - .submodules - .get(std::slice::from_ref(&self.top_module)) - .unwrap(); - - match submodule.ports.get(name) { + match self.ports.get(name) { Some(port) => Ok(port.get_shape()), None => Err(ModuleError::MissingPort(name.to_string())), } } pub fn get_port_direction(&self, name: &str) -> Result { - let submodule = self - .submodules - .get(std::slice::from_ref(&self.top_module)) - .unwrap(); - - match submodule.ports.get(name) { + match self.ports.get(name) { Some(port) => Ok(port.direction.clone()), None => Err(ModuleError::MissingPort(name.to_string())), } } pub fn get_port(&self, name: &str) -> Result { - let submodule = self - .submodules - .get(std::slice::from_ref(&self.top_module)) - .unwrap(); - - let (Some(port), Some(nets)) = (submodule.ports.get(name), submodule.nets.get(name)) else { + let (Some(port), Some(nets)) = (self.ports.get(name), self.get_net(name)) else { return Err(ModuleError::MissingPort(name.to_string())); }; @@ -356,16 +242,12 @@ impl HardwareModule { I: IntoIterator, B: Into, { - let submodule = self - .submodules - .get_mut(std::slice::from_ref(&self.top_module)) - .unwrap(); - - let (Some(_port), Some(nets)) = (submodule.ports.get(name), submodule.nets.get(name)) else { + let (Some(_port), Some(nets)) = (self.ports.get(name), self.get_net(name)) else { return Err(ModuleError::MissingPort(name.to_string())); }; nets + .to_owned() .iter() .zip(vals.into_iter().map(Into::into)) .for_each(|(net, b)| self.signals.set_net(*net, b)); @@ -373,118 +255,46 @@ impl HardwareModule { Ok(()) } - pub fn get_all_signal_nets(&self) -> HashMap> { - let mut all_nets = HashMap::new(); - for (parents, submodule) in &self.submodules { - let parent_name = parents.join("."); + // Returns ALL nets in design + pub fn get_submodule_nets(&self) -> HashMap<&Vec, HashMap<&str, &[usize]>> { + let mut global_module_nets = HashMap::new(); + for (net_id, nets) in &self.netlist.nets { + let module_nets: &mut HashMap<&str, &[usize]> = + global_module_nets.entry(&net_id.parents).or_default(); - for (net_name, nets) in &submodule.nets { - let name = format!("{parent_name}.{net_name}"); - - all_nets.insert(name, nets.to_vec()); - } + module_nets.insert(&net_id.name, nets); } - all_nets + global_module_nets } - pub fn get_all_signal_values(&self) -> HashMap { - let mut all_nets = HashMap::new(); - for (parents, submodule) in &self.submodules { - let parent_name = parents.join("."); + pub fn get_submodule_net_values(&self) -> HashMap<&Vec, HashMap<&str, BitVec>> { + let global_submodule_nets = self.get_submodule_nets(); + let mut global_net_values = HashMap::new(); - for (net_name, nets) in &submodule.nets { - let name = format!("{parent_name}.{net_name}"); + for (parents, module_nets) in global_submodule_nets { + let submodule_net_values: &mut HashMap<&str, BitVec> = + global_net_values.entry(parents).or_default(); + for (net_name, nets) in module_nets { let bits: BitVec = nets .iter() .map(|idx| self.signals.get_net(*idx)) .collect::>() .into(); - all_nets.insert(name, bits); + submodule_net_values.insert(net_name, bits); } } - all_nets + global_net_values } - // TODO: Add tests for these - pub fn get_toggles(&self, category: ToggleCount) -> u64 { - let toggle_fn = match category { - ToggleCount::Falling => Signals::get_toggles_falling, - ToggleCount::Rising => Signals::get_toggles_rising, - ToggleCount::Total => Signals::get_toggles_total, - }; - - (0..self.signals.size).fold(0, |acc, i| acc + toggle_fn(&self.signals, i)) - } - - // Returns all child_nets - fn find_nets_helper( - &self, - parents: Option<&[String]>, - filter_inputs: Option, - global_nets: &mut HashMap, HashSet>, - ) -> Result, ModuleError> { - // First time calling, parse top module - let parents = parents.unwrap_or(std::slice::from_ref(&self.top_module)); - - let Some(submodule) = self.submodules.get(parents) else { - return Err(ModuleError::MissingSubmodule(parents.join("."))); - }; - - // Collect all nets in submodule - let mut submodule_nets = - HashSet::::from_iter(submodule.nets.values().flatten().copied()); - - // First time calling, don't filter input nets - if filter_inputs.unwrap_or(false) { - // Collect all input nets - let mut input_nets = HashSet::::new(); - for (port_name, port_info) in &submodule.ports { - if port_info.direction == PortDirection::Input - && let Some(nets) = submodule.nets.get(port_name) - { - input_nets.extend(nets); - } - } - - submodule_nets = submodule_nets.difference(&input_nets).copied().collect(); - } - - // Parse children, collect all nets - let mut total_nets = HashSet::::new(); - for (cell_name, cell_type) in &submodule.cells { - if let CellType::Submodule(_) = cell_type { - // Submodule child - let child_parents = [parents, &[cell_name.to_owned()]].concat(); - let child_nets = self.find_nets_helper(Some(&child_parents), Some(true), global_nets)?; - - total_nets.extend(child_nets); - } - } - // Filter out child nets - submodule_nets = submodule_nets.difference(&total_nets).copied().collect(); - - global_nets.insert(parents.to_vec(), submodule_nets.clone()); - total_nets.extend(submodule_nets); // Add current submodule nets to total - - Ok(total_nets) - } - - pub fn get_submodule_nets(&self) -> Result, HashSet>, ModuleError> { - let mut submodule_nets = HashMap::new(); - self.find_nets_helper(None, None, &mut submodule_nets)?; - Ok(submodule_nets) - } - - pub fn get_submodule_toggles( + pub fn get_submodule_toggles_by_net( &self, category: ToggleCount, - ) -> Result, HashMap>, ModuleError> { - let mut total_toggles = HashMap::new(); - let submodule_nets = self.get_submodule_nets()?; + ) -> HashMap<&Vec, HashMap<&str, u64>> { + let global_submodule_nets = self.get_submodule_nets(); let toggle_fn = match category { ToggleCount::Falling => Signals::get_toggles_falling, @@ -492,80 +302,32 @@ impl HardwareModule { ToggleCount::Total => Signals::get_toggles_total, }; - for (submodule_name, nets) in submodule_nets { - let toggles = nets - .into_iter() - .map(|n| (n, toggle_fn(&self.signals, n))) - .collect::>(); - - total_toggles.insert(submodule_name, HashMap::from_iter(toggles)); - } - - Ok(total_toggles) - } + let mut global_submodule_toggles = HashMap::new(); + for (parents, module_nets) in global_submodule_nets { + let submodule_toggles: &mut HashMap<&str, u64> = + global_submodule_toggles.entry(parents).or_default(); - pub fn get_submodule_net_map(&self) -> HashMap, HashMap>> { - HashMap::from_iter(self.submodules.iter().map(|(sub_name, sub)| { - ( - sub_name.to_vec(), - HashMap::from_iter( - sub - .nets - .iter() - .map(|(net_name, nets)| (net_name.to_owned(), nets.to_vec())), - ), - ) - })) - } + for (net_name, nets) in module_nets { + let toggles = nets + .iter() + .fold(0, |acc, &n| acc + toggle_fn(&self.signals, n)); - pub fn get_all_signal_nets_reverse(&self) -> HashMap> { - let signal_net_map = self.get_all_signal_nets(); - - let mut reverse_map = HashMap::>::new(); - for (net_name, nets) in &signal_net_map { - if nets.len() > 1 { - for (i, &n) in nets.iter().enumerate() { - let entry = reverse_map.entry(n).or_default(); - entry.push(format!("{net_name}[{i}]")); - } - } else { - let entry = reverse_map.entry(nets[0]).or_default(); - entry.push(net_name.to_owned()) + submodule_toggles.insert(net_name, toggles); } } - reverse_map + global_submodule_toggles } - // fn find_cells_helper( - // &self, - // parents: Option<&[String]>, - // global_cells: &mut HashMap, HashSet>, - // ) -> Result, ModuleError> { - // let parents = parents.unwrap_or(std::slice::from_ref(&self.top_module)); - - // let Some(submodule) = self.submodules.get(parents) else { - // return Err(ModuleError::MissingSubmodule(parents.join("."))); - // }; + pub fn get_submodule_toggles_total(&self, category: ToggleCount) -> HashMap<&Vec, u64> { + let submodule_toggles_by_net = self.get_submodule_toggles_by_net(category); + let mut submodule_toggles = HashMap::new(); - // // let submodule_cells = vec![] - // for (cell_name, cell_type) in &submodule.cells { - // if let CellType::Primitive(cell_type) = cell_type { - // todo!() - // } - // } - - // todo!() - // } - - // TODO: Fix - #[allow(unused)] - pub fn load(path: &str) -> anyhow::Result { - todo!() - } + for (parents, module_nets) in submodule_toggles_by_net { + let total_toggles = module_nets.values().sum(); + submodule_toggles.insert(parents, total_toggles); + } - #[allow(unused)] - pub fn save(&self, path: &str) -> anyhow::Result<()> { - todo!() + submodule_toggles } } diff --git a/crates/arbolta/src/lib.rs b/crates/arbolta/src/lib.rs index b3dd00c..504ff6c 100644 --- a/crates/arbolta/src/lib.rs +++ b/crates/arbolta/src/lib.rs @@ -4,7 +4,9 @@ pub mod bit; pub mod cell; pub mod graph; +pub mod graph3; pub mod hardware_module; +pub mod netlist_wrapper; pub mod port; pub mod signal; pub mod yosys; diff --git a/crates/arbolta/src/netlist_wrapper.rs b/crates/arbolta/src/netlist_wrapper.rs new file mode 100644 index 0000000..7359f8d --- /dev/null +++ b/crates/arbolta/src/netlist_wrapper.rs @@ -0,0 +1,315 @@ +use crate::{ + cell::{Cell, create_cell}, + hardware_module::ModuleError, + port::{Port, PortDirection, parse_bit}, + yosys, + yosys::{RTLID, TopoOrder}, +}; +use indexmap::{IndexSet, indexmap}; +use petgraph::prelude::*; +use serde::{Deserialize, Serialize}; +use std::collections::{BTreeMap, HashMap, HashSet}; + +#[derive(Debug, Deserialize, Serialize, Default, Clone)] +pub struct NetlistWrapper { + pub top_module: String, + netlist: yosys::Netlist, + pub cells: Vec, // Should be in topological order + pub modules: HashSet>, + pub nets: HashMap>, + pub names_to_nets: HashMap>, +} + +impl NetlistWrapper { + pub fn new( + netlist: yosys::Netlist, + top_module: Option<&str>, + torder: TopoOrder, + ) -> Result { + let mut netlist = netlist; + + let top_module = top_module + .or(find_top_module_name(&netlist)) + .ok_or(ModuleError::TopModule)? + .to_string(); + let module = netlist.modules.get_mut(&top_module).unwrap(); + + let torder_cells = IndexSet::<&str>::from_iter( + torder + .get(top_module.as_str()) + .ok_or(ModuleError::TopModule)? + .clone(), + ); + // Rearrange cells in topological order, put $scopeinfo at end + module.cells.sort_by_key(|cell_name, _| { + torder_cells + .get_index_of(cell_name.as_str()) + .unwrap_or(usize::MAX) + }); + + let (cells, cell_modules) = parse_cells(module)?; + let nets = parse_nets(module)?; + let names_to_nets = HashMap::from_iter(nets.iter().map(|(id, n)| (id.to_string(), n.clone()))); + + // Add modules only seen in nets (no synthesized cells) + let modules = HashSet::from_iter( + cell_modules + .into_iter() + .chain(nets.keys().map(|id| id.parents.clone())), + ); + + Ok(Self { + top_module, + netlist, + cells, + modules, + nets, + names_to_nets, + }) + } + + pub fn find_module_ports>( + &self, + parents: Option<&[S]>, + ) -> Result, ModuleError> { + let parents: Vec<&str> = match parents { + Some(p) => p.iter().map(|s| s.as_ref()).collect(), + None => vec![self.top_module.as_ref()], + }; + + // TODO: better errors + let module = self + .netlist + .modules + .get(&parents.join(".")) + .ok_or(ModuleError::MissingModule)?; + + let mut ports = HashMap::new(); + for (port_name, port_info) in &module.ports { + ports.insert(port_name.clone(), Port::try_from(port_info)?); + } + + Ok(ports) + } + + // Only looks in TOP module + pub fn find_cell(&self, id: &RTLID) -> Option<&yosys::Cell> { + let module = &self.netlist.modules[&self.top_module]; + // TODO: make this more efficient + module.cells.get(&id.to_string()) + } + + fn build_cell(&self, cell: &RTLID) -> Result { + let synth_cell = self + .find_cell(cell) + .ok_or(ModuleError::MissingCell(cell.to_string()))?; + + let mut connections = BTreeMap::new(); + for (port_name, bits) in &synth_cell.connections { + let nets: Vec = bits.iter().map(parse_bit).collect::, _>>()?; + connections.insert(port_name.as_str(), nets.into_boxed_slice()); + } + + let mut parameters = BTreeMap::new(); + for (param_name, param) in &synth_cell.parameters { + // TODO: Error handling + if let Some(param) = param.to_number() { + parameters.insert(param_name.as_str(), param); + } else { + // TODO: Handle later + println!("Ignoring parameter `{param_name}={param:?}`"); + } + } + + Ok(create_cell( + &synth_cell.cell_type, + &connections, + ¶meters, + )?) + } + + pub fn build_cells(&self) -> Result, ModuleError> { + let cells = self + .cells + .iter() + .rev() + .map(|c| Self::build_cell(self, c)) + .collect::, _>>()?; + Ok(cells) + } + + // CLEAN + pub fn build_graph(&self) -> Result { + let mut graph = NetlistGraph::new(); + let mut cell_nodes = BTreeMap::<&RTLID, NodeIndex>::new(); + let mut bit_drivers = BTreeMap::>::new(); + let mut bit_users = BTreeMap::>::new(); + + let input_cell = RTLID::new(&[], &"$input"); + let output_cell = RTLID::new(&[], &"$output"); + let module = &self.netlist.modules[&self.top_module]; + for port_info in module.ports.values() { + if port_info.direction == yosys::PortDirection::Input { + let nets: Vec = port_info + .bits + .iter() + .map(parse_bit) + .collect::, _>>()?; + for n in nets { + bit_drivers.entry(n).or_default().insert(&input_cell); + } + } else if port_info.direction == yosys::PortDirection::Output { + let nets: Vec = port_info + .bits + .iter() + .map(parse_bit) + .collect::, _>>()?; + for n in nets { + bit_users.entry(n).or_default().insert(&output_cell); + } + } + } + cell_nodes.insert(&input_cell, graph.add_node(input_cell.clone())); + cell_nodes.insert(&output_cell, graph.add_node(output_cell.clone())); + + for cell_id in &self.cells { + let cell = self.find_cell(cell_id).unwrap(); + + for (port_name, bits) in &cell.connections { + if let Some(direction) = cell.port_directions.get(port_name) { + let direction = PortDirection::try_from(direction)?; + let nets: Vec = bits.iter().map(parse_bit).collect::, _>>()?; + + for n in nets { + match direction { + PortDirection::Input => { + bit_users.entry(n).or_default().insert(cell_id); + } + PortDirection::Output => { + bit_drivers.entry(n).or_default().insert(cell_id); + } + } + } + } + } + cell_nodes.insert(cell_id, graph.add_node(cell_id.clone())); + } + + for (net, user_cells) in &bit_users { + if let Some(drivers) = bit_drivers.get(net) { + for driver_cell in drivers { + let driver_node = &cell_nodes[driver_cell]; + + for user_cell in user_cells { + let user_node = &cell_nodes[user_cell]; + + graph.add_edge(*driver_node, *user_node, *net); + } + } + } + } + + Ok(graph) + } +} + +pub type NetlistGraph = DiGraph; + +/// Returns (cells, modules) +fn parse_cells(module: &mut yosys::Module) -> Result<(Vec, Vec>), ModuleError> { + let mut scope_cells = vec![]; + let mut primitive_cells = vec![]; + + // Separate cells into scope and primitive + module.cells.iter().for_each(|(cell_name, cell_info)| { + if cell_info.cell_type == "$scopeinfo" { + scope_cells.push((cell_name, cell_info)); + } else { + primitive_cells.push((cell_name, cell_info)); + } + }); + + let mut modules = vec![]; + for (scope_name, cell_info) in scope_cells { + if let Some(hdlname) = cell_info.attributes.get("hdlname") + && let Some(hdlname) = hdlname.to_string_if_string() + { + modules.push( + hdlname + .split(" ") + .map(|s| s.to_string()) + .collect::>(), + ); + } else { + modules.push(vec![scope_name.clone()]) + } + } + + let mut cells = vec![]; + let mut new_cells = indexmap! {}; + for (cell_name, cell_info) in primitive_cells { + // TODO: Check if cell is primitive? + let id = if let Some(scopename) = cell_info.attributes.get("scopename") + && let Some(scopename) = scopename.to_string_if_string() + { + RTLID::new(&[scopename], &cell_name.as_str()) + } else { + RTLID::new(&[], cell_name) + }; + + new_cells.insert(id.to_string(), cell_info.clone()); + cells.push(id); + } + + module.cells = new_cells; + + Ok((cells, modules)) +} + +fn parse_nets(module: &mut yosys::Module) -> Result>, ModuleError> { + let mut all_nets = HashMap::new(); + + let mut new_nets = indexmap! {}; + for (net_name, net_info) in &module.netnames { + let id = if let Some(scopename) = net_info.attributes.get("scopename") + && let Some(scopename) = scopename.to_string_if_string() + { + RTLID::new(&[scopename], &net_name.as_str()) + } else if let Some(hdlname) = net_info.attributes.get("hdlname") + && let Some(hdlname) = hdlname.to_string_if_string() + { + let hdlname_split: Vec<&str> = hdlname.split(" ").collect(); + let (name, parents) = hdlname_split.split_last().unwrap(); + RTLID::new(parents, name) + } else { + RTLID::new(&[], net_name) + }; + + new_nets.insert(id.to_string(), net_info.clone()); + + let nets: Vec = net_info + .bits + .iter() + .map(parse_bit) + .collect::>()?; + + all_nets.insert(id, nets.into_boxed_slice()); + } + + module.netnames = new_nets; + + Ok(all_nets) +} + +fn find_top_module_name(netlist: &yosys::Netlist) -> Option<&str> { + for (module_name, module_info) in &netlist.modules { + if let Some(top_val) = module_info.attributes.get("top") + && let Some(top) = top_val.to_number() + && top == 1 + { + return Some(module_name); + } + } + + None +} diff --git a/crates/arbolta/src/port.rs b/crates/arbolta/src/port.rs index 988e813..5e75047 100644 --- a/crates/arbolta/src/port.rs +++ b/crates/arbolta/src/port.rs @@ -1,21 +1,18 @@ // Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. // SPDX-License-Identifier: MIT -use bincode::{Decode, Encode}; +use crate::yosys; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use thiserror::Error; -use yosys_netlist_json as yosys_json; -#[derive( - Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, Deserialize, Serialize, Encode, Decode, -)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, Deserialize, Serialize)] pub enum PortDirection { Input, Output, } -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Encode, Decode)] +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] pub struct Port { pub direction: PortDirection, pub shape: [usize; 2], // TODO: Change this to option? @@ -23,14 +20,14 @@ pub struct Port { /// Parse global net from `BitVal`. /// Errors if bit direction is not supported. -pub fn parse_bit(bit: &yosys_json::BitVal) -> Result { +pub fn parse_bit(bit: &yosys::BitVal) -> Result { match bit { - yosys_json::BitVal::N(net) => Ok(*net), - yosys_json::BitVal::S(constant) => match constant { - yosys_json::SpecialBit::_0 => Ok(0), // Global 0 - yosys_json::SpecialBit::_1 => Ok(1), // Global 1 - yosys_json::SpecialBit::X => Err(PortError::Direction("X".to_string())), - yosys_json::SpecialBit::Z => Err(PortError::Direction("Z".to_string())), + yosys::BitVal::N(net) => Ok(*net), + yosys::BitVal::S(constant) => match constant { + yosys::SpecialBit::_0 => Ok(0), // Global 0 + yosys::SpecialBit::_1 => Ok(1), // Global 1 + yosys::SpecialBit::X => Err(PortError::Direction("X".to_string())), + yosys::SpecialBit::Z => Err(PortError::Direction("Z".to_string())), }, } } @@ -48,31 +45,26 @@ pub enum PortError { }, } -impl TryFrom<&yosys_json::PortDirection> for PortDirection { +impl TryFrom<&yosys::PortDirection> for PortDirection { type Error = PortError; - fn try_from(direction: &yosys_json::PortDirection) -> Result { + fn try_from(direction: &yosys::PortDirection) -> Result { match direction { - yosys_json::PortDirection::InOut => Err(PortError::Direction("inout".to_string())), - yosys_json::PortDirection::Input => Ok(PortDirection::Input), - yosys_json::PortDirection::Output => Ok(PortDirection::Output), + yosys::PortDirection::InOut => Err(PortError::Direction("inout".to_string())), + yosys::PortDirection::Input => Ok(PortDirection::Input), + yosys::PortDirection::Output => Ok(PortDirection::Output), } } } -// impl TryFrom<&yosys_json::Port> for Port { -// type Error = PortError; -// fn try_from(port: &yosys_json::Port) -> Result { -// let direction = PortDirection::try_from(&port.direction)?; -// let nets: Vec = port.bits.iter().map(parse_bit).collect::>()?; -// let shape = [1, nets.len()]; +impl TryFrom<&yosys::Port> for Port { + type Error = PortError; + fn try_from(port: &yosys::Port) -> Result { + let direction = PortDirection::try_from(&port.direction)?; + let shape = [1, port.bits.len()]; -// Ok(Self { -// direction, -// nets: nets.into(), -// shape, -// }) -// } -// } + Ok(Self { direction, shape }) + } +} impl Port { // TODO: Remove and use try_from diff --git a/crates/arbolta/src/signal.rs b/crates/arbolta/src/signal.rs index 4a28d75..44a6d68 100644 --- a/crates/arbolta/src/signal.rs +++ b/crates/arbolta/src/signal.rs @@ -2,11 +2,10 @@ // SPDX-License-Identifier: MIT use crate::bit::Bit; -use bincode::{Decode, Encode}; use serde::{Deserialize, Serialize}; /// Connection between cells/modules and related statistics. -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default, Encode, Decode)] +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)] pub struct Signals { // Total number of nets pub size: usize, diff --git a/crates/arbolta/src/yosys.rs b/crates/arbolta/src/yosys.rs index a404ef0..231bd1e 100644 --- a/crates/arbolta/src/yosys.rs +++ b/crates/arbolta/src/yosys.rs @@ -1,4 +1,82 @@ // Re-export -pub use yosys_netlist_json::Netlist; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, fmt}; + +fn remove_whitespace>(s: &S) -> String { + s.as_ref().chars().filter(|c| !c.is_whitespace()).collect() +} + +#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Deserialize, Serialize, PartialOrd, Ord)] +pub struct RTLID { + pub parents: Vec, + pub name: String, +} + +impl RTLID { + pub fn new>(parents: &[S], name: &S) -> Self { + let mut name_clean = remove_whitespace(name); + let parents_clean: Vec = parents.iter().map(|p| remove_whitespace(p)).collect(); + + if !parents_clean.is_empty() { + let name_stripped = name_clean.strip_prefix("$flatten\\").unwrap_or(&name_clean); + let parents_prefix = format!("{}.", parents_clean.join(".")); + + if let Some(actual_name) = name_stripped.strip_prefix(&parents_prefix) { + name_clean = actual_name.to_string(); + } else { + name_clean = name_stripped.to_string(); + } + } + + Self { + parents: parents_clean, + name: name_clean, + } + } +} + +impl fmt::Display for RTLID { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.parents.is_empty() { + write!(f, "{}", self.name) + } else { + write!(f, "{}.{}", self.parents.join("."), self.name) + } + } +} + +pub type TopoOrder<'a> = HashMap<&'a str, Vec<&'a str>>; + +pub fn parse_torder<'a>(raw: &'a str) -> TopoOrder<'a> { + let mut torder = TopoOrder::new(); + let mut current_module: Option<&'a str> = None; + + for line in raw.lines() { + let line = line.trim(); + + // Start new module + if let Some(module_name) = line.strip_prefix("module ") { + let module_name = if let Some((_cell_type, module_name)) = module_name.split_once("$") { + module_name + } else { + module_name + }; + + current_module = Some(module_name); + torder.insert(module_name, vec![]); + } else if let Some(module_path) = current_module + && let Some(cell_name) = line.strip_prefix("cell ") + { + torder.entry(module_path).or_default().push(cell_name); + } + } + + torder +} + +// Re-export +pub use yosys_netlist_json::{ + AttributeVal, BitVal, Cell, Module, Netlist, Netname, Port, PortDirection, SpecialBit, +}; // TODO: Maybe re-add Yosys bindings... diff --git a/crates/arbolta/tests/deps/simlib_wrappers.json b/crates/arbolta/tests/deps/simlib_wrappers.json new file mode 100644 index 0000000..e37d309 --- /dev/null +++ b/crates/arbolta/tests/deps/simlib_wrappers.json @@ -0,0 +1,48 @@ +{ + "creator": "Yosys 0.14+51 (git sha1 286caa09b, gcc 9.3.0-13 -fPIC -Os)", + "modules": { + "$add_wrapper": { + "ports": { + "A": { + "direction": "input", + "bits": [ 2, 3, 4, 5, 6, 7, 8, 9 ] + }, + "B": { + "direction": "input", + "bits": [ 10, 11, 12, 13, 14, 15, 16, 17 ] + }, + "Y": { + "direction": "output", + "bits": [ 18, 19, 20, 21, 22, 23, 24, 25 ] + } + }, + "cells": { + "$1": { + "type": "$add", + "parameters": { + "A_SIGNED": "00000000000000000000000000000001", + "A_WIDTH": "00000000000000000000000000010000", + "B_SIGNED": "00000000000000000000000000000001", + "B_WIDTH": "00000000000000000000000000010000", + "Y_WIDTH": "00000000000000000000000000010000" + }, + "port_directions": { + "A": "input", + "B": "input", + "Y": "output" + }, + "connections": { + "A": [ 2, 3, 4, 5, 6, 7, 8, 9 ], + "B": [ 10, 11, 12, 13, 14, 15, 16, 17 ], + "Y": [ 18, 19, 20, 21, 22, 23, 24, 25 ] + } + } + }, + "netnames": { + "A": {"bits": [ 2, 3, 4, 5, 6, 7, 8, 9 ]}, + "B": {"bits": [ 10, 11, 12, 13, 14, 15, 16, 17 ]}, + "Y": {"bits": [ 18, 19, 20, 21, 22, 23, 24, 25 ]} + } + } + } +} diff --git a/crates/arbolta/tests/deps/simlib_wrappers.sv b/crates/arbolta/tests/deps/simlib_wrappers.sv index 3b74983..4926296 100644 --- a/crates/arbolta/tests/deps/simlib_wrappers.sv +++ b/crates/arbolta/tests/deps/simlib_wrappers.sv @@ -1,29 +1,29 @@ `include "/opt/oss-cad-suite/share/yosys/simlib.v" -module not_wrapper ( - A, - Y -); - - parameter A_SIGNED = 0; - parameter A_WIDTH = 0; - parameter Y_WIDTH = 0; - - input [A_WIDTH-1:0] A; - output [Y_WIDTH-1:0] Y; - - \$not #( - .A_SIGNED(A_SIGNED), - .A_WIDTH (A_WIDTH), - .Y_WIDTH (Y_WIDTH) - ) _TECHMAP_REPLACE_ ( - .A(A), - .Y(Y) - ); - -endmodule - -module add_wrapper ( +// module not_wrapper ( +// A, +// Y +// ); + +// parameter A_SIGNED = 0; +// parameter A_WIDTH = 0; +// parameter Y_WIDTH = 0; + +// input [A_WIDTH-1:0] A; +// output [Y_WIDTH-1:0] Y; + +// \$not #( +// .A_SIGNED(A_SIGNED), +// .A_WIDTH (A_WIDTH), +// .Y_WIDTH (Y_WIDTH) +// ) _TECHMAP_REPLACE_ ( +// .A(A), +// .Y(Y) +// ); + +// endmodule + +module \$add_wrapper ( A, B, Y diff --git a/crates/arbolta/tests/helpers.rs b/crates/arbolta/tests/helpers.rs new file mode 100644 index 0000000..9d8e8d4 --- /dev/null +++ b/crates/arbolta/tests/helpers.rs @@ -0,0 +1,97 @@ +use arbolta::yosys::*; +use indexmap::*; + +fn allocate_nets(offset: Option, widths: &[usize]) -> Vec> { + let mut all_nets: Vec> = vec![]; + let offset = offset.unwrap_or(0); + + for width in widths { + let start: usize = match all_nets.last() { + Some(last) => last[last.len() - 1] + 1, + None => offset, + }; + + all_nets.push((start..start + width).collect()); + } + + let mut bits = vec![]; + for nets in all_nets { + let new_bits: Vec = nets.iter().map(|&n| BitVal::N(n)).collect(); + bits.push(new_bits) + } + + bits +} + +pub fn int_to_attr(x: u32) -> AttributeVal { + AttributeVal::S(format!("{:032b}", x)) +} + +pub fn build_netlist( + cell_type: &str, + module_name: &'static str, + parameters: IndexMap<&str, AttributeVal>, + ports: IndexMap<&str, (PortDirection, usize)>, // width +) -> (Netlist, TopoOrder<'static>) { + let mut netlist = Netlist::new("test"); + + let widths: Vec = ports.values().map(|(_, w)| *w).collect(); + let nets = allocate_nets(Some(2), &widths); + + let mut actual_parameters = IndexMap::new(); + for (param_name, val) in parameters { + actual_parameters.insert(param_name.to_string(), val); + } + + let mut port_directions = IndexMap::new(); + for (port_name, port_info) in &ports { + port_directions.insert(port_name.to_string(), port_info.0); + } + + let mut connections = IndexMap::new(); + for (i, port_name) in ports.keys().enumerate() { + connections.insert(port_name.to_string(), nets[i].clone()); + } + + let cell = Cell { + cell_type: cell_type.into(), + parameters: actual_parameters, + port_directions, + connections, + ..Default::default() + }; + + let mut actual_ports = IndexMap::new(); + let mut netnames = IndexMap::new(); + for (i, (port_name, port_info)) in ports.iter().enumerate() { + let new_port = Port { + direction: port_info.0, + bits: nets[i].clone(), + offset: 0, + upto: 0, + signed: 0, + }; + let netname = Netname { + bits: nets[i].clone(), + ..Default::default() + }; + actual_ports.insert(port_name.to_string(), new_port); + netnames.insert(port_name.to_string(), netname); + } + + let module = Module { + attributes: indexmap! { + "top".into() => int_to_attr(1) + }, + ports: actual_ports, + netnames, + cells: indexmap! {"$1".into() => cell}, + ..Default::default() + }; + + netlist.modules.insert(module_name.to_string(), module); + + let torder = TopoOrder::from([(module_name, vec!["$1"])]); + + (netlist, torder) +} diff --git a/crates/arbolta/tests/test_module.rs b/crates/arbolta/tests/test_simcell_module.rs similarity index 80% rename from crates/arbolta/tests/test_module.rs rename to crates/arbolta/tests/test_simcell_module.rs index ff41afd..8bcca68 100644 --- a/crates/arbolta/tests/test_module.rs +++ b/crates/arbolta/tests/test_simcell_module.rs @@ -4,6 +4,7 @@ use arbolta::{bit::BitVec, hardware_module::HardwareModule}; use once_cell::sync::Lazy; use rstest::rstest; +use std::collections::HashMap; use yosys_netlist_json::Netlist; static CELL_WRAPPER_NETLIST: Lazy = @@ -19,7 +20,9 @@ static CELL_WRAPPER_NETLIST: Lazy = (1, 0), ])] fn test_module_unary_cell(#[case] cell: &str, #[case] cases: [(u8, u8); 2]) { - let mut module = HardwareModule::new(cell, &CELL_WRAPPER_NETLIST).unwrap(); + let cell_type = cell.strip_suffix("_WRAPPER").unwrap(); + let torder = HashMap::from([(cell, vec![cell_type])]); + let mut module = HardwareModule::new(CELL_WRAPPER_NETLIST.clone(), Some(cell), torder).unwrap(); for (a, expected) in cases { module.set_port("A", BitVec::from_int(a, None)).unwrap(); @@ -73,7 +76,9 @@ fn test_module_unary_cell(#[case] cell: &str, #[case] cases: [(u8, u8); 2]) { (1, 1, 1), ])] fn test_module_binary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8); 4]) { - let mut module = HardwareModule::new(cell, &CELL_WRAPPER_NETLIST).unwrap(); + let cell_type = cell.strip_suffix("_WRAPPER").unwrap(); + let torder = HashMap::from([(cell, vec![cell_type])]); + let mut module = HardwareModule::new(CELL_WRAPPER_NETLIST.clone(), Some(cell), torder).unwrap(); for (a, b, expected) in cases { module.set_port("A", BitVec::from_int(a, None)).unwrap(); @@ -106,7 +111,10 @@ fn test_module_binary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8); 4]) (1, 1, 1, 0), ])] fn test_module_ternary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8, u8); 8]) { - let mut module = HardwareModule::new(cell, &CELL_WRAPPER_NETLIST).unwrap(); + let cell_type = cell.strip_suffix("_WRAPPER").unwrap(); + let torder = HashMap::from([(cell, vec![cell_type])]); + let mut module = HardwareModule::new(CELL_WRAPPER_NETLIST.clone(), Some(cell), torder).unwrap(); + for (a, b, c, expected) in cases { module.set_port("A", BitVec::from_int(a, None)).unwrap(); module.set_port("B", BitVec::from_int(b, None)).unwrap(); @@ -116,6 +124,7 @@ fn test_module_ternary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8, u8) assert_eq!(actual, expected, "inputs: `{a}`, `{b}`, `{c}`"); } } + #[rstest] #[case::mux2("$_MUX__WRAPPER", [ (0, 0, 0, 0), @@ -138,7 +147,10 @@ fn test_module_ternary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8, u8) (1, 1, 1, 0), ])] fn test_module_mux_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8, u8); 8]) { - let mut module = HardwareModule::new(cell, &CELL_WRAPPER_NETLIST).unwrap(); + let cell_type = cell.strip_suffix("_WRAPPER").unwrap(); + let torder = HashMap::from([(cell, vec![cell_type])]); + let mut module = HardwareModule::new(CELL_WRAPPER_NETLIST.clone(), Some(cell), torder).unwrap(); + for (a, b, s, expected) in cases { module.set_port("A", BitVec::from_int(a, None)).unwrap(); module.set_port("B", BitVec::from_int(b, None)).unwrap(); diff --git a/crates/arbolta/tests/test_simlib_module.rs b/crates/arbolta/tests/test_simlib_module.rs new file mode 100644 index 0000000..0776032 --- /dev/null +++ b/crates/arbolta/tests/test_simlib_module.rs @@ -0,0 +1,236 @@ +mod helpers; + +use arbolta::{ + bit::{Bit, BitVec}, + hardware_module::HardwareModule, + yosys::PortDirection, +}; +use helpers::{build_netlist, int_to_attr}; +use indexmap::indexmap; +use rstest::rstest; + +#[rstest] +// 37738 + 4365 = 42103 +#[case::unsigned_normal(false, "1001001101101010", "0001000100001101", "1010010001110111")] +// 155 + 7 = 162 +#[case::unsigned_normal(false, "10011011", "111", "0000000010100010")] +// 54 + 4234 = 4288 +#[case::unsigned_normal(false, "00110110", "0001000010001010", "1000011000000")] +// 37738 + 4365 = 1143 +#[case::unsigned_overflow(false, "1001001101101010", "0001000100001101", "0010001110111")] +#[case(false, "00000111", "00000111", "00001110")] // 7 + 7 = 49 +#[case(false, "00000111", "111", "01110")] // 7 + 7 = 49 +#[case(false, "111", "111", "1110")] // 7 + 7 = 14 +#[case(false, "111", "111", "10")] // 7 + 7 = 4 (overflow) +#[case(true, "00000111", "00000111", "00001110")] // 7 + 7 = 49 +#[case(true, "00000111", "1001", "00000")] // 7 + -7 = 0 +#[case(true, "1001", "11001", "11110010")] // -7 + -7 = -14 +#[case(true, "1001", "1001", "10")] // -7 + -7 = -4 (overflow) +#[case(true, "111", "111", "10")] // 7 + 7 = 4 (overflow) +#[case(true, "1", "0", "111111111111")] // sign extend +#[case(true, "0", "1", "111111111111")] // sign extend +#[case(true, "1", "1", "111111111110")] // -1 + -1 = -2 +#[case(true, "01", "1", "000000000000")] // 1 + -1 = 0 +fn test_add(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { + let (netlist, torder) = build_netlist( + "$add", + "$add_wrapper", + indexmap! { + "A_SIGNED" => int_to_attr(signed as u32), + "B_SIGNED" => int_to_attr(signed as u32), + "Y_SIGNED" => int_to_attr(signed as u32), + "A_WIDTH" => int_to_attr(a.len() as u32), + "B_WIDTH" => int_to_attr(b.len() as u32), + "Y_WIDTH" => int_to_attr(expected.len() as u32), + }, + indexmap! { + "A" => (PortDirection::Input, a.len()), + "B" => (PortDirection::Input, b.len()), + "Y" => (PortDirection::Output, expected.len()), + }, + ); + + let mut module = HardwareModule::new(netlist, None, torder).unwrap(); + + module.set_port("A", a).unwrap(); + module.set_port("B", b).unwrap(); + module.eval(); + + let actual = module.get_port("Y").unwrap(); + assert_eq!(actual, expected); +} + +#[rstest] +#[case(Bit::ZERO, "000", "001", "000")] +#[case(Bit::ONE, "000", "001", "001")] +#[case(Bit::ZERO, "111", "000", "111")] +#[case(Bit::ONE, "111", "000", "000")] +fn test_mux(#[case] select: Bit, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { + let (netlist, torder) = build_netlist( + "$mux", + "$mux_wrapper", + indexmap! { + "WIDTH" => int_to_attr(a.len() as u32), + }, + indexmap! { + "A" => (PortDirection::Input, a.len()), + "B" => (PortDirection::Input, b.len()), + "S" => (PortDirection::Input, 1), + "Y" => (PortDirection::Output, expected.len()), + }, + ); + + let mut module = HardwareModule::new(netlist, None, torder).unwrap(); + + module.set_port("S", [select]).unwrap(); + module.set_port("A", a).unwrap(); + module.set_port("B", b).unwrap(); + module.eval(); + + let actual = module.get_port("Y").unwrap(); + assert_eq!(actual, expected); +} + +#[rstest] +#[case::zero(Bit::ONE, "000000000000")] // 0 +#[case::one(Bit::ONE, "000000000001")] // 1 +#[case(Bit::ONE, "1101111001101100")] // 56940 +#[case(Bit::ZERO, "000000000000")] // 0 +#[case(Bit::ZERO, "000000000001")] // 1 +#[case(Bit::ZERO, "1101111001101100")] // 56940 +fn test_reg(#[case] polarity: Bit, #[case] data_in: BitVec) { + let (netlist, torder) = build_netlist( + "$dff", + "$dff_wrapper", + indexmap! { + "CLK_POLARITY" => int_to_attr(polarity.to_int()), + "WIDTH" => int_to_attr(data_in.len() as u32), + }, + indexmap! { + "CLK" => (PortDirection::Input, 1), + "D" => (PortDirection::Input, data_in.len()), + "Q" => (PortDirection::Output, data_in.len()), + }, + ); + + let mut module = HardwareModule::new(netlist, None, torder).unwrap(); + let clock_net = module.get_net("CLK").unwrap()[0]; + module.set_clock(clock_net, polarity).unwrap(); + + module.set_port("CLK", [!polarity]).unwrap(); // Reset + module.eval(); + assert_eq!(module.get_port("Q").unwrap().to_int::(), 0); + + module.set_port("D", &data_in).unwrap(); + assert_eq!(module.get_port("Q").unwrap().to_int::(), 0); + + module.eval_clocked(Some(1)).unwrap(); + assert_eq!(module.get_port("Q").unwrap(), data_in); + + module.set_port("D", BitVec::from_int(0, None)).unwrap(); // Zero + assert_eq!(module.get_port("Q").unwrap(), data_in); + + module.eval_clocked(Some(1)).unwrap(); + assert_eq!(module.get_port("Q").unwrap().to_int::(), 0); +} + +#[rstest] +#[case("0", "0", "0")] +#[case("0", "1", "0")] +#[case("1", "0", "0")] +#[case("1", "1", "1")] +#[case("00000000", "0000000", "0000")] +#[case("10000000", "0000001", "0001")] +#[case("1", "0000001", "01")] +#[case("1111111", "01", "01")] +#[case("1010100", "0000", "00000")] +fn test_logic_and(#[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { + let (netlist, torder) = build_netlist( + "$logic_and", + "$logic_and_wrapper", + indexmap! { + "A_SIGNED" => int_to_attr(false as u32), + "A_WIDTH" => int_to_attr(a.len() as u32), + "B_SIGNED" => int_to_attr(false as u32), + "B_WIDTH" => int_to_attr(b.len() as u32), + "Y_WIDTH" => int_to_attr(expected.len() as u32), + }, + indexmap! { + "A" => (PortDirection::Input, a.len()), + "B" => (PortDirection::Input, b.len()), + "Y" => (PortDirection::Output, expected.len()), + }, + ); + + let mut module = HardwareModule::new(netlist, None, torder).unwrap(); + + module.set_port("A", a).unwrap(); + module.set_port("B", b).unwrap(); + module.eval(); + + let actual = module.get_port("Y").unwrap(); + assert_eq!(actual, expected); +} + +#[rstest] +#[case("0", "1")] +#[case("1", "0")] +#[case("00000000", "0000001")] +#[case("10000000", "0000000")] +#[case("1", "0000000")] +#[case("1111111", "00")] +#[case("1010100", "00")] +fn test_logic_not(#[case] a: BitVec, #[case] expected: BitVec) { + let (netlist, torder) = build_netlist( + "$logic_not", + "$logic_not_wrapper", + indexmap! { + "A_SIGNED" => int_to_attr(false as u32), + "A_WIDTH" => int_to_attr(a.len() as u32), + "Y_WIDTH" => int_to_attr(expected.len() as u32), + }, + indexmap! { + "A" => (PortDirection::Input, a.len()), + "Y" => (PortDirection::Output, expected.len()), + }, + ); + + let mut module = HardwareModule::new(netlist, None, torder).unwrap(); + + module.set_port("A", a).unwrap(); + module.eval(); + + let actual = module.get_port("Y").unwrap(); + assert_eq!(actual, expected); +} + +#[rstest] +#[case("0", "0")] +#[case("00000000", "0000000")] +#[case("10000000", "0000001")] +#[case("1", "0000001")] +#[case("1111111", "01")] +#[case("1010100", "01")] +fn test_reduce_or(#[case] a: BitVec, #[case] expected: BitVec) { + let (netlist, torder) = build_netlist( + "$reduce_or", + "$reduce_or_wrapper", + indexmap! { + "A_SIGNED" => int_to_attr(false as u32), + "A_WIDTH" => int_to_attr(a.len() as u32), + "Y_WIDTH" => int_to_attr(expected.len() as u32), + }, + indexmap! { + "A" => (PortDirection::Input, a.len()), + "Y" => (PortDirection::Output, expected.len()), + }, + ); + + let mut module = HardwareModule::new(netlist, None, torder).unwrap(); + + module.set_port("A", a).unwrap(); + module.eval(); + + let actual = module.get_port("Y").unwrap(); + assert_eq!(actual, expected); +} diff --git a/crates/fault/src/main.rs b/crates/fault/src/main.rs index 5f937b4..5aca1d9 100644 --- a/crates/fault/src/main.rs +++ b/crates/fault/src/main.rs @@ -1,130 +1,132 @@ -use anyhow::{Result, anyhow, ensure}; -use arbolta::{bit::Bit, hardware_module::HardwareModule, yosys::Netlist}; -use clap::Parser; -use fault::utils::matmul; -// use indicatif::ParallelProgressIterator; -use ndarray::prelude::*; //parallel::prelude::* -use ndarray_npy::read_npy; -use std::path::PathBuf; -// use std::sync::Mutex; - -#[derive(Parser, Debug)] -struct Args { - /// Name of top module in netlist. - #[clap(long)] - top_module: String, - /// Path to topological cell order file. - #[clap(long)] - torder_path: String, - /// Path to Yosys netlist JSON file. - #[clap(long)] - netlist_path: String, - /// Path to NumPy file for matrix A. - #[clap(long)] - a_path: PathBuf, - /// Path to NumPy file for matrix B. - #[clap(long)] - b_path: PathBuf, - /// Path to NumPy targets file. - // #[clap(long)] - // targets: PathBuf, - /// Path to list of nets for fault injection campaign. - // #[clap(long)] - // nets: PathBuf, - #[clap(long)] - rows: usize, - #[clap(long)] - cols: usize, - // #[clap(long)] - // iterations: usize, - #[clap(long)] - x_width: usize, - #[clap(long)] - k_width: usize, - #[clap(long)] - m_width: usize, -} - -// TODO: Move setup to generic function and do dynamic dispatch here... -#[allow(non_snake_case)] -fn main() -> Result<()> { - let flags = Args::parse(); - - // Load and parse netlist and topological cell order - let raw_netlist = std::fs::read(flags.netlist_path)?; - let netlist = Netlist::from_slice(&raw_netlist)?; - - // Setup design - let (ROWS, COLS) = (flags.rows, flags.cols); // Copy for easier use - - let mut design = HardwareModule::new(&flags.top_module, &netlist)?; - design.set_clock(2, Bit::ONE)?; - design.set_reset(3, Bit::ZERO)?; - design.set_port_shape("sx_data_i", &[ROWS, flags.x_width])?; - design.set_port_shape("sk_data_i", &[COLS, flags.k_width])?; - design.set_port_shape("m_data_o", &[ROWS, flags.m_width])?; - - // Load and process inputs - let a: Array2 = read_npy(flags.a_path)?; - let b: Array2 = read_npy(flags.b_path)?; - - // a: (X, K), b: (K, Z) - let (X, K, Z) = (a.nrows(), a.ncols(), b.ncols()); - ensure!(K == b.nrows(), anyhow!("{} != {}", K, b.nrows())); - - // Pad `a` and `b` for alignment with systolic array - let (out_x, out_z) = (X.div_ceil(COLS) * COLS, (Z.div_ceil(ROWS) * ROWS)); - - // TODO: May have to adjust for Array3 - let mut a_pad = Array2::::zeros((out_x, K)); - let mut b_pad = Array2::::zeros((K, out_z)); - a_pad.slice_mut(s![0..X, 0..K]).assign(&a.view()); - b_pad.slice_mut(s![0..K, 0..Z]).assign(&b.view()); - - // Setup padded output array (must crop later) - let mut out = Array2::::zeros((out_x, out_z)); - matmul( - &mut design, - COLS, - ROWS, - a_pad.view(), - b_pad.view(), - out.view_mut(), - )?; - - let temp = out.slice(s![0..X, 0..Z]).to_owned(); - println!("{temp}"); - - /* - // Load everything - let mut design = HardwareModule::new_from_path(&flags.netlist, &flags.top_module)?; - let inputs: Array3 = read_npy(&flags.inputs) - .with_context(|| format!("Failed to read NumPy array from {:?}", flags.inputs))?; // (N, 28, 28) - let weights: Array2 = read_npy(&flags.weights) - .with_context(|| format!("Failed to read NumPy array from {:?}", flags.weights))?; // (10, 784) - // let targets: Array2 = read_npy(&flags.targets) - // .with_context(|| format!("Failed to read NumPy array from {:?}", flags.targets))?; - - let nets: Vec = std::fs::read_to_string(&flags.nets)? - .split_whitespace() - .map(|x| x.parse().unwrap()) - .collect(); - - - - let designs: Vec> = (0..rayon::current_num_threads()) - .map(|_| Mutex::new(design.clone())) - .collect(); - - // Start simulation - nets.into_par_iter().progress().for_each(|n| { - for stuck_val in [Bit::ZERO, Bit::ONE] { - let thread_idx = rayon::current_thread_index().unwrap(); - let design = &mut designs[thread_idx].lock().unwrap(); - worker(design, &inputs.view(), &weights.view(), n, stuck_val).unwrap(); - } - }); - - */ - Ok(()) +// use anyhow::{Result, anyhow, ensure}; +// use arbolta::{bit::Bit, hardware_module::HardwareModule, yosys::Netlist}; +// use clap::Parser; +// use fault::utils::matmul; +// // use indicatif::ParallelProgressIterator; +// use ndarray::prelude::*; //parallel::prelude::* +// use ndarray_npy::read_npy; +// use std::path::PathBuf; +// // use std::sync::Mutex; + +// #[derive(Parser, Debug)] +// struct Args { +// /// Name of top module in netlist. +// #[clap(long)] +// top_module: String, +// /// Path to topological cell order file. +// #[clap(long)] +// torder_path: String, +// /// Path to Yosys netlist JSON file. +// #[clap(long)] +// netlist_path: String, +// /// Path to NumPy file for matrix A. +// #[clap(long)] +// a_path: PathBuf, +// /// Path to NumPy file for matrix B. +// #[clap(long)] +// b_path: PathBuf, +// /// Path to NumPy targets file. +// // #[clap(long)] +// // targets: PathBuf, +// /// Path to list of nets for fault injection campaign. +// // #[clap(long)] +// // nets: PathBuf, +// #[clap(long)] +// rows: usize, +// #[clap(long)] +// cols: usize, +// // #[clap(long)] +// // iterations: usize, +// #[clap(long)] +// x_width: usize, +// #[clap(long)] +// k_width: usize, +// #[clap(long)] +// m_width: usize, +// } + +// // TODO: Move setup to generic function and do dynamic dispatch here... +// #[allow(non_snake_case)] +// fn main() -> Result<()> { +// let flags = Args::parse(); + +// // Load and parse netlist and topological cell order +// let raw_netlist = std::fs::read(flags.netlist_path)?; +// let netlist = Netlist::from_slice(&raw_netlist)?; + +// // Setup design +// let (ROWS, COLS) = (flags.rows, flags.cols); // Copy for easier use + +// let mut design = HardwareModule::new(&flags.top_module, &netlist)?; +// design.set_clock(2, Bit::ONE)?; +// design.set_reset(3, Bit::ZERO)?; +// design.set_port_shape("sx_data_i", &[ROWS, flags.x_width])?; +// design.set_port_shape("sk_data_i", &[COLS, flags.k_width])?; +// design.set_port_shape("m_data_o", &[ROWS, flags.m_width])?; + +// // Load and process inputs +// let a: Array2 = read_npy(flags.a_path)?; +// let b: Array2 = read_npy(flags.b_path)?; + +// // a: (X, K), b: (K, Z) +// let (X, K, Z) = (a.nrows(), a.ncols(), b.ncols()); +// ensure!(K == b.nrows(), anyhow!("{} != {}", K, b.nrows())); + +// // Pad `a` and `b` for alignment with systolic array +// let (out_x, out_z) = (X.div_ceil(COLS) * COLS, (Z.div_ceil(ROWS) * ROWS)); + +// // TODO: May have to adjust for Array3 +// let mut a_pad = Array2::::zeros((out_x, K)); +// let mut b_pad = Array2::::zeros((K, out_z)); +// a_pad.slice_mut(s![0..X, 0..K]).assign(&a.view()); +// b_pad.slice_mut(s![0..K, 0..Z]).assign(&b.view()); + +// // Setup padded output array (must crop later) +// let mut out = Array2::::zeros((out_x, out_z)); +// matmul( +// &mut design, +// COLS, +// ROWS, +// a_pad.view(), +// b_pad.view(), +// out.view_mut(), +// )?; + +// let temp = out.slice(s![0..X, 0..Z]).to_owned(); +// println!("{temp}"); + +// /* +// // Load everything +// let mut design = HardwareModule::new_from_path(&flags.netlist, &flags.top_module)?; +// let inputs: Array3 = read_npy(&flags.inputs) +// .with_context(|| format!("Failed to read NumPy array from {:?}", flags.inputs))?; // (N, 28, 28) +// let weights: Array2 = read_npy(&flags.weights) +// .with_context(|| format!("Failed to read NumPy array from {:?}", flags.weights))?; // (10, 784) +// // let targets: Array2 = read_npy(&flags.targets) +// // .with_context(|| format!("Failed to read NumPy array from {:?}", flags.targets))?; + +// let nets: Vec = std::fs::read_to_string(&flags.nets)? +// .split_whitespace() +// .map(|x| x.parse().unwrap()) +// .collect(); + +// let designs: Vec> = (0..rayon::current_num_threads()) +// .map(|_| Mutex::new(design.clone())) +// .collect(); + +// // Start simulation +// nets.into_par_iter().progress().for_each(|n| { +// for stuck_val in [Bit::ZERO, Bit::ONE] { +// let thread_idx = rayon::current_thread_index().unwrap(); +// let design = &mut designs[thread_idx].lock().unwrap(); +// worker(design, &inputs.view(), &weights.view(), n, stuck_val).unwrap(); +// } +// }); + +// */ +// Ok(()) +// } + +fn main() { + todo!() } diff --git a/crates/python_bindings/Cargo.toml b/crates/python_bindings/Cargo.toml index 4d38b4e..6a10979 100644 --- a/crates/python_bindings/Cargo.toml +++ b/crates/python_bindings/Cargo.toml @@ -9,11 +9,11 @@ name = "arbolta" crate-type = ["cdylib"] [dependencies] -pyo3 = "0.24.2" numpy = "0.24.0" num-traits = "0.2" +postcard = { workspace = true } +pyo3 = "0.24.2" serde = { version = "1.0", features = ["derive"] } -bincode = { workspace = true } thiserror = { workspace = true } [dependencies.arbolta] diff --git a/crates/python_bindings/py_src/arbolta/design.py b/crates/python_bindings/py_src/arbolta/design.py index a5cad20..1719922 100644 --- a/crates/python_bindings/py_src/arbolta/design.py +++ b/crates/python_bindings/py_src/arbolta/design.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: MIT from dataclasses import dataclass +from pathlib import Path from typing import Any, Dict, List, Literal, Optional, Tuple, Union import numpy as np @@ -106,7 +107,11 @@ def __setattr__(self, name: str, value: Any) -> None: class HardwareDesign: def __init__( # TODO: de-duplicate defaults - self, top_module: str, netlist_path: str, config: dict[str, PortConfig] + self, + netlist_path: str | Path, + torder_path: str | Path, + config: dict[str, PortConfig], + top_module: Optional[str] = None, ): """ Parameters @@ -118,8 +123,7 @@ def __init__( # TODO: de-duplicate defaults config : dict[str, PortConfig] Configuration for design ports. """ - self.top_module = top_module - self.design = Design(top_module, netlist_path) + self.design = Design(netlist_path, top_module, torder_path) self.ports = HardwarePorts(config, self.design) def reset(self): diff --git a/crates/python_bindings/src/design.rs b/crates/python_bindings/src/design.rs index 83ecd65..2e03d40 100644 --- a/crates/python_bindings/src/design.rs +++ b/crates/python_bindings/src/design.rs @@ -7,81 +7,85 @@ use crate::conversion::{ use arbol::{ hardware_module::{HardwareModule, ToggleCount}, port::PortDirection, - yosys::Netlist, + yosys::{Netlist, parse_torder}, }; -use bincode::{Decode, Encode}; use pyo3::{ exceptions::{PyAttributeError, PyException, PyValueError}, prelude::*, types::{PyBytes, PyDict}, }; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::{collections::HashMap, path::PathBuf}; #[pyclass(dict, module = "arbolta", name = "Design")] -#[derive(Deserialize, Serialize, Decode, Encode)] +#[derive(Deserialize, Serialize)] //Decode, Encode pub struct PyDesign { #[pyo3(get)] pub top_module: String, - pub netlist_path: String, // TODO: Replace this with actual netlist? - design: HardwareModule, // TODO: RENAME + design: HardwareModule, // TODO: RENAME } #[pymethods] impl PyDesign { #[new] - #[pyo3(signature = (top_module, netlist_path))] - fn __new__(top_module: &str, netlist_path: &str) -> PyResult { + #[pyo3(signature = (netlist_path, top_module, torder_path))] + fn __new__( + netlist_path: PathBuf, + top_module: Option<&str>, + torder_path: PathBuf, + ) -> PyResult { // Read raw JSON netlist let raw_netlist = std::fs::read(netlist_path)?; let netlist = Netlist::from_slice(&raw_netlist).map_err(|e| PyValueError::new_err(format!("{e}")))?; - let design = HardwareModule::new(top_module, &netlist) + // Read raw torder + let raw_torder = std::fs::read_to_string(torder_path)?; + let torder = parse_torder(&raw_torder); + + let design = HardwareModule::new(netlist, top_module, torder) .map_err(|e| PyValueError::new_err(format!("{e}")))?; - Ok(Self { - top_module: top_module.to_string(), - netlist_path: netlist_path.to_string(), - design, - }) - } + let top_module = design.netlist.top_module.clone(); - fn __setstate__(&mut self, state: &Bound<'_, PyBytes>) { - let config = bincode::config::standard(); - (*self, _) = bincode::decode_from_slice(state.as_bytes(), config).unwrap(); + Ok(Self { top_module, design }) } - fn __getstate__<'py>(&self, py: Python<'py>) -> PyResult> { - let config = bincode::config::standard(); - match bincode::encode_to_vec(self, config) { - Ok(bytes) => Ok(PyBytes::new(py, &bytes)), - Err(err) => Err(PyValueError::new_err(format!("{err}"))), - } - } + // fn __setstate__(&mut self, state: &Bound<'_, PyBytes>) { + // let config = bincode::config::standard(); + // (*self, _) = bincode::decode_from_slice(state.as_bytes(), config).unwrap(); + // } - fn __getnewargs__(&self) -> (String, String) { - (self.top_module.clone(), self.netlist_path.clone()) - } + // fn __getstate__<'py>(&self, py: Python<'py>) -> PyResult> { + // let config = bincode::config::standard(); + // match bincode::encode_to_vec(self, config) { + // Ok(bytes) => Ok(PyBytes::new(py, &bytes)), + // Err(err) => Err(PyValueError::new_err(format!("{err}"))), + // } + // } - fn save(&self, path: &str) -> PyResult<()> { - self - .design - .save(path) - .map_err(|e| PyValueError::new_err(format!("{e}"))) - } + // fn __getnewargs__(&self) -> (String, String) { + // (self.top_module.clone(), self.netlist_path.clone()) + // } - #[staticmethod] - fn load(path: &str) -> PyResult { - let design = HardwareModule::load(path).map_err(|e| PyValueError::new_err(format!("{e}")))?; - let top_module = design.top_module.clone(); + // fn save(&self, path: &str) -> PyResult<()> { + // self + // .design + // .save(path) + // .map_err(|e| PyValueError::new_err(format!("{e}"))) + // } - Ok(Self { - top_module, - netlist_path: String::new(), // TODO: Fix - design, - }) - } + // #[staticmethod] + // fn load(path: &str) -> PyResult { + // let design = HardwareModule::load(path).map_err(|e| PyValueError::new_err(format!("{e}")))?; + // let top_module = design.top_module.clone(); + + // Ok(Self { + // top_module, + // netlist_path: String::new(), // TODO: Fix + // design, + // }) + // } fn get_port_shape(&self, name: &str) -> PyResult<[usize; 2]> { match self.design.get_port_shape(name) { @@ -105,101 +109,108 @@ impl PyDesign { .map_err(|e| PyValueError::new_err(format!("{e}"))) } - fn get_graph(&self) -> String { - self.design.graph.clone() - } + // fn get_graph(&self) -> String { + // self.design.graph.clone() + // } fn get_module_names(&self) -> Vec { - self.design.submodules.keys().map(|p| p.join(".")).collect() - } - - fn get_signal_map(&self) -> HashMap> { - self.design.get_all_signal_nets() - } - - fn get_all_signal_nets(&self) -> HashMap> { - self.design.get_all_signal_nets() - } - - fn get_all_signal_nets_reverse(&self) -> HashMap> { - self.design.get_all_signal_nets_reverse() - } - - fn get_all_signal_values(&self) -> HashMap> { - let signal_values = self.design.get_all_signal_values(); - HashMap::from_iter( - signal_values - .into_iter() - .map(|(name, bits)| (name, bits.into())), - ) - } - - fn get_submodule_nets(&self) -> PyResult>> { - let submodule_nets = self - .design - .get_submodule_nets() - .map_err(|e| PyAttributeError::new_err(format!("{e}")))?; - - let submodule_nets = HashMap::from_iter( - submodule_nets - .into_iter() - .map(|(name, nets)| (name.join("."), nets.into_iter().collect())), - ); - - Ok(submodule_nets) - } - - fn get_submodule_toggles( - &self, - category: &str, - ) -> PyResult>> { - let category = match category { - "falling" => ToggleCount::Falling, - "rising" => ToggleCount::Rising, - "total" => ToggleCount::Total, - _ => { - return Err(PyValueError::new_err(format!( - "Unsupported category `{category}`" - ))); - } - }; - - let submodule_toggles = self + // TODO: How to handle top_module? + self .design - .get_submodule_toggles(category) - .map_err(|e| PyValueError::new_err(format!("{e}")))?; - - let submodule_toggles = HashMap::from_iter( - submodule_toggles - .into_iter() - .map(|(name, toggles)| (name.join("."), toggles)), - ); - - Ok(submodule_toggles) - } - - fn get_submodule_net_map(&self) -> HashMap>> { - HashMap::from_iter( - self - .design - .get_submodule_net_map() - .into_iter() - .map(|(name, nets)| (name.join("."), nets)), - ) - } - - fn get_cell_info(&self, py: Python<'_>) -> PyResult { - let all_cell_info = PyDict::new(py); - for (name, cell_type) in self.design.cell_info.iter() { - let cell_info = PyDict::new(py); - cell_info.set_item("type", cell_type)?; - - // TODO: Add other fields - all_cell_info.set_item(name.clone(), cell_info)?; - } - - Ok(all_cell_info.into()) - } + .netlist + .modules + .iter() + .map(|p| p.join(".")) + .collect() + } + + // fn get_signal_map(&self) -> HashMap> { + // self.design.get_all_signal_nets() + // } + + // fn get_all_signal_nets(&self) -> HashMap> { + // self.design.get_all_signal_nets() + // } + + // fn get_all_signal_nets_reverse(&self) -> HashMap> { + // self.design.get_all_signal_nets_reverse() + // } + + // fn get_all_signal_values(&self) -> HashMap> { + // let signal_values = self.design.get_all_signal_values(); + // HashMap::from_iter( + // signal_values + // .into_iter() + // .map(|(name, bits)| (name, bits.into())), + // ) + // } + + // fn get_submodule_nets(&self) -> PyResult>> { + // let submodule_nets = self + // .design + // .get_submodule_nets() + // .map_err(|e| PyAttributeError::new_err(format!("{e}")))?; + + // let submodule_nets = HashMap::from_iter( + // submodule_nets + // .into_iter() + // .map(|(name, nets)| (name.join("."), nets.into_iter().collect())), + // ); + + // Ok(submodule_nets) + // } + + // fn get_submodule_toggles( + // &self, + // category: &str, + // ) -> PyResult>> { + // let category = match category { + // "falling" => ToggleCount::Falling, + // "rising" => ToggleCount::Rising, + // "total" => ToggleCount::Total, + // _ => { + // return Err(PyValueError::new_err(format!( + // "Unsupported category `{category}`" + // ))); + // } + // }; + + // let submodule_toggles = self + // .design + // .get_submodule_toggles(category) + // .map_err(|e| PyValueError::new_err(format!("{e}")))?; + + // let submodule_toggles = HashMap::from_iter( + // submodule_toggles + // .into_iter() + // .map(|(name, toggles)| (name.join("."), toggles)), + // ); + + // Ok(submodule_toggles) + // } + + // fn get_submodule_net_map(&self) -> HashMap>> { + // HashMap::from_iter( + // self + // .design + // .get_submodule_net_map() + // .into_iter() + // .map(|(name, nets)| (name.join("."), nets)), + // ) + // } + + // fn get_cell_info(&self, py: Python<'_>) -> PyResult { + // let all_cell_info = PyDict::new(py); + // for (name, cell_type) in self.design.cell_info.iter() { + // let cell_info = PyDict::new(py); + // cell_info.set_item("type", cell_type)?; + + // // TODO: Add other fields + // all_cell_info.set_item(name.clone(), cell_info)?; + // } + + // Ok(all_cell_info.into()) + // } pub fn stick_signal(&mut self, net: usize, val: bool) -> PyResult<()> { self @@ -218,7 +229,7 @@ impl PyDesign { fn set_clock(&mut self, name: &str, polarity: bool) -> PyResult<()> { let nets = self .design - .get_nets(name, None) + .get_net(name) .ok_or(PyException::new_err(format!("No signal `{name}`")))?; if nets.len() != 1 { @@ -234,7 +245,7 @@ impl PyDesign { fn set_reset(&mut self, name: &str, polarity: bool) -> PyResult<()> { let nets = self .design - .get_nets(name, None) + .get_net(name) .ok_or(PyException::new_err(format!("No signal `{name}`")))?; if nets.len() != 1 { From e40e71cd3085fbf357132b1a4987eb5c3599c327 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Mon, 22 Dec 2025 23:33:53 +0000 Subject: [PATCH 33/64] Moved to uv and rust-only bindings with pyi stub --- .gitignore | 1 + rustfmt.toml => .rustfmt.toml | 0 Cargo.toml | 11 +- crates/arbolta/Cargo.toml | 1 + crates/arbolta/src/bit.rs | 21 +- crates/arbolta/src/hardware_module.rs | 20 +- crates/arbolta/src/lib.rs | 2 - crates/arbolta_pyo3/Cargo.toml | 18 + crates/arbolta_pyo3/README.md | 0 crates/arbolta_pyo3/arbolta.pyi | 112 +++++ crates/arbolta_pyo3/pyproject.toml | 29 ++ .../src/conversion.rs | 2 +- crates/arbolta_pyo3/src/hardware_module.rs | 193 +++++++++ crates/arbolta_pyo3/src/lib.rs | 13 + crates/arbolta_pyo3/src/ports.rs | 202 +++++++++ crates/fault/Cargo.toml | 16 - crates/fault/src/lib.rs | 6 - crates/fault/src/main.rs | 132 ------ crates/fault/src/utils.rs | 135 ------- crates/python_bindings/Cargo.toml | 26 -- .../py_src/arbolta/__init__.py | 8 - .../python_bindings/py_src/arbolta/design.py | 291 ------------- crates/python_bindings/pyproject.toml | 23 -- crates/python_bindings/src/design.rs | 382 ------------------ crates/python_bindings/src/lib.rs | 16 - crates/yosys_server/Cargo.toml | 20 - crates/yosys_server/src/lib.rs | 41 -- crates/yosys_server/src/main.rs | 54 --- crates/yosys_server/src/server.rs | 246 ----------- 29 files changed, 603 insertions(+), 1418 deletions(-) rename rustfmt.toml => .rustfmt.toml (100%) create mode 100644 crates/arbolta_pyo3/Cargo.toml create mode 100644 crates/arbolta_pyo3/README.md create mode 100644 crates/arbolta_pyo3/arbolta.pyi create mode 100644 crates/arbolta_pyo3/pyproject.toml rename crates/{python_bindings => arbolta_pyo3}/src/conversion.rs (98%) create mode 100644 crates/arbolta_pyo3/src/hardware_module.rs create mode 100644 crates/arbolta_pyo3/src/lib.rs create mode 100644 crates/arbolta_pyo3/src/ports.rs delete mode 100644 crates/fault/Cargo.toml delete mode 100644 crates/fault/src/lib.rs delete mode 100644 crates/fault/src/main.rs delete mode 100644 crates/fault/src/utils.rs delete mode 100644 crates/python_bindings/Cargo.toml delete mode 100644 crates/python_bindings/py_src/arbolta/__init__.py delete mode 100644 crates/python_bindings/py_src/arbolta/design.py delete mode 100644 crates/python_bindings/pyproject.toml delete mode 100644 crates/python_bindings/src/design.rs delete mode 100644 crates/python_bindings/src/lib.rs delete mode 100644 crates/yosys_server/Cargo.toml delete mode 100644 crates/yosys_server/src/lib.rs delete mode 100644 crates/yosys_server/src/main.rs delete mode 100644 crates/yosys_server/src/server.rs diff --git a/.gitignore b/.gitignore index 015915c..d1c20d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ ### Python ### +uv.lock # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/rustfmt.toml b/.rustfmt.toml similarity index 100% rename from rustfmt.toml rename to .rustfmt.toml diff --git a/Cargo.toml b/Cargo.toml index d2469e3..834e37d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,12 @@ [workspace] members = ["crates/*"] -resolver = "2" +resolver = "3" [workspace.dependencies] +arbolta = { path = "crates/arbolta" } anyhow = "1" -clap = { version = "4.5", features = ["derive"] } derive_more = { version = "2", features = ["full"] } enum_dispatch = "0.3" -flexbuffers = "25.9" -futures = "0.3" indexmap = "2.12" ndarray = "0.16.1" num-traits = "0.2" @@ -16,12 +14,7 @@ once_cell = "1.21" postcard = "1" rstest = "0.26" serde = { version = "1", features = ["derive"] } -serde-error = "0.1" -smallvec = { version = "1", features = ["serde", "impl_bincode"] } -tarpc = { version = "0.36", features = ["full"] } -tempfile = "3.23" thiserror = "2.0" -tokio = { version = "1", features = ["full"] } yosys-netlist-json = { git = "https://github.com/alexredd99/yosys-netlist-json.git" } [profile.release] diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index c7b7c96..47f5a0a 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -18,5 +18,6 @@ petgraph = { version = "0.8.3", features = ["serde-1"] } postcard = { workspace = true } rstest = { workspace = true } serde = { workspace = true } +serde_json = "1.0.145" thiserror = { workspace = true } yosys-netlist-json = { workspace = true } diff --git a/crates/arbolta/src/bit.rs b/crates/arbolta/src/bit.rs index c881001..aef2be4 100644 --- a/crates/arbolta/src/bit.rs +++ b/crates/arbolta/src/bit.rs @@ -2,12 +2,12 @@ // SPDX-License-Identifier: MIT use anyhow::Result; -use core::fmt; use derive_more::{BitAnd, BitOr, BitXor, Debug, IntoIterator, Not}; use num_traits::{PrimInt, WrappingAdd, WrappingShl, WrappingSub}; use serde::{Deserialize, Serialize}; use std::{ convert::{From, Into}, + fmt, str::FromStr, }; use thiserror::Error; @@ -33,8 +33,11 @@ use thiserror::Error; pub struct Bit(#[debug("{}", if *_0 {"1"} else {"0"})] pub bool); #[derive(Debug, PartialEq, Eq, Error)] -#[error("Couldn't convert `{0}`")] -pub struct ParseBitError(char); +#[error("Couldn't convert `{0}` to a Bit, must be 0 or 1")] +pub enum ParseBitError { + Char(char), + Int(String), // Easier than using generics +} impl Bit { pub const ZERO: Bit = Bit(false); @@ -46,6 +49,16 @@ impl Bit { Self(true) => T::one(), } } + + pub fn from_int(val: T) -> Result { + if val == T::zero() { + Ok(Self::ZERO) + } else if val == T::one() { + Ok(Self::ONE) + } else { + Err(ParseBitError::Int(format!("{val}"))) + } + } } impl TryFrom for Bit { @@ -54,7 +67,7 @@ impl TryFrom for Bit { match val { '0' => Ok(Bit::ZERO), '1' => Ok(Bit::ONE), - _ => Err(ParseBitError(val)), + _ => Err(ParseBitError::Char(val)), } } } diff --git a/crates/arbolta/src/hardware_module.rs b/crates/arbolta/src/hardware_module.rs index da6935f..fa34368 100644 --- a/crates/arbolta/src/hardware_module.rs +++ b/crates/arbolta/src/hardware_module.rs @@ -26,8 +26,6 @@ pub struct HardwareModule { // TODO: Refactor these #[derive(Debug, Error)] pub enum ModuleError { - #[error("Module is not flattened or empty")] - UnFlattened, #[error("Missing top module")] TopModule, #[error("Cell `{0}` doesn't exist")] @@ -36,9 +34,9 @@ pub enum ModuleError { MissingModule, #[error("Module `{0}` missing from topological order")] TopoOrder(String), - #[error("Cell error `{0}`")] + #[error(transparent)] Cell(#[from] CellError), - #[error("Port error `{0}`")] + #[error(transparent)] Port(#[from] PortError), #[error("No reset configured")] MissingReset, @@ -50,8 +48,10 @@ pub enum ModuleError { MissingNet(usize), #[error("Port `{0}` doesn't exist")] MissingPort(String), - #[error("Submodule `{0}` doesn't exist")] - MissingSubmodule(String), + #[error(transparent)] + Netlist(#[from] serde_json::Error), + #[error("Signal cannot be configured as both a reset and clock")] + DoubleAssign, } #[derive(Clone, Debug, Copy)] @@ -133,6 +133,10 @@ impl HardwareModule { pub fn set_clock(&mut self, net: usize, polarity: Bit) -> Result<(), ModuleError> { if net >= self.signals.size { Err(ModuleError::MissingNet(net)) + } else if let Some((reset_net, _)) = self.reset_net + && reset_net == net + { + Err(ModuleError::DoubleAssign) } else { self.clock_net = Some((net, polarity)); Ok(()) @@ -142,6 +146,10 @@ impl HardwareModule { pub fn set_reset(&mut self, net: usize, polarity: Bit) -> Result<(), ModuleError> { if net >= self.signals.size { Err(ModuleError::MissingNet(net)) + } else if let Some((clock_net, _)) = self.clock_net + && clock_net == net + { + Err(ModuleError::DoubleAssign) } else { self.reset_net = Some((net, polarity)); Ok(()) diff --git a/crates/arbolta/src/lib.rs b/crates/arbolta/src/lib.rs index 504ff6c..0504695 100644 --- a/crates/arbolta/src/lib.rs +++ b/crates/arbolta/src/lib.rs @@ -3,8 +3,6 @@ pub mod bit; pub mod cell; -pub mod graph; -pub mod graph3; pub mod hardware_module; pub mod netlist_wrapper; pub mod port; diff --git a/crates/arbolta_pyo3/Cargo.toml b/crates/arbolta_pyo3/Cargo.toml new file mode 100644 index 0000000..97fd598 --- /dev/null +++ b/crates/arbolta_pyo3/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "arbolta_pyo3" +version = "0.1.0" +authors = ["AMD Research & Advanced Development"] +edition = "2024" + +[lib] +name = "arbolta" +crate-type = ["cdylib"] + +[dependencies] +anyhow = "*" +arbolta = { workspace = true } +pyo3 = { version = "0.27", features = ["anyhow"] } +numpy = "0.27" +num-traits = { workspace = true } +serde = { version = "1.0", features = ["derive"] } +thiserror = { workspace = true } diff --git a/crates/arbolta_pyo3/README.md b/crates/arbolta_pyo3/README.md new file mode 100644 index 0000000..e69de29 diff --git a/crates/arbolta_pyo3/arbolta.pyi b/crates/arbolta_pyo3/arbolta.pyi new file mode 100644 index 0000000..fa2833e --- /dev/null +++ b/crates/arbolta_pyo3/arbolta.pyi @@ -0,0 +1,112 @@ +from dataclasses import dataclass +from os import PathLike +from pathlib import Path +from typing import Any, Literal, Optional, Type + +import numpy as np +from numpy.typing import ArrayLike + +@dataclass +class PortConfig: + """ + Configuration for a HardwareDesign port. + + :var shape: Interpret port bits with shape, defaults to (1, 1) + :vartype shape: tuple[int, int] + :var dtype: Interpret port bits as `dtype`, defaults to `np.uint` + :vartype dtype: np.dtype | Type[Any]], optional + :var clock: Port is a clock signal, defaults to None + :vartype clock: bool, optional + :var reset: Port is a reset signal, defaults to None + :vartype reset: bool, optional + :var polarity: Clock polarity of port, defaults to None + :vartype polarity: 0 | 1, optional + """ + + shape: tuple[int, int] = (1, 1) + dtype: Optional[np.dtype | Type[Any]] = np.uint + clock: Optional[bool] = None + reset: Optional[bool] = None + polarity: Optional[Literal[0, 1]] = None + +# TODO: Add `raises` docs +class Ports: + """ + Access to simulated module ports. Port names accessed as attributes. + """ + def __getattr__(self, name: str) -> np.ndarray: ... + def __setattr__(self, name: str, value: np.ndarray | ArrayLike) -> None: ... + + """ + Parameters + ---------- + netlist_path : str | Path | PathLike + Path to Yosys netlist JSON. + netlist_path : str | Path | PathLike + Path to Yosys topological order. + config : dict[str, PortConfig] + Configuration for design ports. + top_module : str, optional + Name of top module. + """ + +class HardwareDesign: + """ + Simulated hardware design + + :param netlist_path: Path to Yosys netlist JSON + :type netlist_path: str | Path | PathLike + :param torder_path: Path to Yosys topological order + :type torder_path: str | Path | PathLike + :param config: Configuration for design ports + :type config: dict[str, PortConfig] + :param top_module: Name of top module + :type top_module: str, optional + + :var ports: Access to simulated module ports + :vartype ports: Ports + """ + + ports: Ports + + def __init__( + self, + netlist_path: str | Path | PathLike, + torder_path: str | Path | PathLike, + config: dict[str, PortConfig], + top_module: Optional[str] = None, + ) -> None: ... + def reset(self) -> None: + """ + Reset all design signals and registers to zero. + Resets all toggle to zero. + """ + + def eval(self) -> None: + """ + Evaluates all cells in design. + """ + + def eval_clocked(self, cycles: Optional[int] = None) -> None: + """ + Clocks and evaluates design for `cycles`. + + :param cycles: Number of cycles to clock design, defaults to 1 + :type cycles: int, optional + + :raises AttributeError: No clock signal configured + """ + + def eval_reset_clocked(self, cycles: Optional[int] = None) -> None: + """ + Asserts reset signal and clocks design for `cycles`. + + :param cycles: Number of cycles to clock design, defaults to 1 + :type cycles: int, optional + + :raises AttributeError: No clock signal configured + :raises AttributeError: No reset signal configured + """ + + def stick_signal(self, net: int, val: Literal[0, 1]) -> None: ... + def unstick_signal(self, net: int) -> None: ... diff --git a/crates/arbolta_pyo3/pyproject.toml b/crates/arbolta_pyo3/pyproject.toml new file mode 100644 index 0000000..7f0aba9 --- /dev/null +++ b/crates/arbolta_pyo3/pyproject.toml @@ -0,0 +1,29 @@ +[build-system] +requires = ["maturin>=1.10,<2.0"] +build-backend = "maturin" + +[project] +name = "arbolta" +dynamic = ["version"] +readme = "README.md" +requires-python = ">=3.14" +dependencies = [ + "maturin>=1.10.2", + "numpy>=2.3.5", +] + +[tool.maturin] +manifest-path = "Cargo.toml" + +[tool.uv] +cache-keys = [ + { file = "pyproject.toml" }, + { file = "Cargo.toml" }, + { file = "src/**/*.rs" }, +] + +[dependency-groups] +dev = [ + "pre-commit>=4.5.1", + "ruff>=0.14.10", +] diff --git a/crates/python_bindings/src/conversion.rs b/crates/arbolta_pyo3/src/conversion.rs similarity index 98% rename from crates/python_bindings/src/conversion.rs rename to crates/arbolta_pyo3/src/conversion.rs index e97b117..7a7a808 100644 --- a/crates/python_bindings/src/conversion.rs +++ b/crates/arbolta_pyo3/src/conversion.rs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. // SPDX-License-Identifier: MIT -use arbol::bit::BitVec; +use arbolta::bit::BitVec; use num_traits::{PrimInt, WrappingAdd, WrappingShl, WrappingSub}; use numpy::{PyArrayMethods, PyReadonlyArray1, PyReadwriteArray1}; use pyo3::prelude::*; diff --git a/crates/arbolta_pyo3/src/hardware_module.rs b/crates/arbolta_pyo3/src/hardware_module.rs new file mode 100644 index 0000000..1c8f9b9 --- /dev/null +++ b/crates/arbolta_pyo3/src/hardware_module.rs @@ -0,0 +1,193 @@ +// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// SPDX-License-Identifier: MIT + +use crate::conversion::{ + bits_to_bool_numpy, bits_to_int_numpy, bool_numpy_to_bits, int_numpy_to_bits, +}; +use crate::ports::{PortConfig, Ports}; +use arbolta::bit::Bit; +use arbolta::{ + hardware_module::HardwareModule, + port::PortDirection, + yosys::{Netlist, parse_torder}, +}; +use pyo3::{ + exceptions::{PyAttributeError, PyValueError}, + prelude::*, + types::PyDict, +}; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, path::PathBuf}; + +#[pyclass(weakref, dict)] +#[derive(Deserialize, Serialize)] +pub struct HardwareDesign { + pub module: HardwareModule, +} + +impl HardwareDesign { + fn new_base( + netlist_path: PathBuf, + top_module: Option<&str>, + torder_path: PathBuf, + ) -> anyhow::Result { + // Read raw JSON netlist + let raw_netlist = std::fs::read(netlist_path)?; + let netlist = Netlist::from_slice(&raw_netlist)?; + + // Read raw torder + let raw_torder = std::fs::read_to_string(torder_path)?; + let torder = parse_torder(&raw_torder); + + let module = HardwareModule::new(netlist, top_module, torder)?; + + Ok(Self { module }) + } + + pub fn get_port_shape(&self, name: &str) -> anyhow::Result<[usize; 2]> { + Ok(self.module.get_port_shape(name)?) + } + + pub fn get_port_numpy(&self, name: &str, numpy_array: &Bound<'_, PyAny>) -> PyResult<()> { + let item_type = numpy_array.getattr("dtype")?.getattr("str")?.to_string(); + let elem_size = self.get_port_shape(name)?[1]; + + let bits = self + .module + .get_port(name) + .map_err(|e| PyAttributeError::new_err(format!("{e}")))?; + + match item_type.as_str() { + "|b1" => bits_to_bool_numpy(&bits, numpy_array), + "|u1" | " bits_to_int_numpy::(&bits, elem_size, numpy_array), + " bits_to_int_numpy::(&bits, elem_size, numpy_array), + " bits_to_int_numpy::(&bits, elem_size, numpy_array), + " bits_to_int_numpy::(&bits, elem_size, numpy_array), + "|i1" => bits_to_int_numpy::(&bits, elem_size, numpy_array), + " bits_to_int_numpy::(&bits, elem_size, numpy_array), + " bits_to_int_numpy::(&bits, elem_size, numpy_array), + " bits_to_int_numpy::(&bits, elem_size, numpy_array), + // Cast f16 to u16 + " bits_to_int_numpy::( + &bits, + elem_size, + &numpy_array.call_method1("view", ("uint16",))?, + ), + // Cast f32 to u32 + " bits_to_int_numpy::( + &bits, + elem_size, + &numpy_array.call_method1("view", ("uint32",))?, + ), + _ => Err(PyValueError::new_err(format!( + "Unsupported item type: {item_type}" + ))), + } + } + + pub fn set_port_numpy(&mut self, name: &str, numpy_array: &Bound<'_, PyAny>) -> PyResult<()> { + let item_type: String = numpy_array.getattr("dtype")?.getattr("str")?.to_string(); + let shape = self.get_port_shape(name)?; + let elem_size = shape[1]; + + let bits = match item_type.as_str() { + "|b1" => bool_numpy_to_bits(numpy_array)?, + "|u1" => int_numpy_to_bits::(numpy_array, elem_size)?, + " int_numpy_to_bits::(numpy_array, elem_size)?, + " int_numpy_to_bits::(numpy_array, elem_size)?, + " int_numpy_to_bits::(numpy_array, elem_size)?, + "|i1" => int_numpy_to_bits::(numpy_array, elem_size)?, + " int_numpy_to_bits::(numpy_array, elem_size)?, + " int_numpy_to_bits::(numpy_array, elem_size)?, + " int_numpy_to_bits::(numpy_array, elem_size)?, + // Cast to raw uint8 + " int_numpy_to_bits::(&numpy_array.call_method1("view", ("uint8",))?, elem_size)?, + // Cast f16 to u16 + " { + int_numpy_to_bits::(&numpy_array.call_method1("view", ("uint16",))?, elem_size)? + } + // Cast f32 to u32 + " { + int_numpy_to_bits::(&numpy_array.call_method1("view", ("uint32",))?, elem_size)? + } + _ => { + return Err(PyValueError::new_err(format!( + "Unsupported item type: {item_type}" + ))); + } + }; + + self + .module + .set_port(name, bits) + .map_err(|e| PyAttributeError::new_err(format!("{e}"))) + } +} + +#[pymethods] +impl HardwareDesign { + #[new] + #[pyo3(signature = (netlist_path, torder_path, config, top_module=None))] + pub fn new( + py: Python<'_>, + netlist_path: PathBuf, + torder_path: PathBuf, + config: HashMap>, + top_module: Option<&str>, + ) -> anyhow::Result> { + let new_module = Py::new(py, Self::new_base(netlist_path, top_module, torder_path)?)?; + + // Add custom members (can't make them static for serialization) + let temp_binding = new_module.getattr(py, "__dict__")?; + let temp_dict = temp_binding.cast_bound::(py).unwrap(); + + // Add ports field + let ports = Py::new(py, Ports::new(py, config, new_module.clone_ref(py))?)?; + temp_dict.set_item("ports", ports)?; + + Ok(new_module) + } + + pub fn reset(&mut self) { + self.module.reset() + } + + pub fn eval(&mut self) { + self.module.eval() + } + + #[pyo3(signature = (cycles=None))] + pub fn eval_clocked(&mut self, cycles: Option) -> anyhow::Result<()> { + Ok(self.module.eval_clocked(cycles)?) + } + + #[pyo3(signature = (cycles=None))] + pub fn eval_reset_clocked(&mut self, cycles: Option) -> anyhow::Result<()> { + Ok(self.module.eval_reset_clocked(cycles)?) + } + + pub fn stick_signal(&mut self, net: usize, val: u8) -> anyhow::Result<()> { + Ok(self.module.stick_signal(net, Bit::from_int(val)?)?) + } + + pub fn unstick_signal(&mut self, net: usize) -> anyhow::Result<()> { + Ok(self.module.unstick_signal(net)?) + } + // TODO + + pub fn get_module_names(&self) -> Vec { + // TODO: How to handle top_module? + self + .module + .netlist + .modules + .iter() + .map(|p| p.join(".")) + .collect() + } + + pub fn is_port_input(&self, name: &str) -> anyhow::Result { + let direction = self.module.get_port_direction(name)?; + Ok(direction == PortDirection::Input) + } +} diff --git a/crates/arbolta_pyo3/src/lib.rs b/crates/arbolta_pyo3/src/lib.rs new file mode 100644 index 0000000..161de52 --- /dev/null +++ b/crates/arbolta_pyo3/src/lib.rs @@ -0,0 +1,13 @@ +use pyo3::prelude::*; +mod conversion; +mod hardware_module; +mod ports; + +#[pymodule] +fn arbolta(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + Ok(()) +} diff --git a/crates/arbolta_pyo3/src/ports.rs b/crates/arbolta_pyo3/src/ports.rs new file mode 100644 index 0000000..c7c137a --- /dev/null +++ b/crates/arbolta_pyo3/src/ports.rs @@ -0,0 +1,202 @@ +use crate::hardware_module::HardwareDesign; +use arbolta::{bit::Bit, port::PortDirection}; +use pyo3::{ + exceptions::{PyAttributeError, PyValueError}, + prelude::*, + types::PyDict, +}; +use std::collections::{HashMap, HashSet}; + +// Treat as a dataclass +#[pyclass] +pub struct PortConfig { + #[pyo3(get, set)] + pub shape: (usize, usize), // Defaults (1,1) + #[pyo3(get, set)] + pub dtype: Py, // Numpy datatype, defaults to np.uint + #[pyo3(get, set)] + pub clock: bool, + #[pyo3(get, set)] + pub reset: bool, + #[pyo3(get, set)] + pub polarity: Option, // Literal [0, 1] +} + +#[pymethods] +impl PortConfig { + #[new] + #[pyo3(signature = (shape=(1,1), dtype=None, clock=false, reset=false, polarity=None))] + fn new( + py: Python<'_>, + shape: (usize, usize), + dtype: Option>, + clock: bool, + reset: bool, + polarity: Option, + ) -> PyResult { + let dtype = match dtype { + Some(d) => d, + None => py.import("numpy")?.getattr("uint")?.into(), + }; + + Ok(Self { + shape, + dtype, + clock, + reset, + polarity, + }) + } +} + +#[pyclass(dict)] +pub struct Ports { + module: Py, + input_ports: HashSet, + output_ports: HashSet, +} + +#[pymethods] +impl Ports { + fn __setattr__(self_: Bound<'_, Self>, name: &str, value: &Bound<'_, PyAny>) -> PyResult<()> { + let binding = &mut self_.borrow(); + + if binding.output_ports.contains(name) { + return Err(PyAttributeError::new_err(format!( + "Cannot set output port `{name}`" + ))); + } + + if !binding.input_ports.contains(name) { + return Err(PyAttributeError::new_err(format!( + "Port `{name}` doesn't exist" + ))); + } + + let py = self_.py(); + let object_type = py + .get_type::() + .py() + .import("builtins")? + .getattr("object")?; + + let buffer_ref = object_type.call_method1("__getattribute__", (&self_, name))?; + + let np = py.import("numpy")?; + np.getattr("copyto")?.call1((&buffer_ref, value))?; + + binding + .module + .borrow_mut(py) + .set_port_numpy(name, &buffer_ref)?; + + Ok(()) + } + + fn __getattribute__(self_: Bound<'_, Self>, name: &str) -> PyResult> { + let binding = &mut self_.borrow(); + + let py = self_.py(); + let object_type = py + .get_type::() + .py() + .import("builtins")? + .getattr("object")?; + let buffer_ref = object_type.call_method1("__getattribute__", (&self_, name))?; + + // Update + if binding.output_ports.contains(name) { + binding + .module + .borrow_mut(py) + .get_port_numpy(name, &buffer_ref)?; + } + + Ok(buffer_ref.into()) + } +} + +impl Ports { + fn new_base(module: Py) -> Self { + Self { + module, + input_ports: Default::default(), + output_ports: Default::default(), + } + } + + pub fn new( + py: Python<'_>, + config: HashMap>, + module: Py, + ) -> anyhow::Result> { + let np = py.import("numpy")?; + let module_ref = &mut module.bind(py).borrow_mut().module; + + let new_self = Py::new(py, Self::new_base(module))?; + let temp_binding = new_self.getattr(py, "__dict__")?; + let ports = temp_binding.cast_bound::(py).unwrap(); + + let binding = &mut new_self.bind(py).borrow_mut(); + + let port_names: Vec = module_ref.ports.keys().cloned().collect(); + for port_name in port_names { + let direction = module_ref.get_port_direction(&port_name)?; + let kwargs = PyDict::new(py); + let buffer_len: usize; + + if let Some(port_config) = config.get(&port_name) { + if port_config.reset || port_config.clock { + if let Some(polarity) = port_config.polarity { + let polarity = Bit::from_int(polarity)?; + let nets = module_ref + .get_net(&port_name) + .ok_or(PyAttributeError::new_err(format!("No net `{port_name}`")))?; + + if nets.len() != 1 { + return Err(PyAttributeError::new_err(">1 clock or reset bits".to_string()).into()); + } + + let net = *nets.first().unwrap(); + if port_config.reset { + module_ref.set_reset(net, polarity)?; + } + + if port_config.clock { + module_ref.set_clock(net, polarity)?; + } + } else { + return Err(PyValueError::new_err("No polarity given".to_string()).into()); + } + } + + let shape: [usize; 2] = [port_config.shape.0, port_config.shape.1]; + + if shape[0] != 1 { + return Err(PyValueError::new_err(format!("Only 1D shapes supported: {shape:?}")).into()); + } + + let internal_shape = module_ref.get_port_shape(&port_name)?; + let (num_elems, elem_size) = (shape[1], internal_shape[1] / shape[1]); + module_ref.set_port_shape(&port_name, &[num_elems, elem_size])?; + kwargs.set_item("dtype", port_config.dtype.bind(py))?; + buffer_len = num_elems; + // No config given + } else { + kwargs.set_item("dtype", np.getattr("uint")?)?; + buffer_len = module_ref.get_port_shape(&port_name)?[0]; + } + + let buffer = np.getattr("zeros")?.call((buffer_len,), Some(&kwargs))?; + ports.set_item(port_name.clone(), buffer)?; + + match direction { + PortDirection::Input => &mut binding.input_ports, + PortDirection::Output => &mut binding.output_ports, + } + .insert(port_name); + } + + Ok(new_self) + } +} diff --git a/crates/fault/Cargo.toml b/crates/fault/Cargo.toml deleted file mode 100644 index fbf3a5f..0000000 --- a/crates/fault/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "fault" -version = "0.1.0" -edition = "2024" - -[dependencies] -ndarray-npy = "0.9.1" -ndarray = { version = "0.16.1", features = ["rayon"] } -ndarray-stats = "0.6.0" -num-traits = "0.2" -rayon = "1.11.0" -arbolta = { path = "../arbolta" } -once_cell = "1.21.3" -clap = { version = "4.5.53", features = ["derive"] } -anyhow = "1.0" -indicatif = { version = "0.17.11", features = ["rayon"] } diff --git a/crates/fault/src/lib.rs b/crates/fault/src/lib.rs deleted file mode 100644 index 5ee948e..0000000 --- a/crates/fault/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod utils; - -// #[inline(always)] -// pub fn div_ceil(num: usize, denom: usize) -> usize { -// (num + denom - 1) / denom -// } diff --git a/crates/fault/src/main.rs b/crates/fault/src/main.rs deleted file mode 100644 index 5aca1d9..0000000 --- a/crates/fault/src/main.rs +++ /dev/null @@ -1,132 +0,0 @@ -// use anyhow::{Result, anyhow, ensure}; -// use arbolta::{bit::Bit, hardware_module::HardwareModule, yosys::Netlist}; -// use clap::Parser; -// use fault::utils::matmul; -// // use indicatif::ParallelProgressIterator; -// use ndarray::prelude::*; //parallel::prelude::* -// use ndarray_npy::read_npy; -// use std::path::PathBuf; -// // use std::sync::Mutex; - -// #[derive(Parser, Debug)] -// struct Args { -// /// Name of top module in netlist. -// #[clap(long)] -// top_module: String, -// /// Path to topological cell order file. -// #[clap(long)] -// torder_path: String, -// /// Path to Yosys netlist JSON file. -// #[clap(long)] -// netlist_path: String, -// /// Path to NumPy file for matrix A. -// #[clap(long)] -// a_path: PathBuf, -// /// Path to NumPy file for matrix B. -// #[clap(long)] -// b_path: PathBuf, -// /// Path to NumPy targets file. -// // #[clap(long)] -// // targets: PathBuf, -// /// Path to list of nets for fault injection campaign. -// // #[clap(long)] -// // nets: PathBuf, -// #[clap(long)] -// rows: usize, -// #[clap(long)] -// cols: usize, -// // #[clap(long)] -// // iterations: usize, -// #[clap(long)] -// x_width: usize, -// #[clap(long)] -// k_width: usize, -// #[clap(long)] -// m_width: usize, -// } - -// // TODO: Move setup to generic function and do dynamic dispatch here... -// #[allow(non_snake_case)] -// fn main() -> Result<()> { -// let flags = Args::parse(); - -// // Load and parse netlist and topological cell order -// let raw_netlist = std::fs::read(flags.netlist_path)?; -// let netlist = Netlist::from_slice(&raw_netlist)?; - -// // Setup design -// let (ROWS, COLS) = (flags.rows, flags.cols); // Copy for easier use - -// let mut design = HardwareModule::new(&flags.top_module, &netlist)?; -// design.set_clock(2, Bit::ONE)?; -// design.set_reset(3, Bit::ZERO)?; -// design.set_port_shape("sx_data_i", &[ROWS, flags.x_width])?; -// design.set_port_shape("sk_data_i", &[COLS, flags.k_width])?; -// design.set_port_shape("m_data_o", &[ROWS, flags.m_width])?; - -// // Load and process inputs -// let a: Array2 = read_npy(flags.a_path)?; -// let b: Array2 = read_npy(flags.b_path)?; - -// // a: (X, K), b: (K, Z) -// let (X, K, Z) = (a.nrows(), a.ncols(), b.ncols()); -// ensure!(K == b.nrows(), anyhow!("{} != {}", K, b.nrows())); - -// // Pad `a` and `b` for alignment with systolic array -// let (out_x, out_z) = (X.div_ceil(COLS) * COLS, (Z.div_ceil(ROWS) * ROWS)); - -// // TODO: May have to adjust for Array3 -// let mut a_pad = Array2::::zeros((out_x, K)); -// let mut b_pad = Array2::::zeros((K, out_z)); -// a_pad.slice_mut(s![0..X, 0..K]).assign(&a.view()); -// b_pad.slice_mut(s![0..K, 0..Z]).assign(&b.view()); - -// // Setup padded output array (must crop later) -// let mut out = Array2::::zeros((out_x, out_z)); -// matmul( -// &mut design, -// COLS, -// ROWS, -// a_pad.view(), -// b_pad.view(), -// out.view_mut(), -// )?; - -// let temp = out.slice(s![0..X, 0..Z]).to_owned(); -// println!("{temp}"); - -// /* -// // Load everything -// let mut design = HardwareModule::new_from_path(&flags.netlist, &flags.top_module)?; -// let inputs: Array3 = read_npy(&flags.inputs) -// .with_context(|| format!("Failed to read NumPy array from {:?}", flags.inputs))?; // (N, 28, 28) -// let weights: Array2 = read_npy(&flags.weights) -// .with_context(|| format!("Failed to read NumPy array from {:?}", flags.weights))?; // (10, 784) -// // let targets: Array2 = read_npy(&flags.targets) -// // .with_context(|| format!("Failed to read NumPy array from {:?}", flags.targets))?; - -// let nets: Vec = std::fs::read_to_string(&flags.nets)? -// .split_whitespace() -// .map(|x| x.parse().unwrap()) -// .collect(); - -// let designs: Vec> = (0..rayon::current_num_threads()) -// .map(|_| Mutex::new(design.clone())) -// .collect(); - -// // Start simulation -// nets.into_par_iter().progress().for_each(|n| { -// for stuck_val in [Bit::ZERO, Bit::ONE] { -// let thread_idx = rayon::current_thread_index().unwrap(); -// let design = &mut designs[thread_idx].lock().unwrap(); -// worker(design, &inputs.view(), &weights.view(), n, stuck_val).unwrap(); -// } -// }); - -// */ -// Ok(()) -// } - -fn main() { - todo!() -} diff --git a/crates/fault/src/utils.rs b/crates/fault/src/utils.rs deleted file mode 100644 index faabc81..0000000 --- a/crates/fault/src/utils.rs +++ /dev/null @@ -1,135 +0,0 @@ -use anyhow::Result; -use arbolta::{ - bit::{Bit, BitVec}, - hardware_module::HardwareModule, -}; -use ndarray::prelude::*; -use ndarray_npy::write_npy; -use ndarray_stats::QuantileExt; -use num_traits::{PrimInt, WrappingAdd, WrappingShl, WrappingSub}; - -// pub fn worker( -// design: &mut HardwareModule, -// inputs: &ArrayView3, -// weights: &ArrayView2, -// net: usize, -// stuck_val: Bit, -// ) -> Result<()> { -// design.reset(); -// design.stick_signal(net, stuck_val)?; - -// let preds: Array1 = inputs -// .axis_iter(Axis(0)) -// .map(|image| { -// // TODO: Fix hardcoded -// let x = image.to_shape((1, 28 * 28)).unwrap(); -// let mut logits = Array2::::zeros((10, 1)); - -// run_sa(design, &x.t().view(), &weights.t().view(), &mut logits).unwrap(); - -// logits.flatten().argmax().unwrap() as u8 -// }) -// .collect(); - -// design.unstick_signal(net)?; - -// write_npy(format!("net_{net}_val_{stuck_val}.npy",), &preds)?; - -// Ok(()) -// } - -/// Does a: (X_pad, K) @ b: (K, Z_pad) -> (X_pad, Z_pad) -pub fn matmul( - design: &mut HardwareModule, - cols: usize, - rows: usize, - a_pad: ArrayView2, // (X_pad, K) - b_pad: ArrayView2, // (K, Z_pad) - mut out: ArrayViewMut2, // (X_pad, Z_pad) -) -> Result<()> { - // Number of tiles in each dimension - let tiles_x = out.nrows() / cols; // along rows of a/out - let tiles_z = out.ncols() / rows; // along cols of b/out - - for i in 0..tiles_z { - let x_block = b_pad.slice(s![.., i * rows..(i + 1) * rows]); - - for j in 0..tiles_x { - let k_block = a_pad.slice(s![j * cols..(j + 1) * cols, ..]); - let mut out_block = out.slice_mut(s![j * cols..(j + 1) * cols, i * rows..(i + 1) * rows]); - run_sa( - design, - x_block.view(), - k_block.t().view(), - out_block.view_mut(), - )?; - } - } - - Ok(()) -} - -/// Run inputs through systolic array. -/// Expects x: (K,R), k: (K,C) -> y: (C,R) -#[inline] -pub fn run_sa( - design: &mut HardwareModule, - x: ArrayView2, // (K, R) - k: ArrayView2, // (K, C) - mut y: ArrayViewMut2, // (C, R) -) -> Result<()> { - let iterations: usize = x.shape()[0]; // K - // TODO: CHECK - let sx_size = design.get_port_shape("sx_data_i")?[1]; - let sz_size = design.get_port_shape("sk_data_i")?[1]; - - design.eval_reset_clocked(Some(1))?; - - // TODO: Check iterations, report failed... - for i in 0..iterations { - // SA not ready for inputs - while design.get_port("s_ready_o")?.bits[0] == Bit::ZERO { - // TODO: Check some cycle limit - design.eval_clocked(Some(1))?; - } - - design.set_port("s_valid_i", [Bit::ONE])?; - design.set_port( - "sx_data_i", - BitVec::from_ints(x.row(i).iter().copied(), Some(sx_size)), - )?; - design.set_port( - "sk_data_i", - BitVec::from_ints(k.row(i).iter().copied(), Some(sz_size)), - )?; - - if i == iterations - 1 { - design.set_port("s_last_i", [Bit::ONE])?; - } - - design.eval_clocked(Some(1))?; - } - - design.set_port("m_ready_i", [Bit::ONE])?; - design.set_port("s_valid_i", [Bit::ZERO])?; - design.set_port("s_last_i", [Bit::ZERO])?; - - let mut idx = 0; - loop { - if design.get_port("m_valid_o")?.bits[0] == Bit::ONE { - let m_data = design.get_port("m_data_o")?; - let m_data = m_data.to_ints::(None); // Module should set size - y.row_mut(idx).assign(&Array1::from_iter(m_data)); - - idx += 1; - } - - if design.get_port("m_last_o")?.bits[0] == Bit::ONE { - break; - } - - design.eval_clocked(Some(1))?; - } - - Ok(()) -} diff --git a/crates/python_bindings/Cargo.toml b/crates/python_bindings/Cargo.toml deleted file mode 100644 index 6a10979..0000000 --- a/crates/python_bindings/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "arbolta-python" -version = "0.1.0" -authors = ["AMD Research & Advanced Development"] -edition = "2024" - -[lib] -name = "arbolta" -crate-type = ["cdylib"] - -[dependencies] -numpy = "0.24.0" -num-traits = "0.2" -postcard = { workspace = true } -pyo3 = "0.24.2" -serde = { version = "1.0", features = ["derive"] } -thiserror = { workspace = true } - -[dependencies.arbolta] -path = "../arbolta" - -[features] -defaut = ["pyo3/extension-module"] - -[package.metadata.maturin] -python-source = "py_src" diff --git a/crates/python_bindings/py_src/arbolta/__init__.py b/crates/python_bindings/py_src/arbolta/__init__.py deleted file mode 100644 index 3556f74..0000000 --- a/crates/python_bindings/py_src/arbolta/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. -# SPDX-License-Identifier: MIT - -# type: ignore -from .arbolta import Design # For pickling -from .design import HardwareDesign, PortConfig, load, save - -__all__ = ["Design", "PortConfig", "HardwareDesign", "save", "load"] diff --git a/crates/python_bindings/py_src/arbolta/design.py b/crates/python_bindings/py_src/arbolta/design.py deleted file mode 100644 index 1719922..0000000 --- a/crates/python_bindings/py_src/arbolta/design.py +++ /dev/null @@ -1,291 +0,0 @@ -# Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. -# SPDX-License-Identifier: MIT - -from dataclasses import dataclass -from pathlib import Path -from typing import Any, Dict, List, Literal, Optional, Tuple, Union - -import numpy as np - -from .arbolta import Design - -__all__ = ["PortConfig", "HardwareDesign", "save", "load"] - - -@dataclass -class PortConfig: - """ - Configuration for a HardwareDesign port. - - Attributes - ---------- - shape : tuple - Interpret port bits with shape. - dtype : np.dtype | type - Interpret port bits as type. - clock : bool, optional - Port is a clock signal. - reset : bool, optional - Port is a reset signal. - polarity: bool, optional - Clock polarity of port. - """ - - shape: Tuple[int, int] = (1, 1) - dtype: Union[np.dtype, type] = np.uint - clock: bool = False - reset: bool = False - polarity: Optional[Literal[0, 1]] = None - - -@dataclass -class Port: - data: np.ndarray - updated: bool = False - - -class HardwarePorts: - def __init__(self, config: dict[str, PortConfig], design: Design): - _ports: Dict[str, Port] = {} - - port_name: str - port_config: PortConfig - for port_name, port_config in config.items(): - if port_config.reset and port_config.clock: - raise AttributeError(f"Port `{port_name}` cannot be a reset and clock") - - if port_config.reset: - if port_config.polarity not in [0, 1]: - raise ValueError(f"Unsupported polarity: `{port_config.polarity}`") - - design.set_reset(port_name, bool(port_config.polarity)) - - elif port_config.clock: - if port_config.polarity not in [0, 1]: - raise ValueError(f"Unsupported polarity: `{port_config.polarity}`") - - design.set_clock(port_name, bool(port_config.polarity)) - - design.set_port_shape(port_name, port_config.shape) - _ports[port_name] = Port( - np.zeros(port_config.shape[1], dtype=port_config.dtype) - ) - - super().__setattr__("_ports", _ports) - super().__setattr__("_design", design) - - def __getattr__(self, name: str) -> Any: - if "_ports" in self.__dict__ and "_design" in self.__dict__: - _ports: dict[str, Port] = self.__dict__["_ports"] - - if name not in _ports: - raise AttributeError(f"Port `{name}` does not exist") - - _design = self.__dict__["_design"] - - if not _design.is_port_input(name): - _design.get_port_numpy(name, _ports[name].data) - - return _ports[name].data - - else: - raise AttributeError("Ports not initialized") - - def __setattr__(self, name: str, value: Any) -> None: - if "_ports" in self.__dict__ and "_design" in self.__dict__: - _ports: dict[str, Port] = self.__dict__["_ports"] - - if name not in _ports: - raise AttributeError(f"Port `{name}` does not exist") - - np.copyto(_ports[name].data, value) - _ports[name].updated = True - - else: - raise AttributeError("Ports not initialized") - - -class HardwareDesign: - def __init__( # TODO: de-duplicate defaults - self, - netlist_path: str | Path, - torder_path: str | Path, - config: dict[str, PortConfig], - top_module: Optional[str] = None, - ): - """ - Parameters - ---------- - top_module : str - Name of top module. - netlist_path : str - Path to Yosys netlist JSON. - config : dict[str, PortConfig] - Configuration for design ports. - """ - self.design = Design(netlist_path, top_module, torder_path) - self.ports = HardwarePorts(config, self.design) - - def reset(self): - """ - Reset all design signals and registers to zero. - Resets all toggle to zero. - """ - self.design.reset() - - def eval_reset_clocked(self, cycles: Optional[int] = 1): - """ - Asserts reset signal and clocks design for 1 cycle. - - Raises - ------ - AttributeError: No reset and/or clock signal configured. - """ - self.design.eval_reset_clocked(cycles) - - def eval(self): - """ - Evaluates all cells in design. - """ - port: Port - for port_name, port in self.ports._ports.items(): - if port.updated: - self.design.set_port_numpy(port_name, port.data) - port.updated = False - - self.design.eval() - - def eval_clocked(self, cycles: Optional[int] = 1): - """ - Clocks and evaluates design for 1 cycle. - - Raises - ------ - AttributeError: No clock signal configured. - """ - port: Port - for port_name, port in self.ports._ports.items(): - if port.updated: - self.design.set_port_numpy(port_name, port.data) - port.updated = False - - self.design.eval_clocked(cycles) - - def cell_breakdown(self, module_name: Optional[str] = None) -> Dict[str, int]: - """ - Get count of each cell type in module. - - Parameters - ---------- - module_name : str, optional - Name of module. Defaults to top module. - - Returns - ------- - breakdown : dict - Dictionary of cell types and their count in given module. - - Raises - ------ - AttributeError: Specified module doesn't exist in design. - """ - if module_name is None: - return self.design.get_module_breakdown(self.design.top_module) - else: - return self.design.get_module_breakdown(module_name) - - def area(self, module_name: Optional[str] = None) -> float: - """ - Get area of module. Area units are specified by cell library used for - synthesis. - - Parameters - ---------- - module_name : str, optional - Name of module. Defaults to top module. - - Returns - ------- - area : float - Area of given module. - - Raises - ------ - AttributeError: Specified module doesn't exist in design. - """ - if module_name is None: - return self.design.get_module_area(self.design.top_module) - else: - return self.design.get_module_area(module_name) - - def total_toggle_count(self, module_name: Optional[str] = None) -> int: - """ - Get total toggle count (rising + falling) of module. - - Parameters - ---------- - module_name : str, optional - Name of module. Defaults to top module. - - Returns - ------- - toggle_count : int - Total toggle count of given module. - - Raises - ------ - AttributeError: Specified module doesn't exist in design. - """ - if module_name is None: - return self.design.get_module_total_toggle_count(self.design.top_module) - else: - return self.design.get_module_total_toggle_count(module_name) - - def submodule_toggles( - self, category: Literal["falling", "rising", "total"] - ) -> Dict[str, Dict[int, int]]: - return self.design.get_submodule_toggles(category) - - def submodule_nets(self) -> Dict[str, int]: - return self.design.get_submodule_nets() - - def submodule_net_map(self) -> Dict[str, Dict[str, list[int]]]: - return self.design.get_submodule_net_map() - - def module_names(self) -> List[str]: - """ - Get names of modules in top-level design module. - - Returns - ------- - modules : list - Module names. - """ - return self.design.get_module_names() - - def get_graph(self) -> str: - return self.design.get_graph() - - def signal_map(self) -> Dict[str, List[int]]: - return self.design.get_signal_map() - - def signal_nets_reverse(self) -> Dict[int, list[str]]: - return self.design.get_all_signal_nets_reverse() - - def stick_signal(self, net: int, val: Union[int, bool]) -> None: - return self.design.stick_signal(net, bool(val)) - - def unstick_signal(self, net: int) -> None: - return self.design.unstick_signal(net) - - def cell_info(self) -> Dict[str, Dict]: - return self.design.get_cell_info() - - -def save(path: str, design: HardwareDesign) -> None: - design.design.save(path) - - -def load(path: str) -> HardwareDesign: - return Design.load(path) - # assert RuntimeError("Unimplemented") diff --git a/crates/python_bindings/pyproject.toml b/crates/python_bindings/pyproject.toml deleted file mode 100644 index fa5abb9..0000000 --- a/crates/python_bindings/pyproject.toml +++ /dev/null @@ -1,23 +0,0 @@ -[project] -name = "arbolta" -requires-python = ">=3.8" -authors = [ - {name = 'AMD Research & Advanced Development'} -] -classifiers = [ - "Programming Language :: Rust", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", -] -dynamic = ["version"] - -dependencies = ["numpy>=2.1"] - -[build-system] -requires = ["maturin>=1.7,<2.0"] -build-backend = "maturin" - -[tool.maturin] -python-source = "py_src" -module-name = "arbolta.arbolta" -features = ["pyo3/extension-module"] diff --git a/crates/python_bindings/src/design.rs b/crates/python_bindings/src/design.rs deleted file mode 100644 index 2e03d40..0000000 --- a/crates/python_bindings/src/design.rs +++ /dev/null @@ -1,382 +0,0 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. -// SPDX-License-Identifier: MIT - -use crate::conversion::{ - bits_to_bool_numpy, bits_to_int_numpy, bool_numpy_to_bits, int_numpy_to_bits, -}; -use arbol::{ - hardware_module::{HardwareModule, ToggleCount}, - port::PortDirection, - yosys::{Netlist, parse_torder}, -}; -use pyo3::{ - exceptions::{PyAttributeError, PyException, PyValueError}, - prelude::*, - types::{PyBytes, PyDict}, -}; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, path::PathBuf}; - -#[pyclass(dict, module = "arbolta", name = "Design")] -#[derive(Deserialize, Serialize)] //Decode, Encode -pub struct PyDesign { - #[pyo3(get)] - pub top_module: String, - design: HardwareModule, // TODO: RENAME -} - -#[pymethods] -impl PyDesign { - #[new] - #[pyo3(signature = (netlist_path, top_module, torder_path))] - fn __new__( - netlist_path: PathBuf, - top_module: Option<&str>, - torder_path: PathBuf, - ) -> PyResult { - // Read raw JSON netlist - let raw_netlist = std::fs::read(netlist_path)?; - let netlist = - Netlist::from_slice(&raw_netlist).map_err(|e| PyValueError::new_err(format!("{e}")))?; - - // Read raw torder - let raw_torder = std::fs::read_to_string(torder_path)?; - let torder = parse_torder(&raw_torder); - - let design = HardwareModule::new(netlist, top_module, torder) - .map_err(|e| PyValueError::new_err(format!("{e}")))?; - - let top_module = design.netlist.top_module.clone(); - - Ok(Self { top_module, design }) - } - - // fn __setstate__(&mut self, state: &Bound<'_, PyBytes>) { - // let config = bincode::config::standard(); - // (*self, _) = bincode::decode_from_slice(state.as_bytes(), config).unwrap(); - // } - - // fn __getstate__<'py>(&self, py: Python<'py>) -> PyResult> { - // let config = bincode::config::standard(); - // match bincode::encode_to_vec(self, config) { - // Ok(bytes) => Ok(PyBytes::new(py, &bytes)), - // Err(err) => Err(PyValueError::new_err(format!("{err}"))), - // } - // } - - // fn __getnewargs__(&self) -> (String, String) { - // (self.top_module.clone(), self.netlist_path.clone()) - // } - - // fn save(&self, path: &str) -> PyResult<()> { - // self - // .design - // .save(path) - // .map_err(|e| PyValueError::new_err(format!("{e}"))) - // } - - // #[staticmethod] - // fn load(path: &str) -> PyResult { - // let design = HardwareModule::load(path).map_err(|e| PyValueError::new_err(format!("{e}")))?; - // let top_module = design.top_module.clone(); - - // Ok(Self { - // top_module, - // netlist_path: String::new(), // TODO: Fix - // design, - // }) - // } - - fn get_port_shape(&self, name: &str) -> PyResult<[usize; 2]> { - match self.design.get_port_shape(name) { - Ok(shape) => Ok(shape), - Err(err) => Err(PyAttributeError::new_err(format!("{err}"))), - } - } - - fn set_port_shape(&mut self, name: &str, shape: [usize; 2]) -> PyResult<()> { - if shape[0] != 1 { - return Err(PyValueError::new_err(format!( - "Only 1D shapes supported: {shape:?}" - ))); - } - - let internal_shape = self.get_port_shape(name)?; - let (num_elems, elem_size) = (shape[1], internal_shape[1] / shape[1]); - self - .design - .set_port_shape(name, &[num_elems, elem_size]) - .map_err(|e| PyValueError::new_err(format!("{e}"))) - } - - // fn get_graph(&self) -> String { - // self.design.graph.clone() - // } - - fn get_module_names(&self) -> Vec { - // TODO: How to handle top_module? - self - .design - .netlist - .modules - .iter() - .map(|p| p.join(".")) - .collect() - } - - // fn get_signal_map(&self) -> HashMap> { - // self.design.get_all_signal_nets() - // } - - // fn get_all_signal_nets(&self) -> HashMap> { - // self.design.get_all_signal_nets() - // } - - // fn get_all_signal_nets_reverse(&self) -> HashMap> { - // self.design.get_all_signal_nets_reverse() - // } - - // fn get_all_signal_values(&self) -> HashMap> { - // let signal_values = self.design.get_all_signal_values(); - // HashMap::from_iter( - // signal_values - // .into_iter() - // .map(|(name, bits)| (name, bits.into())), - // ) - // } - - // fn get_submodule_nets(&self) -> PyResult>> { - // let submodule_nets = self - // .design - // .get_submodule_nets() - // .map_err(|e| PyAttributeError::new_err(format!("{e}")))?; - - // let submodule_nets = HashMap::from_iter( - // submodule_nets - // .into_iter() - // .map(|(name, nets)| (name.join("."), nets.into_iter().collect())), - // ); - - // Ok(submodule_nets) - // } - - // fn get_submodule_toggles( - // &self, - // category: &str, - // ) -> PyResult>> { - // let category = match category { - // "falling" => ToggleCount::Falling, - // "rising" => ToggleCount::Rising, - // "total" => ToggleCount::Total, - // _ => { - // return Err(PyValueError::new_err(format!( - // "Unsupported category `{category}`" - // ))); - // } - // }; - - // let submodule_toggles = self - // .design - // .get_submodule_toggles(category) - // .map_err(|e| PyValueError::new_err(format!("{e}")))?; - - // let submodule_toggles = HashMap::from_iter( - // submodule_toggles - // .into_iter() - // .map(|(name, toggles)| (name.join("."), toggles)), - // ); - - // Ok(submodule_toggles) - // } - - // fn get_submodule_net_map(&self) -> HashMap>> { - // HashMap::from_iter( - // self - // .design - // .get_submodule_net_map() - // .into_iter() - // .map(|(name, nets)| (name.join("."), nets)), - // ) - // } - - // fn get_cell_info(&self, py: Python<'_>) -> PyResult { - // let all_cell_info = PyDict::new(py); - // for (name, cell_type) in self.design.cell_info.iter() { - // let cell_info = PyDict::new(py); - // cell_info.set_item("type", cell_type)?; - - // // TODO: Add other fields - // all_cell_info.set_item(name.clone(), cell_info)?; - // } - - // Ok(all_cell_info.into()) - // } - - pub fn stick_signal(&mut self, net: usize, val: bool) -> PyResult<()> { - self - .design - .stick_signal(net, val.into()) - .map_err(|e| PyException::new_err(format!("{e}"))) - } - - pub fn unstick_signal(&mut self, net: usize) -> PyResult<()> { - self - .design - .unstick_signal(net) - .map_err(|e| PyException::new_err(format!("{e}"))) - } - - fn set_clock(&mut self, name: &str, polarity: bool) -> PyResult<()> { - let nets = self - .design - .get_net(name) - .ok_or(PyException::new_err(format!("No signal `{name}`")))?; - - if nets.len() != 1 { - return Err(PyException::new_err("Clock net ambiguous".to_string())); - } - - self - .design - .set_clock(nets[0], polarity.into()) - .map_err(|e| PyException::new_err(format!("{e}"))) - } - - fn set_reset(&mut self, name: &str, polarity: bool) -> PyResult<()> { - let nets = self - .design - .get_net(name) - .ok_or(PyException::new_err(format!("No signal `{name}`")))?; - - if nets.len() != 1 { - return Err(PyException::new_err("Reset net ambiguous".to_string())); - } - - self - .design - .set_reset(nets[0], polarity.into()) - .map_err(|e| PyException::new_err(format!("{e}"))) - } - - fn reset(&mut self) { - self.design.reset() - } - - fn eval(&mut self) { - self.design.eval() - } - - // TODO: Fix this - fn eval_clocked(&mut self, cycles: u32) -> PyResult<()> { - self - .design - .eval_clocked(Some(cycles)) - .map_err(|e| PyException::new_err(format!("{e}"))) - } - - fn eval_reset_clocked(&mut self, cycles: u32) -> PyResult<()> { - self - .design - .eval_reset_clocked(Some(cycles)) - .map_err(|e| PyException::new_err(format!("{e}"))) - } - - #[allow(unused_variables)] - fn get_module_breakdown(&self, name: &str) -> PyResult> { - todo!() - } - - #[allow(unused_variables)] - fn get_module_area(&self, name: &str) -> PyResult { - todo!() - } - - #[allow(unused_variables)] - fn get_module_total_toggle_count(&self, name: &str) -> PyResult { - todo!() - } - - fn is_port_input(&self, name: &str) -> PyResult { - let direction = self - .design - .get_port_direction(name) - .map_err(|e| PyException::new_err(format!("{e}")))?; - - Ok(direction == PortDirection::Input) - } - - fn get_port_numpy(&self, name: &str, numpy_array: &Bound<'_, PyAny>) -> PyResult<()> { - let item_type = numpy_array.getattr("dtype")?.getattr("str")?.to_string(); - let elem_size = self.get_port_shape(name)?[1]; - - let bits = self - .design - .get_port(name) - .map_err(|e| PyAttributeError::new_err(format!("{e}")))?; - - match item_type.as_str() { - "|b1" => bits_to_bool_numpy(&bits, numpy_array), - "|u1" | " bits_to_int_numpy::(&bits, elem_size, numpy_array), - " bits_to_int_numpy::(&bits, elem_size, numpy_array), - " bits_to_int_numpy::(&bits, elem_size, numpy_array), - " bits_to_int_numpy::(&bits, elem_size, numpy_array), - "|i1" => bits_to_int_numpy::(&bits, elem_size, numpy_array), - " bits_to_int_numpy::(&bits, elem_size, numpy_array), - " bits_to_int_numpy::(&bits, elem_size, numpy_array), - " bits_to_int_numpy::(&bits, elem_size, numpy_array), - // Cast f16 to u16 - " bits_to_int_numpy::( - &bits, - elem_size, - &numpy_array.call_method1("view", ("uint16",))?, - ), - // Cast f32 to u32 - " bits_to_int_numpy::( - &bits, - elem_size, - &numpy_array.call_method1("view", ("uint32",))?, - ), - _ => Err(PyValueError::new_err(format!( - "Unsupported item type: {item_type}" - ))), - } - } - - fn set_port_numpy(&mut self, name: &str, numpy_array: &Bound<'_, PyAny>) -> PyResult<()> { - let item_type = numpy_array.getattr("dtype")?.getattr("str")?.to_string(); - let shape = self.get_port_shape(name)?; - let elem_size = shape[1]; - - let bits = match item_type.as_str() { - "|b1" => bool_numpy_to_bits(numpy_array)?, - "|u1" => int_numpy_to_bits::(numpy_array, elem_size)?, - " int_numpy_to_bits::(numpy_array, elem_size)?, - " int_numpy_to_bits::(numpy_array, elem_size)?, - " int_numpy_to_bits::(numpy_array, elem_size)?, - "|i1" => int_numpy_to_bits::(numpy_array, elem_size)?, - " int_numpy_to_bits::(numpy_array, elem_size)?, - " int_numpy_to_bits::(numpy_array, elem_size)?, - " int_numpy_to_bits::(numpy_array, elem_size)?, - // Cast to raw uint8 - " int_numpy_to_bits::(&numpy_array.call_method1("view", ("uint8",))?, elem_size)?, - // Cast f16 to u16 - " { - int_numpy_to_bits::(&numpy_array.call_method1("view", ("uint16",))?, elem_size)? - } - // Cast f32 to u32 - " { - int_numpy_to_bits::(&numpy_array.call_method1("view", ("uint32",))?, elem_size)? - } - _ => { - return Err(PyValueError::new_err(format!( - "Unsupported item type: {item_type}" - ))); - } - }; - - self - .design - .set_port(name, bits) - .map_err(|e| PyAttributeError::new_err(format!("{e}"))) - } -} diff --git a/crates/python_bindings/src/lib.rs b/crates/python_bindings/src/lib.rs deleted file mode 100644 index b6d63e9..0000000 --- a/crates/python_bindings/src/lib.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. -// SPDX-License-Identifier: MIT - -extern crate arbolta as arbol; - -pub mod conversion; -pub mod design; - -use pyo3::prelude::*; - -#[pymodule] -fn arbolta(m: &Bound<'_, PyModule>) -> PyResult<()> { - m.add_class::()?; - - Ok(()) -} diff --git a/crates/yosys_server/Cargo.toml b/crates/yosys_server/Cargo.toml deleted file mode 100644 index 0944ff6..0000000 --- a/crates/yosys_server/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "yosys_server" -version = "0.1.0" -edition = "2024" - -[dependencies] -clap = { workspace = true } -tarpc = { workspace = true } -tokio = { workspace = true } -anyhow = { workspace = true } -futures = { workspace = true } -serde = { workspace = true } -serde-error = { workspace = true } -yosys-netlist-json = { workspace = true } -tempfile = { workspace = true } -thiserror = {workspace = true} - -[lib] -name = "yosys_service" -path = "src/lib.rs" diff --git a/crates/yosys_server/src/lib.rs b/crates/yosys_server/src/lib.rs deleted file mode 100644 index dd36748..0000000 --- a/crates/yosys_server/src/lib.rs +++ /dev/null @@ -1,41 +0,0 @@ -pub mod server; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use yosys_netlist_json::Netlist; - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct FlattenRequest { - pub top_module: String, - pub netlist: Netlist, -} - -#[derive(Debug, Clone, Default, Deserialize, Serialize)] -pub struct SynthConfig { - pub run_synth: bool, // if false, just run proc - pub icells: bool, // -icells - pub lib: bool, // -lib - pub defer: bool, // -defer - pub defines: Option>, // -Dname[=definition] - pub parameters: Option>, // -chparam .. (if top module given) -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct SimpleSynthRequest { - pub verilog_source: String, - pub top_module: Option, // Hierarchy top if set - pub config: SynthConfig, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct NetlistResponse { - pub netlist: Option, - pub topo_order: Option>>, - pub error_log: String, -} - -#[tarpc::service] -pub trait Synthesis { - async fn flatten(request: FlattenRequest) -> Result; - async fn simple_synth(request: SimpleSynthRequest) - -> Result; -} diff --git a/crates/yosys_server/src/main.rs b/crates/yosys_server/src/main.rs deleted file mode 100644 index fa079d1..0000000 --- a/crates/yosys_server/src/main.rs +++ /dev/null @@ -1,54 +0,0 @@ -use anyhow::Result; -use clap::Parser; -use futures::{future, prelude::*}; -use std::net::{IpAddr, Ipv6Addr}; -use std::path::PathBuf; -use tarpc::{ - server::{self, Channel}, - tokio_serde::formats::Json, -}; -use yosys_service::Synthesis; -use yosys_service::server::SynthesisServer; - -async fn spawn(fut: impl Future + Send + 'static) { - tokio::spawn(fut); -} - -#[derive(Parser, Debug)] -struct Args { - #[arg(long, default_value_t = 8080)] - port: u16, - #[arg(long, default_value = "yosys")] - yosys_path: PathBuf, -} - -#[tokio::main] -async fn main() -> Result<()> { - let flags = Args::parse(); - let server_addr = (IpAddr::V6(Ipv6Addr::LOCALHOST), flags.port); - - let mut listener = tarpc::serde_transport::tcp::listen(server_addr, Json::default).await?; - listener.config_mut().max_frame_length(usize::MAX); - - println!("yosys_server"); - println!("\tListening on port: {}", flags.port); - println!("\tUsing Yosys path: {}", flags.yosys_path.display()); - - listener - // Ignore accept errors. - .filter_map(|r| future::ready(r.ok())) - .map(server::BaseChannel::with_defaults) - // serve is generated by the service attribute. It takes as input any type implementing - // the generated World trait. - .map(|channel| { - let server = SynthesisServer { - yosys_path: flags.yosys_path.clone(), - }; - channel.execute(server.serve()).for_each(spawn) - }) - // Max 10 channels. - .buffer_unordered(10) - .for_each(|_| async {}) - .await; - Ok(()) -} diff --git a/crates/yosys_server/src/server.rs b/crates/yosys_server/src/server.rs deleted file mode 100644 index 5e4f84f..0000000 --- a/crates/yosys_server/src/server.rs +++ /dev/null @@ -1,246 +0,0 @@ -use super::Synthesis; -use crate::*; -use anyhow::Result; -use std::fs::File; -use std::io::{Read, Write}; -use std::path::{Path, PathBuf}; -use std::process::Command; -use tarpc::context::Context; -use tempfile::TempDir; - -#[derive(Clone)] -pub struct SynthesisServer { - pub yosys_path: PathBuf, -} - -fn parse_torder(raw: &str) -> HashMap> { - let mut torder: HashMap> = HashMap::new(); - let mut current_module: Option<&str> = None; // If Some, save cells - - for line in raw.lines() { - let line = line.trim(); - - // Start new module - if let Some(module_name) = line.strip_prefix("module ") { - current_module = Some(module_name) - } else if let Some(module_name) = current_module - && let Some(cell_name) = line.strip_prefix("cell ") - { - torder - .entry(module_name.to_string()) - .or_default() - .push(cell_name.to_string()); - } - } - - torder -} - -fn generate_synth_command( - request: &SimpleSynthRequest, - rtl_path: &Path, - export_path: &Path, -) -> String { - // Default to System Verilog - let mut read_pass = vec![format!( - "read_verilog -sv {} {} {}", - if request.config.icells { "-icells" } else { "" }, - if request.config.lib { "-lib" } else { "" }, - if request.config.defer { "-defer" } else { "" }, - )]; - - if let Some(defines) = &request.config.defines { - defines - .iter() - .for_each(|d| read_pass.push(format!("-D{d}"))); - } - - read_pass.push(format!("{};", rtl_path.display())); - let read_pass = read_pass.join(" "); - - let mut hierarchy_pass = vec![]; - if let Some(top_module) = &request.top_module { - hierarchy_pass.push(format!("hierarchy -top {top_module}")); - - if let Some(parameters) = &request.config.parameters { - parameters - .iter() - .for_each(|(k, v)| hierarchy_pass.push(format!("-chparam {k} {v}"))); - } - - hierarchy_pass.push(";".to_string()); - } - let hierarchy_pass = hierarchy_pass.join(" "); - - let proc_pass = "proc; clean; autoname; setundef -zero; flatten -scopename;".to_string(); - let synth_pass = if request.config.run_synth { - format!("synth; {proc_pass}") - } else { - "".to_string() - }; - let export_pass = format!("write_json {}", export_path.display()); - - [ - read_pass, - hierarchy_pass, - proc_pass, - synth_pass, - export_pass, - ] - .join(" ") -} - -impl Synthesis for SynthesisServer { - async fn flatten( - self, - _context: Context, - request: FlattenRequest, - ) -> Result { - let temp_dir = TempDir::new().map_err(|e| serde_error::Error::new(&e))?; - - let original_netlist_path = temp_dir.path().join("original.json"); - let original_netlist_file = - File::create(&original_netlist_path).map_err(|e| serde_error::Error::new(&e))?; - - let flattened_netlist_path = temp_dir.path().join("flattened.json"); - let flattened_netlist_file = - File::create_new(&flattened_netlist_path).map_err(|e| serde_error::Error::new(&e))?; - - let topo_order_path = temp_dir.path().join("topo.txt"); - let mut topo_order_file = - File::create_new(&topo_order_path).map_err(|e| serde_error::Error::new(&e))?; - - // Write old netlist to file - request - .netlist - .to_writer(original_netlist_file) - .map_err(|e| serde_error::Error::new(&e))?; - - // Run Yosys - let command = Command::new(self.yosys_path) - .arg("-f") - .arg("json") - .arg(original_netlist_path) - .arg("-p") - .arg(format!( - "flatten -scopename; write_json {}; tee -o {} torder", - flattened_netlist_path.display(), - topo_order_path.display() - )) - .output() - .map_err(|e| serde_error::Error::new(&e))?; - - // Yosys failed - let error_log = String::from_utf8(command.stderr).unwrap(); - if !error_log.is_empty() { - return Ok(NetlistResponse { - netlist: None, - topo_order: None, - error_log, - }); - } - - // Load in flattened netlist - let flattened_netlist = yosys_netlist_json::Netlist::from_reader(flattened_netlist_file) - .map_err(|e| serde_error::Error::new(&e))?; - - // Load topological cell order - let mut topo_order = String::new(); - _ = topo_order_file - .read_to_string(&mut topo_order) - .map_err(|e| serde_error::Error::new(&e))?; - - let topo_order = parse_torder(&topo_order); - - Ok(NetlistResponse { - netlist: Some(flattened_netlist), - topo_order: Some(topo_order), - error_log, - }) - } - - // Can't have include unless full path... - async fn simple_synth( - self, - _context: Context, - request: SimpleSynthRequest, - ) -> Result { - let temp_dir = TempDir::new().map_err(|e| serde_error::Error::new(&e))?; - - let rtl_path = temp_dir.path().join("design.v"); - let mut rtl_file = File::create(&rtl_path).map_err(|e| serde_error::Error::new(&e))?; - - // Write source to file - rtl_file - .write(request.verilog_source.as_bytes()) - .map_err(|e| serde_error::Error::new(&e))?; - - let netlist_path = temp_dir.path().join("flattened.json"); - let netlist_file = File::create_new(&netlist_path).map_err(|e| serde_error::Error::new(&e))?; - - let torder_path = temp_dir.path().join("topo.txt"); - let mut torder_file = - File::create_new(&torder_path).map_err(|e| serde_error::Error::new(&e))?; - - let passes = generate_synth_command(&request, &rtl_path, &netlist_path); - // TODO: Hacky, fix this - let passes = [passes, format!("; tee -o {} torder", torder_path.display())].concat(); - println!("{passes}"); - - // Run Yosys - let command = Command::new(self.yosys_path) - .arg("-p") - .arg(passes) - .output() - .map_err(|e| serde_error::Error::new(&e))?; - - // Yosys failed - let error_log = String::from_utf8(command.stderr).unwrap(); - if !error_log.is_empty() { - return Ok(NetlistResponse { - netlist: None, - topo_order: None, - error_log, - }); - } - - // Load in netlist - let netlist = yosys_netlist_json::Netlist::from_reader(netlist_file) - .map_err(|e| serde_error::Error::new(&e))?; - - // Load topological cell order - let mut torder = String::new(); - _ = torder_file - .read_to_string(&mut torder) - .map_err(|e| serde_error::Error::new(&e))?; - - let torder = parse_torder(&torder); - - Ok(NetlistResponse { - netlist: Some(netlist), - topo_order: Some(torder), - error_log, - }) - } -} - -#[test] -fn test_parse_torder() { - let raw = r#" - - 8. Executing TORDER pass (print cells in topological order). - module alu - cell $flatten\i_adder.$add$ - cell $flatten\i_subtracter.$sub$ - cell $procmux$7 - - module alu2 - cell $flatten\i_adder.$add$ - cell $flatten\i_subtracter.$sub$ - cell $procmux$7 - - - "#; - let torder = parse_torder(raw); - println!("{torder:#?}"); -} From 3ff714c9f6945dfa2b2227ed77f5ddc701db4263 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 7 Jan 2026 00:45:55 +0000 Subject: [PATCH 34/64] Added support for dynamically registering cells --- crates/arbolta/Cargo.toml | 1 + crates/arbolta/src/cell/mod.rs | 36 +- crates/arbolta/src/cell/simcells.rs | 476 ++++++++------------ crates/arbolta/src/hardware_module.rs | 138 +++--- crates/arbolta/src/netlist_wrapper.rs | 51 ++- crates/arbolta/tests/test_simcell.rs | 255 ++++++++--- crates/arbolta/tests/test_simcell_module.rs | 36 +- crates/arbolta/tests/test_simlib_module.rs | 12 +- crates/arbolta_pyo3/arbolta.pyi | 8 +- crates/arbolta_pyo3/src/hardware_module.rs | 26 +- 10 files changed, 589 insertions(+), 450 deletions(-) diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index 47f5a0a..3203a6c 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -14,6 +14,7 @@ inventory = "0.3.21" ndarray = { workspace = true } num-traits = { workspace = true } once_cell = { workspace = true } +paste = "1.0.15" petgraph = { version = "0.8.3", features = ["serde-1"] } postcard = { workspace = true } rstest = { workspace = true } diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index 20a44f6..ca9106e 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -1,5 +1,6 @@ mod simcells; mod simlib; +// mod temp; mod test_helpers; // Re-export @@ -44,6 +45,13 @@ pub static CELL_DISPATCH: Lazy> = Lazy::new(|| { #[derive(Debug, Serialize, Deserialize, Clone)] /// Proxy for a standard-cell and basic unit of 'compute'. pub enum Cell { + // ASAP7 + HalfAdder, + HalfAdderInv, + FullAdder, + FullAdderInv, + AndOrReduce, + DffInv, // Sim Cells Buffer, Inverter, @@ -57,6 +65,8 @@ pub enum Cell { OrNot, Mux2, NMux2, + And3, + AndOr, AndOrInvert, OrAndInvert, Dff, @@ -101,14 +111,38 @@ pub enum CellError { Direction(String), } +pub type CellMapping = HashMap>)>; + pub fn create_cell( cell_type: &str, connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>, + mapping: Option<&CellMapping>, ) -> Result { + let (cell_type, mut connections) = if let Some(mapping) = mapping + && let Some((mapped_cell_type, mapped_connections)) = mapping.get(cell_type) + { + let connections = match mapped_connections { + Some(mapped_connections) => connections + .iter() + .map(|(&port_name, nets)| (mapped_connections[port_name].as_str(), nets.clone())) + .collect(), + None => connections.clone(), + }; + + (mapped_cell_type.as_str(), connections) + } else { + (cell_type, connections.clone()) + }; + let ctor = CELL_DISPATCH .get(cell_type) .ok_or_else(|| CellError::Unsupported(cell_type.to_string()))?; - Ok(ctor(connections, parameters)) + // Special case, buffer with no output + if cell_type == "$_BUF_" && !connections.contains_key("Y") { + connections.insert("Y", Box::from([0])); + } + + Ok(ctor(&connections, parameters)) } diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index e9768d9..205679a 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -1,108 +1,161 @@ -use super::{Cell, CellFn}; +use super::CellFn; use crate::{bit::Bit, cell::CellRegistration, signal::Signals}; use derive_more::Constructor; +use paste::paste; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; -macro_rules! define_unary_cell { - ($name:ident, $body:expr) => { +macro_rules! create_cell { + // 1-output + ($rtl_name:expr, $cell_type:ident { $($in_netn:ident),* }, $out_net:ident, $body:expr) => { #[derive(Debug, Clone, Constructor, Serialize, Deserialize)] - pub struct $name { - a_net: usize, - y_net: usize, + pub struct $cell_type { + $( + $in_netn: usize, + )* + + $out_net: usize } - impl CellFn for $name { + impl CellFn for $cell_type { #[inline] fn eval(&mut self, signals: &mut Signals) { - let a: Bit = signals.get_net(self.a_net); - signals.set_net(self.y_net, $body(a)); + $( + let $in_netn: Bit = signals.get_net(self.$in_netn); + )* + signals.set_net(self.$out_net, $body($($in_netn,)*)); } fn reset(&mut self) {} } - }; -} - -define_unary_cell!(Buffer, |x: Bit| x); -define_unary_cell!(Inverter, |x: Bit| !x); - -macro_rules! define_binary_cell { - ($name:ident, $body:expr) => { - #[derive(Debug, Clone, Constructor, Serialize, Deserialize)] - pub struct $name { - a_net: usize, - b_net: usize, - y_net: usize, - } - - impl CellFn for $name { - #[inline] - fn eval(&mut self, signals: &mut Signals) { - let inputs: [Bit; 2] = [signals.get_net(self.a_net), signals.get_net(self.b_net)]; - signals.set_net(self.y_net, $body(inputs)); - } - fn reset(&mut self) {} + paste! { + inventory::submit! {CellRegistration::new(&[$rtl_name], + |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { + $cell_type::new( + $( + connections[stringify!([<$in_netn:upper>])][0], + )* + connections[stringify!([<$out_net:upper>])][0] + ).into() + })} } }; -} - -define_binary_cell!(And, |x: [Bit; 2]| x[0] & x[1]); -define_binary_cell!(Nand, |x: [Bit; 2]| !(x[0] & x[1])); -define_binary_cell!(Or, |x: [Bit; 2]| x[0] | x[1]); -define_binary_cell!(Nor, |x: [Bit; 2]| !(x[0] | x[1])); -define_binary_cell!(Xor, |x: [Bit; 2]| x[0] ^ x[1]); -define_binary_cell!(Xnor, |x: [Bit; 2]| !(x[0] ^ x[1])); -define_binary_cell!(AndNot, |x: [Bit; 2]| x[0] & !x[1]); -define_binary_cell!(OrNot, |x: [Bit; 2]| x[0] | !x[1]); -macro_rules! define_ternary_cell { - ($name:ident, $op0_net:ident, $op1_net:ident, $op2_net:ident, $body:expr) => { + // N-output + ($rtl_name:expr, $cell_type:ident { $($in_netn:ident),* $(,)?}, { $($out_netn:ident),+ $(,)?}, $body:expr) => { #[derive(Debug, Clone, Constructor, Serialize, Deserialize)] - pub struct $name { - $op0_net: usize, - $op1_net: usize, - $op2_net: usize, - y_net: usize, + pub struct $cell_type { + $( + $in_netn: usize, + )* + + $( + $out_netn: usize, + )* } - impl CellFn for $name { + impl CellFn for $cell_type { #[inline] fn eval(&mut self, signals: &mut Signals) { - let inputs: [Bit; 3] = [ - signals.get_net(self.$op0_net), - signals.get_net(self.$op1_net), - signals.get_net(self.$op2_net), - ]; - signals.set_net(self.y_net, $body(inputs)); + $( + let $in_netn: Bit = signals.get_net(self.$in_netn); + )* + + let ( $($out_netn),+ ) = $body($($in_netn,)*); + + $( + signals.set_net(self.$out_netn, $out_netn); + )+ } fn reset(&mut self) {} } + + paste! { + inventory::submit! {CellRegistration::new(&[$rtl_name], + |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { + $cell_type::new( + $( + connections[stringify!([<$in_netn:upper>])][0], + )* + $( + connections[stringify!([<$out_netn:upper>])][0], + )* + ).into() + })} + } }; } -define_ternary_cell!(AndOrInvert, a_net, b_net, c_net, |x: [Bit; 3]| !((x[0] - & x[1]) - | x[2])); -define_ternary_cell!(OrAndInvert, a_net, b_net, c_net, |x: [Bit; 3]| !((x[0] - | x[1]) - & x[2])); - -define_ternary_cell!( - Mux2, - a_net, - b_net, - select_net, - |x: [Bit; 3]| if x[2].into() { x[1] } else { x[0] } +create_cell!("$_BUF_", Buffer { a }, y, |a: Bit| a); +create_cell!("$_NOT_", Inverter { a }, y, |a: Bit| !a); +create_cell!("$_AND_", And { a, b }, y, |a: Bit, b: Bit| a & b); +create_cell!("$_AND3_", And3 { a, b, c }, y, |a, b, c| a & b & c); +create_cell!("$_NAND_", Nand { a, b }, y, |a: Bit, b: Bit| !(a & b)); +create_cell!("$_OR_", Or { a, b }, y, |a: Bit, b: Bit| a | b); +create_cell!("$_NOR_", Nor { a, b }, y, |a: Bit, b: Bit| !(a | b)); +create_cell!("$_XOR_", Xor { a, b }, y, |a: Bit, b: Bit| a ^ b); +create_cell!("$_XNOR_", Xnor { a, b }, y, |a: Bit, b: Bit| !(a ^ b)); +create_cell!("$_ANDNOT_", AndNot { a, b }, y, |a: Bit, b: Bit| a & !b); +create_cell!("$_ORNOT_", OrNot { a, b }, y, |a: Bit, b: Bit| a | !b); +create_cell!( + "$_ANDOR_", + AndOr { a, b, c }, + y, + |a: Bit, b: Bit, c: Bit| (a & b) | c +); +create_cell!( + "$_ANDORREDUCE_", + AndOrReduce { a, b, c }, + y, + |a: Bit, b: Bit, c: Bit| (a & c) | (b & c) +); +create_cell!( + "$_AOI3_", + AndOrInvert { a, b, c }, + y, + |a: Bit, b: Bit, c: Bit| { !((a & b) | c) } +); +create_cell!( + "$_OAI3_", + OrAndInvert { a, b, c }, + y, + |a: Bit, b: Bit, c: Bit| { !((a | b) & c) } +); +create_cell!( + "$_MUX_", + Mux2 { a, b, s }, + y, + |a: Bit, b: Bit, select: Bit| if select.into() { b } else { a } ); -define_ternary_cell!( - NMux2, - a_net, - b_net, - select_net, - |x: [Bit; 3]| if x[2].into() { !x[1] } else { !x[0] } +create_cell!( + "$_NMUX_", + NMux2 { a, b, s }, + y, + |a: Bit, b: Bit, select: Bit| if select.into() { !b } else { !a } +); +create_cell!("$_HA_", HalfAdder { a, b }, {s, c}, |a: Bit, b: Bit| ( + a ^ b, + a & b +)); +create_cell!("$_HAI_", HalfAdderInv { a, b }, {sn, con}, |a: Bit, b: Bit| ( + !(a ^ b), + !(a & b) +)); +create_cell!( + "$_FA_", FullAdder {a, b, ci}, {s, co}, + |a: Bit, b: Bit, carry_in: Bit| { + let sum = a ^ b; + (sum ^ carry_in, (a & b) | (sum & carry_in)) + } +); +create_cell!( + "$_FAI_", FullAdderInv {a, b, ci}, {sn, con}, + |a: Bit, b: Bit, carry_in: Bit| { + let sum = a ^ b; + (!(sum ^ carry_in), !((a & b) | (sum & carry_in))) + } ); #[derive(Debug, Clone, Serialize, Deserialize, derive_new::new)] @@ -135,6 +188,60 @@ impl CellFn for Dff { } } +inventory::submit! { + CellRegistration::new(&["$_DFF_P_"], + |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { + Dff::new( + Bit::ONE, + connections["CLK"][0], + connections["D"][0], + connections["Q"][0], + ).into() + }) +} + +#[derive(Debug, Clone, Serialize, Deserialize, derive_new::new)] +pub struct DffInv { + polarity: Bit, + clock_net: usize, + data_in_net: usize, + data_out_net: usize, + #[new(default)] + last_clock: Bit, +} + +impl CellFn for DffInv { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + // Check if clock active for any polarity + let clock = !(signals.get_net(self.clock_net) ^ self.polarity); + + // Rising edge + if clock == Bit::ONE && self.last_clock == Bit::ZERO { + let data_in = signals.get_net(self.data_in_net); + signals.set_net(self.data_out_net, !data_in); + } + + self.last_clock = clock; + } + + fn reset(&mut self) { + self.last_clock = Bit::ZERO; + } +} + +inventory::submit! { + CellRegistration::new(&["$_DFF_PI_"], + |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { + DffInv::new( + Bit::ONE, + connections["CLK"][0], + connections["D"][0], + connections["QN"][0], + ).into() + }) +} + #[derive(Debug, Clone, Serialize, Deserialize, derive_new::new)] pub struct DffReset { clock_polarity: Bit, @@ -173,212 +280,17 @@ impl CellFn for DffReset { } } -// TODO: Create with macro... -// Cell Constructor functions -fn make_buf( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - Buffer::new(connections["A"][0], connections["Y"][0]).into() -} - -fn make_not( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - Inverter::new(connections["A"][0], connections["Y"][0]).into() -} - -fn make_and( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - And::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - ) - .into() -} - -fn make_nand( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - Nand::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - ) - .into() -} - -fn make_or( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - Or::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - ) - .into() -} - -fn make_nor( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - Nor::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - ) - .into() -} - -fn make_xor( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - Xor::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - ) - .into() -} - -fn make_xnor( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - Xnor::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - ) - .into() -} - -fn make_andnot( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - AndNot::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - ) - .into() -} - -fn make_ornot( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - OrNot::new( - connections["A"][0], - connections["B"][0], - connections["Y"][0], - ) - .into() -} - -fn make_mux( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - Mux2::new( - connections["A"][0], - connections["B"][0], - connections["S"][0], - connections["Y"][0], - ) - .into() +inventory::submit! { + CellRegistration::new(&["$_SDFF_PP0_"], + |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { + DffReset::new( + Bit::ONE, + Bit::ONE, + Bit::ZERO, + connections["C"][0], + connections["R"][0], + connections["D"][0], + connections["Q"][0], + ).into() + }) } - -fn make_nmux( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - NMux2::new( - connections["A"][0], - connections["B"][0], - connections["S"][0], - connections["Y"][0], - ) - .into() -} - -fn make_andorinvert( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - AndOrInvert::new( - connections["A"][0], - connections["B"][0], - connections["C"][0], - connections["Y"][0], - ) - .into() -} - -fn make_orandinvert( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - OrAndInvert::new( - connections["A"][0], - connections["B"][0], - connections["C"][0], - connections["Y"][0], - ) - .into() -} - -fn make_dff( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - Dff::new( - Bit::ONE, - connections["C"][0], - connections["D"][0], - connections["Q"][0], - ) - .into() -} - -fn make_dffreset( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - DffReset::new( - Bit::ONE, - Bit::ONE, - Bit::ZERO, - connections["C"][0], - connections["R"][0], - connections["D"][0], - connections["Q"][0], - ) - .into() -} - -inventory::submit! {CellRegistration::new(&["BUF", "$_BUF_"], make_buf)} -inventory::submit! {CellRegistration::new(&["NOT", "$_NOT_"], make_not)} -inventory::submit! {CellRegistration::new(&["AND", "$_AND_"], make_and)} -inventory::submit! {CellRegistration::new(&["NAND", "$_NAND_"], make_nand)} -inventory::submit! {CellRegistration::new(&["OR", "$_OR_"], make_or)} -inventory::submit! {CellRegistration::new(&["NOR", "$_NOR_"], make_nor)} -inventory::submit! {CellRegistration::new(&["XOR", "$_XOR_"], make_xor)} -inventory::submit! {CellRegistration::new(&["XNOR", "$_XNOR_"], make_xnor)} -inventory::submit! {CellRegistration::new(&["ANDNOT", "$_ANDNOT_"], make_andnot)} -inventory::submit! {CellRegistration::new(&["ORNOT", "$_ORNOT_"], make_ornot)} -inventory::submit! {CellRegistration::new(&["$_MUX_"], make_mux)} -inventory::submit! {CellRegistration::new(&["$_NMUX_"], make_nmux)} -inventory::submit! {CellRegistration::new(&["$_AOI3_"], make_andorinvert)} -inventory::submit! {CellRegistration::new(&["$_OAI3_"], make_orandinvert)} -inventory::submit! {CellRegistration::new(&["DFF", "$_DFF_P_"], make_dff)} -inventory::submit! {CellRegistration::new(&["$_SDFF_PP0_"], make_dffreset)} diff --git a/crates/arbolta/src/hardware_module.rs b/crates/arbolta/src/hardware_module.rs index fa34368..3ca1215 100644 --- a/crates/arbolta/src/hardware_module.rs +++ b/crates/arbolta/src/hardware_module.rs @@ -3,7 +3,7 @@ use crate::{ bit::{Bit, BitVec}, - cell::{Cell, CellError, CellFn}, + cell::{Cell, CellError, CellFn, CellMapping}, netlist_wrapper::NetlistWrapper, port::{Port, PortDirection, PortError}, signal::Signals, @@ -66,10 +66,12 @@ impl HardwareModule { netlist: Netlist, top_module: Option<&str>, torder: TopoOrder, + use_slash_hierarchy: bool, + cell_mapping: Option<&CellMapping>, ) -> Result { - let netlist = NetlistWrapper::new(netlist, top_module, torder)?; + let netlist = NetlistWrapper::new(netlist, top_module, torder, use_slash_hierarchy)?; - let cells = netlist.build_cells()?; + let cells = netlist.build_cells(cell_mapping)?; let global_net_max: usize = netlist .nets @@ -93,23 +95,57 @@ impl HardwareModule { }) } - // TODO: Fix - pub fn get_net_bits(&self, name: &str) -> Result { - let Some(nets) = self.get_net(name) else { - return Err(ModuleError::MissingPort(name.to_string())); + pub fn reset(&mut self) { + self.cells.iter_mut().for_each(|c| c.reset()); + self.signals.reset(); + self.signals.set_constant(0, Bit::ZERO); + self.signals.set_constant(1, Bit::ONE); + } + + // Eval until all signals have settled + pub fn eval(&mut self) { + loop { + self.signals.clear_dirty(); + self + .cells + .iter_mut() + .for_each(|cell| cell.eval(&mut self.signals)); + + if !self.signals.is_dirty() { + break; + } + } + } + + pub fn eval_clocked(&mut self, cycles: Option) -> Result<(), ModuleError> { + let Some((clock_net, polarity)) = self.clock_net else { + return Err(ModuleError::MissingClock); }; - let bits: BitVec = nets - .iter() - .map(|idx| self.signals.get_net(*idx)) - .collect::>() - .into(); + let cycles = cycles.unwrap_or(1); - Ok(bits) + for _ in 0..cycles { + self.eval(); + self.signals.set_net(clock_net, polarity); + self.eval(); + self.signals.set_net(clock_net, !polarity); + self.eval(); + } + + Ok(()) } - pub fn get_net(&self, name: &str) -> Option<&[usize]> { - self.netlist.names_to_nets.get(name).map(|v| &**v) + pub fn eval_reset_clocked(&mut self, cycles: Option) -> Result<(), ModuleError> { + let Some((reset_net, polarity)) = self.reset_net else { + return Err(ModuleError::MissingReset); + }; + + self.signals.set_net(reset_net, polarity); + self.eval_clocked(cycles)?; + self.signals.set_net(reset_net, !polarity); + self.eval(); + + Ok(()) } pub fn stick_signal(&mut self, net: usize, value: Bit) -> Result<(), ModuleError> { @@ -130,6 +166,25 @@ impl HardwareModule { } } + // TODO: Fix + pub fn get_net_bits(&self, name: &str) -> Result { + let Some(nets) = self.get_net(name) else { + return Err(ModuleError::MissingPort(name.to_string())); + }; + + let bits: BitVec = nets + .iter() + .map(|idx| self.signals.get_net(*idx)) + .collect::>() + .into(); + + Ok(bits) + } + + pub fn get_net(&self, name: &str) -> Option<&[usize]> { + self.netlist.names_to_nets.get(name).map(|v| &**v) + } + pub fn set_clock(&mut self, net: usize, polarity: Bit) -> Result<(), ModuleError> { if net >= self.signals.size { Err(ModuleError::MissingNet(net)) @@ -156,59 +211,6 @@ impl HardwareModule { } } - // Eval until all signals have settled - pub fn eval(&mut self) { - loop { - self.signals.clear_dirty(); - self - .cells - .iter_mut() - .for_each(|cell| cell.eval(&mut self.signals)); - - if !self.signals.is_dirty() { - break; - } - } - } - - pub fn eval_clocked(&mut self, cycles: Option) -> Result<(), ModuleError> { - let Some((clock_net, polarity)) = self.clock_net else { - return Err(ModuleError::MissingClock); - }; - - let cycles = cycles.unwrap_or(1); - - for _ in 0..cycles { - self.eval(); - self.signals.set_net(clock_net, polarity); - self.eval(); - self.signals.set_net(clock_net, !polarity); - self.eval(); - } - - Ok(()) - } - - pub fn eval_reset_clocked(&mut self, cycles: Option) -> Result<(), ModuleError> { - let Some((reset_net, polarity)) = self.reset_net else { - return Err(ModuleError::MissingReset); - }; - - self.signals.set_net(reset_net, polarity); - self.eval_clocked(cycles)?; - self.signals.set_net(reset_net, !polarity); - self.eval(); - - Ok(()) - } - - pub fn reset(&mut self) { - self.cells.iter_mut().for_each(|c| c.reset()); - self.signals.reset(); - self.signals.set_constant(0, Bit::ZERO); - self.signals.set_constant(1, Bit::ONE); - } - pub fn set_port_shape(&mut self, name: &str, shape: &[usize; 2]) -> Result<(), ModuleError> { match self.ports.get_mut(name) { Some(port) => Ok(port.set_shape(shape)?), diff --git a/crates/arbolta/src/netlist_wrapper.rs b/crates/arbolta/src/netlist_wrapper.rs index 7359f8d..e213472 100644 --- a/crates/arbolta/src/netlist_wrapper.rs +++ b/crates/arbolta/src/netlist_wrapper.rs @@ -1,9 +1,8 @@ use crate::{ - cell::{Cell, create_cell}, + cell::{Cell, CellMapping, create_cell}, hardware_module::ModuleError, port::{Port, PortDirection, parse_bit}, - yosys, - yosys::{RTLID, TopoOrder}, + yosys::{self, Netlist, RTLID, TopoOrder}, }; use indexmap::{IndexSet, indexmap}; use petgraph::prelude::*; @@ -13,7 +12,7 @@ use std::collections::{BTreeMap, HashMap, HashSet}; #[derive(Debug, Deserialize, Serialize, Default, Clone)] pub struct NetlistWrapper { pub top_module: String, - netlist: yosys::Netlist, + netlist: Netlist, pub cells: Vec, // Should be in topological order pub modules: HashSet>, pub nets: HashMap>, @@ -22,9 +21,10 @@ pub struct NetlistWrapper { impl NetlistWrapper { pub fn new( - netlist: yosys::Netlist, + netlist: Netlist, top_module: Option<&str>, torder: TopoOrder, + use_slash_hierarchy: bool, ) -> Result { let mut netlist = netlist; @@ -47,8 +47,8 @@ impl NetlistWrapper { .unwrap_or(usize::MAX) }); - let (cells, cell_modules) = parse_cells(module)?; - let nets = parse_nets(module)?; + let (cells, cell_modules) = parse_cells(module, use_slash_hierarchy)?; + let nets = parse_nets(module, use_slash_hierarchy)?; let names_to_nets = HashMap::from_iter(nets.iter().map(|(id, n)| (id.to_string(), n.clone()))); // Add modules only seen in nets (no synthesized cells) @@ -99,7 +99,8 @@ impl NetlistWrapper { module.cells.get(&id.to_string()) } - fn build_cell(&self, cell: &RTLID) -> Result { + fn build_cell(&self, cell: &RTLID, mapping: Option<&CellMapping>) -> Result { + println!("{cell:?}"); let synth_cell = self .find_cell(cell) .ok_or(ModuleError::MissingCell(cell.to_string()))?; @@ -125,15 +126,16 @@ impl NetlistWrapper { &synth_cell.cell_type, &connections, ¶meters, + mapping, )?) } - pub fn build_cells(&self) -> Result, ModuleError> { + pub fn build_cells(&self, mapping: Option<&CellMapping>) -> Result, ModuleError> { let cells = self .cells .iter() .rev() - .map(|c| Self::build_cell(self, c)) + .map(|c| Self::build_cell(self, c, mapping)) .collect::, _>>()?; Ok(cells) } @@ -216,7 +218,10 @@ impl NetlistWrapper { pub type NetlistGraph = DiGraph; /// Returns (cells, modules) -fn parse_cells(module: &mut yosys::Module) -> Result<(Vec, Vec>), ModuleError> { +fn parse_cells( + module: &mut yosys::Module, + use_slash_hierarchy: bool, +) -> Result<(Vec, Vec>), ModuleError> { let mut scope_cells = vec![]; let mut primitive_cells = vec![]; @@ -253,6 +258,16 @@ fn parse_cells(module: &mut yosys::Module) -> Result<(Vec, Vec = cell_name.split("/").collect(); + if let Some((name, parents)) = split_name.split_last() { + // Add new module + modules.push(parents.iter().map(|s| s.to_string()).collect()); + + RTLID::new(parents, name) + } else { + RTLID::new(&[], cell_name) + } } else { RTLID::new(&[], cell_name) }; @@ -266,7 +281,10 @@ fn parse_cells(module: &mut yosys::Module) -> Result<(Vec, Vec Result>, ModuleError> { +fn parse_nets( + module: &mut yosys::Module, + use_slash_hierarchy: bool, +) -> Result>, ModuleError> { let mut all_nets = HashMap::new(); let mut new_nets = indexmap! {}; @@ -281,6 +299,13 @@ fn parse_nets(module: &mut yosys::Module) -> Result> let hdlname_split: Vec<&str> = hdlname.split(" ").collect(); let (name, parents) = hdlname_split.split_last().unwrap(); RTLID::new(parents, name) + } else if use_slash_hierarchy { + let split_name: Vec<&str> = net_name.split("/").collect(); + if let Some((name, parents)) = split_name.split_last() { + RTLID::new(parents, name) + } else { + RTLID::new(&[], net_name) + } } else { RTLID::new(&[], net_name) }; @@ -301,7 +326,7 @@ fn parse_nets(module: &mut yosys::Module) -> Result> Ok(all_nets) } -fn find_top_module_name(netlist: &yosys::Netlist) -> Option<&str> { +fn find_top_module_name(netlist: &Netlist) -> Option<&str> { for (module_name, module_info) in &netlist.modules { if let Some(top_val) = module_info.attributes.get("top") && let Some(top) = top_val.to_number() diff --git a/crates/arbolta/tests/test_simcell.rs b/crates/arbolta/tests/test_simcell.rs index 00eb7e9..8dc26bc 100644 --- a/crates/arbolta/tests/test_simcell.rs +++ b/crates/arbolta/tests/test_simcell.rs @@ -6,18 +6,28 @@ use arbolta::cell::*; use arbolta::signal::Signals; use rstest::rstest; +fn convert_bits(x: [u8; T]) -> [Bit; T] { + let mut converted = [Bit::ZERO; T]; + for i in 0..T { + converted[i] = Bit::from_int(x[i]).unwrap(); + } + converted +} + #[rstest] #[case::buffer(Box::new(Buffer::new(0,1)), [ // (A, Y) - (Bit::ZERO, Bit::ZERO), - (Bit::ONE, Bit::ONE), + (0, 0), + (1, 1), ])] #[case::inverter(Box::new(Inverter::new(0,1)), [ // (A, Y) - (Bit::ZERO, Bit::ONE), - (Bit::ONE, Bit::ZERO), + (0, 1), + (1, 0), ])] -fn test_cell_unary(#[case] mut cell: Box, #[case] cases: [(Bit, Bit); 2]) { +fn test_cell_unary(#[case] mut cell: Box, #[case] cases: [(u8, u8); 2]) { let mut signals = Signals::new(2); for (a, expected) in cases { + let [a, expected] = convert_bits([a, expected]); + signals.set_net(0, a); cell.eval(&mut signals); assert_eq!(signals.get_net(1), expected, "input `{a}`") @@ -26,56 +36,58 @@ fn test_cell_unary(#[case] mut cell: Box, #[case] cases: [(Bit, Bit) #[rstest] #[case::and(Box::new(And::new(0, 1, 2)), [ // (A, B, Y) - (Bit::ZERO, Bit::ZERO, Bit::ZERO), - (Bit::ZERO, Bit::ONE, Bit::ZERO), - (Bit::ONE, Bit::ZERO, Bit::ZERO), - (Bit::ONE, Bit::ONE, Bit::ONE), + (0, 0, 0), + (0, 1, 0), + (1, 0, 0), + (1, 1, 1), ])] #[case::nand(Box::new(Nand::new(0, 1, 2)), [ // (A, B, Y) - (Bit::ZERO, Bit::ZERO, Bit::ONE), - (Bit::ZERO, Bit::ONE, Bit::ONE), - (Bit::ONE, Bit::ZERO, Bit::ONE), - (Bit::ONE, Bit::ONE, Bit::ZERO), + (0, 0, 1), + (0, 1, 1), + (1, 0, 1), + (1, 1, 0), ])] #[case::or(Box::new(Or::new(0, 1, 2)), [ // (A, B, Y) - (Bit::ZERO, Bit::ZERO, Bit::ZERO), - (Bit::ZERO, Bit::ONE, Bit::ONE), - (Bit::ONE, Bit::ZERO, Bit::ONE), - (Bit::ONE, Bit::ONE, Bit::ONE), + (0, 0, 0), + (0, 1, 1), + (1, 0, 1), + (1, 1, 1), ])] #[case::nor(Box::new(Nor::new(0, 1, 2)), [ // (A, B, Y) - (Bit::ZERO, Bit::ZERO, Bit::ONE), - (Bit::ZERO, Bit::ONE, Bit::ZERO), - (Bit::ONE, Bit::ZERO, Bit::ZERO), - (Bit::ONE, Bit::ONE, Bit::ZERO), + (0, 0, 1), + (0, 1, 0), + (1, 0, 0), + (1, 1, 0), ])] #[case::xor(Box::new(Xor::new(0, 1, 2)), [ // (A, B, Y) - (Bit::ZERO, Bit::ZERO, Bit::ZERO), - (Bit::ZERO, Bit::ONE, Bit::ONE), - (Bit::ONE, Bit::ZERO, Bit::ONE), - (Bit::ONE, Bit::ONE, Bit::ZERO), + (0, 0, 0), + (0, 1, 1), + (1, 0, 1), + (1, 1, 0), ])] #[case::xnor(Box::new(Xnor::new(0, 1, 2)), [ // (A, B, Y) - (Bit::ZERO, Bit::ZERO, Bit::ONE), - (Bit::ZERO, Bit::ONE, Bit::ZERO), - (Bit::ONE, Bit::ZERO, Bit::ZERO), - (Bit::ONE, Bit::ONE, Bit::ONE), + (0, 0, 1), + (0, 1, 0), + (1, 0, 0), + (1, 1, 1), ])] #[case::andnot(Box::new(AndNot::new(0, 1, 2)), [ // (A, B, Y) - (Bit::ZERO, Bit::ZERO, Bit::ZERO), - (Bit::ZERO, Bit::ONE, Bit::ZERO), - (Bit::ONE, Bit::ZERO, Bit::ONE), - (Bit::ONE, Bit::ONE, Bit::ZERO), + (0, 0, 0), + (0, 1, 0), + (1, 0, 1), + (1, 1, 0), ])] #[case::ornot(Box::new(OrNot::new(0, 1, 2)), [ // (A, B, Y) - (Bit::ZERO, Bit::ZERO, Bit::ONE), - (Bit::ZERO, Bit::ONE, Bit::ZERO), - (Bit::ONE, Bit::ZERO, Bit::ONE), - (Bit::ONE, Bit::ONE, Bit::ONE), + (0, 0, 1), + (0, 1, 0), + (1, 0, 1), + (1, 1, 1), ])] -fn test_cell_binary(#[case] mut cell: Box, #[case] cases: [(Bit, Bit, Bit); 4]) { +fn test_cell_binary(#[case] mut cell: Box, #[case] cases: [(u8, u8, u8); 4]) { let mut signals = Signals::new(3); for (a, b, expected) in cases { + let [a, b, expected] = convert_bits([a, b, expected]); + signals.set_net(0, a); signals.set_net(1, b); @@ -86,48 +98,80 @@ fn test_cell_binary(#[case] mut cell: Box, #[case] cases: [(Bit, Bit #[rstest] #[case::mux2(Box::new(Mux2::new(0, 1, 2, 3)), [ // (A, B, S, Y) - (Bit::ZERO, Bit::ZERO, Bit::ZERO, Bit::ZERO), - (Bit::ZERO, Bit::ZERO, Bit::ONE, Bit::ZERO), - (Bit::ZERO, Bit::ONE, Bit::ZERO, Bit::ZERO), - (Bit::ZERO, Bit::ONE, Bit::ONE, Bit::ONE), - (Bit::ONE, Bit::ZERO, Bit::ZERO, Bit::ONE), - (Bit::ONE, Bit::ZERO, Bit::ONE, Bit::ZERO), - (Bit::ONE, Bit::ONE, Bit::ZERO, Bit::ONE), - (Bit::ONE, Bit::ONE, Bit::ONE, Bit::ONE), + (0, 0, 0, 0), + (0, 0, 1, 0), + (0, 1, 0, 0), + (0, 1, 1, 1), + (1, 0, 0, 1), + (1, 0, 1, 0), + (1, 1, 0, 1), + (1, 1, 1, 1), ])] #[case::nmux2(Box::new(NMux2::new(0, 1, 2, 3)), [ // (A, B, S, Y) - (Bit::ZERO, Bit::ZERO, Bit::ZERO, Bit::ONE), - (Bit::ZERO, Bit::ZERO, Bit::ONE, Bit::ONE), - (Bit::ZERO, Bit::ONE, Bit::ZERO, Bit::ONE), - (Bit::ZERO, Bit::ONE, Bit::ONE, Bit::ZERO), - (Bit::ONE, Bit::ZERO, Bit::ZERO, Bit::ZERO), - (Bit::ONE, Bit::ZERO, Bit::ONE, Bit::ONE), - (Bit::ONE, Bit::ONE, Bit::ZERO, Bit::ZERO), - (Bit::ONE, Bit::ONE, Bit::ONE, Bit::ZERO), + (0, 0, 0, 1), + (0, 0, 1, 1), + (0, 1, 0, 1), + (0, 1, 1, 0), + (1, 0, 0, 0), + (1, 0, 1, 1), + (1, 1, 0, 0), + (1, 1, 1, 0), +])] +#[case::and3(Box::new(And3::new(0, 1, 2, 3)), [ // (A, B, C, Y) + (0, 0, 0, 0), + (0, 0, 1, 0), + (0, 1, 0, 0), + (0, 1, 1, 0), + (1, 0, 0, 0), + (1, 0, 1, 0), + (1, 1, 0, 0), + (1, 1, 1, 1), +])] +#[case::andor(Box::new(AndOr::new(0, 1, 2, 3)), [ // (A, B, C, Y) + (0, 0, 0, 0), + (0, 0, 1, 1), + (0, 1, 0, 0), + (0, 1, 1, 1), + (1, 0, 0, 0), + (1, 0, 1, 1), + (1, 1, 0, 1), + (1, 1, 1, 1), ])] #[case::andorinvert(Box::new(AndOrInvert::new(0, 1, 2, 3)), [ // (A, B, C, Y) - (Bit::ZERO, Bit::ZERO, Bit::ZERO, Bit::ONE), - (Bit::ZERO, Bit::ZERO, Bit::ONE, Bit::ZERO), - (Bit::ZERO, Bit::ONE, Bit::ZERO, Bit::ONE), - (Bit::ZERO, Bit::ONE, Bit::ONE, Bit::ZERO), - (Bit::ONE, Bit::ZERO, Bit::ZERO, Bit::ONE), - (Bit::ONE, Bit::ZERO, Bit::ONE, Bit::ZERO), - (Bit::ONE, Bit::ONE, Bit::ZERO, Bit::ZERO), - (Bit::ONE, Bit::ONE, Bit::ONE, Bit::ZERO), + (0, 0, 0, 1), + (0, 0, 1, 0), + (0, 1, 0, 1), + (0, 1, 1, 0), + (1, 0, 0, 1), + (1, 0, 1, 0), + (1, 1, 0, 0), + (1, 1, 1, 0), +])] +#[case::andorreduce(Box::new(AndOrReduce::new(0, 1, 2, 3)), [ // (A, B, C, Y) + (0, 0, 0, 0), + (0, 0, 1, 0), + (0, 1, 0, 0), + (0, 1, 1, 1), + (1, 0, 0, 0), + (1, 0, 1, 1), + (1, 1, 0, 0), + (1, 1, 1, 1), ])] #[case::orandinvert(Box::new(OrAndInvert::new(0, 1, 2, 3)), [ // (A, B, C, Y) - (Bit::ZERO, Bit::ZERO, Bit::ZERO, Bit::ONE), - (Bit::ZERO, Bit::ZERO, Bit::ONE, Bit::ONE), - (Bit::ZERO, Bit::ONE, Bit::ZERO, Bit::ONE), - (Bit::ZERO, Bit::ONE, Bit::ONE, Bit::ZERO), - (Bit::ONE, Bit::ZERO, Bit::ZERO, Bit::ONE), - (Bit::ONE, Bit::ZERO, Bit::ONE, Bit::ZERO), - (Bit::ONE, Bit::ONE, Bit::ZERO, Bit::ONE), - (Bit::ONE, Bit::ONE, Bit::ONE, Bit::ZERO), -])] -fn test_cell_ternary(#[case] mut cell: Box, #[case] cases: [(Bit, Bit, Bit, Bit); 8]) { + (0, 0, 0, 1), + (0, 0, 1, 1), + (0, 1, 0, 1), + (0, 1, 1, 0), + (1, 0, 0, 1), + (1, 0, 1, 0), + (1, 1, 0, 1), + (1, 1, 1, 0), +])] +fn test_cell_ternary(#[case] mut cell: Box, #[case] cases: [(u8, u8, u8, u8); 8]) { let mut signals = Signals::new(4); for (a, b, c, expected) in cases { + let [a, b, c, expected] = convert_bits([a, b, c, expected]); + signals.set_net(0, a); signals.set_net(1, b); signals.set_net(2, c); @@ -136,6 +180,73 @@ fn test_cell_ternary(#[case] mut cell: Box, #[case] cases: [(Bit, Bi } } +#[rstest] +#[case::half_adder(Box::new(HalfAdder::new(0, 1, 2, 3)), [ // A, B, SO, CO + (0, 0, 0, 0), + (0, 1, 1, 0), + (1, 0, 1, 0), + (1, 1, 0, 1), +])] +#[case::half_adder_inv(Box::new(HalfAdderInv::new(0, 1, 2, 3)), [ // A, B, SO, CO + (0, 0, 1, 1), + (0, 1, 0, 1), + (1, 0, 0, 1), + (1, 1, 1, 0), +])] +fn test_cell_binary_two_output( + #[case] mut cell: Box, + #[case] cases: [(u8, u8, u8, u8); 4], +) { + let mut signals = Signals::new(4); + for (a, b, exp_x, exp_y) in cases { + let [a, b, exp_x, exp_y] = convert_bits([a, b, exp_x, exp_y]); + + signals.set_net(0, a); + signals.set_net(1, b); + cell.eval(&mut signals); + assert_eq!(signals.get_net(2), exp_x, "inputs `{a}`, `{b}`"); + assert_eq!(signals.get_net(3), exp_y, "inputs `{a}`, `{b}`"); + } +} + +#[rstest] +#[case::full_adder(Box::new(FullAdder::new(0, 1, 2, 3, 4)), [ // A, B, CI, SO, CO + (0, 0, 0, 0, 0), + (0, 0, 1, 1, 0), + (0, 1, 0, 1, 0), + (0, 1, 1, 0, 1), + (1, 0, 0, 1, 0), + (1, 0, 1, 0, 1), + (1, 1, 0, 0, 1), + (1, 1, 1, 1, 1), +])] +#[case::full_adder_inv(Box::new(FullAdderInv::new(0, 1, 2, 3, 4)), [ // A, B, CI, SO, CO + (0, 0, 0, 1, 1), + (0, 0, 1, 0, 1), + (0, 1, 0, 0, 1), + (0, 1, 1, 1, 0), + (1, 0, 0, 0, 1), + (1, 0, 1, 1, 0), + (1, 1, 0, 1, 0), + (1, 1, 1, 0, 0), +])] +fn test_cell_ternary_two_output( + #[case] mut cell: Box, + #[case] cases: [(u8, u8, u8, u8, u8); 8], +) { + let mut signals = Signals::new(5); + for (a, b, c, exp_x, exp_y) in cases { + let [a, b, c, exp_x, exp_y] = convert_bits([a, b, c, exp_x, exp_y]); + + signals.set_net(0, a); + signals.set_net(1, b); + signals.set_net(2, c); + cell.eval(&mut signals); + assert_eq!(signals.get_net(3), exp_x, "inputs `{a}, {b}, {c}`"); + assert_eq!(signals.get_net(4), exp_y, "inputs `{a}, {b}, {c}`"); + } +} + #[rstest] fn test_cell_dff_posedge() { let (clock, data_in, data_out) = (0, 1, 2); diff --git a/crates/arbolta/tests/test_simcell_module.rs b/crates/arbolta/tests/test_simcell_module.rs index 8bcca68..0a3dd43 100644 --- a/crates/arbolta/tests/test_simcell_module.rs +++ b/crates/arbolta/tests/test_simcell_module.rs @@ -22,7 +22,14 @@ static CELL_WRAPPER_NETLIST: Lazy = fn test_module_unary_cell(#[case] cell: &str, #[case] cases: [(u8, u8); 2]) { let cell_type = cell.strip_suffix("_WRAPPER").unwrap(); let torder = HashMap::from([(cell, vec![cell_type])]); - let mut module = HardwareModule::new(CELL_WRAPPER_NETLIST.clone(), Some(cell), torder).unwrap(); + let mut module = HardwareModule::new( + CELL_WRAPPER_NETLIST.clone(), + Some(cell), + torder, + false, + None, + ) + .unwrap(); for (a, expected) in cases { module.set_port("A", BitVec::from_int(a, None)).unwrap(); @@ -78,7 +85,14 @@ fn test_module_unary_cell(#[case] cell: &str, #[case] cases: [(u8, u8); 2]) { fn test_module_binary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8); 4]) { let cell_type = cell.strip_suffix("_WRAPPER").unwrap(); let torder = HashMap::from([(cell, vec![cell_type])]); - let mut module = HardwareModule::new(CELL_WRAPPER_NETLIST.clone(), Some(cell), torder).unwrap(); + let mut module = HardwareModule::new( + CELL_WRAPPER_NETLIST.clone(), + Some(cell), + torder, + false, + None, + ) + .unwrap(); for (a, b, expected) in cases { module.set_port("A", BitVec::from_int(a, None)).unwrap(); @@ -113,7 +127,14 @@ fn test_module_binary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8); 4]) fn test_module_ternary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8, u8); 8]) { let cell_type = cell.strip_suffix("_WRAPPER").unwrap(); let torder = HashMap::from([(cell, vec![cell_type])]); - let mut module = HardwareModule::new(CELL_WRAPPER_NETLIST.clone(), Some(cell), torder).unwrap(); + let mut module = HardwareModule::new( + CELL_WRAPPER_NETLIST.clone(), + Some(cell), + torder, + false, + None, + ) + .unwrap(); for (a, b, c, expected) in cases { module.set_port("A", BitVec::from_int(a, None)).unwrap(); @@ -149,7 +170,14 @@ fn test_module_ternary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8, u8) fn test_module_mux_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8, u8); 8]) { let cell_type = cell.strip_suffix("_WRAPPER").unwrap(); let torder = HashMap::from([(cell, vec![cell_type])]); - let mut module = HardwareModule::new(CELL_WRAPPER_NETLIST.clone(), Some(cell), torder).unwrap(); + let mut module = HardwareModule::new( + CELL_WRAPPER_NETLIST.clone(), + Some(cell), + torder, + false, + None, + ) + .unwrap(); for (a, b, s, expected) in cases { module.set_port("A", BitVec::from_int(a, None)).unwrap(); diff --git a/crates/arbolta/tests/test_simlib_module.rs b/crates/arbolta/tests/test_simlib_module.rs index 0776032..7728c58 100644 --- a/crates/arbolta/tests/test_simlib_module.rs +++ b/crates/arbolta/tests/test_simlib_module.rs @@ -50,7 +50,7 @@ fn test_add(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] }, ); - let mut module = HardwareModule::new(netlist, None, torder).unwrap(); + let mut module = HardwareModule::new(netlist, None, torder, false, None).unwrap(); module.set_port("A", a).unwrap(); module.set_port("B", b).unwrap(); @@ -80,7 +80,7 @@ fn test_mux(#[case] select: Bit, #[case] a: BitVec, #[case] b: BitVec, #[case] e }, ); - let mut module = HardwareModule::new(netlist, None, torder).unwrap(); + let mut module = HardwareModule::new(netlist, None, torder, false, None).unwrap(); module.set_port("S", [select]).unwrap(); module.set_port("A", a).unwrap(); @@ -113,7 +113,7 @@ fn test_reg(#[case] polarity: Bit, #[case] data_in: BitVec) { }, ); - let mut module = HardwareModule::new(netlist, None, torder).unwrap(); + let mut module = HardwareModule::new(netlist, None, torder, false, None).unwrap(); let clock_net = module.get_net("CLK").unwrap()[0]; module.set_clock(clock_net, polarity).unwrap(); @@ -162,7 +162,7 @@ fn test_logic_and(#[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec }, ); - let mut module = HardwareModule::new(netlist, None, torder).unwrap(); + let mut module = HardwareModule::new(netlist, None, torder, false, None).unwrap(); module.set_port("A", a).unwrap(); module.set_port("B", b).unwrap(); @@ -195,7 +195,7 @@ fn test_logic_not(#[case] a: BitVec, #[case] expected: BitVec) { }, ); - let mut module = HardwareModule::new(netlist, None, torder).unwrap(); + let mut module = HardwareModule::new(netlist, None, torder, false, None).unwrap(); module.set_port("A", a).unwrap(); module.eval(); @@ -226,7 +226,7 @@ fn test_reduce_or(#[case] a: BitVec, #[case] expected: BitVec) { }, ); - let mut module = HardwareModule::new(netlist, None, torder).unwrap(); + let mut module = HardwareModule::new(netlist, None, torder, false, None).unwrap(); module.set_port("A", a).unwrap(); module.eval(); diff --git a/crates/arbolta_pyo3/arbolta.pyi b/crates/arbolta_pyo3/arbolta.pyi index fa2833e..bae03a0 100644 --- a/crates/arbolta_pyo3/arbolta.pyi +++ b/crates/arbolta_pyo3/arbolta.pyi @@ -60,8 +60,12 @@ class HardwareDesign: :type torder_path: str | Path | PathLike :param config: Configuration for design ports :type config: dict[str, PortConfig] - :param top_module: Name of top module + :param use_slash_hierarchy: Use slash hierarchy, defaults to False + :type use_slash_hierarchy: bool + :param top_module: Name of top module, defaults to None (find automatically) :type top_module: str, optional + :param cell_mapping: Define additional cell types + :type cell_mapping: dict[str, tuple[str, Optional[dict[str, str]]]], optional :var ports: Access to simulated module ports :vartype ports: Ports @@ -74,7 +78,9 @@ class HardwareDesign: netlist_path: str | Path | PathLike, torder_path: str | Path | PathLike, config: dict[str, PortConfig], + use_slash_hierarchy: bool = False, top_module: Optional[str] = None, + cell_mapping: Optional[dict[str, tuple[str, Optional[dict[str, str]]]]] = None, ) -> None: ... def reset(self) -> None: """ diff --git a/crates/arbolta_pyo3/src/hardware_module.rs b/crates/arbolta_pyo3/src/hardware_module.rs index 1c8f9b9..7535d83 100644 --- a/crates/arbolta_pyo3/src/hardware_module.rs +++ b/crates/arbolta_pyo3/src/hardware_module.rs @@ -7,6 +7,7 @@ use crate::conversion::{ use crate::ports::{PortConfig, Ports}; use arbolta::bit::Bit; use arbolta::{ + cell::CellMapping, hardware_module::HardwareModule, port::PortDirection, yosys::{Netlist, parse_torder}, @@ -30,6 +31,8 @@ impl HardwareDesign { netlist_path: PathBuf, top_module: Option<&str>, torder_path: PathBuf, + use_slash_hierarchy: bool, + cell_mapping: Option<&CellMapping>, ) -> anyhow::Result { // Read raw JSON netlist let raw_netlist = std::fs::read(netlist_path)?; @@ -39,7 +42,13 @@ impl HardwareDesign { let raw_torder = std::fs::read_to_string(torder_path)?; let torder = parse_torder(&raw_torder); - let module = HardwareModule::new(netlist, top_module, torder)?; + let module = HardwareModule::new( + netlist, + top_module, + torder, + use_slash_hierarchy, + cell_mapping, + )?; Ok(Self { module }) } @@ -127,15 +136,26 @@ impl HardwareDesign { #[pymethods] impl HardwareDesign { #[new] - #[pyo3(signature = (netlist_path, torder_path, config, top_module=None))] + #[pyo3(signature = (netlist_path, torder_path, config, use_slash_hierarchy=false, top_module=None, cell_mapping=None))] pub fn new( py: Python<'_>, netlist_path: PathBuf, torder_path: PathBuf, config: HashMap>, + use_slash_hierarchy: bool, top_module: Option<&str>, + cell_mapping: Option, ) -> anyhow::Result> { - let new_module = Py::new(py, Self::new_base(netlist_path, top_module, torder_path)?)?; + let new_module = Py::new( + py, + Self::new_base( + netlist_path, + top_module, + torder_path, + use_slash_hierarchy, + cell_mapping.as_ref(), + )?, + )?; // Add custom members (can't make them static for serialization) let temp_binding = new_module.getattr(py, "__dict__")?; From b33593e56db8e2cf76c6e409a959f19ee4272d71 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 7 Jan 2026 01:20:44 +0000 Subject: [PATCH 35/64] Fixing some new cell issues --- crates/arbolta/src/cell/mod.rs | 4 +++- crates/arbolta/src/cell/simcells.rs | 18 +++++++++++++++--- crates/arbolta/src/netlist_wrapper.rs | 1 - crates/arbolta/tests/test_simcell.rs | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index ca9106e..5b440ac 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -52,6 +52,9 @@ pub enum Cell { FullAdderInv, AndOrReduce, DffInv, + Or4, + AndOr21, + AndOr32, // Sim Cells Buffer, Inverter, @@ -66,7 +69,6 @@ pub enum Cell { Mux2, NMux2, And3, - AndOr, AndOrInvert, OrAndInvert, Dff, diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index 205679a..b53d137 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -94,16 +94,28 @@ create_cell!("$_AND_", And { a, b }, y, |a: Bit, b: Bit| a & b); create_cell!("$_AND3_", And3 { a, b, c }, y, |a, b, c| a & b & c); create_cell!("$_NAND_", Nand { a, b }, y, |a: Bit, b: Bit| !(a & b)); create_cell!("$_OR_", Or { a, b }, y, |a: Bit, b: Bit| a | b); +create_cell!( + "$_OR4_", + Or4 { a, b, c, d }, + y, + |a: Bit, b: Bit, c: Bit, d: Bit| a | b | c | d +); create_cell!("$_NOR_", Nor { a, b }, y, |a: Bit, b: Bit| !(a | b)); create_cell!("$_XOR_", Xor { a, b }, y, |a: Bit, b: Bit| a ^ b); create_cell!("$_XNOR_", Xnor { a, b }, y, |a: Bit, b: Bit| !(a ^ b)); create_cell!("$_ANDNOT_", AndNot { a, b }, y, |a: Bit, b: Bit| a & !b); create_cell!("$_ORNOT_", OrNot { a, b }, y, |a: Bit, b: Bit| a | !b); create_cell!( - "$_ANDOR_", - AndOr { a, b, c }, + "$_ANDOR21_", + AndOr21 { a1, a2, b }, + y, + |a1: Bit, a2: Bit, b: Bit| (a1 & a2) | b +); +create_cell!( + "$_ANDOR32_", + AndOr32 { a1, a2, a3, b1, b2 }, y, - |a: Bit, b: Bit, c: Bit| (a & b) | c + |a1, a2, a3, b1, b2| (a1 & a2 & a3) | (b1 & b2) ); create_cell!( "$_ANDORREDUCE_", diff --git a/crates/arbolta/src/netlist_wrapper.rs b/crates/arbolta/src/netlist_wrapper.rs index e213472..5044d87 100644 --- a/crates/arbolta/src/netlist_wrapper.rs +++ b/crates/arbolta/src/netlist_wrapper.rs @@ -100,7 +100,6 @@ impl NetlistWrapper { } fn build_cell(&self, cell: &RTLID, mapping: Option<&CellMapping>) -> Result { - println!("{cell:?}"); let synth_cell = self .find_cell(cell) .ok_or(ModuleError::MissingCell(cell.to_string()))?; diff --git a/crates/arbolta/tests/test_simcell.rs b/crates/arbolta/tests/test_simcell.rs index 8dc26bc..f1798c6 100644 --- a/crates/arbolta/tests/test_simcell.rs +++ b/crates/arbolta/tests/test_simcell.rs @@ -127,7 +127,7 @@ fn test_cell_binary(#[case] mut cell: Box, #[case] cases: [(u8, u8, (1, 1, 0, 0), (1, 1, 1, 1), ])] -#[case::andor(Box::new(AndOr::new(0, 1, 2, 3)), [ // (A, B, C, Y) +#[case::andor(Box::new(AndOr21::new(0, 1, 2, 3)), [ // (A, B, C, Y) (0, 0, 0, 0), (0, 0, 1, 1), (0, 1, 0, 0), From e9ee0edb9e6be461da5a996a12640ee82764ec96 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 7 Jan 2026 01:55:09 +0000 Subject: [PATCH 36/64] Adding more cells --- crates/arbolta/src/cell/mod.rs | 4 ++++ crates/arbolta/src/cell/simcells.rs | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index 5b440ac..d909b4f 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -52,9 +52,13 @@ pub enum Cell { FullAdderInv, AndOrReduce, DffInv, + Or3, Or4, + And4, AndOr21, + AndOr211, AndOr32, + OrAnd21, // Sim Cells Buffer, Inverter, diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index b53d137..f0f57bd 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -92,8 +92,15 @@ create_cell!("$_BUF_", Buffer { a }, y, |a: Bit| a); create_cell!("$_NOT_", Inverter { a }, y, |a: Bit| !a); create_cell!("$_AND_", And { a, b }, y, |a: Bit, b: Bit| a & b); create_cell!("$_AND3_", And3 { a, b, c }, y, |a, b, c| a & b & c); +create_cell!("$_AND4_", And4 { a, b, c, d }, y, |a, b, c, d| a + & b + & c + & d); create_cell!("$_NAND_", Nand { a, b }, y, |a: Bit, b: Bit| !(a & b)); create_cell!("$_OR_", Or { a, b }, y, |a: Bit, b: Bit| a | b); +create_cell!("$_OR3_", Or3 { a, b, c }, y, |a: Bit, b: Bit, c: Bit| a + | b + | c); create_cell!( "$_OR4_", Or4 { a, b, c, d }, @@ -111,6 +118,12 @@ create_cell!( y, |a1: Bit, a2: Bit, b: Bit| (a1 & a2) | b ); +create_cell!( + "$_ANDOR211_", + AndOr211 { a1, a2, b, c }, + y, + |a1: Bit, a2: Bit, b: Bit, c: Bit| (a1 & a2) | b | c +); create_cell!( "$_ANDOR32_", AndOr32 { a1, a2, a3, b1, b2 }, @@ -123,6 +136,12 @@ create_cell!( y, |a: Bit, b: Bit, c: Bit| (a & c) | (b & c) ); +create_cell!( + "$_OAI21_", + OrAnd21 { a1, a2, b }, + y, + |a1: Bit, a2: Bit, b: Bit| (!a1 & !a2) | !b +); create_cell!( "$_AOI3_", AndOrInvert { a, b, c }, From 3be41801af55ef33c0b4bde1235798361c5e5960 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 7 Jan 2026 02:02:48 +0000 Subject: [PATCH 37/64] Adding more cells (x2) --- crates/arbolta/src/cell/mod.rs | 1 + crates/arbolta/src/cell/simcells.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index d909b4f..87f7e3a 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -59,6 +59,7 @@ pub enum Cell { AndOr211, AndOr32, OrAnd21, + AOI21, // Sim Cells Buffer, Inverter, diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index f0f57bd..c844f7a 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -142,6 +142,12 @@ create_cell!( y, |a1: Bit, a2: Bit, b: Bit| (!a1 & !a2) | !b ); +create_cell!( + "$_AOI21_", + AOI21 { a1, a2, b }, + y, + |a1: Bit, a2: Bit, b: Bit| { (!a1 & !b) | (!a2 & !b) } +); create_cell!( "$_AOI3_", AndOrInvert { a, b, c }, From 58bc4baa441ca7776095adfcc4cf3a99df40aaed Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 7 Jan 2026 02:06:17 +0000 Subject: [PATCH 38/64] Adding more cells (x3) --- crates/arbolta/src/cell/mod.rs | 1 + crates/arbolta/src/cell/simcells.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index 87f7e3a..c4900f1 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -60,6 +60,7 @@ pub enum Cell { AndOr32, OrAnd21, AOI21, + OA211, // Sim Cells Buffer, Inverter, diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index c844f7a..36fa50c 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -148,6 +148,12 @@ create_cell!( y, |a1: Bit, a2: Bit, b: Bit| { (!a1 & !b) | (!a2 & !b) } ); +create_cell!( + "$_OA211_", + OA211 { a1, a2, b, c }, + y, + |a1: Bit, a2: Bit, b: Bit, c: Bit| { (a1 & b & c) | (a2 & b & c) } +); create_cell!( "$_AOI3_", AndOrInvert { a, b, c }, From 74de3baa3bfd67453d66b45bc7875b43e99ac52f Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 7 Jan 2026 02:13:16 +0000 Subject: [PATCH 39/64] Adding more cells (x4) --- crates/arbolta/src/cell/mod.rs | 2 ++ crates/arbolta/src/cell/simcells.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index c4900f1..1fa8cb7 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -61,6 +61,8 @@ pub enum Cell { OrAnd21, AOI21, OA211, + Nor3, + OA21, // Sim Cells Buffer, Inverter, diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index 36fa50c..3f3aa99 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -108,6 +108,12 @@ create_cell!( |a: Bit, b: Bit, c: Bit, d: Bit| a | b | c | d ); create_cell!("$_NOR_", Nor { a, b }, y, |a: Bit, b: Bit| !(a | b)); +create_cell!( + "$_NOR3_", + Nor3 { a, b, c }, + y, + |a: Bit, b: Bit, c: Bit| !(a | b | c) +); create_cell!("$_XOR_", Xor { a, b }, y, |a: Bit, b: Bit| a ^ b); create_cell!("$_XNOR_", Xnor { a, b }, y, |a: Bit, b: Bit| !(a ^ b)); create_cell!("$_ANDNOT_", AndNot { a, b }, y, |a: Bit, b: Bit| a & !b); @@ -148,6 +154,12 @@ create_cell!( y, |a1: Bit, a2: Bit, b: Bit| { (!a1 & !b) | (!a2 & !b) } ); +create_cell!( + "$_OA21_", + OA21 { a1, a2, b }, + y, + |a1: Bit, a2: Bit, b: Bit| { (a1 & b) | (a2 & b) } +); create_cell!( "$_OA211_", OA211 { a1, a2, b, c }, From 7c280468614178142280e12463732039d2b189d7 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 7 Jan 2026 02:27:39 +0000 Subject: [PATCH 40/64] Adding more cells (x5) --- crates/arbolta/src/cell/mod.rs | 6 +++++ crates/arbolta/src/cell/simcells.rs | 36 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index 1fa8cb7..2b06111 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -63,6 +63,12 @@ pub enum Cell { OA211, Nor3, OA21, + OA31, + OA22, + AO221, + AOI211, + AO31, + OAI22, // Sim Cells Buffer, Inverter, diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index 3f3aa99..bac1c45 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -148,18 +148,54 @@ create_cell!( y, |a1: Bit, a2: Bit, b: Bit| (!a1 & !a2) | !b ); +create_cell!( + "$_OAI22_", + OAI22 { a1, a2, b1, b2 }, + y, + |a1: Bit, a2: Bit, b1: Bit, b2: Bit| (!a1 & !a2) | (!b1 & !b2) +); create_cell!( "$_AOI21_", AOI21 { a1, a2, b }, y, |a1: Bit, a2: Bit, b: Bit| { (!a1 & !b) | (!a2 & !b) } ); +create_cell!( + "$_AOI211_", + AOI211 { a1, a2, b, c }, + y, + |a1: Bit, a2: Bit, b: Bit, c: Bit| { (!a1 & !b & !c) | (!a2 & !b & !c) } +); +create_cell!( + "$_AO221_", + AO221 { a1, a2, b1, b2, c }, + y, + |a1: Bit, a2: Bit, b1: Bit, b2: Bit, c: Bit| { (a1 & a2) | (b1 & b2) | c } +); +create_cell!( + "$_AO31_", + AO31 { a1, a2, a3, b }, + y, + |a1: Bit, a2: Bit, a3: Bit, b: Bit| { (a1 & a2 & a3) | b } +); create_cell!( "$_OA21_", OA21 { a1, a2, b }, y, |a1: Bit, a2: Bit, b: Bit| { (a1 & b) | (a2 & b) } ); +create_cell!( + "$_OA31_", + OA31 { a1, a2, a3, b1 }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit| (a1 & b1) | (a2 & b1) | (a3 | b1) +); +create_cell!( + "$_OA22_", + OA22 { a1, a2, b1, b2 }, + y, + |a1: Bit, a2: Bit, b1: Bit, b2: Bit| (a1 & b1) | (a1 & b2) | (a2 | b1) | (a2 | b2) +); create_cell!( "$_OA211_", OA211 { a1, a2, b, c }, From 8f7001eab2326596e863db4635fb28fb58ba363a Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 7 Jan 2026 02:29:58 +0000 Subject: [PATCH 41/64] Adding more cells (x6) --- crates/arbolta/src/cell/mod.rs | 2 ++ crates/arbolta/src/cell/simcells.rs | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index 2b06111..ff02257 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -69,6 +69,8 @@ pub enum Cell { AOI211, AO31, OAI22, + Or5, + And5, // Sim Cells Buffer, Inverter, diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index bac1c45..31eae7e 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -96,6 +96,11 @@ create_cell!("$_AND4_", And4 { a, b, c, d }, y, |a, b, c, d| a & b & c & d); +create_cell!("$_AND5_", And5 { a, b, c, d, e }, y, |a, b, c, d, e| a + & b + & c + & d + & e); create_cell!("$_NAND_", Nand { a, b }, y, |a: Bit, b: Bit| !(a & b)); create_cell!("$_OR_", Or { a, b }, y, |a: Bit, b: Bit| a | b); create_cell!("$_OR3_", Or3 { a, b, c }, y, |a: Bit, b: Bit, c: Bit| a @@ -107,6 +112,12 @@ create_cell!( y, |a: Bit, b: Bit, c: Bit, d: Bit| a | b | c | d ); +create_cell!( + "$_OR5_", + Or5 { a, b, c, d, e }, + y, + |a: Bit, b: Bit, c: Bit, d: Bit, e: Bit| a | b | c | d | e +); create_cell!("$_NOR_", Nor { a, b }, y, |a: Bit, b: Bit| !(a | b)); create_cell!( "$_NOR3_", From f1fcf9af89188bbf4a3e48aa84d94fcb4a0e03b8 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 7 Jan 2026 02:31:38 +0000 Subject: [PATCH 42/64] Adding more cells (x6) --- crates/arbolta/src/cell/mod.rs | 1 + crates/arbolta/src/cell/simcells.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index ff02257..d5ae82e 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -71,6 +71,7 @@ pub enum Cell { OAI22, Or5, And5, + Nand3, // Sim Cells Buffer, Inverter, diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index 31eae7e..16b1db0 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -102,6 +102,12 @@ create_cell!("$_AND5_", And5 { a, b, c, d, e }, y, |a, b, c, d, e| a & d & e); create_cell!("$_NAND_", Nand { a, b }, y, |a: Bit, b: Bit| !(a & b)); +create_cell!( + "$_NAND3_", + Nand3 { a, b, c }, + y, + |a: Bit, b: Bit, c: Bit| !(a & b & c) +); create_cell!("$_OR_", Or { a, b }, y, |a: Bit, b: Bit| a | b); create_cell!("$_OR3_", Or3 { a, b, c }, y, |a: Bit, b: Bit, c: Bit| a | b From d147c200b6da88136268c09bb55006268d38c064 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Thu, 8 Jan 2026 19:49:46 +0000 Subject: [PATCH 43/64] Temp hardcoding Asap& --- crates/arbolta/src/cell/mod.rs | 131 ++- crates/arbolta/src/cell/simcells.rs | 1227 +++++++++++++++++++++----- crates/arbolta/tests/test_simcell.rs | 70 +- 3 files changed, 1132 insertions(+), 296 deletions(-) diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index d5ae82e..34bc6c6 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -46,50 +46,103 @@ pub static CELL_DISPATCH: Lazy> = Lazy::new(|| { /// Proxy for a standard-cell and basic unit of 'compute'. pub enum Cell { // ASAP7 - HalfAdder, - HalfAdderInv, - FullAdder, - FullAdderInv, - AndOrReduce, - DffInv, - Or3, - Or4, - And4, - AndOr21, - AndOr211, - AndOr32, - OrAnd21, - AOI21, - OA211, - Nor3, - OA21, - OA31, - OA22, - AO221, - AOI211, - AO31, - OAI22, - Or5, - And5, - Nand3, + Asap7Buf, + Asap7Inv, + Asap7And2, + Asap7And3, + Asap7And4, + Asap7And5, + Asap7FullAdderInv, + Asap7HalfAdderInv, + Asap7MajorityInv, + Asap7Majority, + Asap7Nand2, + Asap7Nand3, + Asap7Nand4, + Asap7Nand5, + Asap7Nor2, + Asap7Nor3, + Asap7Nor4, + Asap7Nor5, + Asap7Or2, + Asap7Or3, + Asap7Or4, + Asap7Or5, + Asap7TieHigh, + Asap7TieLow, + Asap7Xnor2, + Asap7Xor2, + Asap7Or2And1Or1Inv, + Asap7OrAnd211, + Asap7OrAnd21, + Asap7OrAnd221, + Asap7OrAnd222, + Asap7OrAnd22, + Asap7OrAnd31, + Asap7OrAnd331, + Asap7OrAnd332, + Asap7OrAnd333, + Asap7OrAnd33, + Asap7OrAndInv211, + Asap7OrAndInv21, + Asap7OrAndInv221, + Asap7OrAndInv222, + Asap7OrAndInv22, + Asap7OrAndInv311, + Asap7OrAndInv31, + Asap7OrAndInv321, + Asap7OrAndInv322, + Asap7OrAndInv32, + Asap7OrAndInv331, + Asap7OrAndInv332, + Asap7OrAndInv333, + Asap7OrAndInv33, + Asap7And2Or1And1Inv, + Asap7And2Or1And1Or1Inv, + Asap7AndOr211, + Asap7AndOr21, + Asap7AndOr221, + Asap7AndOr222, + Asap7AndOr22, + Asap7AndOr31, + Asap7AndOr322, + Asap7AndOr32, + Asap7AndOr331, + Asap7AndOr332, + Asap7AndOr333, + Asap7AndOr33, + Asap7AndOrInv211, + Asap7AndOrInv21, + Asap7AndOrInv221, + Asap7AndOrInv222, + Asap7AndOrInv22, + Asap7AndOrInv311, + Asap7AndOrInv31, + Asap7AndOrInv321, + Asap7AndOrInv322, + Asap7AndOrInv32, + Asap7AndOrInv331, + Asap7AndOrInv332, + Asap7AndOrInv333, + Asap7AndOrInv33, + Asap7DffInv, // Sim Cells + And2, + AndNot2, + AndOrInvert3, Buffer, + Dff, + DffReset, Inverter, - And, - Nand, - Or, - Nor, - Xor, - Xnor, - AndNot, - OrNot, Mux2, + Nand2, NMux2, - And3, - AndOrInvert, - OrAndInvert, - Dff, - DffReset, + Nor2, + Or2, + OrAndInvert3, + OrNot2, + Xnor2, + Xor2, // Sim Lib Not, Neg, diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index 16b1db0..7fc308a 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -6,8 +6,35 @@ use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; macro_rules! create_cell { - // 1-output - ($rtl_name:expr, $cell_type:ident { $($in_netn:ident),* }, $out_net:ident, $body:expr) => { + // 1-output, no inputs + ($rtl_names:expr, $cell_type:ident, $out_net:ident, $body:expr) => { + #[derive(Debug, Clone, Constructor, Serialize, Deserialize)] + pub struct $cell_type { + $out_net: usize + } + + impl CellFn for $cell_type { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + signals.set_net(self.$out_net, $body()); + } + + fn reset(&mut self) {} + } + + paste! { + inventory::submit! {CellRegistration::new($rtl_names, + |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { + $cell_type::new( + connections[stringify!([<$out_net:upper>])][0] + ).into() + })} + } + }; + + // 1-output, N-inputs + ($rtl_names:expr, $cell_type:ident { $($in_netn:ident),* $(,)?}, $out_net:ident, $body:expr) => { + #[allow(clippy::too_many_arguments)] #[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct $cell_type { $( @@ -30,7 +57,7 @@ macro_rules! create_cell { } paste! { - inventory::submit! {CellRegistration::new(&[$rtl_name], + inventory::submit! {CellRegistration::new($rtl_names, |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { $cell_type::new( $( @@ -42,8 +69,8 @@ macro_rules! create_cell { } }; - // N-output - ($rtl_name:expr, $cell_type:ident { $($in_netn:ident),* $(,)?}, { $($out_netn:ident),+ $(,)?}, $body:expr) => { + // N-output, N-inputs + ($rtl_names:expr, $cell_type:ident { $($in_netn:ident),* $(,)?}, { $($out_netn:ident),+ $(,)?}, $body:expr) => { #[derive(Debug, Clone, Constructor, Serialize, Deserialize)] pub struct $cell_type { $( @@ -73,7 +100,7 @@ macro_rules! create_cell { } paste! { - inventory::submit! {CellRegistration::new(&[$rtl_name], + inventory::submit! {CellRegistration::new($rtl_names, |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { $cell_type::new( $( @@ -88,183 +115,41 @@ macro_rules! create_cell { }; } -create_cell!("$_BUF_", Buffer { a }, y, |a: Bit| a); -create_cell!("$_NOT_", Inverter { a }, y, |a: Bit| !a); -create_cell!("$_AND_", And { a, b }, y, |a: Bit, b: Bit| a & b); -create_cell!("$_AND3_", And3 { a, b, c }, y, |a, b, c| a & b & c); -create_cell!("$_AND4_", And4 { a, b, c, d }, y, |a, b, c, d| a - & b - & c - & d); -create_cell!("$_AND5_", And5 { a, b, c, d, e }, y, |a, b, c, d, e| a - & b - & c - & d - & e); -create_cell!("$_NAND_", Nand { a, b }, y, |a: Bit, b: Bit| !(a & b)); -create_cell!( - "$_NAND3_", - Nand3 { a, b, c }, - y, - |a: Bit, b: Bit, c: Bit| !(a & b & c) -); -create_cell!("$_OR_", Or { a, b }, y, |a: Bit, b: Bit| a | b); -create_cell!("$_OR3_", Or3 { a, b, c }, y, |a: Bit, b: Bit, c: Bit| a - | b - | c); -create_cell!( - "$_OR4_", - Or4 { a, b, c, d }, - y, - |a: Bit, b: Bit, c: Bit, d: Bit| a | b | c | d -); -create_cell!( - "$_OR5_", - Or5 { a, b, c, d, e }, - y, - |a: Bit, b: Bit, c: Bit, d: Bit, e: Bit| a | b | c | d | e -); -create_cell!("$_NOR_", Nor { a, b }, y, |a: Bit, b: Bit| !(a | b)); -create_cell!( - "$_NOR3_", - Nor3 { a, b, c }, - y, - |a: Bit, b: Bit, c: Bit| !(a | b | c) -); -create_cell!("$_XOR_", Xor { a, b }, y, |a: Bit, b: Bit| a ^ b); -create_cell!("$_XNOR_", Xnor { a, b }, y, |a: Bit, b: Bit| !(a ^ b)); -create_cell!("$_ANDNOT_", AndNot { a, b }, y, |a: Bit, b: Bit| a & !b); -create_cell!("$_ORNOT_", OrNot { a, b }, y, |a: Bit, b: Bit| a | !b); -create_cell!( - "$_ANDOR21_", - AndOr21 { a1, a2, b }, - y, - |a1: Bit, a2: Bit, b: Bit| (a1 & a2) | b -); -create_cell!( - "$_ANDOR211_", - AndOr211 { a1, a2, b, c }, - y, - |a1: Bit, a2: Bit, b: Bit, c: Bit| (a1 & a2) | b | c -); -create_cell!( - "$_ANDOR32_", - AndOr32 { a1, a2, a3, b1, b2 }, - y, - |a1, a2, a3, b1, b2| (a1 & a2 & a3) | (b1 & b2) -); -create_cell!( - "$_ANDORREDUCE_", - AndOrReduce { a, b, c }, - y, - |a: Bit, b: Bit, c: Bit| (a & c) | (b & c) -); -create_cell!( - "$_OAI21_", - OrAnd21 { a1, a2, b }, - y, - |a1: Bit, a2: Bit, b: Bit| (!a1 & !a2) | !b -); -create_cell!( - "$_OAI22_", - OAI22 { a1, a2, b1, b2 }, - y, - |a1: Bit, a2: Bit, b1: Bit, b2: Bit| (!a1 & !a2) | (!b1 & !b2) -); -create_cell!( - "$_AOI21_", - AOI21 { a1, a2, b }, - y, - |a1: Bit, a2: Bit, b: Bit| { (!a1 & !b) | (!a2 & !b) } -); -create_cell!( - "$_AOI211_", - AOI211 { a1, a2, b, c }, - y, - |a1: Bit, a2: Bit, b: Bit, c: Bit| { (!a1 & !b & !c) | (!a2 & !b & !c) } -); -create_cell!( - "$_AO221_", - AO221 { a1, a2, b1, b2, c }, - y, - |a1: Bit, a2: Bit, b1: Bit, b2: Bit, c: Bit| { (a1 & a2) | (b1 & b2) | c } -); -create_cell!( - "$_AO31_", - AO31 { a1, a2, a3, b }, - y, - |a1: Bit, a2: Bit, a3: Bit, b: Bit| { (a1 & a2 & a3) | b } -); -create_cell!( - "$_OA21_", - OA21 { a1, a2, b }, - y, - |a1: Bit, a2: Bit, b: Bit| { (a1 & b) | (a2 & b) } -); +/* ++++++++ Sim Cells ++++++++ */ +create_cell!(&["$_AND_"], And2 { a, b }, y, |a: Bit, b: Bit| a & b); +create_cell!(&["$_ANDNOT_"], AndNot2 { a, b }, y, |a: Bit, b: Bit| a & !b); create_cell!( - "$_OA31_", - OA31 { a1, a2, a3, b1 }, - y, - |a1: Bit, a2: Bit, a3: Bit, b1: Bit| (a1 & b1) | (a2 & b1) | (a3 | b1) -); -create_cell!( - "$_OA22_", - OA22 { a1, a2, b1, b2 }, - y, - |a1: Bit, a2: Bit, b1: Bit, b2: Bit| (a1 & b1) | (a1 & b2) | (a2 | b1) | (a2 | b2) -); -create_cell!( - "$_OA211_", - OA211 { a1, a2, b, c }, - y, - |a1: Bit, a2: Bit, b: Bit, c: Bit| { (a1 & b & c) | (a2 & b & c) } -); -create_cell!( - "$_AOI3_", - AndOrInvert { a, b, c }, + &["$_AOI3_"], + AndOrInvert3 { a, b, c }, y, |a: Bit, b: Bit, c: Bit| { !((a & b) | c) } ); +create_cell!(&["$_BUF_"], Buffer { a }, y, |a: Bit| a); +create_cell!(&["$_NOT_"], Inverter { a }, y, |a: Bit| !a); create_cell!( - "$_OAI3_", - OrAndInvert { a, b, c }, - y, - |a: Bit, b: Bit, c: Bit| { !((a | b) & c) } -); -create_cell!( - "$_MUX_", + &["$_MUX_"], Mux2 { a, b, s }, y, |a: Bit, b: Bit, select: Bit| if select.into() { b } else { a } ); +create_cell!(&["$_NAND_"], Nand2 { a, b }, y, |a: Bit, b: Bit| !(a & b)); create_cell!( - "$_NMUX_", + &["$_NMUX_"], NMux2 { a, b, s }, y, |a: Bit, b: Bit, select: Bit| if select.into() { !b } else { !a } ); -create_cell!("$_HA_", HalfAdder { a, b }, {s, c}, |a: Bit, b: Bit| ( - a ^ b, - a & b -)); -create_cell!("$_HAI_", HalfAdderInv { a, b }, {sn, con}, |a: Bit, b: Bit| ( - !(a ^ b), - !(a & b) -)); -create_cell!( - "$_FA_", FullAdder {a, b, ci}, {s, co}, - |a: Bit, b: Bit, carry_in: Bit| { - let sum = a ^ b; - (sum ^ carry_in, (a & b) | (sum & carry_in)) - } -); +create_cell!(&["$_NOR_"], Nor2 { a, b }, y, |a: Bit, b: Bit| !(a | b)); +create_cell!(&["$_OR_"], Or2 { a, b }, y, |a: Bit, b: Bit| a | b); create_cell!( - "$_FAI_", FullAdderInv {a, b, ci}, {sn, con}, - |a: Bit, b: Bit, carry_in: Bit| { - let sum = a ^ b; - (!(sum ^ carry_in), !((a & b) | (sum & carry_in))) - } + &["$_OAI3_"], + OrAndInvert3 { a, b, c }, + y, + |a: Bit, b: Bit, c: Bit| { !((a | b) & c) } ); +create_cell!(&["$_ORNOT_"], OrNot2 { a, b }, y, |a: Bit, b: Bit| a | !b); +create_cell!(&["$_XNOR_"], Xnor2 { a, b }, y, |a: Bit, b: Bit| !(a ^ b)); +create_cell!(&["$_XOR_"], Xor2 { a, b }, y, |a: Bit, b: Bit| a ^ b); #[derive(Debug, Clone, Serialize, Deserialize, derive_new::new)] pub struct Dff { @@ -309,25 +194,33 @@ inventory::submit! { } #[derive(Debug, Clone, Serialize, Deserialize, derive_new::new)] -pub struct DffInv { - polarity: Bit, +pub struct DffReset { + clock_polarity: Bit, + reset_polarity: Bit, + reset_val: Bit, clock_net: usize, + reset_net: usize, data_in_net: usize, data_out_net: usize, #[new(default)] last_clock: Bit, } -impl CellFn for DffInv { +impl CellFn for DffReset { #[inline] fn eval(&mut self, signals: &mut Signals) { // Check if clock active for any polarity - let clock = !(signals.get_net(self.clock_net) ^ self.polarity); + let clock = !(signals.get_net(self.clock_net) ^ self.clock_polarity); // Rising edge if clock == Bit::ONE && self.last_clock == Bit::ZERO { - let data_in = signals.get_net(self.data_in_net); - signals.set_net(self.data_out_net, !data_in); + // Check if reset active for any polarity + if signals.get_net(self.reset_net) == self.reset_polarity { + signals.set_net(self.data_out_net, self.reset_val); + } else { + let data_in = signals.get_net(self.data_in_net); + signals.set_net(self.data_out_net, data_in); + } } self.last_clock = clock; @@ -339,45 +232,981 @@ impl CellFn for DffInv { } inventory::submit! { - CellRegistration::new(&["$_DFF_PI_"], + CellRegistration::new(&["$_SDFF_PP0_"], |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { - DffInv::new( + DffReset::new( Bit::ONE, - connections["CLK"][0], + Bit::ONE, + Bit::ZERO, + connections["C"][0], + connections["R"][0], connections["D"][0], - connections["QN"][0], + connections["Q"][0], ).into() }) } +/* ++++++++ ASAP7 Cells ++++++++ */ +create_cell!( + &[ + "BUFx10_ASAP7_75t_R", + "BUFx12_ASAP7_75t_R", + "BUFx12f_ASAP7_75t_R", + "BUFx16f_ASAP7_75t_R", + "BUFx24_ASAP7_75t_R", + "BUFx2_ASAP7_75t_R", + "BUFx3_ASAP7_75t_R", + "BUFx4_ASAP7_75t_R", + "BUFx4f_ASAP7_75t_R", + "BUFx5_ASAP7_75t_R", + "BUFx6f_ASAP7_75t_R", + "BUFx8_ASAP7_75t_R", + "HB1xp67_ASAP7_75t_R", + "HB2xp67_ASAP7_75t_R", + "HB3xp67_ASAP7_75t_R", + "HB4xp67_ASAP7_75t_R", + ], + Asap7Buf { a }, + y, + |a: Bit| a +); +create_cell!( + &[ + "CKINVDCx10_ASAP7_75t_R", + "CKINVDCx11_ASAP7_75t_R", + "CKINVDCx12_ASAP7_75t_R", + "CKINVDCx14_ASAP7_75t_R", + "CKINVDCx16_ASAP7_75t_R", + "CKINVDCx20_ASAP7_75t_R", + "CKINVDCx5p33_ASAP7_75t_R", + "CKINVDCx6p67_ASAP7_75t_R", + "CKINVDCx8_ASAP7_75t_R", + "CKINVDCx9p33_ASAP7_75t_R", + "INVx11_ASAP7_75t_R", + "INVx13_ASAP7_75t_R", + "INVx1_ASAP7_75t_R", + "INVx2_ASAP7_75t_R", + "INVx3_ASAP7_75t_R", + "INVx4_ASAP7_75t_R", + "INVx5_ASAP7_75t_R", + "INVx6_ASAP7_75t_R", + "INVx8_ASAP7_75t_R", + "INVxp33_ASAP7_75t_R", + "INVxp67_ASAP7_75t_R", + ], + Asap7Inv { a }, + y, + |a: Bit| !a +); +create_cell!( + &[ + "AND2x2_ASAP7_75t_R", + "AND2x4_ASAP7_75t_R", + "AND2x6_ASAP7_75t_R", + ], + Asap7And2 { a, b }, + y, + |a: Bit, b: Bit| a & b +); +create_cell!( + &[ + "AND3x1_ASAP7_75t_R", + "AND3x2_ASAP7_75t_R", + "AND3x4_ASAP7_75t_R", + ], + Asap7And3 { a, b, c }, + y, + |a: Bit, b: Bit, c: Bit| a & b & c +); +create_cell!( + &["AND4x1_ASAP7_75t_R", "AND4x2_ASAP7_75t_R",], + Asap7And4 { a, b, c, d }, + y, + |a: Bit, b: Bit, c: Bit, d: Bit| a & b & c & d +); +create_cell!( + &["AND5x1_ASAP7_75t_R", "AND5x2_ASAP7_75t_R",], + Asap7And5 { a, b, c, d, e }, + y, + |a: Bit, b: Bit, c: Bit, d: Bit, e: Bit| a & b & c & d & e +); +create_cell!( + &["FAx1_ASAP7_75t_R"], + Asap7FullAdderInv {a, b, ci}, {sn, con}, + |a: Bit, b: Bit, carry_in: Bit| { + let sum = a ^ b; + (!(sum ^ carry_in), !((a & b) | (sum & carry_in))) + } +); +create_cell!( + &["HAxp5_ASAP7_75t_R"], + Asap7HalfAdderInv { a, b }, {sn, con}, |a: Bit, b: Bit| ( + !(a ^ b), + !(a & b) +)); +create_cell!( + &["MAJIxp5_ASAP7_75t_R"], + Asap7MajorityInv { a, b, c }, + y, + |a: Bit, b: Bit, c: Bit| (!a & !b) | (!a & !c) | (!b & !c) +); +create_cell!( + &["MAJx2_ASAP7_75t_R", "MAJx3_ASAP7_75t_R"], + Asap7Majority { a, b, c }, + y, + |a: Bit, b: Bit, c: Bit| (a & b) | (a & c) | (b & c) +); +create_cell!( + &[ + "NAND2x1_ASAP7_75t_R", + "NAND2x1p5_ASAP7_75t_R", + "NAND2x2_ASAP7_75t_R", + "NAND2xp33_ASAP7_75t_R", + "NAND2xp5_ASAP7_75t_R", + "NAND2xp67_ASAP7_75t_R", + ], + Asap7Nand2 { a, b }, + y, + |a: Bit, b: Bit| !(a & b) +); +create_cell!( + &[ + "NAND3x1_ASAP7_75t_R", + "NAND3x2_ASAP7_75t_R", + "NAND3xp33_ASAP7_75t_R", + ], + Asap7Nand3 { a, b, c }, + y, + |a: Bit, b: Bit, c: Bit| !(a & b & c) +); +create_cell!( + &["NAND4xp25_ASAP7_75t_R", "NAND4xp75_ASAP7_75t_R",], + Asap7Nand4 { a, b, c, d }, + y, + |a: Bit, b: Bit, c: Bit, d: Bit| !(a & b & c & d) +); +create_cell!( + &["NAND5xp2_ASAP7_75t_R",], + Asap7Nand5 { a, b, c, d, e }, + y, + |a: Bit, b: Bit, c: Bit, d: Bit, e: Bit| !(a & b & c & d & e) +); +create_cell!( + &[ + "NOR2x1_ASAP7_75t_R", + "NOR2x1p5_ASAP7_75t_R", + "NOR2x2_ASAP7_75t_R", + "NOR2xp33_ASAP7_75t_R", + "NOR2xp67_ASAP7_75t_R", + ], + Asap7Nor2 { a, b }, + y, + |a: Bit, b: Bit| !(a | b) +); +create_cell!( + &[ + "NOR3x1_ASAP7_75t_R", + "NOR3x2_ASAP7_75t_R", + "NOR3xp33_ASAP7_75t_R", + ], + Asap7Nor3 { a, b, c }, + y, + |a: Bit, b: Bit, c: Bit| !(a | b | c) +); +create_cell!( + &["NOR4xp25_ASAP7_75t_R", "NOR4xp75_ASAP7_75t_R"], + Asap7Nor4 { a, b, c, d }, + y, + |a: Bit, b: Bit, c: Bit, d: Bit| !(a | b | c | d) +); +create_cell!( + &["NOR5xp2_ASAP7_75t_R"], + Asap7Nor5 { a, b, c, d, e }, + y, + |a: Bit, b: Bit, c: Bit, d: Bit, e: Bit| !(a | b | c | d | e) +); +create_cell!( + &[ + "OR2x2_ASAP7_75t_R", + "OR2x4_ASAP7_75t_R", + "OR2x6_ASAP7_75t_R", + ], + Asap7Or2 { a, b }, + y, + |a: Bit, b: Bit| a | b +); +create_cell!( + &[ + "OR3x1_ASAP7_75t_R", + "OR3x2_ASAP7_75t_R", + "OR3x4_ASAP7_75t_R", + ], + Asap7Or3 { a, b, c }, + y, + |a: Bit, b: Bit, c: Bit| a | b | c +); +create_cell!( + &["OR4x1_ASAP7_75t_R", "OR4x2_ASAP7_75t_R"], + Asap7Or4 { a, b, c, d }, + y, + |a: Bit, b: Bit, c: Bit, d: Bit| a | b | c | d +); +create_cell!( + &["OR5x1_ASAP7_75t_R", "OR5x2_ASAP7_75t_R"], + Asap7Or5 { a, b, c, d, e }, + y, + |a: Bit, b: Bit, c: Bit, d: Bit, e: Bit| a | b | c | d | e +); +create_cell!(&["TIEHIx1_ASAP7_75t_R"], Asap7TieHigh, h, || Bit::ONE); +create_cell!(&["TIELOx1_ASAP7_75t_R"], Asap7TieLow, h, || Bit::ZERO); +create_cell!( + &[ + "XNOR2x1_ASAP7_75t_R", + "XNOR2x2_ASAP7_75t_R", + "XNOR2xp5_ASAP7_75t_R", + ], + Asap7Xnor2 { a, b }, + y, + |a: Bit, b: Bit| !(a ^ b) +); +create_cell!( + &[ + "XOR2x1_ASAP7_75t_R", + "XOR2x2_ASAP7_75t_R", + "XOR2xp5_ASAP7_75t_R", + ], + Asap7Xor2 { a, b }, + y, + |a: Bit, b: Bit| a ^ b +); +create_cell!( + &["O2A1O1Ixp33_ASAP7_75t_R", "O2A1O1Ixp5_ASAP7_75t_R"], + Asap7Or2And1Or1Inv { a1, a2, b, c }, + y, + |a1: Bit, a2: Bit, b: Bit, c: Bit| (!a1 & !a2 & !c) | (!b & !c) +); +create_cell!( + &["OA211x2_ASAP7_75t_R"], + Asap7OrAnd211 { a1, a2, b, c }, + y, + |a1: Bit, a2: Bit, b: Bit, c: Bit| (a1 & b & c) | (a2 & b & c) +); +create_cell!( + &["OA21x2_ASAP7_75t_R"], + Asap7OrAnd21 { a1, a2, b }, + y, + |a1: Bit, a2: Bit, b: Bit| (a1 & b) | (a2 & b) +); +create_cell!( + &["OA221x2_ASAP7_75t_R"], + Asap7OrAnd221 { a1, a2, b1, b2, c }, + y, + |a1: Bit, a2: Bit, b1: Bit, b2: Bit, c: Bit| (a2 & b2 & c) + | (a2 & b1 & c) + | (a1 & b2 & c) + | (a1 & b1 & c) +); +create_cell!( + &["OA222x2_ASAP7_75t_R"], + Asap7OrAnd222 { + a1, + a2, + b1, + b2, + c1, + c2 + }, + y, + |a1: Bit, a2: Bit, b1: Bit, b2: Bit, c1: Bit, c2: Bit| (a2 & b2 & c2) + | (a2 & b2 & c1) + | (a2 & b1 & c2) + | (a2 & b1 & c1) + | (a1 & b2 & c2) + | (a1 & b2 & c1) + | (a1 & b1 & c2) + | (a1 & b1 & c1) +); +create_cell!( + &["OA22x2_ASAP7_75t_R"], + Asap7OrAnd22 { a1, a2, b1, b2 }, + y, + |a1: Bit, a2: Bit, b1: Bit, b2: Bit| (a2 & b2) | (a2 & b1) | (a1 & b2) | (a1 & b1) +); +create_cell!( + &["OA31x2_ASAP7_75t_R"], + Asap7OrAnd31 { a1, a2, a3, b1 }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit| (a3 & b1) | (a2 & b1) | (a1 & b1) +); +create_cell!( + &["OA331x1_ASAP7_75t_R", "OA331x2_ASAP7_75t_R"], + Asap7OrAnd331 { + a1, + a2, + a3, + b1, + b2, + b3, + c1, + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, b3: Bit, c1: Bit| (a3 & b3 & c1) + | (a3 & b2 & c1) + | (a3 & b1 & c1) + | (a2 & b3 & c1) + | (a2 & b2 & c1) + | (a2 & b1 & c1) + | (a1 & b3 & c1) + | (a1 & b2 & c1) + | (a1 & b1 & c1) +); +create_cell!( + &["OA332x1_ASAP7_75t_R", "OA332x2_ASAP7_75t_R",], + Asap7OrAnd332 { + a1, + a2, + a3, + b1, + b2, + b3, + c1, + c2, + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, b3: Bit, c1: Bit, c2: Bit| (a3 & b3 & c2) + | (a3 & b3 & c1) + | (a3 & b2 & c2) + | (a3 & b2 & c1) + | (a3 & b1 & c2) + | (a3 & b1 & c1) + | (a2 & b3 & c2) + | (a2 & b3 & c1) + | (a2 & b2 & c2) + | (a2 & b2 & c1) + | (a2 & b1 & c2) + | (a2 & b1 & c1) + | (a1 & b3 & c2) + | (a1 & b3 & c1) + | (a1 & b2 & c2) + | (a1 & b2 & c1) + | (a1 & b1 & c2) + | (a1 & b1 & c1) +); +create_cell!( + &["OA333x1_ASAP7_75t_R", "OA333x2_ASAP7_75t_R",], + Asap7OrAnd333 { + a1, + a2, + a3, + b1, + b2, + b3, + c1, + c2, + c3, + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, b3: Bit, c1: Bit, c2: Bit, c3: Bit| (a3 & b3 & c3) + | (a3 & b3 & c2) + | (a3 & b3 & c1) + | (a3 & b2 & c3) + | (a3 & b2 & c2) + | (a3 & b2 & c1) + | (a3 & b1 & c3) + | (a3 & b1 & c2) + | (a3 & b1 & c1) + | (a2 & b3 & c3) + | (a2 & b3 & c2) + | (a2 & b3 & c1) + | (a2 & b2 & c3) + | (a2 & b2 & c2) + | (a2 & b2 & c1) + | (a2 & b1 & c3) + | (a2 & b1 & c2) + | (a2 & b1 & c1) + | (a1 & b3 & c3) + | (a1 & b3 & c2) + | (a1 & b3 & c1) + | (a1 & b2 & c3) + | (a1 & b2 & c2) + | (a1 & b2 & c1) + | (a1 & b1 & c3) + | (a1 & b1 & c2) + | (a1 & b1 & c1) +); +create_cell!( + &["OA33x2_ASAP7_75t_R"], + Asap7OrAnd33 { + a1, + a2, + a3, + b1, + b2, + b3 + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, b3: Bit| (a3 & b3) + | (a3 & b2) + | (a3 & b1) + | (a2 & b3) + | (a2 & b2) + | (a2 & b1) + | (a1 & b3) + | (a1 & b2) + | (a1 & b1) +); +create_cell!( + &["OAI211xp5_ASAP7_75t_R"], + Asap7OrAndInv211 { a1, a2, b, c }, + y, + |a1: Bit, a2: Bit, b: Bit, c: Bit| (!a1 & !a2) | !b | !c +); +create_cell!( + &[ + "OAI21x1_ASAP7_75t_R", + "OAI21xp33_ASAP7_75t_R", + "OAI21xp5_ASAP7_75t_R", + ], + Asap7OrAndInv21 { a1, a2, b }, + y, + |a1: Bit, a2: Bit, b: Bit| (!a1 & !a2) | !b +); +create_cell!( + &["OAI221xp5_ASAP7_75t_R"], + Asap7OrAndInv221 { a1, a2, b1, b2, c }, + y, + |a1: Bit, a2: Bit, b1: Bit, b2: Bit, c: Bit| { (!a1 & !a2) | (!b1 & !b2) | !c } +); +create_cell!( + &["OAI222xp33_ASAP7_75t_R"], + Asap7OrAndInv222 { + a1, + a2, + b1, + b2, + c1, + c2 + }, + y, + |a1: Bit, a2: Bit, b1: Bit, b2: Bit, c1: Bit, c2: Bit| (!a1 & !a2) | (!b1 & !b2) | (!c1 & !c2) +); +create_cell!( + &[ + "OAI22x1_ASAP7_75t_R", + "OAI22xp33_ASAP7_75t_R", + "OAI22xp5_ASAP7_75t_R" + ], + Asap7OrAndInv22 { a1, a2, b1, b2 }, + y, + |a1: Bit, a2: Bit, b1: Bit, b2: Bit| (!a1 & !a2) | (!b1 & !b2) +); +create_cell!( + &["OAI311xp33_ASAP7_75t_R"], + Asap7OrAndInv311 { a1, a2, a3, b1, c1 }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, c1: Bit| (!a1 & !a2 & !a3) | !b1 | !c1 +); +create_cell!( + &["OAI31xp33_ASAP7_75t_R", "OAI31xp67_ASAP7_75t_R"], + Asap7OrAndInv31 { a1, a2, a3, b }, + y, + |a1: Bit, a2: Bit, a3: Bit, b: Bit| (!a1 & !a2 & !a3) | !b +); +create_cell!( + &["OAI321xp33_ASAP7_75t_R"], + Asap7OrAndInv321 { + a1, + a2, + a3, + b1, + b2, + c + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, c: Bit| (!a1 & !a2 & !a3) | (!b1 & !b2) | !c +); +create_cell!( + &["OAI322xp33_ASAP7_75t_R"], + Asap7OrAndInv322 { + a1, + a2, + a3, + b1, + b2, + c1, + c2 + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, c1: Bit, c2: Bit| (!a1 & !a2 & !a3) + | (!b1 & !b2) + | (!c1 & !c2) +); +create_cell!( + &["OAI32xp33_ASAP7_75t_R"], + Asap7OrAndInv32 { a1, a2, a3, b1, b2 }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit| (!a1 & !a2 & !a3) | (!b1 & !b2) +); +create_cell!( + &["OAI331xp33_ASAP7_75t_R"], + Asap7OrAndInv331 { + a1, + a2, + a3, + b1, + b2, + b3, + c1 + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, b3: Bit, c1: Bit| (!a1 & !a2 & !a3) + | (!b1 & !b2 & !b3) + | !c1 +); +create_cell!( + &["OAI332xp33_ASAP7_75t_R"], + Asap7OrAndInv332 { + a1, + a2, + a3, + b1, + b2, + b3, + c1, + c2 + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, b3: Bit, c1: Bit, c2: Bit| (!a1 & !a2 & !a3) + | (!b1 & !b2 & !b3) + | (!c1 & !c2) +); +create_cell!( + &["OAI333xp33_ASAP7_75t_R"], + Asap7OrAndInv333 { + a1, + a2, + a3, + b1, + b2, + b3, + c1, + c2, + c3 + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, b3: Bit, c1: Bit, c2: Bit, c3: Bit| (!a1 + & !a2 + & !a3) + | (!b1 & !b2 & !b3) + | (!c1 & !c2 & !c3) +); +create_cell!( + &["OAI33xp33_ASAP7_75t_R"], + Asap7OrAndInv33 { + a1, + a2, + a3, + b1, + b2, + b3 + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, b3: Bit| (!a1 & !a2 & !a3) | (!b1 & !b2 & !b3) +); +create_cell!( + &["A2O1A1Ixp33_ASAP7_75t_R"], + Asap7And2Or1And1Inv { a1, a2, b, c }, + y, + |a1: Bit, a2: Bit, b: Bit, c: Bit| (!a1 & !b) | (!a2 & !b) | !c +); +create_cell!( + &["A2O1A1O1Ixp25_ASAP7_75t_R"], + Asap7And2Or1And1Or1Inv { a1, a2, b, c, d }, + y, + |a1: Bit, a2: Bit, b: Bit, c: Bit, d: Bit| (!a1 & !b & !d) | (!a2 & !b & !d) | (!c & !d) +); +create_cell!( + &["AO211x2_ASAP7_75t_R"], + Asap7AndOr211 { a1, a2, b, c }, + y, + |a1: Bit, a2: Bit, b: Bit, c: Bit| (a1 & a2) | b | c +); +create_cell!( + &["AO21x1_ASAP7_75t_R", "AO21x2_ASAP7_75t_R",], + Asap7AndOr21 { a1, a2, b }, + y, + |a1: Bit, a2: Bit, b: Bit| (a1 & a2) | b +); +create_cell!( + &["AO221x1_ASAP7_75t_R", "AO221x2_ASAP7_75t_R",], + Asap7AndOr221 { a1, a2, b1, b2, c }, + y, + |a1: Bit, a2: Bit, b1: Bit, b2: Bit, c: Bit| (a1 & a2) | (b1 & b2) | c +); +create_cell!( + &["AO222x2_ASAP7_75t_R",], + Asap7AndOr222 { + a1, + a2, + b1, + b2, + c1, + c2 + }, + y, + |a1: Bit, a2: Bit, b1: Bit, b2: Bit, c1: Bit, c2: Bit| (a1 & a2) | (b1 & b2) | (c1 & c2) +); +create_cell!( + &["AO22x1_ASAP7_75t_R", "AO22x2_ASAP7_75t_R",], + Asap7AndOr22 { a1, a2, b1, b2 }, + y, + |a1: Bit, a2: Bit, b1: Bit, b2: Bit| (a1 & a2) | (b1 & b2) +); + +create_cell!( + &["AO31x2_ASAP7_75t_R"], + Asap7AndOr31 { a1, a2, a3, b }, + y, + |a1, a2, a3, b| (a1 & a2 & a3) | b +); +create_cell!( + &["AO322x2_ASAP7_75t_R"], + Asap7AndOr322 { + a1, + a2, + a3, + b1, + b2, + c1, + c2 + }, + y, + |a1, a2, a3, b1, b2, c1, c2| (a1 & a2 & a3) | (b1 & b2) | (c1 & c2) +); +create_cell!( + &["AO32x1_ASAP7_75t_R", "AO32x2_ASAP7_75t_R"], + Asap7AndOr32 { a1, a2, a3, b1, b2 }, + y, + |a1, a2, a3, b1, b2| (a1 & a2 & a3) | (b1 & b2) +); +create_cell!( + &["AO331x1_ASAP7_75t_R", "AO331x2_ASAP7_75t_R"], + Asap7AndOr331 { + a1, + a2, + a3, + b1, + b2, + b3, + c + }, + y, + |a1, a2, a3, b1, b2, b3, c| (a1 & a2 & a3) | (b1 & b2 & b3) | c +); +create_cell!( + &["AO332x1_ASAP7_75t_R", "AO332x2_ASAP7_75t_R"], + Asap7AndOr332 { + a1, + a2, + a3, + b1, + b2, + b3, + c1, + c2 + }, + y, + |a1, a2, a3, b1, b2, b3, c1, c2| (a1 & a2 & a3) | (b1 & b2 & b3) | (c1 & c2) +); +create_cell!( + &["AO333x1_ASAP7_75t_R", "AO333x2_ASAP7_75t_R"], + Asap7AndOr333 { + a1, + a2, + a3, + b1, + b2, + b3, + c1, + c2, + c3 + }, + y, + |a1, a2, a3, b1, b2, b3, c1, c2, c3| (a1 & a2 & a3) | (b1 & b2 & b3) | (c1 & c2 & c3) +); +create_cell!( + &["AO33x2_ASAP7_75t_R"], + Asap7AndOr33 { + a1, + a2, + a3, + b1, + b2, + b3 + }, + y, + |a1, a2, a3, b1, b2, b3| (a1 & a2 & a3) | (b1 & b2 & b3) +); +create_cell!( + &["AOI211x1_ASAP7_75t_R", "AOI211xp5_ASAP7_75t_R"], + Asap7AndOrInv211 { a1, a2, b, c }, + y, + |a1: Bit, a2: Bit, b: Bit, c: Bit| (!a1 & !b & !c) | (!a2 & !b & !c) +); +create_cell!( + &[ + "AOI21x1_ASAP7_75t_R", + "AOI21xp33_ASAP7_75t_R", + "AOI21xp5_ASAP7_75t_R" + ], + Asap7AndOrInv21 { a1, a2, b }, + y, + |a1: Bit, a2: Bit, b: Bit| (!a1 & !b) | (!a2 & !b) +); +create_cell!( + &["AOI221x1_ASAP7_75t_R", "AOI221xp5_ASAP7_75t_R"], + Asap7AndOrInv221 { a1, a2, b1, b2, c }, + y, + |a1: Bit, a2: Bit, b1: Bit, b2: Bit, c: Bit| (!a1 & !b1 & !c) + | (!a1 & !b2 & !c) + | (!a2 & !b1 & !c) + | (!a2 & !b2 & !c) +); +create_cell!( + &["AOI222xp33_ASAP7_75t_R"], + Asap7AndOrInv222 { + a1, + a2, + b1, + b2, + c1, + c2 + }, + y, + |a1: Bit, a2: Bit, b1: Bit, b2: Bit, c1: Bit, c2: Bit| (!a1 & !b1 & !c1) + | (!a1 & !b1 & !c2) + | (!a1 & !b2 & !c1) + | (!a1 & !b2 & !c2) + | (!a2 & !b1 & !c1) + | (!a2 & !b1 & !c2) + | (!a2 & !b2 & !c1) + | (!a2 & !b2 & !c2) +); +create_cell!( + &[ + "AOI22x1_ASAP7_75t_R", + "AOI22xp33_ASAP7_75t_R", + "AOI22xp5_ASAP7_75t_R" + ], + Asap7AndOrInv22 { a1, a2, b1, b2 }, + y, + |a1: Bit, a2: Bit, b1: Bit, b2: Bit| (!a1 & !b1) | (!a1 & !b2) | (!a2 & !b1) | (!a2 & !b2) +); +create_cell!( + &["AOI311xp33_ASAP7_75t_R"], + Asap7AndOrInv311 { a1, a2, a3, b, c }, + y, + |a1: Bit, a2: Bit, a3: Bit, b: Bit, c: Bit| (!a1 & !b & !c) | (!a2 & !b & !c) | (!a3 & !b & !c) +); +create_cell!( + &["AOI31xp33_ASAP7_75t_R", "AOI31xp67_ASAP7_75t_R"], + Asap7AndOrInv31 { a1, a2, a3, b }, + y, + |a1: Bit, a2: Bit, a3: Bit, b: Bit| (!a1 & !b) | (!a2 & !b) | (!a3 & !b) +); +create_cell!( + &["AOI321xp33_ASAP7_75t_R"], + Asap7AndOrInv321 { + a1, + a2, + a3, + b1, + b2, + c + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, c: Bit| (a1 & b1 & c) + | (a1 & b2 & c) + | (a2 & b1 & c) + | (a2 & b2 & c) + | (a3 & b1 & c) + | (a3 & b2 & c) +); +create_cell!( + &["AOI322xp5_ASAP7_75t_R"], + Asap7AndOrInv322 { + a1, + a2, + a3, + b1, + b2, + c1, + c2 + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, c1: Bit, c2: Bit| (!a1 & !b1 & !c1) + | (!a1 & !b1 & !c2) + | (!a1 & !b2 & !c1) + | (!a1 & !b2 & !c2) + | (!a2 & !b1 & !c1) + | (!a2 & !b1 & !c2) + | (!a2 & !b2 & !c1) + | (!a2 & !b2 & !c2) + | (!a3 & !b1 & !c1) + | (!a3 & !b1 & !c2) + | (!a3 & !b2 & !c1) + | (!a3 & !b2 & !c2) +); +create_cell!( + &["AOI32xp33_ASAP7_75t_R"], + Asap7AndOrInv32 { a1, a2, a3, b1, b2 }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit| (!a1 & !b1) + | (!a1 & !b2) + | (!a2 & !b1) + | (!a2 & !b2) + | (!a3 & !b1) + | (!a3 & !b2) +); +create_cell!( + &["AOI331xp33_ASAP7_75t_R"], + Asap7AndOrInv331 { + a1, + a2, + a3, + b1, + b2, + b3, + c1 + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, b3: Bit, c1: Bit| (!a1 & !b1 & !c1) + | (!a1 & !b2 & !c1) + | (!a1 & !b3 & !c1) + | (!a2 & !b1 & !c1) + | (!a2 & !b2 & !c1) + | (!a2 & !b3 & !c1) + | (!a3 & !b1 & !c1) + | (!a3 & !b2 & !c1) + | (!a3 & !b3 & !c1) +); +create_cell!( + &["AOI332xp33_ASAP7_75t_R"], + Asap7AndOrInv332 { + a1, + a2, + a3, + b1, + b2, + b3, + c1, + c2 + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, b3: Bit, c1: Bit, c2: Bit| (!a1 & !b1 & !c1) + | (!a1 & !b1 & !c2) + | (!a1 & !b2 & !c1) + | (!a1 & !b2 & !c2) + | (!a1 & !b3 & !c1) + | (!a1 & !b3 & !c2) + | (!a2 & !b1 & !c1) + | (!a2 & !b1 & !c2) + | (!a2 & !b2 & !c1) + | (!a2 & !b2 & !c2) + | (!a2 & !b3 & !c1) + | (!a2 & !b3 & !c2) + | (!a3 & !b1 & !c1) + | (!a3 & !b1 & !c2) + | (!a3 & !b2 & !c1) + | (!a3 & !b2 & !c2) + | (!a3 & !b3 & !c1) + | (!a3 & !b3 & !c2) +); +create_cell!( + &["AOI333xp33_ASAP7_75t_R"], + Asap7AndOrInv333 { + a1, + a2, + a3, + b1, + b2, + b3, + c1, + c2, + c3 + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, b3: Bit, c1: Bit, c2: Bit, c3: Bit| (!a1 + & !b1 + & !c1) + | (!a1 & !b1 & !c2) + | (!a1 & !b1 & !c3) + | (!a1 & !b2 & !c1) + | (!a1 & !b2 & !c2) + | (!a1 & !b2 & !c3) + | (!a1 & !b3 & !c1) + | (!a1 & !b3 & !c2) + | (!a1 & !b3 & !c3) + | (!a2 & !b1 & !c1) + | (!a2 & !b1 & !c2) + | (!a2 & !b1 & !c3) + | (!a2 & !b2 & !c1) + | (!a2 & !b2 & !c2) + | (!a2 & !b2 & !c3) + | (!a2 & !b3 & !c1) + | (!a2 & !b3 & !c2) + | (!a2 & !b3 & !c3) + | (!a3 & !b1 & !c1) + | (!a3 & !b1 & !c2) + | (!a3 & !b1 & !c3) + | (!a3 & !b2 & !c1) + | (!a3 & !b2 & !c2) + | (!a3 & !b2 & !c3) + | (!a3 & !b3 & !c1) + | (!a3 & !b3 & !c2) + | (!a3 & !b3 & !c3) +); +create_cell!( + &["AOI33xp33_ASAP7_75t_R"], + Asap7AndOrInv33 { + a1, + a2, + a3, + b1, + b2, + b3 + }, + y, + |a1: Bit, a2: Bit, a3: Bit, b1: Bit, b2: Bit, b3: Bit| (!a1 & !b1) + | (!a1 & !b2) + | (!a1 & !b3) + | (!a2 & !b1) + | (!a2 & !b2) + | (!a2 & !b3) + | (!a3 & !b1) + | (!a3 & !b2) + | (!a3 & !b3) +); + #[derive(Debug, Clone, Serialize, Deserialize, derive_new::new)] -pub struct DffReset { - clock_polarity: Bit, - reset_polarity: Bit, - reset_val: Bit, +pub struct Asap7DffInv { clock_net: usize, - reset_net: usize, data_in_net: usize, data_out_net: usize, #[new(default)] last_clock: Bit, } -impl CellFn for DffReset { +impl CellFn for Asap7DffInv { #[inline] fn eval(&mut self, signals: &mut Signals) { // Check if clock active for any polarity - let clock = !(signals.get_net(self.clock_net) ^ self.clock_polarity); + let clock = signals.get_net(self.clock_net); // Rising edge if clock == Bit::ONE && self.last_clock == Bit::ZERO { - // Check if reset active for any polarity - if signals.get_net(self.reset_net) == self.reset_polarity { - signals.set_net(self.data_out_net, self.reset_val); - } else { - let data_in = signals.get_net(self.data_in_net); - signals.set_net(self.data_out_net, data_in); - } + let data_in = signals.get_net(self.data_in_net); + signals.set_net(self.data_out_net, !data_in); // Inverse } self.last_clock = clock; @@ -389,16 +1218,16 @@ impl CellFn for DffReset { } inventory::submit! { - CellRegistration::new(&["$_SDFF_PP0_"], + CellRegistration::new(&[ + "DFFHQNx1_ASAP7_75t_R", + "DFFHQNx2_ASAP7_75t_R", + "DFFHQNx3_ASAP7_75t_R", + ], |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { - DffReset::new( - Bit::ONE, - Bit::ONE, - Bit::ZERO, - connections["C"][0], - connections["R"][0], + Asap7DffInv::new( + connections["CLK"][0], connections["D"][0], - connections["Q"][0], + connections["QN"][0], ).into() }) } diff --git a/crates/arbolta/tests/test_simcell.rs b/crates/arbolta/tests/test_simcell.rs index f1798c6..5f1aa22 100644 --- a/crates/arbolta/tests/test_simcell.rs +++ b/crates/arbolta/tests/test_simcell.rs @@ -35,49 +35,49 @@ fn test_cell_unary(#[case] mut cell: Box, #[case] cases: [(u8, u8); } #[rstest] -#[case::and(Box::new(And::new(0, 1, 2)), [ // (A, B, Y) +#[case::and(Box::new(And2::new(0, 1, 2)), [ // (A, B, Y) (0, 0, 0), (0, 1, 0), (1, 0, 0), (1, 1, 1), ])] -#[case::nand(Box::new(Nand::new(0, 1, 2)), [ // (A, B, Y) +#[case::nand(Box::new(Nand2::new(0, 1, 2)), [ // (A, B, Y) (0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 0), ])] -#[case::or(Box::new(Or::new(0, 1, 2)), [ // (A, B, Y) +#[case::or(Box::new(Or2::new(0, 1, 2)), [ // (A, B, Y) (0, 0, 0), (0, 1, 1), (1, 0, 1), (1, 1, 1), ])] -#[case::nor(Box::new(Nor::new(0, 1, 2)), [ // (A, B, Y) +#[case::nor(Box::new(Nor2::new(0, 1, 2)), [ // (A, B, Y) (0, 0, 1), (0, 1, 0), (1, 0, 0), (1, 1, 0), ])] -#[case::xor(Box::new(Xor::new(0, 1, 2)), [ // (A, B, Y) +#[case::xor(Box::new(Xor2::new(0, 1, 2)), [ // (A, B, Y) (0, 0, 0), (0, 1, 1), (1, 0, 1), (1, 1, 0), ])] -#[case::xnor(Box::new(Xnor::new(0, 1, 2)), [ // (A, B, Y) +#[case::xnor(Box::new(Xnor2::new(0, 1, 2)), [ // (A, B, Y) (0, 0, 1), (0, 1, 0), (1, 0, 0), (1, 1, 1), ])] -#[case::andnot(Box::new(AndNot::new(0, 1, 2)), [ // (A, B, Y) +#[case::andnot(Box::new(AndNot2::new(0, 1, 2)), [ // (A, B, Y) (0, 0, 0), (0, 1, 0), (1, 0, 1), (1, 1, 0), ])] -#[case::ornot(Box::new(OrNot::new(0, 1, 2)), [ // (A, B, Y) +#[case::ornot(Box::new(OrNot2::new(0, 1, 2)), [ // (A, B, Y) (0, 0, 1), (0, 1, 0), (1, 0, 1), @@ -117,27 +117,7 @@ fn test_cell_binary(#[case] mut cell: Box, #[case] cases: [(u8, u8, (1, 1, 0, 0), (1, 1, 1, 0), ])] -#[case::and3(Box::new(And3::new(0, 1, 2, 3)), [ // (A, B, C, Y) - (0, 0, 0, 0), - (0, 0, 1, 0), - (0, 1, 0, 0), - (0, 1, 1, 0), - (1, 0, 0, 0), - (1, 0, 1, 0), - (1, 1, 0, 0), - (1, 1, 1, 1), -])] -#[case::andor(Box::new(AndOr21::new(0, 1, 2, 3)), [ // (A, B, C, Y) - (0, 0, 0, 0), - (0, 0, 1, 1), - (0, 1, 0, 0), - (0, 1, 1, 1), - (1, 0, 0, 0), - (1, 0, 1, 1), - (1, 1, 0, 1), - (1, 1, 1, 1), -])] -#[case::andorinvert(Box::new(AndOrInvert::new(0, 1, 2, 3)), [ // (A, B, C, Y) +#[case::andorinvert(Box::new(AndOrInvert3::new(0, 1, 2, 3)), [ // (A, B, C, Y) (0, 0, 0, 1), (0, 0, 1, 0), (0, 1, 0, 1), @@ -147,17 +127,7 @@ fn test_cell_binary(#[case] mut cell: Box, #[case] cases: [(u8, u8, (1, 1, 0, 0), (1, 1, 1, 0), ])] -#[case::andorreduce(Box::new(AndOrReduce::new(0, 1, 2, 3)), [ // (A, B, C, Y) - (0, 0, 0, 0), - (0, 0, 1, 0), - (0, 1, 0, 0), - (0, 1, 1, 1), - (1, 0, 0, 0), - (1, 0, 1, 1), - (1, 1, 0, 0), - (1, 1, 1, 1), -])] -#[case::orandinvert(Box::new(OrAndInvert::new(0, 1, 2, 3)), [ // (A, B, C, Y) +#[case::orandinvert(Box::new(OrAndInvert3::new(0, 1, 2, 3)), [ // (A, B, C, Y) (0, 0, 0, 1), (0, 0, 1, 1), (0, 1, 0, 1), @@ -181,13 +151,7 @@ fn test_cell_ternary(#[case] mut cell: Box, #[case] cases: [(u8, u8, } #[rstest] -#[case::half_adder(Box::new(HalfAdder::new(0, 1, 2, 3)), [ // A, B, SO, CO - (0, 0, 0, 0), - (0, 1, 1, 0), - (1, 0, 1, 0), - (1, 1, 0, 1), -])] -#[case::half_adder_inv(Box::new(HalfAdderInv::new(0, 1, 2, 3)), [ // A, B, SO, CO +#[case::half_adder_inv(Box::new(Asap7HalfAdderInv::new(0, 1, 2, 3)), [ // A, B, SO, CO (0, 0, 1, 1), (0, 1, 0, 1), (1, 0, 0, 1), @@ -210,17 +174,7 @@ fn test_cell_binary_two_output( } #[rstest] -#[case::full_adder(Box::new(FullAdder::new(0, 1, 2, 3, 4)), [ // A, B, CI, SO, CO - (0, 0, 0, 0, 0), - (0, 0, 1, 1, 0), - (0, 1, 0, 1, 0), - (0, 1, 1, 0, 1), - (1, 0, 0, 1, 0), - (1, 0, 1, 0, 1), - (1, 1, 0, 0, 1), - (1, 1, 1, 1, 1), -])] -#[case::full_adder_inv(Box::new(FullAdderInv::new(0, 1, 2, 3, 4)), [ // A, B, CI, SO, CO +#[case::full_adder_inv(Box::new(Asap7FullAdderInv::new(0, 1, 2, 3, 4)), [ // A, B, CI, SO, CO (0, 0, 0, 1, 1), (0, 0, 1, 0, 1), (0, 1, 0, 0, 1), From bf674ae8c1787accfa7e93919ac447d87304e60d Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Thu, 8 Jan 2026 20:09:36 +0000 Subject: [PATCH 44/64] Fix for buffers with no output --- crates/arbolta/src/cell/mod.rs | 14 ++++++++------ crates/arbolta/src/cell/simcells.rs | 29 +++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index 34bc6c6..b73f8f3 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -10,7 +10,10 @@ use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; pub use simcells::*; pub use simlib::*; -use std::collections::{BTreeMap, HashMap}; +use std::{ + collections::{BTreeMap, HashMap}, + env, +}; use thiserror::Error; #[enum_dispatch] @@ -191,6 +194,10 @@ pub fn create_cell( parameters: &BTreeMap<&str, usize>, mapping: Option<&CellMapping>, ) -> Result { + if env::var("ARBOLTA_DEBUG").is_ok() { + println!("Parsing cell `{cell_type}`") + } + let (cell_type, mut connections) = if let Some(mapping) = mapping && let Some((mapped_cell_type, mapped_connections)) = mapping.get(cell_type) { @@ -211,10 +218,5 @@ pub fn create_cell( .get(cell_type) .ok_or_else(|| CellError::Unsupported(cell_type.to_string()))?; - // Special case, buffer with no output - if cell_type == "$_BUF_" && !connections.contains_key("Y") { - connections.insert("Y", Box::from([0])); - } - Ok(ctor(&connections, parameters)) } diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index 7fc308a..fe6b7dc 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -3,7 +3,7 @@ use crate::{bit::Bit, cell::CellRegistration, signal::Signals}; use derive_more::Constructor; use paste::paste; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; +use std::{collections::BTreeMap, env}; macro_rules! create_cell { // 1-output, no inputs @@ -25,6 +25,10 @@ macro_rules! create_cell { paste! { inventory::submit! {CellRegistration::new($rtl_names, |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { + if env::var("ARBOLTA_DEBUG").is_ok() { + println!("Parsing connections: {:#?}", connections); + } + $cell_type::new( connections[stringify!([<$out_net:upper>])][0] ).into() @@ -59,11 +63,21 @@ macro_rules! create_cell { paste! { inventory::submit! {CellRegistration::new($rtl_names, |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { + if env::var("ARBOLTA_DEBUG").is_ok() { + println!("Parsing connections: {:#?}", connections); + } + + // Special case for buffers + let output_net = match connections.get(stringify!([<$out_net:upper>])) { + Some(nets) => nets[0], + None => 0 // Write to zero does nothing + }; + $cell_type::new( $( connections[stringify!([<$in_netn:upper>])][0], )* - connections[stringify!([<$out_net:upper>])][0] + output_net ).into() })} } @@ -102,6 +116,10 @@ macro_rules! create_cell { paste! { inventory::submit! {CellRegistration::new($rtl_names, |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { + if env::var("ARBOLTA_DEBUG").is_ok() { + println!("Parsing connections: {:#?}", connections); + } + $cell_type::new( $( connections[stringify!([<$in_netn:upper>])][0], @@ -234,6 +252,13 @@ impl CellFn for DffReset { inventory::submit! { CellRegistration::new(&["$_SDFF_PP0_"], |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { + + let output_net = match connections.get("Y") { + Some(nets) => nets[0], + None => 0 + }; + + DffReset::new( Bit::ONE, Bit::ONE, From 3785e6d378b5bd46f7228c4a436436195e0470fe Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Fri, 9 Jan 2026 00:04:15 +0000 Subject: [PATCH 45/64] Fixed bug in tie low cell --- crates/arbolta/src/cell/simcells.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index fe6b7dc..31fd525 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -483,7 +483,7 @@ create_cell!( |a: Bit, b: Bit, c: Bit, d: Bit, e: Bit| a | b | c | d | e ); create_cell!(&["TIEHIx1_ASAP7_75t_R"], Asap7TieHigh, h, || Bit::ONE); -create_cell!(&["TIELOx1_ASAP7_75t_R"], Asap7TieLow, h, || Bit::ZERO); +create_cell!(&["TIELOx1_ASAP7_75t_R"], Asap7TieLow, l, || Bit::ZERO); create_cell!( &[ "XNOR2x1_ASAP7_75t_R", From 01fa6f54a1812061ab6a65044103062e1b15c75f Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Sat, 10 Jan 2026 00:52:51 +0000 Subject: [PATCH 46/64] Improved toggle count reporting --- crates/arbolta_pyo3/arbolta.pyi | 4 ++ crates/arbolta_pyo3/src/hardware_module.rs | 80 ++++++++++++++-------- 2 files changed, 57 insertions(+), 27 deletions(-) diff --git a/crates/arbolta_pyo3/arbolta.pyi b/crates/arbolta_pyo3/arbolta.pyi index bae03a0..57d08ef 100644 --- a/crates/arbolta_pyo3/arbolta.pyi +++ b/crates/arbolta_pyo3/arbolta.pyi @@ -69,9 +69,12 @@ class HardwareDesign: :var ports: Access to simulated module ports :vartype ports: Ports + :var modules: List of all submodules in design + :vartype modules: list[str] """ ports: Ports + modules: list[str] def __init__( self, @@ -116,3 +119,4 @@ class HardwareDesign: def stick_signal(self, net: int, val: Literal[0, 1]) -> None: ... def unstick_signal(self, net: int) -> None: ... + def toggle_count(self, category: str = "total") -> dict[str, dict[str, int]]: ... diff --git a/crates/arbolta_pyo3/src/hardware_module.rs b/crates/arbolta_pyo3/src/hardware_module.rs index 7535d83..8deb93f 100644 --- a/crates/arbolta_pyo3/src/hardware_module.rs +++ b/crates/arbolta_pyo3/src/hardware_module.rs @@ -5,17 +5,17 @@ use crate::conversion::{ bits_to_bool_numpy, bits_to_int_numpy, bool_numpy_to_bits, int_numpy_to_bits, }; use crate::ports::{PortConfig, Ports}; -use arbolta::bit::Bit; use arbolta::{ + bit::Bit, cell::CellMapping, - hardware_module::HardwareModule, + hardware_module::{HardwareModule, ToggleCount}, port::PortDirection, yosys::{Netlist, parse_torder}, }; use pyo3::{ exceptions::{PyAttributeError, PyValueError}, prelude::*, - types::PyDict, + types::{PyDict, PyList}, }; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, path::PathBuf}; @@ -146,26 +146,37 @@ impl HardwareDesign { top_module: Option<&str>, cell_mapping: Option, ) -> anyhow::Result> { - let new_module = Py::new( - py, - Self::new_base( - netlist_path, - top_module, - torder_path, - use_slash_hierarchy, - cell_mapping.as_ref(), - )?, + let new_module = Self::new_base( + netlist_path, + top_module, + torder_path, + use_slash_hierarchy, + cell_mapping.as_ref(), )?; + // Get submodules before binding to Python + let submodules = new_module + .module + .netlist + .modules + .iter() + .map(|p| p.join(".")) + .collect::>(); + + let py_module = Py::new(py, new_module)?; + // Add custom members (can't make them static for serialization) - let temp_binding = new_module.getattr(py, "__dict__")?; + let temp_binding = py_module.getattr(py, "__dict__")?; let temp_dict = temp_binding.cast_bound::(py).unwrap(); // Add ports field - let ports = Py::new(py, Ports::new(py, config, new_module.clone_ref(py))?)?; + let ports = Py::new(py, Ports::new(py, config, py_module.clone_ref(py))?)?; temp_dict.set_item("ports", ports)?; - Ok(new_module) + // Add modules/submodules field + temp_dict.set_item("modules", PyList::new(py, submodules)?)?; + + Ok(py_module) } pub fn reset(&mut self) { @@ -193,21 +204,36 @@ impl HardwareDesign { pub fn unstick_signal(&mut self, net: usize) -> anyhow::Result<()> { Ok(self.module.unstick_signal(net)?) } - // TODO - - pub fn get_module_names(&self) -> Vec { - // TODO: How to handle top_module? - self - .module - .netlist - .modules - .iter() - .map(|p| p.join(".")) - .collect() - } pub fn is_port_input(&self, name: &str) -> anyhow::Result { let direction = self.module.get_port_direction(name)?; Ok(direction == PortDirection::Input) } + + #[pyo3(signature = (category="total"))] + pub fn toggle_count(&self, category: &str) -> PyResult>> { + let category = match category { + "falling" => ToggleCount::Falling, + "rising" => ToggleCount::Rising, + "total" => ToggleCount::Total, + _ => { + return Err(PyValueError::new_err(format!( + "Invalid toggle category `{category}`" + ))); + } + }; + + let mut toggles = HashMap::>::new(); + for (submodule_names, nets_ref) in self.module.get_submodule_toggles_by_net(category) { + let name = submodule_names.join("."); + let nets = nets_ref + .into_iter() + .map(|(n, c)| (n.to_string(), c)) + .collect(); + + toggles.insert(name, nets); + } + + Ok(toggles) + } } From 2dfde125d4ed2c3c32b03b3bbab04a3e1d5ee3a9 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Sat, 10 Jan 2026 01:21:27 +0000 Subject: [PATCH 47/64] Fixed toggle count interface --- crates/arbolta_pyo3/arbolta.pyi | 4 ++- crates/arbolta_pyo3/src/hardware_module.rs | 31 ++++++++++++++-------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/crates/arbolta_pyo3/arbolta.pyi b/crates/arbolta_pyo3/arbolta.pyi index 57d08ef..33d21c9 100644 --- a/crates/arbolta_pyo3/arbolta.pyi +++ b/crates/arbolta_pyo3/arbolta.pyi @@ -119,4 +119,6 @@ class HardwareDesign: def stick_signal(self, net: int, val: Literal[0, 1]) -> None: ... def unstick_signal(self, net: int) -> None: ... - def toggle_count(self, category: str = "total") -> dict[str, dict[str, int]]: ... + def toggle_count( + self, category: str = "total", by_net: bool = True + ) -> dict[str, dict[str, int]] | dict[str, int]: ... diff --git a/crates/arbolta_pyo3/src/hardware_module.rs b/crates/arbolta_pyo3/src/hardware_module.rs index 8deb93f..95a0a39 100644 --- a/crates/arbolta_pyo3/src/hardware_module.rs +++ b/crates/arbolta_pyo3/src/hardware_module.rs @@ -12,6 +12,7 @@ use arbolta::{ port::PortDirection, yosys::{Netlist, parse_torder}, }; +use pyo3::types::IntoPyDict; use pyo3::{ exceptions::{PyAttributeError, PyValueError}, prelude::*, @@ -210,8 +211,8 @@ impl HardwareDesign { Ok(direction == PortDirection::Input) } - #[pyo3(signature = (category="total"))] - pub fn toggle_count(&self, category: &str) -> PyResult>> { + #[pyo3(signature = (category="total", by_net=true))] + pub fn toggle_count(&self, py: Python<'_>, category: &str, by_net: bool) -> PyResult> { let category = match category { "falling" => ToggleCount::Falling, "rising" => ToggleCount::Rising, @@ -223,17 +224,25 @@ impl HardwareDesign { } }; - let mut toggles = HashMap::>::new(); - for (submodule_names, nets_ref) in self.module.get_submodule_toggles_by_net(category) { - let name = submodule_names.join("."); - let nets = nets_ref - .into_iter() - .map(|(n, c)| (n.to_string(), c)) - .collect(); + let toggles = PyDict::new(py); + if by_net { + for (submodule_names, nets_ref) in self.module.get_submodule_toggles_by_net(category) { + let name = submodule_names.join("."); + let nets: HashMap = nets_ref + .into_iter() + .map(|(n, c)| (n.to_string(), c)) + .collect(); - toggles.insert(name, nets); + toggles.set_item(name, nets)?; + } + } else { + for (submodule_names, count) in self.module.get_submodule_toggles_total(category) { + let name = submodule_names.join("."); + + toggles.set_item(name, count)?; + } } - Ok(toggles) + Ok(toggles.into()) } } From 2b8e9277fe54d21d1090e5ba712cca2adb2213eb Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Thu, 15 Jan 2026 22:48:02 +0000 Subject: [PATCH 48/64] Fixed cell --- crates/arbolta/src/cell/mod.rs | 4 +++- crates/arbolta/src/cell/simcells.rs | 7 ------ crates/arbolta/src/cell/simlib/arithmetic.rs | 13 +++++++++- .../src/cell/simlib/logic_reduce_ops.rs | 15 +++++++++--- crates/arbolta/src/cell/simlib/mod.rs | 24 +++++++++++++++++++ crates/arbolta_pyo3/src/hardware_module.rs | 1 - 6 files changed, 51 insertions(+), 13 deletions(-) diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index b73f8f3..acd980c 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -156,6 +156,7 @@ pub enum Cell { Div, Modulus, Le, + Lt, Ge, Gt, Shl, @@ -167,6 +168,7 @@ pub enum Cell { PMux, LogicAnd, LogicNot, + LogicOr, ReduceOr, ReduceAnd, ProcAnd, @@ -198,7 +200,7 @@ pub fn create_cell( println!("Parsing cell `{cell_type}`") } - let (cell_type, mut connections) = if let Some(mapping) = mapping + let (cell_type, connections) = if let Some(mapping) = mapping && let Some((mapped_cell_type, mapped_connections)) = mapping.get(cell_type) { let connections = match mapped_connections { diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index 31fd525..e66aa00 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -252,13 +252,6 @@ impl CellFn for DffReset { inventory::submit! { CellRegistration::new(&["$_SDFF_PP0_"], |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { - - let output_net = match connections.get("Y") { - Some(nets) => nets[0], - None => 0 - }; - - DffReset::new( Bit::ONE, Bit::ONE, diff --git a/crates/arbolta/src/cell/simlib/arithmetic.rs b/crates/arbolta/src/cell/simlib/arithmetic.rs index 1b4c516..cb7a67e 100644 --- a/crates/arbolta/src/cell/simlib/arithmetic.rs +++ b/crates/arbolta/src/cell/simlib/arithmetic.rs @@ -9,7 +9,8 @@ define_arithmetic_cell!(Sub, wrapping_sub); define_arithmetic_cell!(Mul, wrapping_mul); define_arithmetic_cell!(Div, wrapping_div); define_arithmetic_cell!(Modulus, rem); -define_arithmetic_cell!(Le, <); +define_arithmetic_cell!(Lt, <); +define_arithmetic_cell!(Le, &le); define_arithmetic_cell!(Gt, >); define_arithmetic_cell!(Ge, &ge); @@ -117,10 +118,20 @@ mod tests { // 37738 < 4365 = 0 #[case::unsigned_normal(false, "1001001101101010", "0001000100001101", "0")] #[case::signed_normal(true, "1001001101101010", "0001000100001101", "1")] + #[case::unsigned_equal(false, "101010", "00101010", "1")] fn le(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { run_binary_cell_case_signed!(Le, signed, a, b, expected); } + #[rstest] + // 37738 < 4365 = 0 + #[case::unsigned_normal(false, "1001001101101010", "0001000100001101", "0")] + #[case::signed_normal(true, "1001001101101010", "0001000100001101", "1")] + #[case::unsigned_equal(false, "101010", "00101010", "0")] + fn lt(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { + run_binary_cell_case_signed!(Lt, signed, a, b, expected); + } + #[rstest] fn ge() { println!("TODO") diff --git a/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs b/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs index ff7d785..9fc3d0b 100644 --- a/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs +++ b/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs @@ -81,7 +81,7 @@ impl CellFn for LogicNot { fn reset(&mut self) {} } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Constructor)] pub struct LogicOr { a_nets: Box<[usize]>, b_nets: Box<[usize]>, @@ -149,7 +149,16 @@ mod tests { } #[rstest] - fn logic_or() { - println!("TODO") + #[case("0", "0", "0")] + #[case("0", "1", "1")] + #[case("1", "0", "1")] + #[case("1", "1", "1")] + #[case("00000000", "0000000", "0000")] + #[case("10000000", "0000001", "0001")] + #[case("1", "0000001", "01")] + #[case("1111111", "01", "01")] + #[case("1010100", "0000", "00001")] + fn logic_or(#[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { + run_binary_cell_case!(LogicOr, a, b, expected); } } diff --git a/crates/arbolta/src/cell/simlib/mod.rs b/crates/arbolta/src/cell/simlib/mod.rs index dd08e61..8548101 100644 --- a/crates/arbolta/src/cell/simlib/mod.rs +++ b/crates/arbolta/src/cell/simlib/mod.rs @@ -264,6 +264,16 @@ fn make_le(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&st .into() } +fn make_lt(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>) -> Cell { + Lt::new( + (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + fn make_ge(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>) -> Cell { Ge::new( (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), @@ -396,6 +406,18 @@ fn make_logic_not( LogicNot::new(connections["A"].clone(), connections["Y"].clone()).into() } +fn make_logic_or( + connections: &BTreeMap<&str, Box<[usize]>>, + _parameters: &BTreeMap<&str, usize>, +) -> Cell { + LogicOr::new( + connections["A"].clone(), + connections["B"].clone(), + connections["Y"].clone(), + ) + .into() +} + fn make_reduce_or( connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>, @@ -475,6 +497,7 @@ inventory::submit! {CellRegistration::new(&["$mul"], make_mul)} inventory::submit! {CellRegistration::new(&["$div"], make_div)} inventory::submit! {CellRegistration::new(&["$mod"], make_mod)} inventory::submit! {CellRegistration::new(&["$le"], make_le)} +inventory::submit! {CellRegistration::new(&["$lt"], make_lt)} inventory::submit! {CellRegistration::new(&["$ge"], make_ge)} inventory::submit! {CellRegistration::new(&["$gt"], make_gt)} inventory::submit! {CellRegistration::new(&["$shl"], make_shl)} @@ -486,6 +509,7 @@ inventory::submit! {CellRegistration::new(&["$bmux"], make_bmux)} inventory::submit! {CellRegistration::new(&["$pmux"], make_pmux)} inventory::submit! {CellRegistration::new(&["$logic_and"], make_logic_and)} inventory::submit! {CellRegistration::new(&["$logic_not"], make_logic_not)} +inventory::submit! {CellRegistration::new(&["$logic_or"], make_logic_or)} inventory::submit! {CellRegistration::new(&["$reduce_or", "$reduce_bool"], make_reduce_or)} inventory::submit! {CellRegistration::new(&["$reduce_and"], make_reduce_and)} inventory::submit! {CellRegistration::new(&["$and"], make_and)} diff --git a/crates/arbolta_pyo3/src/hardware_module.rs b/crates/arbolta_pyo3/src/hardware_module.rs index 95a0a39..7b9d128 100644 --- a/crates/arbolta_pyo3/src/hardware_module.rs +++ b/crates/arbolta_pyo3/src/hardware_module.rs @@ -12,7 +12,6 @@ use arbolta::{ port::PortDirection, yosys::{Netlist, parse_torder}, }; -use pyo3::types::IntoPyDict; use pyo3::{ exceptions::{PyAttributeError, PyValueError}, prelude::*, From f3c081b0e6e4b4d87e99b07aee866c0b60186dd6 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Thu, 5 Feb 2026 18:22:09 +0000 Subject: [PATCH 49/64] Improved optional hierarchy separation --- crates/arbolta/src/cell/mod.rs | 59 +-- crates/arbolta/src/cell/simcells.rs | 22 +- crates/arbolta/src/cell/simlib/arithmetic.rs | 144 +++---- crates/arbolta/src/cell/simlib/bool_ops.rs | 6 +- crates/arbolta/src/cell/simlib/compare_ops.rs | 22 -- .../src/cell/simlib/logic_reduce_ops.rs | 141 +++---- crates/arbolta/src/cell/simlib/mod.rs | 372 +++--------------- crates/arbolta/src/cell/simlib/shift_ops.rs | 157 ++++---- crates/arbolta/src/hardware_module.rs | 4 +- crates/arbolta/src/netlist_wrapper.rs | 39 +- crates/arbolta/tests/test_simcell.rs | 52 +-- crates/arbolta/tests/test_simcell_module.rs | 40 +- crates/arbolta/tests/test_simlib_module.rs | 12 +- crates/arbolta_pyo3/arbolta.pyi | 6 +- crates/arbolta_pyo3/src/hardware_module.rs | 10 +- 15 files changed, 352 insertions(+), 734 deletions(-) delete mode 100644 crates/arbolta/src/cell/simlib/compare_ops.rs diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index acd980c..56fe40e 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -1,6 +1,5 @@ mod simcells; mod simlib; -// mod temp; mod test_helpers; // Re-export @@ -130,52 +129,58 @@ pub enum Cell { Asap7AndOrInv33, Asap7DffInv, // Sim Cells - And2, - AndNot2, - AndOrInvert3, + // - Unary Buffer, - Dff, - DffReset, Inverter, - Mux2, + // - Binary + And2, + AndNot2, Nand2, - NMux2, Nor2, Or2, - OrAndInvert3, OrNot2, Xnor2, Xor2, + // - Ternary + AndOrInvert3, + Mux2, + NMux2, + OrAndInvert3, + // - Memory + Dff, + DffReset, // Sim Lib - Not, - Neg, - Pos, + // - Arithmetic Add, - Sub, - Mul, Div, + Equal, + GreaterEqual, + GreaterThan, + LessEqual, + LessThan, Modulus, - Le, - Lt, - Ge, - Gt, - Shl, - Shr, - Reg, + Mul, + Negate, + NotEqual, + Sub, + // ALDff, - Mux, BMux, - PMux, LogicAnd, LogicNot, LogicOr, - ReduceOr, - ReduceAnd, + Mux, + Not, + PMux, + Pos, ProcAnd, - Eq, - Ne, ProcOr, ProcXor, + ReduceAnd, + ReduceOr, + Reg, + Shl, + Shr, } #[derive(Debug, Error)] diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index e66aa00..69764b0 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -134,41 +134,47 @@ macro_rules! create_cell { } /* ++++++++ Sim Cells ++++++++ */ +// Unary +create_cell!(&["$_BUF_"], Buffer { a }, y, |a: Bit| a); +create_cell!(&["$_NOT_"], Inverter { a }, y, |a: Bit| !a); + +// Binary create_cell!(&["$_AND_"], And2 { a, b }, y, |a: Bit, b: Bit| a & b); create_cell!(&["$_ANDNOT_"], AndNot2 { a, b }, y, |a: Bit, b: Bit| a & !b); +create_cell!(&["$_NAND_"], Nand2 { a, b }, y, |a: Bit, b: Bit| !(a & b)); +create_cell!(&["$_NOR_"], Nor2 { a, b }, y, |a: Bit, b: Bit| !(a | b)); +create_cell!(&["$_OR_"], Or2 { a, b }, y, |a: Bit, b: Bit| a | b); +create_cell!(&["$_ORNOT_"], OrNot2 { a, b }, y, |a: Bit, b: Bit| a | !b); +create_cell!(&["$_XNOR_"], Xnor2 { a, b }, y, |a: Bit, b: Bit| !(a ^ b)); +create_cell!(&["$_XOR_"], Xor2 { a, b }, y, |a: Bit, b: Bit| a ^ b); + +// Ternary create_cell!( &["$_AOI3_"], AndOrInvert3 { a, b, c }, y, |a: Bit, b: Bit, c: Bit| { !((a & b) | c) } ); -create_cell!(&["$_BUF_"], Buffer { a }, y, |a: Bit| a); -create_cell!(&["$_NOT_"], Inverter { a }, y, |a: Bit| !a); create_cell!( &["$_MUX_"], Mux2 { a, b, s }, y, |a: Bit, b: Bit, select: Bit| if select.into() { b } else { a } ); -create_cell!(&["$_NAND_"], Nand2 { a, b }, y, |a: Bit, b: Bit| !(a & b)); create_cell!( &["$_NMUX_"], NMux2 { a, b, s }, y, |a: Bit, b: Bit, select: Bit| if select.into() { !b } else { !a } ); -create_cell!(&["$_NOR_"], Nor2 { a, b }, y, |a: Bit, b: Bit| !(a | b)); -create_cell!(&["$_OR_"], Or2 { a, b }, y, |a: Bit, b: Bit| a | b); create_cell!( &["$_OAI3_"], OrAndInvert3 { a, b, c }, y, |a: Bit, b: Bit, c: Bit| { !((a | b) & c) } ); -create_cell!(&["$_ORNOT_"], OrNot2 { a, b }, y, |a: Bit, b: Bit| a | !b); -create_cell!(&["$_XNOR_"], Xnor2 { a, b }, y, |a: Bit, b: Bit| !(a ^ b)); -create_cell!(&["$_XOR_"], Xor2 { a, b }, y, |a: Bit, b: Bit| a ^ b); +// Memory #[derive(Debug, Clone, Serialize, Deserialize, derive_new::new)] pub struct Dff { polarity: Bit, diff --git a/crates/arbolta/src/cell/simlib/arithmetic.rs b/crates/arbolta/src/cell/simlib/arithmetic.rs index cb7a67e..9b9f906 100644 --- a/crates/arbolta/src/cell/simlib/arithmetic.rs +++ b/crates/arbolta/src/cell/simlib/arithmetic.rs @@ -4,50 +4,18 @@ use derive_more::Constructor; use serde::{Deserialize, Serialize}; use std::ops::Rem; -define_arithmetic_cell!(Add, wrapping_add); -define_arithmetic_cell!(Sub, wrapping_sub); -define_arithmetic_cell!(Mul, wrapping_mul); -define_arithmetic_cell!(Div, wrapping_div); -define_arithmetic_cell!(Modulus, rem); -define_arithmetic_cell!(Lt, <); -define_arithmetic_cell!(Le, &le); -define_arithmetic_cell!(Gt, >); -define_arithmetic_cell!(Ge, &ge); - -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] -pub struct Neg { - signed: bool, - a_nets: Box<[usize]>, - y_nets: Box<[usize]>, -} - -impl CellFn for Neg { - #[inline] - fn eval(&mut self, signals: &mut Signals) { - let a = BitVec::from(bits_from_nets(signals, &self.a_nets)); - let output_size = self.y_nets.len(); - - let y: BitVec = if output_size <= 64 { - let a = -a.to_int::(); - if self.signed { - BitVec::from_int(a, Some(output_size)) - } else { - BitVec::from_int(a as u64, Some(output_size)) - } - } else { - let a = -a.to_int::(); - if self.signed { - BitVec::from_int(a, Some(output_size)) - } else { - BitVec::from_int(a as u128, Some(output_size)) - } - }; - - copy_bits(signals, &self.y_nets, &y); - } - - fn reset(&mut self) {} -} +define_arithmetic_cell!(&["$add"], Add { a, b }, y, a.wrapping_add(b)); +define_arithmetic_cell!(&["$div"], Div { a, b }, y, a.wrapping_div(b)); +define_arithmetic_cell!(&["$eq"], Equal { a, b }, y, a.eq(&b)); +define_arithmetic_cell!(&["$ge"], GreaterEqual { a, b }, y, a.ge(&b)); +define_arithmetic_cell!(&["$gt"], GreaterThan { a, b }, y, a.gt(&b)); +define_arithmetic_cell!(&["$le"], LessEqual { a, b }, y, a.le(&b)); +define_arithmetic_cell!(&["$lt"], LessThan { a, b }, y, a.lt(&b)); +define_arithmetic_cell!(&["$mod"], Modulus { a, b }, y, a.rem(b)); +define_arithmetic_cell!(&["$mul"], Mul { a, b }, y, a.wrapping_mul(b)); +define_arithmetic_cell!(&["$neg"], Negate { a }, y, a.wrapping_neg()); +define_arithmetic_cell!(&["$ne"], NotEqual { a, b }, y, a.ne(&b)); +define_arithmetic_cell!(&["$sub"], Sub { a, b }, y, a.wrapping_sub(b)); #[cfg(test)] mod tests { @@ -82,45 +50,50 @@ mod tests { } #[rstest] - fn sub() { + fn div() { println!("TODO") } #[rstest] - // 8712 * 5366 = 46748592 - #[case::unsigned_normal( - false, - "0010001000001000", - "0001010011110110", - "00000010110010010101001110110000" - )] - // 155 * 7 = 1085 - #[case::unsigned_normal(false, "10011011", "111", "0000010000111101")] - // 54 * 4234 = 228636 - #[case::unsigned_normal(false, "00110110", "0001000010001010", "00110111110100011100")] - // 37738 + 4365 = 99938 - #[case::unsigned_overflow(false, "1001001101101010", "0001000100001101", "00011000011001100010")] - fn mul(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { - run_binary_cell_case_signed!(Mul, signed, a, b, expected); + fn equal() { + println!("TODO") } #[rstest] - fn div() { - println!("TODO") + #[case::zero_unsigned(false, "0000", "0000", "0001")] // 0 >= 0 + #[case::zero_signed(true, "0000", "0000", "0001")] // 0 >= 0 + #[case(false, "0000", "1111", "0000")] // 0 >= 15 + #[case(true, "0000", "1111", "0001")] // 0 >= -1 + #[case(false, "0111", "000011", "0001")] // 7 >= 3 + #[case(true, "0111", "000011", "0001")] // 7 >= 3 + fn greater_equal( + #[case] signed: bool, + #[case] a: BitVec, + #[case] b: BitVec, + #[case] expected: BitVec, + ) { + run_binary_cell_case_signed!(GreaterEqual, signed, a, b, expected); } #[rstest] - fn modulus() { + fn greater_than() { println!("TODO") } #[rstest] + #[case::zero_unsigned(false, "0000", "0000", "1")] + #[case::zero_signed(true, "0000", "0000", "1")] // 37738 < 4365 = 0 #[case::unsigned_normal(false, "1001001101101010", "0001000100001101", "0")] #[case::signed_normal(true, "1001001101101010", "0001000100001101", "1")] #[case::unsigned_equal(false, "101010", "00101010", "1")] - fn le(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { - run_binary_cell_case_signed!(Le, signed, a, b, expected); + fn less_equal( + #[case] signed: bool, + #[case] a: BitVec, + #[case] b: BitVec, + #[case] expected: BitVec, + ) { + run_binary_cell_case_signed!(LessEqual, signed, a, b, expected); } #[rstest] @@ -128,18 +101,36 @@ mod tests { #[case::unsigned_normal(false, "1001001101101010", "0001000100001101", "0")] #[case::signed_normal(true, "1001001101101010", "0001000100001101", "1")] #[case::unsigned_equal(false, "101010", "00101010", "0")] - fn lt(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { - run_binary_cell_case_signed!(Lt, signed, a, b, expected); + fn less_than( + #[case] signed: bool, + #[case] a: BitVec, + #[case] b: BitVec, + #[case] expected: BitVec, + ) { + run_binary_cell_case_signed!(LessThan, signed, a, b, expected); } #[rstest] - fn ge() { + fn modulus() { println!("TODO") } #[rstest] - fn gt() { - println!("TODO") + // 8712 * 5366 = 46748592 + #[case::unsigned_normal( + false, + "0010001000001000", + "0001010011110110", + "00000010110010010101001110110000" + )] + // 155 * 7 = 1085 + #[case::unsigned_normal(false, "10011011", "111", "0000010000111101")] + // 54 * 4234 = 228636 + #[case::unsigned_normal(false, "00110110", "0001000010001010", "00110111110100011100")] + // 37738 + 4365 = 99938 + #[case::unsigned_overflow(false, "1001001101101010", "0001000100001101", "00011000011001100010")] + fn mul(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { + run_binary_cell_case_signed!(Mul, signed, a, b, expected); } #[rstest] @@ -149,7 +140,16 @@ mod tests { #[case::signed_normal(true, "001001001101101010", "11110110110010010110")] // -(-27798) = 27798 #[case::signed_normal(true, "1001001101101010", "0110110010010110")] - fn neg(#[case] signed: bool, #[case] a: BitVec, #[case] expected: BitVec) { - run_unary_cell_case_signed!(Neg, signed, a, expected); + fn negate(#[case] signed: bool, #[case] a: BitVec, #[case] expected: BitVec) { + run_unary_cell_case_signed!(Negate, signed, a, expected); + } + + #[rstest] + fn not_equal() { + println!("TODO") + } + #[rstest] + fn sub() { + println!("TODO") } } diff --git a/crates/arbolta/src/cell/simlib/bool_ops.rs b/crates/arbolta/src/cell/simlib/bool_ops.rs index 6810738..a92351a 100644 --- a/crates/arbolta/src/cell/simlib/bool_ops.rs +++ b/crates/arbolta/src/cell/simlib/bool_ops.rs @@ -27,9 +27,9 @@ impl CellFn for Not { fn reset(&mut self) {} } -define_arithmetic_cell!(ProcAnd, bitand); -define_arithmetic_cell!(ProcOr, bitor); -define_arithmetic_cell!(ProcXor, bitxor); +define_arithmetic_cell!(&["$and"], ProcAnd { a, b }, y, a.bitand(b)); +define_arithmetic_cell!(&["$or"], ProcOr { a, b }, y, a.bitor(b)); +define_arithmetic_cell!(&["$xor"], ProcXor { a, b }, y, a.bitxor(b)); #[cfg(test)] mod tests { diff --git a/crates/arbolta/src/cell/simlib/compare_ops.rs b/crates/arbolta/src/cell/simlib/compare_ops.rs deleted file mode 100644 index 67fe431..0000000 --- a/crates/arbolta/src/cell/simlib/compare_ops.rs +++ /dev/null @@ -1,22 +0,0 @@ -use super::*; -use crate::{bit::BitVec, signal::Signals}; -use derive_more::Constructor; -use serde::{Deserialize, Serialize}; - -define_arithmetic_cell!(Eq, &eq); -define_arithmetic_cell!(Ne, &ne); - -#[cfg(test)] -mod tests { - use rstest::rstest; - - #[rstest] - fn eq() { - println!("TODO") - } - - #[rstest] - fn ne() { - println!("TODO") - } -} diff --git a/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs b/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs index 9fc3d0b..821d672 100644 --- a/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs +++ b/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs @@ -1,103 +1,58 @@ -use super::CellFn; +use super::*; use crate::{bit::Bit, signal::Signals}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; -macro_rules! reduce_nets { - ($signals:expr, $nets:expr, $initial:expr, $op:tt) => { - $nets - .iter() - .fold($initial, |acc, i| acc $op $signals.get_net(*i)) +macro_rules! define_reduce_cell { + ($rtl_names:expr, $cell_type:ident { $($in_netn:ident),* $(,)?}, $out_net:ident, $op:tt, $body:expr) => { + #[derive(Debug, Clone, Constructor, Serialize, Deserialize)] + pub struct $cell_type { + $( + $in_netn: Box<[usize]>, + )* + + $out_net: Box<[usize]> + } + + impl CellFn for $cell_type { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + $( + let $in_netn: Bit = self + .$in_netn + .iter() + .fold(Bit::ZERO, |acc, &n| acc $op signals.get_net(n)); + )* + + signals.set_net(self.$out_net[0], $body ); + } + + fn reset(&mut self) {} + } + + paste! { + inventory::submit! {CellRegistration::new($rtl_names, + |connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>| { + if env::var("ARBOLTA_DEBUG").is_ok() { + println!("Parsing connections: {:#?}", connections); + } + + $cell_type::new( + $( + connections[stringify!([<$in_netn:upper>])].clone(), + )* + connections[stringify!([<$out_net:upper>])].clone() + ).into() + })} + } }; } -#[derive(Debug, Clone, Serialize, Deserialize, Constructor)] -pub struct ReduceAnd { - a_nets: Box<[usize]>, - y_nets: Box<[usize]>, -} - -impl CellFn for ReduceAnd { - #[inline] - fn eval(&mut self, signals: &mut Signals) { - signals.set_net( - self.y_nets[0], - reduce_nets!(signals, self.a_nets, Bit::ZERO, &), - ); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Serialize, Deserialize, Constructor)] -pub struct ReduceOr { - pub a_nets: Box<[usize]>, - pub y_nets: Box<[usize]>, -} - -impl CellFn for ReduceOr { - #[inline] - fn eval(&mut self, signals: &mut Signals) { - signals.set_net( - self.y_nets[0], - reduce_nets!(signals, self.a_nets, Bit::ZERO, |), - ); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Serialize, Deserialize, Constructor)] -pub struct LogicAnd { - pub a_nets: Box<[usize]>, - pub b_nets: Box<[usize]>, - pub y_nets: Box<[usize]>, -} - -impl CellFn for LogicAnd { - #[inline] - fn eval(&mut self, signals: &mut Signals) { - let a: Bit = reduce_nets!(signals, self.a_nets, Bit::ZERO, |); - let b: Bit = reduce_nets!(signals, self.b_nets, Bit::ZERO, |); - signals.set_net(self.y_nets[0], a & b); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Serialize, Deserialize, Constructor)] -pub struct LogicNot { - pub a_nets: Box<[usize]>, - pub y_nets: Box<[usize]>, -} - -impl CellFn for LogicNot { - #[inline] - fn eval(&mut self, signals: &mut Signals) { - let a: Bit = reduce_nets!(signals, self.a_nets, Bit::ZERO, |); - signals.set_net(self.y_nets[0], !a); - } - - fn reset(&mut self) {} -} - -#[derive(Debug, Clone, Serialize, Deserialize, Constructor)] -pub struct LogicOr { - a_nets: Box<[usize]>, - b_nets: Box<[usize]>, - y_nets: Box<[usize]>, -} - -impl CellFn for LogicOr { - #[inline] - fn eval(&mut self, signals: &mut Signals) { - let a: Bit = reduce_nets!(signals, self.a_nets, Bit::ZERO, |); - let b: Bit = reduce_nets!(signals, self.b_nets, Bit::ZERO, |); - signals.set_net(self.y_nets[0], a | b); - } - - fn reset(&mut self) {} -} +define_reduce_cell!(&["$reduce_and"], ReduceAnd { a }, y, &, a); +define_reduce_cell!(&["$reduce_or", "$reduce_bool"], ReduceOr { a }, y, |, a); +define_reduce_cell!(&["$logic_and"], LogicAnd { a, b }, y, |, a & b); +define_reduce_cell!(&["$logic_not"], LogicNot { a }, y, |, !a); +define_reduce_cell!(&["$logic_or"], LogicOr { a, b }, y, |, a | b); #[cfg(test)] mod tests { diff --git a/crates/arbolta/src/cell/simlib/mod.rs b/crates/arbolta/src/cell/simlib/mod.rs index 8548101..5aac1d6 100644 --- a/crates/arbolta/src/cell/simlib/mod.rs +++ b/crates/arbolta/src/cell/simlib/mod.rs @@ -1,10 +1,9 @@ use super::{Cell, CellFn, CellRegistration}; use crate::{bit::Bit, signal::Signals}; -use std::collections::BTreeMap; - +use paste::paste; +use std::{collections::BTreeMap, env}; mod arithmetic; mod bool_ops; -mod compare_ops; mod logic_reduce_ops; mod registers; mod shift_ops; @@ -55,88 +54,73 @@ fn copy_bits<'a>( #[macro_export] macro_rules! define_arithmetic_cell { - // Takes `b` by value - ($name:ident, $op:ident) => { + ($rtl_names:expr, $cell_type:ident { $($in_netn:ident),* $(,)?}, $out_net:ident, $body:expr) => { #[derive(Debug, Clone, Constructor, Serialize, Deserialize)] - pub struct $name { + pub struct $cell_type { pub signed: bool, - pub a_nets: Box<[usize]>, - pub b_nets: Box<[usize]>, - pub y_nets: Box<[usize]>, + + $( + $in_netn: Box<[usize]>, + )* + + $out_net: Box<[usize]> } - impl CellFn for $name { + impl CellFn for $cell_type { #[inline] fn eval(&mut self, signals: &mut Signals) { - let a = BitVec::from(bits_from_nets(signals, &self.a_nets)); - let b = BitVec::from(bits_from_nets(signals, &self.b_nets)); + $( + let $in_netn = BitVec::from(bits_from_nets(signals, &self.$in_netn)); + )* - let output_size = self.y_nets.len(); + let output_size = self.$out_net.len(); - let y: BitVec = if self.signed { + let $out_net: BitVec = if self.signed { if output_size <= 64 { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int((a.$op(b)) as i64, Some(output_size)) + let ($($in_netn,)*) = ($($in_netn.to_int::(),)*); + BitVec::from_int( $body as i64, Some(output_size)) } else { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int((a.$op(b)) as i128, Some(output_size)) + let ($($in_netn,)*) = ($($in_netn.to_int::(),)*); + BitVec::from_int( $body as i128, Some(output_size)) } } else { if output_size <= 64 { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int((a.$op(b)) as u64, Some(output_size)) + let ($($in_netn,)*) = ($($in_netn.to_int::(),)*); + BitVec::from_int( $body as u64, Some(output_size)) } else { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int((a.$op(b)) as u128, Some(output_size)) + let ($($in_netn,)*) = ($($in_netn.to_int::(),)*); + BitVec::from_int( $body as u128, Some(output_size)) } }; - copy_bits(signals, &self.y_nets, &y); + copy_bits(signals, &self.$out_net, &$out_net); } fn reset(&mut self) {} } - }; - // Takes `b` by reference - ($name:ident, & $op:ident) => { - #[derive(Debug, Clone, Constructor, Serialize, Deserialize)] - pub struct $name { - signed: bool, - a_nets: Box<[usize]>, - b_nets: Box<[usize]>, - y_nets: Box<[usize]>, - } - impl CellFn for $name { - #[inline] - fn eval(&mut self, signals: &mut Signals) { - let a = BitVec::from(bits_from_nets(signals, &self.a_nets)); - let b = BitVec::from(bits_from_nets(signals, &self.b_nets)); - - let output_size = self.y_nets.len(); - - let y: BitVec = if self.signed { - if output_size <= 64 { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int((a.$op(&b)) as i64, Some(output_size)) - } else { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int((a.$op(&b)) as i128, Some(output_size)) - } - } else { - if output_size <= 64 { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int((a.$op(&b)) as u64, Some(output_size)) - } else { - let (a, b) = (a.to_int::(), b.to_int::()); - BitVec::from_int((a.$op(&b)) as u128, Some(output_size)) + paste! { + inventory::submit! {CellRegistration::new($rtl_names, + |connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>| { + if env::var("ARBOLTA_DEBUG").is_ok() { + println!("Parsing connections: {:#?}", connections); } - }; - copy_bits(signals, &self.y_nets, &y); - } - - fn reset(&mut self) {} + let mut signed = false; + $( + if let Some(&net_signed) = parameters.get(stringify!([<$in_netn:upper _SIGNED>])) { + signed |= (net_signed != 0); + } + )* + + $cell_type::new( + signed, + $( + connections[stringify!([<$in_netn:upper>])].clone(), + )* + connections[stringify!([<$out_net:upper>])].clone() + ).into() + })} } }; } @@ -147,7 +131,6 @@ pub(crate) use define_arithmetic_cell; // Re-export pub use arithmetic::*; pub use bool_ops::*; -pub use compare_ops::*; pub use logic_reduce_ops::*; pub use registers::*; pub use shift_ops::*; @@ -177,148 +160,6 @@ fn make_pos( ) .into() } -fn make_neg( - connections: &BTreeMap<&str, Box<[usize]>>, - parameters: &BTreeMap<&str, usize>, -) -> Cell { - Neg::new( - parameters["A_SIGNED"] != 0, - connections["A"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_add( - connections: &BTreeMap<&str, Box<[usize]>>, - parameters: &BTreeMap<&str, usize>, -) -> Cell { - Add::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_sub( - connections: &BTreeMap<&str, Box<[usize]>>, - parameters: &BTreeMap<&str, usize>, -) -> Cell { - Sub::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_mul( - connections: &BTreeMap<&str, Box<[usize]>>, - parameters: &BTreeMap<&str, usize>, -) -> Cell { - Mul::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_div( - connections: &BTreeMap<&str, Box<[usize]>>, - parameters: &BTreeMap<&str, usize>, -) -> Cell { - Div::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_mod( - connections: &BTreeMap<&str, Box<[usize]>>, - parameters: &BTreeMap<&str, usize>, -) -> Cell { - Modulus::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_le(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>) -> Cell { - Le::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_lt(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>) -> Cell { - Lt::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_ge(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>) -> Cell { - Ge::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_gt(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>) -> Cell { - Gt::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_shl( - connections: &BTreeMap<&str, Box<[usize]>>, - parameters: &BTreeMap<&str, usize>, -) -> Cell { - Shl::new( - parameters["A_SIGNED"] != 0, - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_shr( - connections: &BTreeMap<&str, Box<[usize]>>, - parameters: &BTreeMap<&str, usize>, -) -> Cell { - Shr::new( - parameters["A_SIGNED"] != 0, - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} fn make_dff( connections: &BTreeMap<&str, Box<[usize]>>, @@ -387,133 +228,10 @@ fn make_pmux( .into() } -fn make_logic_and( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - LogicAnd::new( - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_logic_not( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - LogicNot::new(connections["A"].clone(), connections["Y"].clone()).into() -} - -fn make_logic_or( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - LogicOr::new( - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_reduce_or( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - ReduceOr::new(connections["A"].clone(), connections["Y"].clone()).into() -} - -fn make_reduce_and( - connections: &BTreeMap<&str, Box<[usize]>>, - _parameters: &BTreeMap<&str, usize>, -) -> Cell { - ReduceAnd::new(connections["A"].clone(), connections["Y"].clone()).into() -} - -fn make_and( - connections: &BTreeMap<&str, Box<[usize]>>, - parameters: &BTreeMap<&str, usize>, -) -> Cell { - ProcAnd::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_or(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>) -> Cell { - ProcOr::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_xor( - connections: &BTreeMap<&str, Box<[usize]>>, - parameters: &BTreeMap<&str, usize>, -) -> Cell { - ProcXor::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_eq(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>) -> Cell { - Eq::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - -fn make_ne(connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>) -> Cell { - Ne::new( - (parameters["A_SIGNED"] != 0) && (parameters["B_SIGNED"] != 0), - connections["A"].clone(), - connections["B"].clone(), - connections["Y"].clone(), - ) - .into() -} - inventory::submit! {CellRegistration::new(&["$not"], make_not)} inventory::submit! {CellRegistration::new(&["$pos"], make_pos)} -inventory::submit! {CellRegistration::new(&["$neg"], make_neg)} -inventory::submit! {CellRegistration::new(&["$add"], make_add)} -inventory::submit! {CellRegistration::new(&["$sub"], make_sub)} -inventory::submit! {CellRegistration::new(&["$mul"], make_mul)} -inventory::submit! {CellRegistration::new(&["$div"], make_div)} -inventory::submit! {CellRegistration::new(&["$mod"], make_mod)} -inventory::submit! {CellRegistration::new(&["$le"], make_le)} -inventory::submit! {CellRegistration::new(&["$lt"], make_lt)} -inventory::submit! {CellRegistration::new(&["$ge"], make_ge)} -inventory::submit! {CellRegistration::new(&["$gt"], make_gt)} -inventory::submit! {CellRegistration::new(&["$shl"], make_shl)} -inventory::submit! {CellRegistration::new(&["$shr"], make_shr)} inventory::submit! {CellRegistration::new(&["$dff"], make_dff)} inventory::submit! {CellRegistration::new(&["$aldff"], make_aldff)} inventory::submit! {CellRegistration::new(&["$mux"], make_mux)} inventory::submit! {CellRegistration::new(&["$bmux"], make_bmux)} inventory::submit! {CellRegistration::new(&["$pmux"], make_pmux)} -inventory::submit! {CellRegistration::new(&["$logic_and"], make_logic_and)} -inventory::submit! {CellRegistration::new(&["$logic_not"], make_logic_not)} -inventory::submit! {CellRegistration::new(&["$logic_or"], make_logic_or)} -inventory::submit! {CellRegistration::new(&["$reduce_or", "$reduce_bool"], make_reduce_or)} -inventory::submit! {CellRegistration::new(&["$reduce_and"], make_reduce_and)} -inventory::submit! {CellRegistration::new(&["$and"], make_and)} -inventory::submit! {CellRegistration::new(&["$or"], make_or)} -inventory::submit! {CellRegistration::new(&["$xor"], make_xor)} -inventory::submit! {CellRegistration::new(&["$eq"], make_eq)} -inventory::submit! {CellRegistration::new(&["$ne"], make_ne)} diff --git a/crates/arbolta/src/cell/simlib/shift_ops.rs b/crates/arbolta/src/cell/simlib/shift_ops.rs index dc9a461..95c32e5 100644 --- a/crates/arbolta/src/cell/simlib/shift_ops.rs +++ b/crates/arbolta/src/cell/simlib/shift_ops.rs @@ -3,108 +3,93 @@ use crate::{bit::BitVec, signal::Signals}; use derive_more::Constructor; use serde::{Deserialize, Serialize}; -// define_arithmetic_cell!(Shl, <<); -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] -pub struct Shl { - signed: bool, - a_nets: Box<[usize]>, - b_nets: Box<[usize]>, - y_nets: Box<[usize]>, -} - -impl CellFn for Shl { - #[inline] - fn eval(&mut self, signals: &mut Signals) { - let a = BitVec::from(bits_from_nets(signals, &self.a_nets)); - let shift: u32 = BitVec::from(bits_from_nets(signals, &self.b_nets)).to_int(); +macro_rules! define_shift_cell { + ($rtl_names:expr, $cell_type:ident { $op_net:ident, $shift_net:ident }, $out_net:ident, $body:expr) => { + #[derive(Debug, Clone, Constructor, Serialize, Deserialize)] + pub struct $cell_type { + pub signed: bool, + $op_net: Box<[usize]>, + $shift_net: Box<[usize]>, + $out_net: Box<[usize]>, + } - let output_size = self.y_nets.len(); - let y: BitVec = if self.signed { - if output_size <= 64 { - BitVec::from_int( - (a.to_int::().wrapping_shl(shift)) as i64, - Some(output_size), - ) - } else { - BitVec::from_int( - (a.to_int::().wrapping_shl(shift)) as i128, - Some(output_size), - ) - } - } else if output_size <= 64 { - BitVec::from_int( - (a.to_int::().wrapping_shl(shift)) as u64, - Some(output_size), - ) - } else { - BitVec::from_int( - (a.to_int::().wrapping_shl(shift)) as u128, - Some(output_size), - ) - }; - - copy_bits(signals, &self.y_nets, &y); - } + impl CellFn for $cell_type { + #[inline] + fn eval(&mut self, signals: &mut Signals) { + let $op_net = BitVec::from(bits_from_nets(signals, &self.$op_net)); + let $shift_net = BitVec::from(bits_from_nets(signals, &self.$shift_net)).to_int::(); + let output_size = self.$out_net.len(); - fn reset(&mut self) {} -} + let $out_net: BitVec = if self.signed { + if output_size <= 64 { + let $op_net = $op_net.to_int::(); + BitVec::from_int($body as i64, Some(output_size)) + } else { + let $op_net = $op_net.to_int::(); + BitVec::from_int($body as i128, Some(output_size)) + } + } else { + if output_size <= 64 { + let $op_net = $op_net.to_int::(); + BitVec::from_int($body as u64, Some(output_size)) + } else { + let $op_net = $op_net.to_int::(); + BitVec::from_int($body as u128, Some(output_size)) + } + }; -#[derive(Debug, Clone, Constructor, Serialize, Deserialize)] -pub struct Shr { - signed: bool, - a_nets: Box<[usize]>, - b_nets: Box<[usize]>, - y_nets: Box<[usize]>, -} + copy_bits(signals, &self.$out_net, &$out_net); + } -impl CellFn for Shr { - #[inline] - fn eval(&mut self, signals: &mut Signals) { - let a = BitVec::from(bits_from_nets(signals, &self.a_nets)); - let shift: u32 = BitVec::from(bits_from_nets(signals, &self.b_nets)).to_int(); + fn reset(&mut self) {} + } - let output_size = self.y_nets.len(); - let y: BitVec = if self.signed { - if output_size <= 64 { - BitVec::from_int( - (a.to_int::().wrapping_shr(shift)) as i64, - Some(output_size), - ) - } else { - BitVec::from_int( - (a.to_int::().wrapping_shr(shift)) as i128, - Some(output_size), - ) - } - } else if output_size <= 64 { - BitVec::from_int( - (a.to_int::().wrapping_shr(shift)) as u64, - Some(output_size), - ) - } else { - BitVec::from_int( - (a.to_int::().wrapping_shr(shift)) as u128, - Some(output_size), - ) - }; + paste! { + inventory::submit! {CellRegistration::new($rtl_names, + |connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>| { + if env::var("ARBOLTA_DEBUG").is_ok() { + println!("Parsing connections: {:#?}", connections); + } - copy_bits(signals, &self.y_nets, &y); - } + let signed = match parameters.get(stringify!([<$op_net:upper _SIGNED>])) { + Some(&net_signed) => net_signed != 0, + None => false + }; - fn reset(&mut self) {} + $cell_type::new( + signed, + connections[stringify!([<$op_net:upper>])].clone(), + connections[stringify!([<$shift_net:upper>])].clone(), + connections[stringify!([<$out_net:upper>])].clone() + ).into() + })} + } + }; } +define_shift_cell!(&["$shl"], Shl { a, b }, y, a.wrapping_shl(b)); +define_shift_cell!(&["$shr"], Shr { a, b }, y, a.wrapping_shr(b)); #[cfg(test)] mod tests { + use super::*; + use crate::cell::test_helpers::*; use rstest::rstest; #[rstest] - fn shl() { - println!("TODO") + #[case(false, "10011011", "0", "0000000010011011")] // 155 << 0 + #[case(true, "10011011", "0", "1111111110011011")] // -101 << 0 + #[case(false, "10011011", "111", "0100110110000000")] // 155 << 7 + #[case(true, "10011011", "111", "1100110110000000")] // -101 << 7 + fn shl(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { + run_binary_cell_case_signed!(Shl, signed, a, b, expected); } #[rstest] - fn shr() { - println!("TODO") + #[case(false, "10011011", "0", "0000000010011011")] // 155 >> 0 + #[case(true, "10011011", "0", "1111111110011011")] // -101 >> 0 + #[case(false, "10011011", "111", "0000000000000001")] // 155 >> 7 + #[case(true, "10011011", "111", "1111111111111111")] // -101 >> 7 + fn shr(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec) { + run_binary_cell_case_signed!(Shr, signed, a, b, expected); } } diff --git a/crates/arbolta/src/hardware_module.rs b/crates/arbolta/src/hardware_module.rs index 3ca1215..59abbc9 100644 --- a/crates/arbolta/src/hardware_module.rs +++ b/crates/arbolta/src/hardware_module.rs @@ -66,10 +66,10 @@ impl HardwareModule { netlist: Netlist, top_module: Option<&str>, torder: TopoOrder, - use_slash_hierarchy: bool, + hierarchy_separator: Option<&str>, cell_mapping: Option<&CellMapping>, ) -> Result { - let netlist = NetlistWrapper::new(netlist, top_module, torder, use_slash_hierarchy)?; + let netlist = NetlistWrapper::new(netlist, top_module, torder, hierarchy_separator)?; let cells = netlist.build_cells(cell_mapping)?; diff --git a/crates/arbolta/src/netlist_wrapper.rs b/crates/arbolta/src/netlist_wrapper.rs index 5044d87..8e1da87 100644 --- a/crates/arbolta/src/netlist_wrapper.rs +++ b/crates/arbolta/src/netlist_wrapper.rs @@ -24,7 +24,7 @@ impl NetlistWrapper { netlist: Netlist, top_module: Option<&str>, torder: TopoOrder, - use_slash_hierarchy: bool, + hierarchy_separator: Option<&str>, ) -> Result { let mut netlist = netlist; @@ -47,8 +47,8 @@ impl NetlistWrapper { .unwrap_or(usize::MAX) }); - let (cells, cell_modules) = parse_cells(module, use_slash_hierarchy)?; - let nets = parse_nets(module, use_slash_hierarchy)?; + let (cells, cell_modules) = parse_cells(module, hierarchy_separator)?; + let nets = parse_nets(module, hierarchy_separator)?; let names_to_nets = HashMap::from_iter(nets.iter().map(|(id, n)| (id.to_string(), n.clone()))); // Add modules only seen in nets (no synthesized cells) @@ -219,7 +219,7 @@ pub type NetlistGraph = DiGraph; /// Returns (cells, modules) fn parse_cells( module: &mut yosys::Module, - use_slash_hierarchy: bool, + hierarchy_separator: Option<&str>, ) -> Result<(Vec, Vec>), ModuleError> { let mut scope_cells = vec![]; let mut primitive_cells = vec![]; @@ -257,16 +257,13 @@ fn parse_cells( && let Some(scopename) = scopename.to_string_if_string() { RTLID::new(&[scopename], &cell_name.as_str()) - } else if use_slash_hierarchy { - let split_name: Vec<&str> = cell_name.split("/").collect(); - if let Some((name, parents)) = split_name.split_last() { - // Add new module - modules.push(parents.iter().map(|s| s.to_string()).collect()); - - RTLID::new(parents, name) - } else { - RTLID::new(&[], cell_name) - } + } else if let Some(split_name) = + hierarchy_separator.map(|s| cell_name.split(s).collect::>()) + && let Some((name, parents)) = split_name.split_last() + { + // Add new module + modules.push(parents.iter().map(|s| s.to_string()).collect()); + RTLID::new(parents, name) } else { RTLID::new(&[], cell_name) }; @@ -282,7 +279,7 @@ fn parse_cells( fn parse_nets( module: &mut yosys::Module, - use_slash_hierarchy: bool, + hierarchy_separator: Option<&str>, ) -> Result>, ModuleError> { let mut all_nets = HashMap::new(); @@ -298,13 +295,11 @@ fn parse_nets( let hdlname_split: Vec<&str> = hdlname.split(" ").collect(); let (name, parents) = hdlname_split.split_last().unwrap(); RTLID::new(parents, name) - } else if use_slash_hierarchy { - let split_name: Vec<&str> = net_name.split("/").collect(); - if let Some((name, parents)) = split_name.split_last() { - RTLID::new(parents, name) - } else { - RTLID::new(&[], net_name) - } + } else if let Some(split_name) = + hierarchy_separator.map(|s| net_name.split(s).collect::>()) + && let Some((name, parents)) = split_name.split_last() + { + RTLID::new(parents, name) } else { RTLID::new(&[], net_name) }; diff --git a/crates/arbolta/tests/test_simcell.rs b/crates/arbolta/tests/test_simcell.rs index 5f1aa22..dd1cf41 100644 --- a/crates/arbolta/tests/test_simcell.rs +++ b/crates/arbolta/tests/test_simcell.rs @@ -41,17 +41,17 @@ fn test_cell_unary(#[case] mut cell: Box, #[case] cases: [(u8, u8); (1, 0, 0), (1, 1, 1), ])] -#[case::nand(Box::new(Nand2::new(0, 1, 2)), [ // (A, B, Y) - (0, 0, 1), - (0, 1, 1), +#[case::andnot(Box::new(AndNot2::new(0, 1, 2)), [ // (A, B, Y) + (0, 0, 0), + (0, 1, 0), (1, 0, 1), (1, 1, 0), ])] -#[case::or(Box::new(Or2::new(0, 1, 2)), [ // (A, B, Y) - (0, 0, 0), +#[case::nand(Box::new(Nand2::new(0, 1, 2)), [ // (A, B, Y) + (0, 0, 1), (0, 1, 1), (1, 0, 1), - (1, 1, 1), + (1, 1, 0), ])] #[case::nor(Box::new(Nor2::new(0, 1, 2)), [ // (A, B, Y) (0, 0, 1), @@ -59,11 +59,17 @@ fn test_cell_unary(#[case] mut cell: Box, #[case] cases: [(u8, u8); (1, 0, 0), (1, 1, 0), ])] -#[case::xor(Box::new(Xor2::new(0, 1, 2)), [ // (A, B, Y) +#[case::or(Box::new(Or2::new(0, 1, 2)), [ // (A, B, Y) (0, 0, 0), (0, 1, 1), (1, 0, 1), - (1, 1, 0), + (1, 1, 1), +])] +#[case::ornot(Box::new(OrNot2::new(0, 1, 2)), [ // (A, B, Y) + (0, 0, 1), + (0, 1, 0), + (1, 0, 1), + (1, 1, 1), ])] #[case::xnor(Box::new(Xnor2::new(0, 1, 2)), [ // (A, B, Y) (0, 0, 1), @@ -71,18 +77,12 @@ fn test_cell_unary(#[case] mut cell: Box, #[case] cases: [(u8, u8); (1, 0, 0), (1, 1, 1), ])] -#[case::andnot(Box::new(AndNot2::new(0, 1, 2)), [ // (A, B, Y) +#[case::xor(Box::new(Xor2::new(0, 1, 2)), [ // (A, B, Y) (0, 0, 0), - (0, 1, 0), + (0, 1, 1), (1, 0, 1), (1, 1, 0), ])] -#[case::ornot(Box::new(OrNot2::new(0, 1, 2)), [ // (A, B, Y) - (0, 0, 1), - (0, 1, 0), - (1, 0, 1), - (1, 1, 1), -])] fn test_cell_binary(#[case] mut cell: Box, #[case] cases: [(u8, u8, u8); 4]) { let mut signals = Signals::new(3); for (a, b, expected) in cases { @@ -97,6 +97,16 @@ fn test_cell_binary(#[case] mut cell: Box, #[case] cases: [(u8, u8, } #[rstest] +#[case::andorinvert(Box::new(AndOrInvert3::new(0, 1, 2, 3)), [ // (A, B, C, Y) + (0, 0, 0, 1), + (0, 0, 1, 0), + (0, 1, 0, 1), + (0, 1, 1, 0), + (1, 0, 0, 1), + (1, 0, 1, 0), + (1, 1, 0, 0), + (1, 1, 1, 0), +])] #[case::mux2(Box::new(Mux2::new(0, 1, 2, 3)), [ // (A, B, S, Y) (0, 0, 0, 0), (0, 0, 1, 0), @@ -117,16 +127,6 @@ fn test_cell_binary(#[case] mut cell: Box, #[case] cases: [(u8, u8, (1, 1, 0, 0), (1, 1, 1, 0), ])] -#[case::andorinvert(Box::new(AndOrInvert3::new(0, 1, 2, 3)), [ // (A, B, C, Y) - (0, 0, 0, 1), - (0, 0, 1, 0), - (0, 1, 0, 1), - (0, 1, 1, 0), - (1, 0, 0, 1), - (1, 0, 1, 0), - (1, 1, 0, 0), - (1, 1, 1, 0), -])] #[case::orandinvert(Box::new(OrAndInvert3::new(0, 1, 2, 3)), [ // (A, B, C, Y) (0, 0, 0, 1), (0, 0, 1, 1), diff --git a/crates/arbolta/tests/test_simcell_module.rs b/crates/arbolta/tests/test_simcell_module.rs index 0a3dd43..f66b809 100644 --- a/crates/arbolta/tests/test_simcell_module.rs +++ b/crates/arbolta/tests/test_simcell_module.rs @@ -22,14 +22,8 @@ static CELL_WRAPPER_NETLIST: Lazy = fn test_module_unary_cell(#[case] cell: &str, #[case] cases: [(u8, u8); 2]) { let cell_type = cell.strip_suffix("_WRAPPER").unwrap(); let torder = HashMap::from([(cell, vec![cell_type])]); - let mut module = HardwareModule::new( - CELL_WRAPPER_NETLIST.clone(), - Some(cell), - torder, - false, - None, - ) - .unwrap(); + let mut module = + HardwareModule::new(CELL_WRAPPER_NETLIST.clone(), Some(cell), torder, None, None).unwrap(); for (a, expected) in cases { module.set_port("A", BitVec::from_int(a, None)).unwrap(); @@ -85,14 +79,8 @@ fn test_module_unary_cell(#[case] cell: &str, #[case] cases: [(u8, u8); 2]) { fn test_module_binary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8); 4]) { let cell_type = cell.strip_suffix("_WRAPPER").unwrap(); let torder = HashMap::from([(cell, vec![cell_type])]); - let mut module = HardwareModule::new( - CELL_WRAPPER_NETLIST.clone(), - Some(cell), - torder, - false, - None, - ) - .unwrap(); + let mut module = + HardwareModule::new(CELL_WRAPPER_NETLIST.clone(), Some(cell), torder, None, None).unwrap(); for (a, b, expected) in cases { module.set_port("A", BitVec::from_int(a, None)).unwrap(); @@ -127,14 +115,8 @@ fn test_module_binary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8); 4]) fn test_module_ternary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8, u8); 8]) { let cell_type = cell.strip_suffix("_WRAPPER").unwrap(); let torder = HashMap::from([(cell, vec![cell_type])]); - let mut module = HardwareModule::new( - CELL_WRAPPER_NETLIST.clone(), - Some(cell), - torder, - false, - None, - ) - .unwrap(); + let mut module = + HardwareModule::new(CELL_WRAPPER_NETLIST.clone(), Some(cell), torder, None, None).unwrap(); for (a, b, c, expected) in cases { module.set_port("A", BitVec::from_int(a, None)).unwrap(); @@ -170,14 +152,8 @@ fn test_module_ternary_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8, u8) fn test_module_mux_cell(#[case] cell: &str, #[case] cases: [(u8, u8, u8, u8); 8]) { let cell_type = cell.strip_suffix("_WRAPPER").unwrap(); let torder = HashMap::from([(cell, vec![cell_type])]); - let mut module = HardwareModule::new( - CELL_WRAPPER_NETLIST.clone(), - Some(cell), - torder, - false, - None, - ) - .unwrap(); + let mut module = + HardwareModule::new(CELL_WRAPPER_NETLIST.clone(), Some(cell), torder, None, None).unwrap(); for (a, b, s, expected) in cases { module.set_port("A", BitVec::from_int(a, None)).unwrap(); diff --git a/crates/arbolta/tests/test_simlib_module.rs b/crates/arbolta/tests/test_simlib_module.rs index 7728c58..0f4b237 100644 --- a/crates/arbolta/tests/test_simlib_module.rs +++ b/crates/arbolta/tests/test_simlib_module.rs @@ -50,7 +50,7 @@ fn test_add(#[case] signed: bool, #[case] a: BitVec, #[case] b: BitVec, #[case] }, ); - let mut module = HardwareModule::new(netlist, None, torder, false, None).unwrap(); + let mut module = HardwareModule::new(netlist, None, torder, None, None).unwrap(); module.set_port("A", a).unwrap(); module.set_port("B", b).unwrap(); @@ -80,7 +80,7 @@ fn test_mux(#[case] select: Bit, #[case] a: BitVec, #[case] b: BitVec, #[case] e }, ); - let mut module = HardwareModule::new(netlist, None, torder, false, None).unwrap(); + let mut module = HardwareModule::new(netlist, None, torder, None, None).unwrap(); module.set_port("S", [select]).unwrap(); module.set_port("A", a).unwrap(); @@ -113,7 +113,7 @@ fn test_reg(#[case] polarity: Bit, #[case] data_in: BitVec) { }, ); - let mut module = HardwareModule::new(netlist, None, torder, false, None).unwrap(); + let mut module = HardwareModule::new(netlist, None, torder, None, None).unwrap(); let clock_net = module.get_net("CLK").unwrap()[0]; module.set_clock(clock_net, polarity).unwrap(); @@ -162,7 +162,7 @@ fn test_logic_and(#[case] a: BitVec, #[case] b: BitVec, #[case] expected: BitVec }, ); - let mut module = HardwareModule::new(netlist, None, torder, false, None).unwrap(); + let mut module = HardwareModule::new(netlist, None, torder, None, None).unwrap(); module.set_port("A", a).unwrap(); module.set_port("B", b).unwrap(); @@ -195,7 +195,7 @@ fn test_logic_not(#[case] a: BitVec, #[case] expected: BitVec) { }, ); - let mut module = HardwareModule::new(netlist, None, torder, false, None).unwrap(); + let mut module = HardwareModule::new(netlist, None, torder, None, None).unwrap(); module.set_port("A", a).unwrap(); module.eval(); @@ -226,7 +226,7 @@ fn test_reduce_or(#[case] a: BitVec, #[case] expected: BitVec) { }, ); - let mut module = HardwareModule::new(netlist, None, torder, false, None).unwrap(); + let mut module = HardwareModule::new(netlist, None, torder, None, None).unwrap(); module.set_port("A", a).unwrap(); module.eval(); diff --git a/crates/arbolta_pyo3/arbolta.pyi b/crates/arbolta_pyo3/arbolta.pyi index 33d21c9..e29127f 100644 --- a/crates/arbolta_pyo3/arbolta.pyi +++ b/crates/arbolta_pyo3/arbolta.pyi @@ -60,8 +60,8 @@ class HardwareDesign: :type torder_path: str | Path | PathLike :param config: Configuration for design ports :type config: dict[str, PortConfig] - :param use_slash_hierarchy: Use slash hierarchy, defaults to False - :type use_slash_hierarchy: bool + :param hierarchy_separator: Additional hierarchy separator for submodules + :type hierarchy_separator: str, optional :param top_module: Name of top module, defaults to None (find automatically) :type top_module: str, optional :param cell_mapping: Define additional cell types @@ -81,7 +81,7 @@ class HardwareDesign: netlist_path: str | Path | PathLike, torder_path: str | Path | PathLike, config: dict[str, PortConfig], - use_slash_hierarchy: bool = False, + hierarchy_separator: Optional[str] = None, top_module: Optional[str] = None, cell_mapping: Optional[dict[str, tuple[str, Optional[dict[str, str]]]]] = None, ) -> None: ... diff --git a/crates/arbolta_pyo3/src/hardware_module.rs b/crates/arbolta_pyo3/src/hardware_module.rs index 7b9d128..f329245 100644 --- a/crates/arbolta_pyo3/src/hardware_module.rs +++ b/crates/arbolta_pyo3/src/hardware_module.rs @@ -31,7 +31,7 @@ impl HardwareDesign { netlist_path: PathBuf, top_module: Option<&str>, torder_path: PathBuf, - use_slash_hierarchy: bool, + hierarchy_separator: Option<&str>, cell_mapping: Option<&CellMapping>, ) -> anyhow::Result { // Read raw JSON netlist @@ -46,7 +46,7 @@ impl HardwareDesign { netlist, top_module, torder, - use_slash_hierarchy, + hierarchy_separator, cell_mapping, )?; @@ -136,13 +136,13 @@ impl HardwareDesign { #[pymethods] impl HardwareDesign { #[new] - #[pyo3(signature = (netlist_path, torder_path, config, use_slash_hierarchy=false, top_module=None, cell_mapping=None))] + #[pyo3(signature = (netlist_path, torder_path, config, hierarchy_separator=None, top_module=None, cell_mapping=None))] pub fn new( py: Python<'_>, netlist_path: PathBuf, torder_path: PathBuf, config: HashMap>, - use_slash_hierarchy: bool, + hierarchy_separator: Option<&str>, top_module: Option<&str>, cell_mapping: Option, ) -> anyhow::Result> { @@ -150,7 +150,7 @@ impl HardwareDesign { netlist_path, top_module, torder_path, - use_slash_hierarchy, + hierarchy_separator, cell_mapping.as_ref(), )?; From 866200ea3752d661869d9d23f356525772187ecd Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Thu, 19 Feb 2026 23:53:48 +0000 Subject: [PATCH 50/64] Added netlist graph output --- Cargo.toml | 3 +- crates/arbolta/Cargo.toml | 2 +- crates/arbolta/src/netlist_wrapper.rs | 2 +- crates/arbolta_pyo3/Cargo.toml | 5 +- crates/arbolta_pyo3/arbolta.pyi | 11 +- crates/arbolta_pyo3/pyproject.toml | 1 + crates/arbolta_pyo3/src/hardware_module.rs | 117 ++++++++++++++++++++- 7 files changed, 130 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 834e37d..a0fa78e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,14 +3,15 @@ members = ["crates/*"] resolver = "3" [workspace.dependencies] -arbolta = { path = "crates/arbolta" } anyhow = "1" +arbolta = { path = "crates/arbolta" } derive_more = { version = "2", features = ["full"] } enum_dispatch = "0.3" indexmap = "2.12" ndarray = "0.16.1" num-traits = "0.2" once_cell = "1.21" +petgraph = { version = "0.8.3", features = ["serde-1"] } postcard = "1" rstest = "0.26" serde = { version = "1", features = ["derive"] } diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index 3203a6c..5a8d84b 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -15,7 +15,7 @@ ndarray = { workspace = true } num-traits = { workspace = true } once_cell = { workspace = true } paste = "1.0.15" -petgraph = { version = "0.8.3", features = ["serde-1"] } +petgraph = { workspace = true } postcard = { workspace = true } rstest = { workspace = true } serde = { workspace = true } diff --git a/crates/arbolta/src/netlist_wrapper.rs b/crates/arbolta/src/netlist_wrapper.rs index 8e1da87..ca32d39 100644 --- a/crates/arbolta/src/netlist_wrapper.rs +++ b/crates/arbolta/src/netlist_wrapper.rs @@ -12,7 +12,7 @@ use std::collections::{BTreeMap, HashMap, HashSet}; #[derive(Debug, Deserialize, Serialize, Default, Clone)] pub struct NetlistWrapper { pub top_module: String, - netlist: Netlist, + pub netlist: Netlist, pub cells: Vec, // Should be in topological order pub modules: HashSet>, pub nets: HashMap>, diff --git a/crates/arbolta_pyo3/Cargo.toml b/crates/arbolta_pyo3/Cargo.toml index 97fd598..94ed953 100644 --- a/crates/arbolta_pyo3/Cargo.toml +++ b/crates/arbolta_pyo3/Cargo.toml @@ -11,8 +11,9 @@ crate-type = ["cdylib"] [dependencies] anyhow = "*" arbolta = { workspace = true } -pyo3 = { version = "0.27", features = ["anyhow"] } -numpy = "0.27" num-traits = { workspace = true } +numpy = "0.27" +petgraph = { workspace = true } +pyo3 = { version = "0.27", features = ["anyhow"] } serde = { version = "1.0", features = ["derive"] } thiserror = { workspace = true } diff --git a/crates/arbolta_pyo3/arbolta.pyi b/crates/arbolta_pyo3/arbolta.pyi index e29127f..e2c092e 100644 --- a/crates/arbolta_pyo3/arbolta.pyi +++ b/crates/arbolta_pyo3/arbolta.pyi @@ -1,10 +1,11 @@ from dataclasses import dataclass from os import PathLike from pathlib import Path -from typing import Any, Literal, Optional, Type +from typing import Literal, Optional import numpy as np -from numpy.typing import ArrayLike +from networkx import DiGraph +from numpy.typing import ArrayLike, DTypeLike @dataclass class PortConfig: @@ -14,7 +15,7 @@ class PortConfig: :var shape: Interpret port bits with shape, defaults to (1, 1) :vartype shape: tuple[int, int] :var dtype: Interpret port bits as `dtype`, defaults to `np.uint` - :vartype dtype: np.dtype | Type[Any]], optional + :vartype dtype: DTypeLike, optional :var clock: Port is a clock signal, defaults to None :vartype clock: bool, optional :var reset: Port is a reset signal, defaults to None @@ -24,7 +25,7 @@ class PortConfig: """ shape: tuple[int, int] = (1, 1) - dtype: Optional[np.dtype | Type[Any]] = np.uint + dtype: Optional[DTypeLike] = np.uint clock: Optional[bool] = None reset: Optional[bool] = None polarity: Optional[Literal[0, 1]] = None @@ -122,3 +123,5 @@ class HardwareDesign: def toggle_count( self, category: str = "total", by_net: bool = True ) -> dict[str, dict[str, int]] | dict[str, int]: ... + def netlist(self) -> dict: ... + def netlist_graph(self) -> DiGraph: ... diff --git a/crates/arbolta_pyo3/pyproject.toml b/crates/arbolta_pyo3/pyproject.toml index 7f0aba9..6eae319 100644 --- a/crates/arbolta_pyo3/pyproject.toml +++ b/crates/arbolta_pyo3/pyproject.toml @@ -9,6 +9,7 @@ readme = "README.md" requires-python = ">=3.14" dependencies = [ "maturin>=1.10.2", + "networkx>=3.6.1", "numpy>=2.3.5", ] diff --git a/crates/arbolta_pyo3/src/hardware_module.rs b/crates/arbolta_pyo3/src/hardware_module.rs index f329245..82f5c2d 100644 --- a/crates/arbolta_pyo3/src/hardware_module.rs +++ b/crates/arbolta_pyo3/src/hardware_module.rs @@ -9,16 +9,20 @@ use arbolta::{ bit::Bit, cell::CellMapping, hardware_module::{HardwareModule, ToggleCount}, - port::PortDirection, + port::{PortDirection, parse_bit}, yosys::{Netlist, parse_torder}, }; +use petgraph::visit::EdgeRef; use pyo3::{ exceptions::{PyAttributeError, PyValueError}, prelude::*, types::{PyDict, PyList}, }; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, path::PathBuf}; +use std::{ + collections::{HashMap, HashSet}, + path::PathBuf, +}; #[pyclass(weakref, dict)] #[derive(Deserialize, Serialize)] @@ -244,4 +248,113 @@ impl HardwareDesign { Ok(toggles.into()) } + + pub fn netlist(&self, py: Python<'_>) -> PyResult> { + let json = py.import("json")?; + let netlist = self + .module + .netlist + .netlist + .to_string() + .map_err(|e| PyAttributeError::new_err(format!("{e}")))?; + + let netlist_dict: Bound<'_, PyDict> = json.getattr("loads")?.call1((netlist,))?.cast_into()?; + Ok(netlist_dict.unbind()) + } + + pub fn netlist_graph(&self, py: Python<'_>) -> PyResult> { + let networkx = py.import("networkx")?; + + let graph = self + .module + .netlist + .build_graph() + .map_err(|e| PyAttributeError::new_err(format!("{e}")))?; + + let nx_graph = networkx.getattr("DiGraph")?.call0()?; + + // Add nodes + let mut nodes: Vec<(usize, Py)> = vec![]; + for i in graph.node_indices() { + let rtlid = graph.node_weight(i).unwrap(); + + let entry = PyDict::new(py); + entry.set_item("rtlid", rtlid.to_string())?; + + // $input or $output cell, early continue + let Some(cell) = self.module.netlist.find_cell(rtlid) else { + nodes.push((i.index(), entry.unbind())); + continue; + }; + + // Extract other fields + entry.set_item("cell_type", cell.cell_type.clone())?; + + if let Some(src_attr) = cell.attributes.get("src") + && let Some(src) = src_attr.to_string_if_string() + { + entry.set_item("src", src.to_string())?; + } + + nodes.push((i.index(), entry.unbind())); + } + nx_graph.call_method1("add_nodes_from", (nodes,))?; + + // Add edges + let mut edges: Vec<(usize, usize, Py)> = vec![]; + for edge in graph.edge_references() { + let net_driver_node = edge.source(); + let net_user_node = edge.target(); + + let net_driver = graph.node_weight(net_driver_node).unwrap(); + let net_user = graph.node_weight(net_user_node).unwrap(); + let net = *edge.weight(); + + let entry = PyDict::new(py); + entry.set_item("net", net)?; + + if let Some(driver_cell) = self.module.netlist.find_cell(net_driver) { + for (port_name, bits) in &driver_cell.connections { + // TODO: pre-process this somehow + let nets = HashSet::::from_iter( + bits + .iter() + .map(parse_bit) + .collect::, _>>() + .map_err(|e| PyAttributeError::new_err(format!("{e}")))?, + ); + + if nets.contains(&net) { + entry.set_item("driver", port_name.clone())?; + } + } + } + + if let Some(user_cell) = self.module.netlist.find_cell(net_user) { + for (port_name, bits) in &user_cell.connections { + // TODO: pre-process this somehow + let nets = HashSet::::from_iter( + bits + .iter() + .map(parse_bit) + .collect::, _>>() + .map_err(|e| PyAttributeError::new_err(format!("{e}")))?, + ); + + if nets.contains(&net) { + entry.set_item("user", port_name.clone())?; + } + } + } + + edges.push(( + net_driver_node.index(), + net_user_node.index(), + entry.unbind(), + )); + } + nx_graph.call_method1("add_edges_from", (edges,))?; + + Ok(nx_graph.into()) + } } From 7aa367cd6784ff0f991d2971d72dc4ac454a6b79 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 11 Mar 2026 01:23:51 +0000 Subject: [PATCH 51/64] Initial support for additional registers --- crates/arbolta/src/cell/mod.rs | 3 +- crates/arbolta/src/cell/simlib/mod.rs | 28 ++++- crates/arbolta/src/cell/simlib/registers.rs | 126 ++++++++++++++++---- 3 files changed, 133 insertions(+), 24 deletions(-) diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index 56fe40e..4177513 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -164,7 +164,8 @@ pub enum Cell { NotEqual, Sub, // - ALDff, + DffAsyncLoad, + DffAsyncResetEnable, BMux, LogicAnd, LogicNot, diff --git a/crates/arbolta/src/cell/simlib/mod.rs b/crates/arbolta/src/cell/simlib/mod.rs index 5aac1d6..e9803a5 100644 --- a/crates/arbolta/src/cell/simlib/mod.rs +++ b/crates/arbolta/src/cell/simlib/mod.rs @@ -1,5 +1,8 @@ use super::{Cell, CellFn, CellRegistration}; -use crate::{bit::Bit, signal::Signals}; +use crate::{ + bit::{Bit, BitVec}, + signal::Signals, +}; use paste::paste; use std::{collections::BTreeMap, env}; mod arithmetic; @@ -178,7 +181,7 @@ fn make_aldff( connections: &BTreeMap<&str, Box<[usize]>>, parameters: &BTreeMap<&str, usize>, ) -> Cell { - ALDff::new( + DffAsyncLoad::new( (parameters["CLK_POLARITY"] != 0).into(), (parameters["ALOAD_POLARITY"] != 0).into(), connections["CLK"][0], @@ -190,6 +193,26 @@ fn make_aldff( .into() } +fn make_adffe( + connections: &BTreeMap<&str, Box<[usize]>>, + parameters: &BTreeMap<&str, usize>, +) -> Cell { + let reset_val = BitVec::from_int(parameters["ARST_VALUE"], Some(parameters["WIDTH"])); + + DffAsyncResetEnable::new( + (parameters["CLK_POLARITY"] != 0).into(), + (parameters["EN_POLARITY"] != 0).into(), + (parameters["ARST_POLARITY"] != 0).into(), + reset_val.bits.into(), + connections["CLK"][0], + connections["ARST"][0], + connections["EN"][0], + connections["D"].clone(), + connections["Q"].clone(), + ) + .into() +} + fn make_mux( connections: &BTreeMap<&str, Box<[usize]>>, _parameters: &BTreeMap<&str, usize>, @@ -232,6 +255,7 @@ inventory::submit! {CellRegistration::new(&["$not"], make_not)} inventory::submit! {CellRegistration::new(&["$pos"], make_pos)} inventory::submit! {CellRegistration::new(&["$dff"], make_dff)} inventory::submit! {CellRegistration::new(&["$aldff"], make_aldff)} +inventory::submit! {CellRegistration::new(&["$adffe"], make_adffe)} inventory::submit! {CellRegistration::new(&["$mux"], make_mux)} inventory::submit! {CellRegistration::new(&["$bmux"], make_bmux)} inventory::submit! {CellRegistration::new(&["$pmux"], make_pmux)} diff --git a/crates/arbolta/src/cell/simlib/registers.rs b/crates/arbolta/src/cell/simlib/registers.rs index 268e10b..c885140 100644 --- a/crates/arbolta/src/cell/simlib/registers.rs +++ b/crates/arbolta/src/cell/simlib/registers.rs @@ -1,5 +1,5 @@ use super::{CellFn, copy_nets}; -use crate::{bit::Bit, signal::Signals}; +use crate::{bit::Bit, cell::simlib::copy_bits, signal::Signals}; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, derive_new::new)] @@ -30,41 +30,93 @@ impl CellFn for Reg { } #[derive(Debug, Clone, Serialize, Deserialize, derive_new::new)] -pub struct ALDff { +pub struct DffAsyncLoad { clock_polarity: Bit, - al_polarity: Bit, + load_polarity: Bit, clock_net: usize, - al_net: usize, - al_data_in_nets: Box<[usize]>, + async_load_net: usize, + async_data_in_nets: Box<[usize]>, data_in_nets: Box<[usize]>, data_out_nets: Box<[usize]>, #[new(default)] last_clock: Bit, #[new(default)] - last_aload: Bit, + last_load: Bit, } -impl CellFn for ALDff { +impl CellFn for DffAsyncLoad { #[inline] fn eval(&mut self, signals: &mut Signals) { - // TODO: Change this to == - let clock = !(signals.get_net(self.clock_net) ^ self.clock_polarity); - let aload = !(signals.get_net(self.al_net) ^ self.al_polarity); - - // Do asynchronous load - if aload == Bit::ONE && self.last_aload == Bit::ZERO { - copy_nets(signals, &self.al_data_in_nets, &self.data_out_nets); - // Rising edge clock - } else if clock == Bit::ONE && self.last_clock == Bit::ZERO { - copy_nets(signals, &self.data_in_nets, &self.data_out_nets); + let clock = signals.get_net(self.clock_net); + let load = signals.get_net(self.async_load_net); + + let pos_clock = clock == self.clock_polarity; + let pos_load = load == self.load_polarity; + let clock_rising = pos_clock && self.last_clock == !self.clock_polarity; + let load_rising = pos_load && self.last_load == !self.load_polarity; + + if clock_rising || load_rising { + if pos_load { + copy_nets(signals, &self.async_data_in_nets, &self.data_out_nets); + } else { + copy_nets(signals, &self.data_in_nets, &self.data_out_nets); + } } self.last_clock = clock; - self.last_aload = aload; + self.last_load = load; } fn reset(&mut self) { - self.last_clock = Bit::ZERO; + self.last_clock = !self.clock_polarity; + self.last_load = !self.load_polarity; + } +} + +#[allow(clippy::too_many_arguments)] +#[derive(Debug, Clone, Serialize, Deserialize, derive_new::new)] +pub struct DffAsyncResetEnable { + clock_polarity: Bit, + enable_polarity: Bit, + reset_polarity: Bit, + reset_val: Box<[Bit]>, + clock_net: usize, + reset_net: usize, + enable_net: usize, + data_in_nets: Box<[usize]>, + data_out_nets: Box<[usize]>, + #[new(default)] + last_clock: Bit, + #[new(default)] + last_reset: Bit, +} + +impl CellFn for DffAsyncResetEnable { + fn eval(&mut self, signals: &mut Signals) { + let clock = signals.get_net(self.clock_net); + let reset = signals.get_net(self.reset_net); + let enable = signals.get_net(self.enable_net); + + let pos_clock = clock == self.clock_polarity; + let pos_reset = reset == self.reset_polarity; + let clock_rising = pos_clock && self.last_clock == !self.clock_polarity; + let reset_rising = pos_reset && self.last_reset == !self.reset_polarity; + + if clock_rising || reset_rising { + if pos_reset { + copy_bits(signals, &self.data_out_nets, &self.reset_val); + } else if enable == self.enable_polarity { + copy_nets(signals, &self.data_in_nets, &self.data_out_nets); + } + } + + self.last_clock = clock; + self.last_reset = reset; + } + + fn reset(&mut self) { + self.last_clock = !self.clock_polarity; + self.last_reset = !self.reset_polarity; } } @@ -135,7 +187,39 @@ mod tests { } #[rstest] - fn aldff() { - println!("TODO") + #[case::zero(Bit::ONE, Bit::ZERO, "000000000000")] // 0 + #[case::zero(Bit::ONE, Bit::ONE, "000000000000")] // 0 + #[case::one(Bit::ONE, Bit::ZERO, "000000000001")] // 1 + #[case::one(Bit::ONE, Bit::ONE, "000000000001")] // 1 + #[case(Bit::ONE, Bit::ZERO, "1101111001101100")] // 56940 + #[case(Bit::ONE, Bit::ONE, "1101111001101100")] // 56940 + #[case(Bit::ZERO, Bit::ZERO, "000000000000")] // 0 + #[case(Bit::ZERO, Bit::ONE, "000000000000")] // 0 + #[case(Bit::ZERO, Bit::ZERO, "000000000001")] // 1 + #[case(Bit::ZERO, Bit::ONE, "000000000001")] // 1 + #[case(Bit::ZERO, Bit::ZERO, "1101111001101100")] // 56940 + #[case(Bit::ZERO, Bit::ONE, "1101111001101100")] // 56940 + + fn aldff(#[case] clock_polarity: Bit, #[case] load_polarity: Bit, #[case] data_in: BitVec) { + let _reg_size = data_in.len(); + let nets = allocate_nets(Some(2), &[&data_in, &data_in, &data_in]); + let (clock_net, load_net) = (0_usize, 1_usize); + let (data_in_nets, async_data_in_nets, data_out_nets) = (&nets[0], &nets[1], &nets[2]); + + let mut signals = Signals::new(data_out_nets.last().unwrap() + 1); + let mut cell = DffAsyncLoad::new( + clock_polarity, + load_polarity, + clock_net, + load_net, + async_data_in_nets.clone(), + data_in_nets.clone(), + data_out_nets.clone(), + ); + + cell.eval(&mut signals); + let actual = BitVec::from(bits_from_nets(&mut signals, data_out_nets)); + assert_eq!(actual.to_int::(), 0); // Should start w/ 0 + // TODO: Check other cases } } From 8d611524394263f61faa4fb48e973902d3cb1e4d Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 11 Mar 2026 01:53:21 +0000 Subject: [PATCH 52/64] Updated license --- LICENSE | 3 ++- crates/arbolta/src/bit.rs | 3 ++- crates/arbolta/src/cell/mod.rs | 3 +++ crates/arbolta/src/cell/simcells.rs | 3 +++ crates/arbolta/src/cell/simlib/arithmetic.rs | 3 +++ crates/arbolta/src/cell/simlib/bool_ops.rs | 3 +++ crates/arbolta/src/cell/simlib/logic_reduce_ops.rs | 3 +++ crates/arbolta/src/cell/simlib/mod.rs | 3 +++ crates/arbolta/src/cell/simlib/registers.rs | 3 +++ crates/arbolta/src/cell/simlib/shift_ops.rs | 3 +++ crates/arbolta/src/cell/simlib/various.rs | 3 +++ crates/arbolta/src/cell/test_helpers.rs | 3 +++ crates/arbolta/src/hardware_module.rs | 3 ++- crates/arbolta/src/lib.rs | 3 ++- crates/arbolta/src/netlist_wrapper.rs | 3 +++ crates/arbolta/src/port.rs | 3 ++- crates/arbolta/src/signal.rs | 3 ++- crates/arbolta/src/yosys.rs | 3 +++ crates/arbolta/tests/test_bit.rs | 3 ++- crates/arbolta/tests/test_bitvec.rs | 3 ++- crates/arbolta/tests/test_signal.rs | 3 ++- crates/arbolta/tests/test_simcell.rs | 3 ++- crates/arbolta/tests/test_simcell_module.rs | 3 ++- crates/arbolta/tests/test_simlib_module.rs | 3 +++ crates/arbolta_pyo3/arbolta.pyi | 3 +++ crates/arbolta_pyo3/src/conversion.rs | 3 ++- crates/arbolta_pyo3/src/hardware_module.rs | 3 ++- crates/arbolta_pyo3/src/lib.rs | 3 +++ crates/arbolta_pyo3/src/ports.rs | 3 +++ examples/cells/cells.v | 2 +- examples/designs/common/integer.sv | 2 +- examples/designs/common/minifloat.sv | 2 +- examples/designs/common/misc.sv | 2 +- examples/designs/common/sign_magnitude.sv | 2 +- examples/designs/inexact_fma_vector_mac.sv | 2 +- examples/designs/int_vector_mac.sv | 2 +- examples/designs/kulisch_vector_mac.sv | 2 +- examples/designs/misc_int_vector_mac.sv | 2 +- examples/designs/normalized_fma_vector_mac.sv | 2 +- examples/synth.tcl | 2 +- 40 files changed, 85 insertions(+), 24 deletions(-) diff --git a/LICENSE b/LICENSE index c7649b4..ca978de 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License -Copyright (c) 2024 - 2025 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) 2024 - 2025 Advanced Micro Devices, Inc. +Copyright (c) 2026 Alexander Redding Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/arbolta/src/bit.rs b/crates/arbolta/src/bit.rs index aef2be4..4380130 100644 --- a/crates/arbolta/src/bit.rs +++ b/crates/arbolta/src/bit.rs @@ -1,4 +1,5 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. +// Copyright (c) 2026 Alexander Redding // SPDX-License-Identifier: MIT use anyhow::Result; diff --git a/crates/arbolta/src/cell/mod.rs b/crates/arbolta/src/cell/mod.rs index 4177513..9706c9a 100644 --- a/crates/arbolta/src/cell/mod.rs +++ b/crates/arbolta/src/cell/mod.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Alexander Redding +// SPDX-License-Identifier: MIT + mod simcells; mod simlib; mod test_helpers; diff --git a/crates/arbolta/src/cell/simcells.rs b/crates/arbolta/src/cell/simcells.rs index 69764b0..0dcd8ce 100644 --- a/crates/arbolta/src/cell/simcells.rs +++ b/crates/arbolta/src/cell/simcells.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Alexander Redding +// SPDX-License-Identifier: MIT + use super::CellFn; use crate::{bit::Bit, cell::CellRegistration, signal::Signals}; use derive_more::Constructor; diff --git a/crates/arbolta/src/cell/simlib/arithmetic.rs b/crates/arbolta/src/cell/simlib/arithmetic.rs index 9b9f906..8093632 100644 --- a/crates/arbolta/src/cell/simlib/arithmetic.rs +++ b/crates/arbolta/src/cell/simlib/arithmetic.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Alexander Redding +// SPDX-License-Identifier: MIT + use super::*; use crate::{bit::BitVec, signal::Signals}; use derive_more::Constructor; diff --git a/crates/arbolta/src/cell/simlib/bool_ops.rs b/crates/arbolta/src/cell/simlib/bool_ops.rs index a92351a..930c579 100644 --- a/crates/arbolta/src/cell/simlib/bool_ops.rs +++ b/crates/arbolta/src/cell/simlib/bool_ops.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Alexander Redding +// SPDX-License-Identifier: MIT + use std::ops::{BitAnd, BitOr, BitXor}; use super::*; diff --git a/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs b/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs index 821d672..641d68c 100644 --- a/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs +++ b/crates/arbolta/src/cell/simlib/logic_reduce_ops.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Alexander Redding +// SPDX-License-Identifier: MIT + use super::*; use crate::{bit::Bit, signal::Signals}; use derive_more::Constructor; diff --git a/crates/arbolta/src/cell/simlib/mod.rs b/crates/arbolta/src/cell/simlib/mod.rs index e9803a5..69ed17b 100644 --- a/crates/arbolta/src/cell/simlib/mod.rs +++ b/crates/arbolta/src/cell/simlib/mod.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Alexander Redding +// SPDX-License-Identifier: MIT + use super::{Cell, CellFn, CellRegistration}; use crate::{ bit::{Bit, BitVec}, diff --git a/crates/arbolta/src/cell/simlib/registers.rs b/crates/arbolta/src/cell/simlib/registers.rs index c885140..5afb83c 100644 --- a/crates/arbolta/src/cell/simlib/registers.rs +++ b/crates/arbolta/src/cell/simlib/registers.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Alexander Redding +// SPDX-License-Identifier: MIT + use super::{CellFn, copy_nets}; use crate::{bit::Bit, cell::simlib::copy_bits, signal::Signals}; use serde::{Deserialize, Serialize}; diff --git a/crates/arbolta/src/cell/simlib/shift_ops.rs b/crates/arbolta/src/cell/simlib/shift_ops.rs index 95c32e5..9c1f219 100644 --- a/crates/arbolta/src/cell/simlib/shift_ops.rs +++ b/crates/arbolta/src/cell/simlib/shift_ops.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Alexander Redding +// SPDX-License-Identifier: MIT + use super::*; use crate::{bit::BitVec, signal::Signals}; use derive_more::Constructor; diff --git a/crates/arbolta/src/cell/simlib/various.rs b/crates/arbolta/src/cell/simlib/various.rs index 9034707..2e0fa11 100644 --- a/crates/arbolta/src/cell/simlib/various.rs +++ b/crates/arbolta/src/cell/simlib/various.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Alexander Redding +// SPDX-License-Identifier: MIT + use super::{CellFn, bits_from_nets_pad, copy_bits, copy_nets}; use crate::{ bit::{Bit, BitVec}, diff --git a/crates/arbolta/src/cell/test_helpers.rs b/crates/arbolta/src/cell/test_helpers.rs index db82e23..b0d7be4 100644 --- a/crates/arbolta/src/cell/test_helpers.rs +++ b/crates/arbolta/src/cell/test_helpers.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Alexander Redding +// SPDX-License-Identifier: MIT + use crate::bit::BitVec; #[macro_export] diff --git a/crates/arbolta/src/hardware_module.rs b/crates/arbolta/src/hardware_module.rs index 59abbc9..7486fd4 100644 --- a/crates/arbolta/src/hardware_module.rs +++ b/crates/arbolta/src/hardware_module.rs @@ -1,4 +1,5 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. +// Copyright (c) 2026 Alexander Redding // SPDX-License-Identifier: MIT use crate::{ diff --git a/crates/arbolta/src/lib.rs b/crates/arbolta/src/lib.rs index 0504695..c29b1ea 100644 --- a/crates/arbolta/src/lib.rs +++ b/crates/arbolta/src/lib.rs @@ -1,4 +1,5 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. +// Copyright (c) 2026 Alexander Redding // SPDX-License-Identifier: MIT pub mod bit; diff --git a/crates/arbolta/src/netlist_wrapper.rs b/crates/arbolta/src/netlist_wrapper.rs index ca32d39..ed5d32c 100644 --- a/crates/arbolta/src/netlist_wrapper.rs +++ b/crates/arbolta/src/netlist_wrapper.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Alexander Redding +// SPDX-License-Identifier: MIT + use crate::{ cell::{Cell, CellMapping, create_cell}, hardware_module::ModuleError, diff --git a/crates/arbolta/src/port.rs b/crates/arbolta/src/port.rs index 5e75047..fbec008 100644 --- a/crates/arbolta/src/port.rs +++ b/crates/arbolta/src/port.rs @@ -1,4 +1,5 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. +// Copyright (c) 2026 Alexander Redding // SPDX-License-Identifier: MIT use crate::yosys; diff --git a/crates/arbolta/src/signal.rs b/crates/arbolta/src/signal.rs index 44a6d68..1675ba1 100644 --- a/crates/arbolta/src/signal.rs +++ b/crates/arbolta/src/signal.rs @@ -1,4 +1,5 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. +// Copyright (c) 2026 Alexander Redding // SPDX-License-Identifier: MIT use crate::bit::Bit; diff --git a/crates/arbolta/src/yosys.rs b/crates/arbolta/src/yosys.rs index 231bd1e..83d822c 100644 --- a/crates/arbolta/src/yosys.rs +++ b/crates/arbolta/src/yosys.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Alexander Redding +// SPDX-License-Identifier: MIT + // Re-export use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fmt}; diff --git a/crates/arbolta/tests/test_bit.rs b/crates/arbolta/tests/test_bit.rs index c727cfc..e74465a 100644 --- a/crates/arbolta/tests/test_bit.rs +++ b/crates/arbolta/tests/test_bit.rs @@ -1,4 +1,5 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. +// Copyright (c) 2026 Alexander Redding // SPDX-License-Identifier: MIT use arbolta::bit::Bit; diff --git a/crates/arbolta/tests/test_bitvec.rs b/crates/arbolta/tests/test_bitvec.rs index 5e63fe6..132da67 100644 --- a/crates/arbolta/tests/test_bitvec.rs +++ b/crates/arbolta/tests/test_bitvec.rs @@ -1,4 +1,5 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. +// Copyright (c) 2026 Alexander Redding // SPDX-License-Identifier: MIT use arbolta::bit::{Bit, BitVec}; diff --git a/crates/arbolta/tests/test_signal.rs b/crates/arbolta/tests/test_signal.rs index 97b38e2..2579b58 100644 --- a/crates/arbolta/tests/test_signal.rs +++ b/crates/arbolta/tests/test_signal.rs @@ -1,4 +1,5 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. +// Copyright (c) 2026 Alexander Redding // SPDX-License-Identifier: MIT use arbolta::bit::Bit; diff --git a/crates/arbolta/tests/test_simcell.rs b/crates/arbolta/tests/test_simcell.rs index dd1cf41..8b18bcf 100644 --- a/crates/arbolta/tests/test_simcell.rs +++ b/crates/arbolta/tests/test_simcell.rs @@ -1,4 +1,5 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. +// Copyright (c) 2026 Alexander Redding // SPDX-License-Identifier: MIT use arbolta::bit::Bit; diff --git a/crates/arbolta/tests/test_simcell_module.rs b/crates/arbolta/tests/test_simcell_module.rs index f66b809..bcda44e 100644 --- a/crates/arbolta/tests/test_simcell_module.rs +++ b/crates/arbolta/tests/test_simcell_module.rs @@ -1,4 +1,5 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. +// Copyright (c) 2026 Alexander Redding // SPDX-License-Identifier: MIT use arbolta::{bit::BitVec, hardware_module::HardwareModule}; diff --git a/crates/arbolta/tests/test_simlib_module.rs b/crates/arbolta/tests/test_simlib_module.rs index 0f4b237..f3cf835 100644 --- a/crates/arbolta/tests/test_simlib_module.rs +++ b/crates/arbolta/tests/test_simlib_module.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Alexander Redding +// SPDX-License-Identifier: MIT + mod helpers; use arbolta::{ diff --git a/crates/arbolta_pyo3/arbolta.pyi b/crates/arbolta_pyo3/arbolta.pyi index e2c092e..3ea4d6e 100644 --- a/crates/arbolta_pyo3/arbolta.pyi +++ b/crates/arbolta_pyo3/arbolta.pyi @@ -1,3 +1,6 @@ +# Copyright (c) 2026 Alexander Redding +# SPDX-License-Identifier: MIT + from dataclasses import dataclass from os import PathLike from pathlib import Path diff --git a/crates/arbolta_pyo3/src/conversion.rs b/crates/arbolta_pyo3/src/conversion.rs index 7a7a808..3db578c 100644 --- a/crates/arbolta_pyo3/src/conversion.rs +++ b/crates/arbolta_pyo3/src/conversion.rs @@ -1,4 +1,5 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. +// Copyright (c) 2026 Alexander Redding // SPDX-License-Identifier: MIT use arbolta::bit::BitVec; diff --git a/crates/arbolta_pyo3/src/hardware_module.rs b/crates/arbolta_pyo3/src/hardware_module.rs index 82f5c2d..89c5d1d 100644 --- a/crates/arbolta_pyo3/src/hardware_module.rs +++ b/crates/arbolta_pyo3/src/hardware_module.rs @@ -1,4 +1,5 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. +// Copyright (c) 2026 Alexander Redding // SPDX-License-Identifier: MIT use crate::conversion::{ diff --git a/crates/arbolta_pyo3/src/lib.rs b/crates/arbolta_pyo3/src/lib.rs index 161de52..cf3fb77 100644 --- a/crates/arbolta_pyo3/src/lib.rs +++ b/crates/arbolta_pyo3/src/lib.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Alexander Redding +// SPDX-License-Identifier: MIT + use pyo3::prelude::*; mod conversion; mod hardware_module; diff --git a/crates/arbolta_pyo3/src/ports.rs b/crates/arbolta_pyo3/src/ports.rs index c7c137a..14730ad 100644 --- a/crates/arbolta_pyo3/src/ports.rs +++ b/crates/arbolta_pyo3/src/ports.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Alexander Redding +// SPDX-License-Identifier: MIT + use crate::hardware_module::HardwareDesign; use arbolta::{bit::Bit, port::PortDirection}; use pyo3::{ diff --git a/examples/cells/cells.v b/examples/cells/cells.v index dba5a72..a4916ec 100644 --- a/examples/cells/cells.v +++ b/examples/cells/cells.v @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. // SPDX-License-Identifier: MIT module BUF ( diff --git a/examples/designs/common/integer.sv b/examples/designs/common/integer.sv index 59cbd5a..b869915 100644 --- a/examples/designs/common/integer.sv +++ b/examples/designs/common/integer.sv @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. // SPDX-License-Identifier: MIT // ++++++++++++++++++++ Scalar ++++++++++++++++++++ diff --git a/examples/designs/common/minifloat.sv b/examples/designs/common/minifloat.sv index f3cd3cf..1ff7e3c 100644 --- a/examples/designs/common/minifloat.sv +++ b/examples/designs/common/minifloat.sv @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. // SPDX-License-Identifier: MIT // ++++++++++++++++++++ Scalar ++++++++++++++++++++ diff --git a/examples/designs/common/misc.sv b/examples/designs/common/misc.sv index 8893c0b..51fea08 100644 --- a/examples/designs/common/misc.sv +++ b/examples/designs/common/misc.sv @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. // SPDX-License-Identifier: MIT module clocked_register #( diff --git a/examples/designs/common/sign_magnitude.sv b/examples/designs/common/sign_magnitude.sv index 377e4eb..22bd32e 100644 --- a/examples/designs/common/sign_magnitude.sv +++ b/examples/designs/common/sign_magnitude.sv @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. // SPDX-License-Identifier: MIT // ++++++++++++++++++++ Scalar ++++++++++++++++++++ diff --git a/examples/designs/inexact_fma_vector_mac.sv b/examples/designs/inexact_fma_vector_mac.sv index ad76de6..17319fb 100644 --- a/examples/designs/inexact_fma_vector_mac.sv +++ b/examples/designs/inexact_fma_vector_mac.sv @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. // SPDX-License-Identifier: MIT module fma #( diff --git a/examples/designs/int_vector_mac.sv b/examples/designs/int_vector_mac.sv index 49aaddd..65a1b85 100644 --- a/examples/designs/int_vector_mac.sv +++ b/examples/designs/int_vector_mac.sv @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. // SPDX-License-Identifier: MIT diff --git a/examples/designs/kulisch_vector_mac.sv b/examples/designs/kulisch_vector_mac.sv index 9cdcc97..2004226 100644 --- a/examples/designs/kulisch_vector_mac.sv +++ b/examples/designs/kulisch_vector_mac.sv @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. // SPDX-License-Identifier: MIT // Contains following designs: diff --git a/examples/designs/misc_int_vector_mac.sv b/examples/designs/misc_int_vector_mac.sv index 07ba725..94f8165 100644 --- a/examples/designs/misc_int_vector_mac.sv +++ b/examples/designs/misc_int_vector_mac.sv @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. // SPDX-License-Identifier: MIT // Contains following designs: diff --git a/examples/designs/normalized_fma_vector_mac.sv b/examples/designs/normalized_fma_vector_mac.sv index c4e1698..dd2e41f 100644 --- a/examples/designs/normalized_fma_vector_mac.sv +++ b/examples/designs/normalized_fma_vector_mac.sv @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2024 Advanced Micro Devices, Inc. // SPDX-License-Identifier: MIT module mag_sum #( diff --git a/examples/synth.tcl b/examples/synth.tcl index 009d8d7..2c25ef1 100644 --- a/examples/synth.tcl +++ b/examples/synth.tcl @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2024 Advanced Micro Devices, Inc. # SPDX-License-Identifier: MIT yosys -import From a163698be2c4141c3cb7b0c3994a112c70c9970a Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 11 Mar 2026 01:55:54 +0000 Subject: [PATCH 53/64] Update authors --- crates/arbolta/Cargo.toml | 2 +- crates/arbolta_pyo3/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index 5a8d84b..cbb208f 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "arbolta" version = "0.1.0" -authors = ["AMD Research & Advanced Development"] +authors = ["Alexander Redding"] edition = "2024" [dependencies] diff --git a/crates/arbolta_pyo3/Cargo.toml b/crates/arbolta_pyo3/Cargo.toml index 94ed953..b7a2499 100644 --- a/crates/arbolta_pyo3/Cargo.toml +++ b/crates/arbolta_pyo3/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "arbolta_pyo3" version = "0.1.0" -authors = ["AMD Research & Advanced Development"] +authors = ["Alexander Redding"] edition = "2024" [lib] From d96850becb0e785d42159b19eac9060e7de12161 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 11 Mar 2026 17:58:13 +0000 Subject: [PATCH 54/64] Improving metadata and building --- Cargo.toml | 6 ++++++ crates/arbolta/Cargo.toml | 6 ++++-- crates/arbolta_pyo3/Cargo.toml | 7 +++++-- crates/arbolta_pyo3/README.md | 0 crates/arbolta_pyo3/pyproject.toml => pyproject.toml | 11 ++++++++++- 5 files changed, 25 insertions(+), 5 deletions(-) delete mode 100644 crates/arbolta_pyo3/README.md rename crates/arbolta_pyo3/pyproject.toml => pyproject.toml (64%) diff --git a/Cargo.toml b/Cargo.toml index a0fa78e..0519438 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,12 @@ members = ["crates/*"] resolver = "3" +[workspace.package] +edition = "2024" +repository = "https://github.com/KastnerRG/arbolta" +authors = ["Alexander Redding "] +license = "MIT" + [workspace.dependencies] anyhow = "1" arbolta = { path = "crates/arbolta" } diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index cbb208f..e199fdb 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -1,8 +1,10 @@ [package] name = "arbolta" version = "0.1.0" -authors = ["Alexander Redding"] -edition = "2024" +authors = { workspace = true } +edition = { workspace = true } +repository = { workspace = true } +license = { workspace = true } [dependencies] anyhow = { workspace = true } diff --git a/crates/arbolta_pyo3/Cargo.toml b/crates/arbolta_pyo3/Cargo.toml index b7a2499..766b15c 100644 --- a/crates/arbolta_pyo3/Cargo.toml +++ b/crates/arbolta_pyo3/Cargo.toml @@ -1,8 +1,11 @@ [package] name = "arbolta_pyo3" version = "0.1.0" -authors = ["Alexander Redding"] -edition = "2024" +authors = { workspace = true } +edition = { workspace = true } +repository = { workspace = true } +license = { workspace = true } +readme = "../../README.md" [lib] name = "arbolta" diff --git a/crates/arbolta_pyo3/README.md b/crates/arbolta_pyo3/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/crates/arbolta_pyo3/pyproject.toml b/pyproject.toml similarity index 64% rename from crates/arbolta_pyo3/pyproject.toml rename to pyproject.toml index 6eae319..79b8d03 100644 --- a/crates/arbolta_pyo3/pyproject.toml +++ b/pyproject.toml @@ -5,16 +5,25 @@ build-backend = "maturin" [project] name = "arbolta" dynamic = ["version"] +authors = [ + { name = "Alexander Redding", email = "alredding@ucsd.edu" }, +] readme = "README.md" requires-python = ">=3.14" +license = "MIT" +license-files = ["LICENSE"] dependencies = [ "maturin>=1.10.2", "networkx>=3.6.1", "numpy>=2.3.5", ] +[project.urls] +Repository = "https://github.com/KastnerRG/arbolta" + [tool.maturin] -manifest-path = "Cargo.toml" +manifest-path = "crates/arbolta_pyo3/Cargo.toml" +strip = true [tool.uv] cache-keys = [ From 8663478ac40c53003d0e2c7dfd0779e35c7027f3 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 11 Mar 2026 18:22:04 +0000 Subject: [PATCH 55/64] Improving metadata --- Cargo.toml | 1 + crates/arbolta/Cargo.toml | 1 + crates/arbolta_pyo3/Cargo.toml | 1 + pyproject.toml | 5 +++++ 4 files changed, 8 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 0519438..1e98b54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ resolver = "3" [workspace.package] edition = "2024" repository = "https://github.com/KastnerRG/arbolta" +homepage = "https://github.com/KastnerRG/arbolta" authors = ["Alexander Redding "] license = "MIT" diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index e199fdb..7cb04f9 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = { workspace = true } edition = { workspace = true } repository = { workspace = true } +homepage = { workspace = true } license = { workspace = true } [dependencies] diff --git a/crates/arbolta_pyo3/Cargo.toml b/crates/arbolta_pyo3/Cargo.toml index 766b15c..c5eea15 100644 --- a/crates/arbolta_pyo3/Cargo.toml +++ b/crates/arbolta_pyo3/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = { workspace = true } edition = { workspace = true } repository = { workspace = true } +homepage = { workspace = true } license = { workspace = true } readme = "../../README.md" diff --git a/pyproject.toml b/pyproject.toml index 79b8d03..955196e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,7 @@ build-backend = "maturin" [project] name = "arbolta" dynamic = ["version"] +description = "A hardware simulator for efficient HW/SW co-design" authors = [ { name = "Alexander Redding", email = "alredding@ucsd.edu" }, ] @@ -12,6 +13,10 @@ readme = "README.md" requires-python = ">=3.14" license = "MIT" license-files = ["LICENSE"] +classifiers = [ + "Programming Language :: Rust", + "License :: OSI Approved :: MIT License", # for compatibility with tooling such as pip-licenses +] dependencies = [ "maturin>=1.10.2", "networkx>=3.6.1", From 805fe5a49325696b2ef7b8f9aadddaf38c907f3a Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 11 Mar 2026 21:48:58 +0000 Subject: [PATCH 56/64] Improved python source package and added python tests --- crates/arbolta/Cargo.toml | 4 ++++ crates/arbolta_pyo3/Cargo.toml | 1 + pyproject.toml | 18 ++++++++++++------ python/py_src/arbolta/__init__.py | 4 ++++ .../py_src/arbolta}/arbolta.pyi | 0 python/tests/test_port_config.py | 12 ++++++++++++ requirements/requirements_dev.txt | 1 - requirements/requirements_examples.txt | 10 ---------- requirements/requirements_experiments.txt | 4 ---- 9 files changed, 33 insertions(+), 21 deletions(-) create mode 100644 python/py_src/arbolta/__init__.py rename {crates/arbolta_pyo3 => python/py_src/arbolta}/arbolta.pyi (100%) create mode 100644 python/tests/test_port_config.py delete mode 100644 requirements/requirements_dev.txt delete mode 100644 requirements/requirements_examples.txt delete mode 100644 requirements/requirements_experiments.txt diff --git a/crates/arbolta/Cargo.toml b/crates/arbolta/Cargo.toml index 7cb04f9..586566a 100644 --- a/crates/arbolta/Cargo.toml +++ b/crates/arbolta/Cargo.toml @@ -6,6 +6,10 @@ edition = { workspace = true } repository = { workspace = true } homepage = { workspace = true } license = { workspace = true } +description = "Core Arbolta hardware simulator" +exclude = [ + "tests/*" +] [dependencies] anyhow = { workspace = true } diff --git a/crates/arbolta_pyo3/Cargo.toml b/crates/arbolta_pyo3/Cargo.toml index c5eea15..5dc9f0c 100644 --- a/crates/arbolta_pyo3/Cargo.toml +++ b/crates/arbolta_pyo3/Cargo.toml @@ -7,6 +7,7 @@ repository = { workspace = true } homepage = { workspace = true } license = { workspace = true } readme = "../../README.md" +description = "Python bindings for Arbolta hardware simulator" [lib] name = "arbolta" diff --git a/pyproject.toml b/pyproject.toml index 955196e..f74d358 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,22 +23,28 @@ dependencies = [ "numpy>=2.3.5", ] +[dependency-groups] +dev = [ + "pre-commit>=4.5.1", + "pytest>=9.0.2", + "ruff>=0.14.10", +] + [project.urls] Repository = "https://github.com/KastnerRG/arbolta" [tool.maturin] manifest-path = "crates/arbolta_pyo3/Cargo.toml" strip = true +python-source = "python/py_src" +exclude = [ + "python/tests/*", +] [tool.uv] +package = true cache-keys = [ { file = "pyproject.toml" }, { file = "Cargo.toml" }, { file = "src/**/*.rs" }, ] - -[dependency-groups] -dev = [ - "pre-commit>=4.5.1", - "ruff>=0.14.10", -] diff --git a/python/py_src/arbolta/__init__.py b/python/py_src/arbolta/__init__.py new file mode 100644 index 0000000..9fdf61f --- /dev/null +++ b/python/py_src/arbolta/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) 2026 Alexander Redding +# SPDX-License-Identifier: MIT + +from .arbolta import * diff --git a/crates/arbolta_pyo3/arbolta.pyi b/python/py_src/arbolta/arbolta.pyi similarity index 100% rename from crates/arbolta_pyo3/arbolta.pyi rename to python/py_src/arbolta/arbolta.pyi diff --git a/python/tests/test_port_config.py b/python/tests/test_port_config.py new file mode 100644 index 0000000..f9efd9e --- /dev/null +++ b/python/tests/test_port_config.py @@ -0,0 +1,12 @@ +from arbolta import PortConfig + + +def test_port_config(): + x = PortConfig((1, 10), int) + + assert isinstance(x.shape, tuple) and len(x.shape) == 2 + assert x.shape[0] == 1 and x.shape[1] == 10 + assert x.dtype == int + assert isinstance(x.clock, bool) and isinstance(x.reset, bool) + assert not x.clock and not x.reset + assert x.polarity is None diff --git a/requirements/requirements_dev.txt b/requirements/requirements_dev.txt deleted file mode 100644 index 817851a..0000000 --- a/requirements/requirements_dev.txt +++ /dev/null @@ -1 +0,0 @@ -pre-commit>=4.0.1 diff --git a/requirements/requirements_examples.txt b/requirements/requirements_examples.txt deleted file mode 100644 index d18f343..0000000 --- a/requirements/requirements_examples.txt +++ /dev/null @@ -1,10 +0,0 @@ -anywidget>=0.9.13 -graphviz-anywidget>=0.3.0 -ipympl>=0.9.5 -jupyter>=1.1.1 -jupyter_client>=8.6.3 -jupyter_core>=5.7.2 -jupyterlab_widgets>=3.0.13 -matplotlib>=3.9.1.post1 -pandas>=2.2.3 -scipy>=1.14.1 diff --git a/requirements/requirements_experiments.txt b/requirements/requirements_experiments.txt deleted file mode 100644 index 4a3a7c7..0000000 --- a/requirements/requirements_experiments.txt +++ /dev/null @@ -1,4 +0,0 @@ -array-api-compat -brevitas @ git+https://github.com/Xilinx/brevitas.git@dev -fxpmath -tqdm From 6477421b02f44e28feccdd8075db58eb16db45af Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 11 Mar 2026 22:07:08 +0000 Subject: [PATCH 57/64] Downgrade required Python version for compatibility --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f74d358..e57908b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ authors = [ { name = "Alexander Redding", email = "alredding@ucsd.edu" }, ] readme = "README.md" -requires-python = ">=3.14" +requires-python = ">=3.11" license = "MIT" license-files = ["LICENSE"] classifiers = [ From 823e6c593e5622138376acfb31a9433303a90c95 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 11 Mar 2026 22:34:28 +0000 Subject: [PATCH 58/64] Initial commit of PyPI build/publish action --- .github/workflows/release.yml | 183 ++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..14d9ccd --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,183 @@ +# This file is autogenerated by maturin v1.12.6 +# To update, run +# +# maturin generate-ci github +# +name: Release + +on: + push: + tags: ["v*"] + workflow_dispatch: + # TODO: Dry run when changing version in arbolta_pyo3/Cargo.toml + pull_request: + +permissions: + contents: read + +jobs: + linux: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: ubuntu-22.04 + target: x86_64 + - runner: ubuntu-22.04 + target: x86 + - runner: ubuntu-22.04 + target: aarch64 + - runner: ubuntu-22.04 + target: armv7 + - runner: ubuntu-22.04 + target: s390x + - runner: ubuntu-22.04 + target: ppc64le + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + with: + python-version: 3.x + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} + manylinux: auto + - name: Upload wheels + uses: actions/upload-artifact@v6 + with: + name: wheels-linux-${{ matrix.platform.target }} + path: dist + + musllinux: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: ubuntu-22.04 + target: x86_64 + - runner: ubuntu-22.04 + target: x86 + - runner: ubuntu-22.04 + target: aarch64 + - runner: ubuntu-22.04 + target: armv7 + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + with: + python-version: 3.x + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} + manylinux: musllinux_1_2 + - name: Upload wheels + uses: actions/upload-artifact@v6 + with: + name: wheels-musllinux-${{ matrix.platform.target }} + path: dist + + windows: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: windows-latest + target: x64 + python_arch: x64 + - runner: windows-latest + target: x86 + python_arch: x86 + - runner: windows-11-arm + target: aarch64 + python_arch: arm64 + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + with: + python-version: 3.13 + architecture: ${{ matrix.platform.python_arch }} + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} + - name: Upload wheels + uses: actions/upload-artifact@v6 + with: + name: wheels-windows-${{ matrix.platform.target }} + path: dist + + macos: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: macos-15-intel + target: x86_64 + - runner: macos-latest + target: aarch64 + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + with: + python-version: 3.x + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} + - name: Upload wheels + uses: actions/upload-artifact@v6 + with: + name: wheels-macos-${{ matrix.platform.target }} + path: dist + + sdist: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Build sdist + uses: PyO3/maturin-action@v1 + with: + command: sdist + args: --out dist + - name: Upload sdist + uses: actions/upload-artifact@v6 + with: + name: wheels-sdist + path: dist + + release: + name: Release + runs-on: ubuntu-latest + if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }} + needs: [linux, musllinux, windows, macos, sdist] + permissions: + # Use to sign the release artifacts + id-token: write + # Used to upload release artifacts + contents: write + # Used to generate artifact attestation + attestations: write + steps: + - uses: actions/download-artifact@v7 + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v3 + with: + subject-path: 'wheels-*/*' + - name: Install uv + if: ${{ startsWith(github.ref, 'refs/tags/') }} + uses: astral-sh/setup-uv@v7 + - name: Publish to PyPI + if: ${{ startsWith(github.ref, 'refs/tags/') }} + run: uv publish 'wheels-*/*' + env: + UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }} From 963d9e376229e27522700ef666d8534593b055dc Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 11 Mar 2026 23:41:06 +0000 Subject: [PATCH 59/64] Added more classifiers --- pyproject.toml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e57908b..64e13d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,8 +14,14 @@ requires-python = ">=3.11" license = "MIT" license-files = ["LICENSE"] classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Operating System :: OS Independent", "Programming Language :: Rust", - "License :: OSI Approved :: MIT License", # for compatibility with tooling such as pip-licenses + "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", ] dependencies = [ "maturin>=1.10.2", From 15bc150d2e905b61efbdf01cd7f3800e0b377404 Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 11 Mar 2026 23:41:36 +0000 Subject: [PATCH 60/64] Removing experiments --- experiments/cifar10/train.ipynb | 115 ------- experiments/cifar10/utils/dataset.py | 91 ------ experiments/cifar10/utils/minifloat.py | 160 --------- experiments/cifar10/utils/resnet.py | 303 ------------------ experiments/cifar10/utils/train.py | 45 --- experiments/mnist/train.ipynb | 148 --------- experiments/mnist/utils/dataset.py | 46 --- experiments/mnist/utils/hardware.py | 187 ----------- experiments/mnist/utils/minifloat.py | 156 --------- experiments/mnist/utils/model.py | 126 -------- experiments/mnist/utils/train.py | 95 ------ experiments/systolic_array/README.md | 5 - .../systolic_array/axis-systolic-array | 1 - experiments/systolic_array/synth.tcl | 49 --- 14 files changed, 1527 deletions(-) delete mode 100644 experiments/cifar10/train.ipynb delete mode 100644 experiments/cifar10/utils/dataset.py delete mode 100644 experiments/cifar10/utils/minifloat.py delete mode 100644 experiments/cifar10/utils/resnet.py delete mode 100644 experiments/cifar10/utils/train.py delete mode 100644 experiments/mnist/train.ipynb delete mode 100644 experiments/mnist/utils/dataset.py delete mode 100644 experiments/mnist/utils/hardware.py delete mode 100644 experiments/mnist/utils/minifloat.py delete mode 100644 experiments/mnist/utils/model.py delete mode 100644 experiments/mnist/utils/train.py delete mode 100644 experiments/systolic_array/README.md delete mode 160000 experiments/systolic_array/axis-systolic-array delete mode 100644 experiments/systolic_array/synth.tcl diff --git a/experiments/cifar10/train.ipynb b/experiments/cifar10/train.ipynb deleted file mode 100644 index 6083d5e..0000000 --- a/experiments/cifar10/train.ipynb +++ /dev/null @@ -1,115 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "62d18188", - "metadata": {}, - "outputs": [], - "source": [ - "# import numpy as np\n", - "import torch\n", - "import torch.nn as nn\n", - "\n", - "# import torch.nn.functional as F\n", - "import torch.optim as optim\n", - "import torch.optim.lr_scheduler as lrs\n", - "\n", - "from utils.dataset import get_cifar10_dataloaders, create_calibration_dataloader\n", - "from utils.resnet import quant_resnet18, filter_params\n", - "from utils.train import train_for_epoch\n", - "\n", - "DATASET_PATH = \"./data\" # Change me\n", - "EXPORT_PATH = \"./model_outputs\" # Change me" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "05c65f1a", - "metadata": {}, - "outputs": [], - "source": [ - "device = (\n", - " \"xpu\"\n", - " if torch.xpu.is_available() # For the only person who has an Intel GPU\n", - " else \"cuda\"\n", - " if torch.cuda.is_available()\n", - " else \"cpu\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "115d90f7", - "metadata": {}, - "outputs": [], - "source": [ - "# Training parameters\n", - "learning_rate_init = 0.1\n", - "learning_rate_step_size = 30\n", - "learning_rate_gamma = 0.1\n", - "momentum = 0.5\n", - "weight_decay = 1e-5\n", - "\n", - "# Setup model\n", - "model = quant_resnet18(weight_bit_width=4, act_bit_width=4, num_classes=10)\n", - "model = model.to(device)\n", - "criterion = nn.CrossEntropyLoss()\n", - "optimizer = optim.SGD(\n", - " filter_params(model.named_parameters(), weight_decay),\n", - " lr=learning_rate_init,\n", - " weight_decay=weight_decay,\n", - ")\n", - "scheduler = lrs.StepLR(\n", - " optimizer, step_size=learning_rate_step_size, gamma=learning_rate_gamma\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1ca65d52", - "metadata": {}, - "outputs": [], - "source": [ - "train_loader, test_loader = get_cifar10_dataloaders(\n", - " DATASET_PATH, pin_memory=True, pin_memory_device=device\n", - ")\n", - "calib_loader = create_calibration_dataloader(dataset=train_loader.dataset)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cc5b590a", - "metadata": {}, - "outputs": [], - "source": [ - "for epoch in range(20): # 90 default\n", - " train_loss = train_for_epoch(model, device, train_loader, criterion, optimizer)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "arbolta", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/experiments/cifar10/utils/dataset.py b/experiments/cifar10/utils/dataset.py deleted file mode 100644 index 55ea0f3..0000000 --- a/experiments/cifar10/utils/dataset.py +++ /dev/null @@ -1,91 +0,0 @@ -# Based on https://github.com/Xilinx/brevitas/blob/dev/src/brevitas_examples/imagenet_classification/a2q/utils.py -import numpy as np -import torchvision -import torchvision.transforms as transforms -from torch.utils.data import DataLoader, Dataset, Subset - - -def get_cifar10_dataloaders( - data_root: str, - batch_size_train: int = 128, - batch_size_test: int = 100, - num_workers: int = 0, - pin_memory: bool = True, - pin_memory_device: str = "cpu", - download: bool = False, -) -> tuple[DataLoader, DataLoader]: - mean, std = [0.491, 0.482, 0.447], [0.247, 0.243, 0.262] - - # Transformations - transform_train = transforms.Compose( - [ - transforms.RandomCrop(32, padding=4), - transforms.RandomHorizontalFlip(), - transforms.ToTensor(), - transforms.Normalize(mean=mean, std=std), - ] - ) - - transform_test = transforms.Compose( - [ - transforms.ToTensor(), - transforms.Normalize(mean=mean, std=std), - ] - ) - - train_dataset = torchvision.datasets.CIFAR10( - root=data_root, - train=True, - download=download, - transform=transform_train, - ) - - test_dataset = torchvision.datasets.CIFAR10( - root=data_root, - train=False, - download=download, - transform=transform_test, - ) - - train_loader = DataLoader( - train_dataset, - batch_size=batch_size_train, - shuffle=True, - num_workers=num_workers, - pin_memory=pin_memory, - pin_memory_device=pin_memory_device, - persistent_workers=num_workers > 0, # Remove? - ) - - test_loader = DataLoader( - test_dataset, - batch_size=batch_size_test, - shuffle=False, - num_workers=num_workers, - pin_memory=pin_memory, - pin_memory_device=pin_memory_device, - persistent_workers=num_workers > 0, # Remove? - ) - - return train_loader, test_loader - - -def create_calibration_dataloader( - dataset: Dataset, - batch_size: int = 256, - num_workers: int = 0, - subset_size: int = 1000, - pin_memory: bool = True, - pin_memory_device: str = "cpu", -) -> DataLoader: - all_indices = np.arange(len(dataset)) - cur_indices = np.random.choice(all_indices, size=subset_size) - subset = Subset(dataset, cur_indices) - loader = DataLoader( - subset, - batch_size=batch_size, - num_workers=num_workers, - pin_memory=pin_memory, - pin_memory_device=pin_memory_device, - ) - return loader diff --git a/experiments/cifar10/utils/minifloat.py b/experiments/cifar10/utils/minifloat.py deleted file mode 100644 index 888ab44..0000000 --- a/experiments/cifar10/utils/minifloat.py +++ /dev/null @@ -1,160 +0,0 @@ -from typing import Optional, cast - -import array_api_compat -import brevitas.nn as qnn -import numpy as np -import torch.nn as nn -from brevitas.inject import ExtendedInjector -from brevitas.quant.experimental.float_base import ( - FloatActBase, - FloatBase, - FloatWeightBase, -) -from numpy.typing import NDArray -from torch import FloatTensor, IntTensor - -__all__ = ["mf_to_raw", "raw_to_mf", "fp_mixin_factory", "MinifloatQuantizer"] - - -# Helper for converting quantized floats to raw binary -def mf_to_raw( - x: FloatTensor | NDArray[np.float32], - exponent_bit_width: int, - mantissa_bit_width: int, - exponent_bias: int, - eps: float, -) -> IntTensor | NDArray[np.int_]: - xp = array_api_compat.array_namespace(x) - - emin = -exponent_bias + 1 - - sign = xp.astype(xp.signbit(x), int) - exp = xp.astype(xp.floor(xp.log2(xp.abs(x) + eps)), int) - exp = xp.clip(exp, min=emin) - man = xp.abs(x) / xp.exp2(exp) - - exp_bits = exp - xp.astype((man < 1), int) + exponent_bias # Denorm - man_bits = xp.astype((man * (1 << mantissa_bit_width)), int) - man_bits = man_bits & ((1 << mantissa_bit_width) - 1) - - out = ( - (sign << (exponent_bit_width + mantissa_bit_width)) - | (exp_bits << mantissa_bit_width) - | man_bits - ) - - format_width = 1 + exponent_bit_width + mantissa_bit_width - # Cast to unsigned int for interfacing w/ Arbolta - out_dtype = ( - xp.uint8 - if format_width <= 8 - else xp.uint16 - if format_width <= 16 - else xp.uint32 - if format_width <= 32 - else xp.uint64 - ) - - return xp.astype(out, out_dtype) - - -# Helper for converting raw binary minifloats to floats -def raw_to_mf( - x: IntTensor | NDArray[np.int_], - exponent_bit_width: int, - mantissa_bit_width: int, - exponent_bias: int, -) -> FloatTensor | NDArray[np.float32]: - xp = array_api_compat.array_namespace(x) - x = xp.astype(x, int) # Cast to signed int to allow for bit shifting - - emin = -exponent_bias + 1 - - sign_mask = 1 << (exponent_bit_width + mantissa_bit_width) - exp_mask = ((1 << exponent_bit_width) - 1) << mantissa_bit_width - man_mask = (1 << mantissa_bit_width) - 1 - - sign = xp.where((x & sign_mask) == 0, 1.0, -1.0) - exp_denorm = xp.astype((x & exp_mask) >> mantissa_bit_width, xp.float32) - man_denorm = xp.astype(x & man_mask, xp.float32) / (2**mantissa_bit_width) - - exp = xp.where(exp_denorm == 0, emin, exp_denorm - exponent_bias) - man = xp.where(exp_denorm == 0, man_denorm, 1.0 + man_denorm) - - return sign * man * xp.exp2(exp) - - -# Helper for creating custom minifloat quantizers -def fp_mixin_factory( - exponent_bit_width: int, mantissa_bit_width: int, base_class: FloatBase -): - bit_width = 1 + exponent_bit_width + mantissa_bit_width - name = f"Fp{bit_width}e{exponent_bit_width}m{mantissa_bit_width}" - mixin = type( - name + "Mixin", - (ExtendedInjector,), - { - # Add sign bit - "bit_width": bit_width, - "exponent_bit_width": exponent_bit_width, - "mantissa_bit_width": mantissa_bit_width, - "saturating": True, - }, - ) - - if base_class is FloatActBase: - class_name = name + "Act" - elif base_class is FloatWeightBase: - class_name = name + "Weight" - else: - raise TypeError("Unsupported Float Base") - - return type(class_name, (mixin, base_class), {}) - - -class MinifloatQuantizer(nn.Module): - def __init__( - self, - exponent_bit_width: int, - mantissa_bit_width: int, - exponent_bias: Optional[int] = None, - eps: float = 1e-9, - ): - super(MinifloatQuantizer, self).__init__() - if exponent_bias is None: - exponent_bias = (2 ** (exponent_bit_width - 1)) - 1 - - self.exponent_bit_width = exponent_bit_width - self.mantissa_bit_width = mantissa_bit_width - self.exponent_bias = cast(int, exponent_bias) - self.eps = eps - self.ident = qnn.QuantIdentity( - act_quant=fp_mixin_factory( - exponent_bit_width, - mantissa_bit_width, - FloatActBase, # pyright: ignore[reportArgumentType] - ) - ) - - def forward( - self, x: FloatTensor, return_raw: bool = False - ) -> IntTensor | FloatTensor: - out = self.ident(x) - - if return_raw: - out = mf_to_raw( - out, - self.exponent_bit_width, - self.mantissa_bit_width, - self.exponent_bias, - self.eps, - ) - out = cast(IntTensor, out) - - return out - - def dequantize_raw(self, x: IntTensor) -> FloatTensor: - out = raw_to_mf( - x, self.exponent_bit_width, self.mantissa_bit_width, self.exponent_bias - ) - return cast(FloatTensor, out) diff --git a/experiments/cifar10/utils/resnet.py b/experiments/cifar10/utils/resnet.py deleted file mode 100644 index b0f7227..0000000 --- a/experiments/cifar10/utils/resnet.py +++ /dev/null @@ -1,303 +0,0 @@ -# Based on https://github.com/Xilinx/brevitas/blob/master/src/brevitas_examples/bnn_pynq/models/resnet.py -# from abc import ABCMeta, abstractmethod - -import brevitas.nn as qnn - -# import torch -import torch.nn as nn - -# import torch.nn.functional as F -# from brevitas.quant.experimental.float_base import ( -# FloatActBase, -# FloatWeightBase, -# ) -# from brevitas.quant_tensor.float_quant_tensor import FloatQuantTensor -# from brevitas.quant_tensor.int_quant_tensor import IntQuantTensor -from torch import Tensor - -# from .minifloat import fp_mixin_factory, mf_to_raw - -# DELETE -from brevitas.quant import Int8WeightPerChannelFloat -from brevitas.quant import Int8WeightPerTensorFloat -from brevitas.quant import Int32Bias -from brevitas.quant import TruncTo8bit -from brevitas.quant_tensor import QuantTensor - - -def make_quant_conv2d( - in_channels, - out_channels, - kernel_size, - weight_bit_width, - weight_quant, - stride=1, - padding=0, - bias=False, -): - return qnn.QuantConv2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - bias=bias, - weight_quant=weight_quant, - weight_bit_width=weight_bit_width, - ) - - -# No input quantizer? -class QuantBasicBlock(nn.Module): - expansion = 1 - - def __init__( - self, - in_planes, # num input filters - planes, # num output filters - stride=1, - bias=False, - shared_quant_act=None, - act_bit_width=8, # Assumes input is uint8 - weight_bit_width=8, - weight_quant=Int8WeightPerChannelFloat, - ): - super(QuantBasicBlock, self).__init__() - self.conv1 = make_quant_conv2d( - in_planes, - planes, - kernel_size=3, - stride=stride, - padding=1, - bias=bias, - weight_bit_width=weight_bit_width, - weight_quant=weight_quant, - ) - self.bn1 = nn.BatchNorm2d(planes) - self.relu1 = qnn.QuantReLU(bit_width=act_bit_width, return_quant_tensor=True) - self.conv2 = make_quant_conv2d( - planes, - planes, - kernel_size=3, - stride=1, - padding=1, - bias=bias, - weight_bit_width=weight_bit_width, - weight_quant=weight_quant, - ) - self.bn2 = nn.BatchNorm2d(planes) - self.downsample = nn.Sequential() - if stride != 1 or in_planes != self.expansion * planes: - self.downsample = nn.Sequential( - make_quant_conv2d( - in_planes, - self.expansion * planes, - kernel_size=1, - stride=stride, - padding=0, - bias=bias, - weight_bit_width=weight_bit_width, - weight_quant=weight_quant, - ), - nn.BatchNorm2d(self.expansion * planes), - # We add a ReLU activation here because FINN requires the same sign along residual adds - qnn.QuantReLU(bit_width=act_bit_width, return_quant_tensor=True), - ) - # Redefine shared_quant_act whenever shortcut is performing downsampling - shared_quant_act = self.downsample[-1] - if shared_quant_act is None: - shared_quant_act = qnn.QuantReLU( - bit_width=act_bit_width, return_quant_tensor=True - ) - # We add a ReLU activation here because FINN requires the same sign along residual adds - self.relu2 = shared_quant_act - self.relu_out = qnn.QuantReLU(return_quant_tensor=True, bit_width=act_bit_width) - - def forward(self, x): - out = self.relu1(self.bn1(self.conv1(x))) - out = self.relu2(self.bn2(self.conv2(out))) - if len(self.downsample): - x = self.downsample(x) - # Check that the addition is made explicitly among QuantTensor structures - assert isinstance(out, QuantTensor), "Perform add among QuantTensors" - assert isinstance(x, QuantTensor), "Perform add among QuantTensors" - out = out + x - out = self.relu_out(out) - return out - - -class QuantResNet(nn.Module): - def __init__( - self, - block_impl, - num_blocks: list[int], - first_maxpool=False, - zero_init_residual=False, - num_classes=10, - act_bit_width=8, - weight_bit_width=8, - round_average_pool=False, - last_layer_bias_quant=Int32Bias, - weight_quant=Int8WeightPerChannelFloat, - first_layer_weight_quant=Int8WeightPerChannelFloat, - last_layer_weight_quant=Int8WeightPerTensorFloat, - ): - super(QuantResNet, self).__init__() - self.in_planes = 64 - self.conv1 = make_quant_conv2d( - 3, - 64, - kernel_size=3, - stride=1, - padding=1, - weight_bit_width=8, - weight_quant=first_layer_weight_quant, - ) - self.bn1 = nn.BatchNorm2d(64) - shared_quant_act = qnn.QuantReLU( - bit_width=act_bit_width, return_quant_tensor=True - ) - self.relu = shared_quant_act - # MaxPool is typically present for ImageNet but not for CIFAR10 - if first_maxpool: - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - else: - self.maxpool = nn.Identity() - - self.layer1, shared_quant_act = self._make_layer( - block_impl, - 64, - num_blocks[0], - 1, - shared_quant_act, - weight_bit_width, - act_bit_width, - weight_quant, - ) - self.layer2, shared_quant_act = self._make_layer( - block_impl, - 128, - num_blocks[1], - 2, - shared_quant_act, - weight_bit_width, - act_bit_width, - weight_quant, - ) - self.layer3, shared_quant_act = self._make_layer( - block_impl, - 256, - num_blocks[2], - 2, - shared_quant_act, - weight_bit_width, - act_bit_width, - weight_quant, - ) - self.layer4, _ = self._make_layer( - block_impl, - 512, - num_blocks[3], - 2, - shared_quant_act, - weight_bit_width, - act_bit_width, - weight_quant, - ) - - # Performs truncation to 8b (without rounding), which is supported in FINN - avgpool_float_to_int_impl_type = "ROUND" if round_average_pool else "FLOOR" - self.final_pool = qnn.TruncAvgPool2d( - kernel_size=4, - trunc_quant=TruncTo8bit, - float_to_int_impl_type=avgpool_float_to_int_impl_type, - ) - # Keep last layer at 8b - self.linear = qnn.QuantLinear( - 512 * block_impl.expansion, - num_classes, - weight_bit_width=8, - bias=True, - bias_quant=last_layer_bias_quant, - weight_quant=last_layer_weight_quant, - ) - - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu") - elif isinstance(m, nn.BatchNorm2d): - nn.init.constant_(m.weight, 1) - nn.init.constant_(m.bias, 0) - - # Zero-initialize the last BN in each residual branch - if zero_init_residual: - for m in self.modules(): - if isinstance(m, QuantBasicBlock) and m.bn2.weight is not None: - nn.init.constant_(m.bn2.weight, 0) - - def _make_layer( - self, - block_impl, - planes, - num_blocks, - stride, - shared_quant_act, - weight_bit_width, - act_bit_width, - weight_quant, - ): - strides = [stride] + [1] * (num_blocks - 1) - layers = [] - for stride in strides: - block = block_impl( - in_planes=self.in_planes, - planes=planes, - stride=stride, - bias=False, - shared_quant_act=shared_quant_act, - act_bit_width=act_bit_width, - weight_bit_width=weight_bit_width, - weight_quant=weight_quant, - ) - layers.append(block) - shared_quant_act = layers[-1].relu_out - self.in_planes = planes * block_impl.expansion - return nn.Sequential(*layers), shared_quant_act - - def forward(self, x: Tensor): - # There is no input quantizer, we assume the input is already 8b RGB - out = self.relu(self.bn1(self.conv1(x))) - out = self.maxpool(out) - out = self.layer1(out) - out = self.layer2(out) - out = self.layer3(out) - out = self.layer4(out) - out = self.final_pool(out) - out = out.view(out.size(0), -1) - out = self.linear(out) - return out - - -def quant_resnet18(weight_bit_width, act_bit_width, num_classes) -> QuantResNet: - model = QuantResNet( - block_impl=QuantBasicBlock, - num_blocks=[2, 2, 2, 2], - num_classes=num_classes, - weight_bit_width=weight_bit_width, - act_bit_width=act_bit_width, - ) - return model - - -def filter_params(named_params, decay): - decay_params, no_decay_params = [], [] - for name, param in named_params: - # Do not apply weight decay to the bias or any scaling parameters - if "scaling" in name or name.endswith(".bias"): - no_decay_params.append(param) - else: - decay_params.append(param) - return [ - {"params": no_decay_params, "weight_decay": 0.0}, - {"params": decay_params, "weight_decay": decay}, - ] diff --git a/experiments/cifar10/utils/train.py b/experiments/cifar10/utils/train.py deleted file mode 100644 index a4f3ab8..0000000 --- a/experiments/cifar10/utils/train.py +++ /dev/null @@ -1,45 +0,0 @@ -# from typing import Optional - -# import torch -import torch.nn as nn -from torch import Tensor -from torch.nn.modules.loss import _Loss -from torch.optim.optimizer import Optimizer -from torch.utils.data import DataLoader -from tqdm.notebook import tqdm - -__all__ = [ - "train_for_epoch", -] - - -def train_for_epoch( - model: nn.Module, - device: str, - train_loader: DataLoader, - criterion: _Loss, - optimizer: Optimizer, -) -> None: - model.train() - - correct_total = 0 - size_total = 0 - with tqdm(train_loader) as tepoch: - tepoch.set_description("Train") - for images, targets in tepoch: - images, targets = images.to(device), targets.to(device) - - optimizer.zero_grad() - logits: Tensor = model(images) - loss: Tensor = criterion(logits, targets) - loss.backward() - optimizer.step() - - # Compute metrics - preds = logits.argmax(dim=1, keepdim=True).squeeze() - num_correct = (preds == targets).sum().item() - correct_total += num_correct - size_total += len(targets) - accuracy = correct_total / size_total - - tepoch.set_postfix(loss=loss.item(), acc=format(accuracy, "3.2%")) diff --git a/experiments/mnist/train.ipynb b/experiments/mnist/train.ipynb deleted file mode 100644 index b903de0..0000000 --- a/experiments/mnist/train.ipynb +++ /dev/null @@ -1,148 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "586d6d25", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "\n", - "import numpy as np\n", - "import torch\n", - "import torch.nn as nn\n", - "import torch.nn.functional as F\n", - "from utils import (\n", - " FloatQuantLinearModel,\n", - " IntQuantLinearModel,\n", - " get_mnist_dataloaders,\n", - " test_model,\n", - " train_for_epoch,\n", - ")\n", - "\n", - "DATASET_PATH = \"./data\" # Change me\n", - "EXPORT_PATH = \"./model_outputs\" # Change me" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f14bb52f", - "metadata": {}, - "outputs": [], - "source": [ - "# Training parameters\n", - "learning_rate = 0.01\n", - "momentum = 0.5\n", - "\n", - "# ++++ Quantization method (comment/uncomment) ++++\n", - "# Int quant\n", - "model = IntQuantLinearModel(input_bit_width=4, weight_bit_width=4)\n", - "\n", - "# Float quant\n", - "model = FloatQuantLinearModel(\n", - " input_exponent_bit_width=2,\n", - " input_mantissa_bit_width=1,\n", - " weight_exponent_bit_width=4,\n", - " weight_mantissa_bit_width=3,\n", - ")\n", - "\n", - "# ---- Quantization method ----\n", - "\n", - "device = (\n", - " \"xpu\"\n", - " if torch.xpu.is_available() # For the only person who has an Intel GPU\n", - " else \"cuda\"\n", - " if torch.cuda.is_available()\n", - " else \"cpu\"\n", - ")\n", - "\n", - "model = model.to(device)\n", - "criterion = nn.CrossEntropyLoss()\n", - "optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bc062a9f", - "metadata": {}, - "outputs": [], - "source": [ - "train_loader, test_loader = get_mnist_dataloaders(\n", - " DATASET_PATH, pin_memory=True, pin_memory_device=device\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ae77e888", - "metadata": {}, - "outputs": [], - "source": [ - "for epoch in range(10):\n", - " print(f\"Epoch: {epoch}\")\n", - " train_for_epoch(model, device, train_loader, criterion, optimizer)\n", - " test_model(model, device, test_loader, F.cross_entropy)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "979531b2", - "metadata": {}, - "outputs": [], - "source": [ - "# Get all correctly classified images/inputs from test dataset\n", - "correct_inputs, correct_targets = test_model(\n", - " model, device, test_loader, F.cross_entropy, return_correct=True\n", - ")\n", - "\n", - "# Get quantized weights and inputs\n", - "model_weights, model_scale = model.quant_weight()\n", - "correct_inputs, input_scale = model.quant_input(correct_inputs)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "52d7e089", - "metadata": {}, - "outputs": [], - "source": [ - "# Create output directory if it doesn't exist\n", - "if not os.path.exists(EXPORT_PATH):\n", - " os.makedirs(EXPORT_PATH)\n", - "\n", - "# Export NumPy for compatibility with Rust\n", - "np.save(EXPORT_PATH + \"/model_weights.npy\", model_weights.cpu().numpy())\n", - "np.save(EXPORT_PATH + \"/model_scale.npy\", model_scale.cpu().numpy())\n", - "np.save(EXPORT_PATH + \"/correct_inputs.npy\", correct_inputs.cpu().numpy())\n", - "np.save(EXPORT_PATH + \"/input_scale.npy\", input_scale.cpu().numpy())\n", - "np.save(EXPORT_PATH + \"/correct_targets.npy\", correct_targets.cpu().numpy())" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "arbolta", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/experiments/mnist/utils/dataset.py b/experiments/mnist/utils/dataset.py deleted file mode 100644 index 2d13494..0000000 --- a/experiments/mnist/utils/dataset.py +++ /dev/null @@ -1,46 +0,0 @@ -import torchvision -from torch.utils.data import DataLoader - - -def get_mnist_dataloaders( - data_root: str, - num_workers: int = 0, - batch_size_train: int = 64, - batch_size_test: int = 1024, - pin_memory: bool = True, - pin_memory_device: str = "cpu", - download: bool = False, -): - image_transform = torchvision.transforms.Compose( - [ - torchvision.transforms.ToTensor(), - torchvision.transforms.Normalize((0.1307,), (0.3081,)), - ] - ) - - train_dataset = torchvision.datasets.MNIST( - data_root, train=True, download=download, transform=image_transform - ) - test_dataset = torchvision.datasets.MNIST( - data_root, train=False, download=download, transform=image_transform - ) - - train_loader = DataLoader( - train_dataset, - batch_size=batch_size_train, - shuffle=True, - num_workers=num_workers, - pin_memory=pin_memory, - pin_memory_device=pin_memory_device, - ) - - test_loader = DataLoader( - test_dataset, - batch_size=batch_size_test, - shuffle=True, - num_workers=num_workers, - pin_memory=pin_memory, - pin_memory_device=pin_memory_device, - ) - - return train_loader, test_loader diff --git a/experiments/mnist/utils/hardware.py b/experiments/mnist/utils/hardware.py deleted file mode 100644 index 1bd9b2a..0000000 --- a/experiments/mnist/utils/hardware.py +++ /dev/null @@ -1,187 +0,0 @@ -import subprocess -from typing import Any, Literal, Optional - -import numpy as np -import torch -from arbolta import DesignConfig, HardwareDesign, PortConfig -from numpy import ndarray -from torch import Tensor - -__all__ = ["SystolicArray"] - - -def require(name: str, val: Any) -> Any: - if val is None: - raise ValueError(f"Must set `{name}`") - return val - - -def width_to_int(w: int, signed: bool) -> np.dtype: - if signed: - dtype = ( - np.int8 - if w <= 8 - else np.int16 - if w <= 16 - else np.int32 - if w <= 32 - else np.int64 - ) - else: - dtype = ( - np.uint8 - if w <= 8 - else np.uint16 - if w <= 16 - else np.uint32 - if w <= 32 - else np.uint64 - ) - return dtype - - -class SystolicArray: - def __init__( - self, - mac_type: Literal["integer", "minifloat"], - rows: int, - columns: int, - accumulator_bit_width: int, - # Integer parameters - x_bit_width: Optional[int] = None, - k_bit_width: Optional[int] = None, - # Minifloat parameters - x_exponent_bit_width: Optional[int] = None, - x_mantissa_bit_width: Optional[int] = None, - k_exponent_bit_width: Optional[int] = None, - k_mantissa_bit_width: Optional[int] = None, - ): - params: dict[str, int] - x_dtype: np.dtype - match mac_type: - case "integer": - params = { - "MacType": 0, - "WidthX": require("x_bit_width", x_bit_width), - "WidthK": require("k_bit_width", k_bit_width), - } - x_dtype = width_to_int(x_bit_width, signed=True) - k_dtype = width_to_int(k_bit_width, signed=True) - - case "minifloat": - params = { - "MacType": 1, - "ExpWidthX": require("x_exponent_bit_width", x_exponent_bit_width), - "ManWidthX": require("x_mantissa_bit_width", x_mantissa_bit_width), - "ExpWidthK": require("k_exponent_bit_width", k_exponent_bit_width), - "ManWidthK": require("k_mantissa_bit_width", k_mantissa_bit_width), - } - - # For calculating datatype - # TODO: Conversion w/ ml_dtypes - x_bit_width = 1 + x_exponent_bit_width + x_mantissa_bit_width - k_bit_width = 1 + k_exponent_bit_width + k_mantissa_bit_width - x_dtype = width_to_int(x_bit_width, signed=False) - k_dtype = width_to_int(k_bit_width, signed=False) - - case _: - raise ValueError(f"Unsupported MAC type `{mac_type}`") - - params.update({"Rows": rows, "Cols": columns, "WidthY": accumulator_bit_width}) - - self.params = params - self.acc_dtype = width_to_int(accumulator_bit_width, signed=True) - self.x_dtype = x_dtype - self.k_dtype = k_dtype - self.design: HardwareDesign = None - - def load(self, netlist_path: str = "./synth_output/0_proc/synth.json") -> None: - """ - Load systolic array from Yosys JSON netlist. - """ - rows, columns = self.params["Rows"], self.params["Cols"] - config = DesignConfig( - clk_i=PortConfig(clock=True, polarity=1), - rst_ni=PortConfig(reset=True, polarity=0), - s_valid_i=PortConfig(shape=(1, 1)), - s_last_i=PortConfig(shape=(1, 1)), - m_ready_i=PortConfig(shape=(1, 1)), - s_ready_o=PortConfig(shape=(1, 1)), - m_valid_o=PortConfig(shape=(1, 1)), - m_last_o=PortConfig(shape=(1, 1)), - sx_data_i=PortConfig(shape=(1, rows), dtype=self.x_dtype), - sk_data_i=PortConfig(shape=(1, columns), dtype=self.k_dtype), - m_data_o=PortConfig(shape=(1, rows), dtype=self.acc_dtype), - ) - self.design = HardwareDesign("axis_sa", netlist_path, config) - - def synthesize( - self, - script_path: str = "../systolic_array/axis-systolic-array/tcl/synth.tcl", - output_dir: str = "./synth_output", - load: bool = True, - ) -> None: - """ - Synthesizes systolic array with Yosys. - Saves netlists at `./synth_output` and loads. - """ - synth_params = [f"{p_name}={p_val}" for (p_name, p_val) in self.params.items()] - synth_params.append(f"output_dir={output_dir}") - command = ["yosys", "-c", script_path, "--", *synth_params] - p = subprocess.Popen(command, stdout=subprocess.PIPE) - # Ignore output for now - _, err = p.communicate() - - assert err is None, err - - if load: - # Default to process netlist - self.load(f"{output_dir}/0_proc/synth.json") - - def run_matmul(self, x: ndarray | Tensor, k: ndarray | Tensor) -> ndarray | Tensor: - """ - Run inputs through systolic array. - Expects x: (K,R), k: (K,C) -> y: (C,R) - """ - if self.design is None: - raise AttributeError("Must load or synthesize design") - - K, R, C = x.shape[0], x.shape[1], k.shape[1] - actual = np.zeros((C, R), dtype=self.acc_dtype) - - # Start simulation - self.design.eval_reset_clocked() - for i in range(K): - while self.design.ports.s_ready_o == 0: - self.design.eval_clocked() - - self.design.ports.s_valid_i = 1 - self.design.ports.sx_data_i = x[i] - self.design.ports.sk_data_i = k[i] - - if i == K - 1: - self.design.ports.s_last_i = 1 - - self.design.eval_clocked() - - self.design.ports.m_ready_i = 1 - self.design.ports.s_valid_i = 0 - self.design.ports.s_last_i = 0 - - idx = 0 - while True: - if self.design.ports.m_valid_o.item() == 1: - actual[idx] = self.design.ports.m_data_o - idx += 1 - - if self.design.ports.m_last_o.item() == 1: - break - - self.design.eval_clocked() - - # TODO: CALCULATE FRACTION BITS - - if isinstance(x, Tensor) or isinstance(k, Tensor): - return torch.from_numpy(actual) - else: - return actual diff --git a/experiments/mnist/utils/minifloat.py b/experiments/mnist/utils/minifloat.py deleted file mode 100644 index 5eeeab7..0000000 --- a/experiments/mnist/utils/minifloat.py +++ /dev/null @@ -1,156 +0,0 @@ -from typing import Optional - -import array_api_compat -import brevitas.nn as qnn -import torch.nn as nn -from brevitas.inject import ExtendedInjector -from brevitas.quant.experimental.float_base import ( - FloatActBase, - FloatBase, - FloatWeightBase, -) -from numpy import ndarray -from torch import FloatTensor, IntTensor - -__all__ = ["mf_to_raw", "raw_to_mf", "fp_mixin_factory", "MinifloatQuantizer"] - - -# Helper for converting quantized floats to raw binary -def mf_to_raw( - x: FloatTensor | ndarray, - exponent_bit_width: int, - mantissa_bit_width: int, - exponent_bias: int, - eps: float, -) -> IntTensor | ndarray: - xp = array_api_compat.array_namespace(x) - - emin = -exponent_bias + 1 - - sign = xp.astype(xp.signbit(x), int) - exp = xp.astype(xp.floor(xp.log2(xp.abs(x) + eps)), int) - exp = xp.clip(exp, min=emin) - man = xp.abs(x) / xp.exp2(exp) - - exp_bits = exp - xp.astype((man < 1), int) + exponent_bias # Denorm - man_bits = xp.astype((man * (1 << mantissa_bit_width)), int) - man_bits = man_bits & ((1 << mantissa_bit_width) - 1) - - out = ( - (sign << (exponent_bit_width + mantissa_bit_width)) - | (exp_bits << mantissa_bit_width) - | man_bits - ) - - format_width = 1 + exponent_bit_width + mantissa_bit_width - # Cast to unsigned int for interfacing w/ Arbolta - out_dtype = ( - xp.uint8 - if format_width <= 8 - else xp.uint16 - if format_width <= 16 - else xp.uint32 - if format_width <= 32 - else xp.uint64 - ) - - return xp.astype(out, out_dtype) - - -# Helper for converting raw binary minifloats to floats -def raw_to_mf( - x: IntTensor | ndarray, - exponent_bit_width: int, - mantissa_bit_width: int, - exponent_bias: int, -) -> FloatTensor | ndarray: - xp = array_api_compat.array_namespace(x) - x = xp.astype(x, int) # Cast to signed int to allow for bit shifting - - emin = -exponent_bias + 1 - - sign_mask = 1 << (exponent_bit_width + mantissa_bit_width) - exp_mask = ((1 << exponent_bit_width) - 1) << mantissa_bit_width - man_mask = (1 << mantissa_bit_width) - 1 - - sign = xp.where((x & sign_mask) == 0, 1.0, -1.0) - exp_denorm = xp.astype((x & exp_mask) >> mantissa_bit_width, xp.float32) - man_denorm = xp.astype(x & man_mask, xp.float32) / (2**mantissa_bit_width) - - exp = xp.where(exp_denorm == 0, emin, exp_denorm - exponent_bias) - man = xp.where(exp_denorm == 0, man_denorm, 1.0 + man_denorm) - - return sign * man * xp.exp2(exp) - - -# Helper for creating custom minifloat quantizers -def fp_mixin_factory( - exponent_bit_width: int, mantissa_bit_width: int, base_class: FloatBase -): - bit_width = 1 + exponent_bit_width + mantissa_bit_width - name = f"Fp{bit_width}e{exponent_bit_width}m{mantissa_bit_width}" - mixin = type( - name + "Mixin", - (ExtendedInjector,), - { - # Add sign bit - "bit_width": bit_width, - "exponent_bit_width": exponent_bit_width, - "mantissa_bit_width": mantissa_bit_width, - "saturating": True, - }, - ) - - if base_class is FloatActBase: - class_name = name + "Act" - elif base_class is FloatWeightBase: - class_name = name + "Weight" - else: - raise TypeError("Unsupported Float Base") - - return type(class_name, (mixin, base_class), {}) - - -class MinifloatQuantizer(nn.Module): - def __init__( - self, - exponent_bit_width: int, - mantissa_bit_width: int, - exponent_bias: Optional[int] = None, - eps: float = 1e-9, - ): - super(MinifloatQuantizer, self).__init__() - if exponent_bias is None: - exponent_bias = (2 ** (exponent_bit_width - 1)) - 1 - - self.exponent_bit_width = exponent_bit_width - self.mantissa_bit_width = mantissa_bit_width - self.exponent_bias = exponent_bias - self.eps = eps - self.ident = qnn.QuantIdentity( - act_quant=fp_mixin_factory( - exponent_bit_width, mantissa_bit_width, FloatActBase - ) - ) - - def forward( - self, x: FloatTensor, return_raw: bool = False - ) -> IntTensor | FloatTensor: - out = self.ident(x) - - if return_raw: - out = mf_to_raw( - out, - self.exponent_bit_width, - self.mantissa_bit_width, - self.exponent_bias, - self.eps, - ) - - return out - - def dequantize_raw(self, x: IntTensor) -> FloatTensor: - out = raw_to_mf( - x, self.exponent_bit_width, self.mantissa_bit_width, self.exponent_bias - ) - return out diff --git a/experiments/mnist/utils/model.py b/experiments/mnist/utils/model.py deleted file mode 100644 index 8d855cf..0000000 --- a/experiments/mnist/utils/model.py +++ /dev/null @@ -1,126 +0,0 @@ -from abc import ABCMeta, abstractmethod - -import brevitas.nn as qnn -import torch -import torch.nn as nn -import torch.nn.functional as F -from brevitas.quant.experimental.float_base import ( - FloatActBase, - FloatWeightBase, -) -from brevitas.quant.scaled_int import Int8ActPerTensorFloat, Int8WeightPerTensorFloat -from brevitas.quant_tensor.float_quant_tensor import FloatQuantTensor -from brevitas.quant_tensor.int_quant_tensor import IntQuantTensor -from torch import Tensor -from .minifloat import mf_to_raw, fp_mixin_factory - -__all__ = [ - "IntQuantLinearModel", - "FloatQuantLinearModel", -] - - -class QuantLinearModel(nn.Module, metaclass=ABCMeta): - # Pass kwargs to QuantLinear layer - def __init__(self, **kwargs): - super(QuantLinearModel, self).__init__() - self.flatten_inp = nn.Flatten() - self.fc1 = qnn.QuantLinear( - in_features=28 * 28, out_features=10, bias=False, **kwargs - ) - - def forward(self, x: Tensor) -> Tensor: - out = self.flatten_inp(x) - out = self.fc1(out) - out = F.log_softmax(out, dim=-1) - return out - - @abstractmethod - def quant_weight(self) -> tuple[Tensor, Tensor]: - """ - Get quantized model weights as uints - """ - pass - - @abstractmethod - def quant_input(self, x: Tensor) -> tuple[Tensor, Tensor]: - """ - Quantize tensor as uints - """ - pass - - -class IntQuantLinearModel(QuantLinearModel): - def __init__(self, input_bit_width: int = 8, weight_bit_width: int = 8): - super(IntQuantLinearModel, self).__init__( - input_quant=Int8ActPerTensorFloat, - input_bit_width=input_bit_width, - weight_quant=Int8WeightPerTensorFloat, - weight_bit_width=weight_bit_width, - ) - - def quant_weight(self) -> tuple[Tensor, Tensor]: - with torch.no_grad(): - weights: IntQuantTensor = self.fc1.quant_weight() - - return weights.int(), weights.scale - - def quant_input(self, x: Tensor) -> tuple[Tensor, Tensor]: - with torch.no_grad(): - inputs: IntQuantTensor = self.fc1.input_quant(x) - - return inputs.int(), inputs.scale - - -class FloatQuantLinearModel(QuantLinearModel): - def __init__( - self, - input_exponent_bit_width: int = 4, - input_mantissa_bit_width: int = 3, - weight_exponent_bit_width: int = 4, - weight_mantissa_bit_width: int = 3, - ): - input_quant = fp_mixin_factory( - input_exponent_bit_width, input_mantissa_bit_width, FloatActBase - ) - weight_quant = fp_mixin_factory( - weight_exponent_bit_width, weight_mantissa_bit_width, FloatWeightBase - ) - super(FloatQuantLinearModel, self).__init__( - input_quant=input_quant, - weight_quant=weight_quant, - ) - - def quant_weight(self, return_raw: bool = False) -> tuple[Tensor, Tensor]: - with torch.no_grad(): - weights: FloatQuantTensor = self.fc1.quant_weight() - - out = weights.minifloat() - - if return_raw: - out = mf_to_raw( - out, - weights.exponent_bit_width.int(), - weights.mantissa_bit_width.int(), - weights.exponent_bias.int(), - weights.eps, - ).type(torch.uint8) - - return out, weights.scale - - def quant_input(self, x: Tensor, return_raw: bool = False) -> tuple[Tensor, Tensor]: - with torch.no_grad(): - inputs: FloatQuantTensor = self.fc1.input_quant(x) - - out = inputs.minifloat() - - if return_raw: - out = mf_to_raw( - out, - inputs.exponent_bit_width.int(), - inputs.mantissa_bit_width.int(), - inputs.exponent_bias.int(), - inputs.eps, - ).type(torch.uint8) - - return out, inputs.scale diff --git a/experiments/mnist/utils/train.py b/experiments/mnist/utils/train.py deleted file mode 100644 index 1af5175..0000000 --- a/experiments/mnist/utils/train.py +++ /dev/null @@ -1,95 +0,0 @@ -from typing import Optional - -import torch -import torch.nn as nn -from torch import Tensor -from torch.nn.modules.loss import _Loss -from torch.optim.optimizer import Optimizer -from torch.utils.data import DataLoader -from tqdm.notebook import tqdm - -__all__ = ["train_for_epoch", "test_model"] - - -def train_for_epoch( - model: nn.Module, - device: str, - train_loader: DataLoader, - criterion: _Loss, - optimizer: Optimizer, -) -> None: - model.train() - - correct_total = 0 - size_total = 0 - with tqdm(train_loader) as tepoch: - tepoch.set_description("Train") - for images, targets in tepoch: - images, targets = images.to(device), targets.to(device) - - optimizer.zero_grad() - logits: Tensor = model(images) - loss: Tensor = criterion(logits, targets) - loss.backward() - optimizer.step() - - # Compute metrics - preds = logits.argmax(dim=1, keepdim=True).squeeze() - num_correct = (preds == targets).sum().item() - correct_total += num_correct - size_total += len(targets) - accuracy = correct_total / size_total - - tepoch.set_postfix(loss=loss.item(), acc=format(accuracy, "3.2%")) - - -def test_model( - model: nn.Module, - device: str, - test_loader: DataLoader, - criterion: _Loss, - return_correct: bool = False, -) -> Optional[tuple[Tensor, Tensor]]: - """ - Optionally return correct (images, targets) when return_correct == True - """ - model.eval() - - test_loss = 0 - correct_total = 0 - size_total = 0 - correct_images = [] - correct_targets = [] - - with torch.no_grad(): - with tqdm(test_loader) as tepoch: - tepoch.set_description("Test") - for images, targets in tepoch: - images, targets = images.to(device), targets.to(device) - - logits: Tensor = model(images) - loss: Tensor = criterion(logits, targets, reduction="sum") - - preds = logits.argmax(dim=1, keepdim=True).squeeze() - correct_mask = preds == targets - - correct_total += correct_mask.sum().item() - size_total += len(targets) - - if return_correct: - correct_images.extend(images[correct_mask]) - correct_targets.extend(targets[correct_mask]) - - accuracy = correct_total / size_total - - test_loss += loss.item() - avg_loss = test_loss / size_total - - tepoch.set_postfix( - loss=loss.item(), - acc=format(accuracy, "3.2%"), - avg_loss=avg_loss, - ) - - if return_correct: - return torch.vstack(correct_images), torch.vstack(correct_targets) diff --git a/experiments/systolic_array/README.md b/experiments/systolic_array/README.md deleted file mode 100644 index 40f42a2..0000000 --- a/experiments/systolic_array/README.md +++ /dev/null @@ -1,5 +0,0 @@ -Synthesize systolic array: - -```shell -yosys -c synth.tcl -- R= C= -``` diff --git a/experiments/systolic_array/axis-systolic-array b/experiments/systolic_array/axis-systolic-array deleted file mode 160000 index ebda1d5..0000000 --- a/experiments/systolic_array/axis-systolic-array +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ebda1d54b6a1572b64fcc7b30a030780c9886a14 diff --git a/experiments/systolic_array/synth.tcl b/experiments/systolic_array/synth.tcl deleted file mode 100644 index b564797..0000000 --- a/experiments/systolic_array/synth.tcl +++ /dev/null @@ -1,49 +0,0 @@ -yosys -import - -set top_module axis_sa - -set rtl_path axis-systolic-array/rtl/sa -set rtl_files [glob -directory $rtl_path -- "*.sv" "**/*.sv"] - -foreach verilog_source $rtl_files { - read_verilog -defer -sv $verilog_source -} - -# Overwrite module parameters -foreach param [lrange $argv 0 end] { - set param [split $param "="] - set param_name [lindex $param 0] - set param_val [lindex $param 1] - chparam -set $param_name $param_val $top_module -} - -hierarchy -check -top $top_module - -procs;; - -proc export_synth {output_path} { - file mkdir $output_path - write_json $output_path/synth.json - # write_verilog $output_path/synth.v - # tee -o $output_path/torder.txt torder - # show -viewer none -format dot -prefix $output_path/schematic -} - -flatten - -puts [export_synth {output/0_proc}] - -# Synthesize design -synth -top $top_module -clean -purge -autoname - -setundef -zero -puts [export_synth {output/1_synth}] - -# read_verilog -lib cells/cells.v -# dfflibmap -liberty cells/cells.lib -# abc -liberty cells/cells.lib -# opt_clean - -# puts [export_synth {output/2_cell_synth}] From 7ea43fe6f8f2ae0d81bd05eefb478fd27d3e8aac Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 11 Mar 2026 23:44:26 +0000 Subject: [PATCH 61/64] Make sure examples don't get bundled with sdist --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 64e13d2..c8ff695 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,6 +45,7 @@ strip = true python-source = "python/py_src" exclude = [ "python/tests/*", + "examples/*", # Shouldn't get included but just in-case ] [tool.uv] From 3bf3c2146948b06d69fbf49e7f2340085cbccd3f Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 11 Mar 2026 23:47:16 +0000 Subject: [PATCH 62/64] Removed old gitmodules and updated gitignore --- .gitignore | 2 +- .gitmodules | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 .gitmodules diff --git a/.gitignore b/.gitignore index d1c20d0..b75d536 100644 --- a/.gitignore +++ b/.gitignore @@ -158,4 +158,4 @@ Cargo.lock ### Generated ### examples/notebooks/*/output -synth_output +*.ruff_cache diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 4397b2e..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "experiments/systolic_array/axis-systolic-array"] - path = experiments/systolic_array/axis-systolic-array - url = https://github.com/alexredd99/axis-systolic-array.git From ae023703aac84027e100a8bb8c12ec30050aaccf Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Wed, 11 Mar 2026 23:52:28 +0000 Subject: [PATCH 63/64] Ignoring F403 --- python/py_src/arbolta/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/py_src/arbolta/__init__.py b/python/py_src/arbolta/__init__.py index 9fdf61f..5d2cc3e 100644 --- a/python/py_src/arbolta/__init__.py +++ b/python/py_src/arbolta/__init__.py @@ -1,4 +1,4 @@ # Copyright (c) 2026 Alexander Redding # SPDX-License-Identifier: MIT -from .arbolta import * +from .arbolta import * # noqa:F403 From 97231c87afd000f81396e714ffb3b81cd87a08db Mon Sep 17 00:00:00 2001 From: Alexander Redding Date: Thu, 12 Mar 2026 00:15:24 +0000 Subject: [PATCH 64/64] Set environment for PyPI publish --- .github/workflows/release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 14d9ccd..97c598c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -156,7 +156,9 @@ jobs: path: dist release: - name: Release + name: Publish to PyPI + environment: + name: pypi runs-on: ubuntu-latest if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }} needs: [linux, musllinux, windows, macos, sdist]