From ea1ee09c4c3434b00cee8ec16ee1de7e29acf2f3 Mon Sep 17 00:00:00 2001 From: saji Date: Sun, 31 Mar 2024 21:17:32 -0500 Subject: [PATCH] added more net/graph work --- Cargo.lock | 1 + Cargo.toml | 1 + src/yosys_parser.rs | 201 ++++++++++++++++++++++++------------- tests/yosys_parser_test.rs | 12 ++- 4 files changed, 146 insertions(+), 69 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c50f6e..79142c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -192,6 +192,7 @@ dependencies = [ "anyhow", "clap", "itertools", + "log", "regex", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 202ca3a..9128d5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ serde_json = "1.0.114" serde = { version = "1.0.197", features = ["derive"] } serde_with = "3.7.0" regex = "1.10.3" +log = "0.4.21" [lib] name = "galette" diff --git a/src/yosys_parser.rs b/src/yosys_parser.rs index 61bb663..52a139e 100644 --- a/src/yosys_parser.rs +++ b/src/yosys_parser.rs @@ -1,8 +1,10 @@ use crate::gal::Pin; use crate::pcf::PcfFile; +use itertools::Itertools; use serde::{de::Error, Deserialize, Deserializer, Serialize}; use serde_with::{serde_as, BoolFromInt}; use std::collections::HashMap; +use log::info; use std::str; // Custom deserializer for the strange string of binary @@ -163,57 +165,9 @@ fn table_seg_to_pin(num: &Net, seg: &[u8]) -> Option { } } -trait Traverser { - fn get_inputs(&self) -> Vec; - fn get_outputs(&self) -> Vec; -} - -// TODO: figure out if this is needed? -#[derive(Clone)] -pub enum AnyCell { - Dff(DffCell), - Sop(SoPCell), - Not(NotCell), - Input(ModPort), - Output(ModPort), -} - -// we will create a blocklist - it contains inputs, outputs, and cells. -// we look it up by id. But we can also match based on type. -type Netlist = HashMap; - // we know that the yosys module uses unique names. well at least it should. impl YosysDoc { - pub fn to_netlist(&self) -> Netlist { - // create our netlist - let mut nl: Netlist = HashMap::new(); - // now we iterate across the ports and then add each port to our netlist. - for (name, m) in &self.modules { - println!("Processing module {}", name); - - for (name, port) in &m.ports { - println!("Found port {}", name); - match port { - Port::Input(p) => { - nl.insert(name.clone(), AnyCell::Input(p.clone())); - } - Port::Output(p) => { - nl.insert(name.clone(), AnyCell::Output(p.clone())); - } - } - } - for (name, cell) in &m.cells { - println!("Found Cell {}", name); - match cell { - YosysCell::DffCell(d) => nl.insert(name.clone(), AnyCell::Dff(d.clone())), - YosysCell::NotCell(n) => nl.insert(name.clone(), AnyCell::Not(n.clone())), - YosysCell::SoPCell(s) => nl.insert(name.clone(), AnyCell::Sop(s.clone())), - }; - } - } - nl - } pub fn construct_netmap(&self, pcf: &PcfFile) -> Option> { let mut map = HashMap::new(); for (name, m) in &self.modules { @@ -244,32 +198,143 @@ impl YosysDoc { * w * take yosys document -> look at top-level module ports * use constraints pcf file to map. This creates a HashMap for mapping nets to pins. - * only do this for *ports*, not cells. + * only do this for *ports*, not cells. now we have a net->pin map, where we know that there's + * hits. */ -enum PortValue { - Single(u32), - Register(Vec), + + +/// NamedPort is our internal representation of a port. +#[derive(Debug)] +pub struct NamedPort { + name: String, + bits: Vec, + upto: bool, } -fn port_lookup(name: &str, cell: &AnyCell, pcf: &PcfFile) -> Option { - match cell { - AnyCell::Input(p) => { - if p.bits.len() == 1 { - pcf.pin(name).map(PortValue::Single) - } else { - // check upto - if p.upto { - for (idx, net) in p.bits.iter().enumerate() { - } - None - } else { - None - } +impl NamedPort { + fn new(name: &str, p: ModPort) -> Self { + NamedPort { + name: name.to_owned(), + bits: p.bits.clone(), + upto: p.upto, + } + } +} + +type NetAdjPair = (usize, usize, Net); + + +/// A Node is an entry in our graph. A node has inputs and outputs. +#[derive(Debug)] +pub enum Node { + Input(NamedPort), + Output(NamedPort), + Sop(SoPCell), + Not(NotCell), + Dff(DffCell), +} + +impl Node { + /* These functions are a little odd. We invert the input/output bits. CHIP inputs, are viewed + * as outputs internally, likewise we view the outputs of the chip as inputs (to be driven + * internally. So get_outputs returns values on input_cells, since those are driven already. + * Likewise we reverse this for get_inputs, since the chip's outputs are our inputs. + */ + fn get_outputs(&self) -> Vec { + match self { + Self::Input(i) => i.bits.clone(), + Self::Output(_) => Vec::new(), + Self::Sop(s) => s.connections.output.to_vec(), + Self::Not(n) => n.connections.y.to_vec(), + Self::Dff(d) => d.connections.output.to_vec(), + } + } + fn get_inputs(&self) -> Vec { + match self { + Self::Input(_) => Vec::new(), + Self::Output(o) => o.bits.clone(), + Self::Sop(s) => s.connections.inputs.to_vec(), + Self::Not(n) => n.connections.a.to_vec(), + Self::Dff(d) => d.connections.input.to_vec(), + } + } +} + +#[derive(Default, Debug)] +pub struct Graph { + nodelist: Vec, + adjlist: Vec, +} + +impl Graph { + pub fn generate_adjacency(&mut self) { + self.adjlist.clear(); + for (idx, node) in self.nodelist.iter().enumerate() { + for net in node.get_outputs() { // for each output attached to the node. + // find all node idndexes that have our net as an input. + let edges = self + .nodelist + .iter() + .enumerate() + .filter(|&(_, x)| x.get_inputs().contains(&net)) // only get nodes that have + // us as their input. + .map(|(i, _)| (idx, i, net.clone())); // convert into edge pair with our main. + + self.adjlist.extend(edges); } - }, + } + } + pub fn net_lookup(&self, net: &Net) -> (Vec<&Node>, &Node) { + self.adjlist.iter().filter(|_x| true); + todo!("hi"); + } + fn validate(&self) -> Result<(), String> { + + let nets: Vec<&Net> = self.adjlist.iter().map(|(_,_,n)| n).unique().collect_vec(); + // back-travel + // find output ports, find their drivers + // output port -> Vec -> Vec<(Net,Pin)> + // for each node that connects to output port, get the output NET of that node. + // there should only be one. Then damage that node with a pin tag. - _ => None + Ok(()) } } +impl From for Graph { + fn from(value: YosysDoc) -> Self { + let mut g = Graph::default(); + for (mod_name, module) in value.modules { + info!("Processing module {}", mod_name); + for (port_name, port) in module.ports { + info!("Processing port {}", port_name); + let newport = match port { + Port::Input(m) => Node::Input(NamedPort::new(&port_name, m)), + Port::Output(m) => Node::Output(NamedPort::new(&port_name, m)), + }; + g.nodelist.push(newport); + } + for (cell_name, cell) in module.cells { + info!("Processing cell {}", cell_name); + let newcell = match cell { + YosysCell::DffCell(d) => Node::Dff(d), + YosysCell::SoPCell(s) => Node::Sop(s), + YosysCell::NotCell(n) => Node::Not(n) + }; + g.nodelist.push(newcell); + } + } + g.generate_adjacency(); + g + } +} + + +/* + * This graph is too general as it stands. we cannot map individual input pins + * like we can with our output pins. Also, i have no way of finding a specific + * node in the graph. + */ + +// diff --git a/tests/yosys_parser_test.rs b/tests/yosys_parser_test.rs index 749a651..7d424b0 100644 --- a/tests/yosys_parser_test.rs +++ b/tests/yosys_parser_test.rs @@ -7,8 +7,18 @@ use serde_json::from_slice; fn test_load() -> Result<(), Box> { let f = fs::read("testcases/json/test.json")?; + let _data: YosysDoc = from_slice(f.as_slice())?; + + Ok(()) +} + +#[test] +fn test_graph() -> Result<(), Box> { + let f = fs::read("testcases/json/test.json")?; + let data: YosysDoc = from_slice(f.as_slice())?; - println!("{:?}", data); + let g = Graph::from(data); + println!("{:?}", g); Ok(()) }