Skip to content

Commit

Permalink
added more net/graph work
Browse files Browse the repository at this point in the history
  • Loading branch information
5aji committed Apr 1, 2024
1 parent 9107af8 commit ea1ee09
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 69 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
201 changes: 133 additions & 68 deletions src/yosys_parser.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -163,57 +165,9 @@ fn table_seg_to_pin(num: &Net, seg: &[u8]) -> Option<Pin> {
}
}

trait Traverser {
fn get_inputs(&self) -> Vec<Net>;
fn get_outputs(&self) -> Vec<Net>;
}

// 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<String, AnyCell>;

// 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<HashMap<Net, u32>> {
let mut map = HashMap::new();
for (name, m) in &self.modules {
Expand Down Expand Up @@ -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<Net, u32> 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<u32>),


/// NamedPort is our internal representation of a port.
#[derive(Debug)]
pub struct NamedPort {
name: String,
bits: Vec<Net>,
upto: bool,
}

fn port_lookup(name: &str, cell: &AnyCell, pcf: &PcfFile) -> Option<PortValue> {
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<Net> {
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<Net> {
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<Node>,
adjlist: Vec<NetAdjPair>,
}

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<Net> -> 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<YosysDoc> 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.
*/

//
12 changes: 11 additions & 1 deletion tests/yosys_parser_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,18 @@ use serde_json::from_slice;
fn test_load() -> Result<(), Box<dyn Error>> {
let f = fs::read("testcases/json/test.json")?;

let _data: YosysDoc = from_slice(f.as_slice())?;

Ok(())
}

#[test]
fn test_graph() -> Result<(), Box<dyn Error>> {
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(())
}

0 comments on commit ea1ee09

Please sign in to comment.