mirror of
https://github.com/annoyatron255/yosys4gal.git
synced 2024-12-22 10:42:24 +00:00
bugfixes, working on SOP stuff
This commit is contained in:
parent
5ac484b1a8
commit
a550d84750
30
compiler/Cargo.lock
generated
30
compiler/Cargo.lock
generated
|
@ -267,6 +267,29 @@ version = "1.10.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"env_filter",
|
||||
"humantime",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
|
@ -323,6 +346,12 @@ version = "0.4.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.60"
|
||||
|
@ -676,6 +705,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.5.4",
|
||||
"env_logger",
|
||||
"galette",
|
||||
"log",
|
||||
"regex",
|
||||
|
|
|
@ -8,6 +8,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
anyhow = "1.0.81"
|
||||
clap = { version = "4.5.4", features = ["derive"] }
|
||||
env_logger = "0.11.3"
|
||||
galette = "0.3.0"
|
||||
log = "0.4.21"
|
||||
regex = "1.10.4"
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
use std::str::from_utf8;
|
||||
|
||||
use crate::pcf::PcfFile;
|
||||
use crate::yosys_parser::{GalOLMC, GalSop, Graph, NamedPort, Net, Node, NodeIdx, PortDirection};
|
||||
use crate::yosys_parser::{GalInput, GalSop, Graph, NamedPort, Net, Node, NodeIdx, PortDirection};
|
||||
use galette::blueprint::Blueprint;
|
||||
use galette::chips::Chip;
|
||||
use log::info;
|
||||
use log::{debug, info, warn};
|
||||
use thiserror::Error;
|
||||
|
||||
use galette::gal::{Pin, Term};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum MappingError {
|
||||
#[error("OLMC missing output: {0}")]
|
||||
|
@ -26,18 +30,33 @@ pub enum MappingError {
|
|||
// attempt to map graph into blueprint
|
||||
|
||||
/// Acquire the SOP associated with the OLMC. If it's
|
||||
fn get_sop_for_olmc(graph: &Graph, olmc_idx: NodeIdx) -> Result<GalSop, MappingError> {
|
||||
fn get_sop_for_olmc(graph: &Graph, olmc_idx: &NodeIdx) -> Result<GalSop, MappingError> {
|
||||
let input = graph.get_node_port_conns(olmc_idx, "A");
|
||||
assert_eq!(input.len(), 1, "OLMC input should have one netadjpair");
|
||||
let other_node = input[0].get_other(olmc_idx).ok_or(MappingError::Unknown)?;
|
||||
let sop = graph
|
||||
.get_node(other_node.0)
|
||||
.ok_or(MappingError::MissingSOP)?;
|
||||
if let Node::Sop(s) = sop {
|
||||
Ok(s.clone())
|
||||
} else {
|
||||
Err(MappingError::MissingSOP)
|
||||
}
|
||||
let sops_on_net: Vec<_> = input
|
||||
.iter()
|
||||
.filter_map(|i| {
|
||||
let sop = i.get_other(olmc_idx)?;
|
||||
if sop.1 != "Y" {
|
||||
return None;
|
||||
};
|
||||
let node = graph.get_node(&sop.0)?;
|
||||
match node {
|
||||
Node::Sop(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(sops_on_net.len(), 1, "Should only be one sop driving a net");
|
||||
Ok(sops_on_net[0].clone())
|
||||
// let other_node = input[0].get_other(olmc_idx).ok_or(MappingError::Unknown)?;
|
||||
// let sop = graph
|
||||
// .get_node(&other_node.0)
|
||||
// .ok_or(MappingError::MissingSOP)?;
|
||||
// if let Node::Sop(s) = sop {
|
||||
// Ok(s.clone())
|
||||
// } else {
|
||||
// Err(MappingError::MissingSOP)
|
||||
// }
|
||||
}
|
||||
|
||||
fn map_remaining_olmc(
|
||||
|
@ -48,7 +67,7 @@ fn map_remaining_olmc(
|
|||
// (index, size)
|
||||
let mut chosen_row: Option<(usize, usize)> = None;
|
||||
// FIXME: implement.
|
||||
let sop = get_sop_for_olmc(graph, olmc)?;
|
||||
let sop = get_sop_for_olmc(graph, &olmc)?;
|
||||
let sopsize: usize = sop.parameters.depth as usize;
|
||||
|
||||
for (olmc_idx, size) in unused {
|
||||
|
@ -79,6 +98,88 @@ fn map_remaining_olmc(
|
|||
}
|
||||
}
|
||||
|
||||
fn find_hwpin_for_net(graph: &Graph, pcf: &PcfFile, net: &Net) -> Result<u32, MappingError> {
|
||||
// this does a double lookup. first it finds the Input on the net,
|
||||
// then it finds the port on the input of the GAL_INPUT.
|
||||
// find the input on the net.
|
||||
let inputs: Vec<&GalInput> = graph
|
||||
.find_nodes_on_net(net)
|
||||
.iter()
|
||||
.filter_map(|n| match graph.get_node(n) {
|
||||
Some(Node::Input(i)) => Some(i),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
// now we have an array of inputs, this should be one elemnt.
|
||||
if inputs.len() != 1 {
|
||||
return Err(MappingError::Unknown);
|
||||
}
|
||||
|
||||
let port_nets = inputs[0]
|
||||
.connections
|
||||
.get("A")
|
||||
.ok_or(MappingError::Unknown)?;
|
||||
assert_eq!(port_nets.len(), 1, "should only be one input to GAL_INPUT");
|
||||
let pnet = &port_nets[0];
|
||||
|
||||
if let Some(p) = graph.find_port(&pnet) {
|
||||
info!("Found a port after traversing inputs");
|
||||
// look up the pin.
|
||||
p.lookup(pcf)
|
||||
.ok_or(MappingError::MissingConstraint(p.clone()))
|
||||
} else {
|
||||
Err(MappingError::Unknown)
|
||||
}
|
||||
}
|
||||
/// Takes a gal sop, and turns it into a vec of mapped pins.
|
||||
fn make_term_from_sop(graph: &Graph, sop: GalSop, pcf: &PcfFile) -> Term {
|
||||
let table = sop.parameters.table.as_bytes();
|
||||
|
||||
let n_products = sop.parameters.depth;
|
||||
let product_size = sop.parameters.width;
|
||||
let chunksize = product_size * 2; // 00 for dontcare, 01 for negation, 10 for positive i think
|
||||
|
||||
let input_nets = sop.connections.get("A").unwrap();
|
||||
|
||||
let terms: Vec<Vec<Pin>> = table
|
||||
.chunks(chunksize as usize)
|
||||
.map(|chunk| {
|
||||
// chunk is now a block of terms.
|
||||
let terms: Vec<&str> = chunk.chunks(2).map(|c| from_utf8(c).unwrap()).collect();
|
||||
// create our term
|
||||
let pins: Vec<Pin> = terms
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, p)| {
|
||||
let net_for_pin = input_nets.get(idx).unwrap();
|
||||
// now use the helper to find the true hardware pin
|
||||
let hwpin: usize =
|
||||
find_hwpin_for_net(graph, pcf, net_for_pin).unwrap() as usize;
|
||||
// we now have our hardware pin number!
|
||||
match *p {
|
||||
"01" => Some(Pin {
|
||||
pin: hwpin,
|
||||
neg: true,
|
||||
}),
|
||||
"10" => Some(Pin {
|
||||
pin: hwpin,
|
||||
neg: false,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
pins
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(n_products as usize, terms.len());
|
||||
Term {
|
||||
line_num: 0,
|
||||
pins: terms,
|
||||
}
|
||||
}
|
||||
|
||||
fn valid_inputs(chip: Chip) -> Vec<u32> {
|
||||
match chip {
|
||||
Chip::GAL16V8 => vec![
|
||||
|
@ -94,9 +195,6 @@ fn valid_inputs(chip: Chip) -> Vec<u32> {
|
|||
pub fn graph_convert(graph: &Graph, pcf: PcfFile, chip: Chip) -> anyhow::Result<Blueprint> {
|
||||
let mut bp = Blueprint::new(chip);
|
||||
|
||||
// phase zero: input mapping.
|
||||
let mut pinmap: Vec<Option<String>> = vec![None; chip.num_pins()];
|
||||
|
||||
let valid_inp = valid_inputs(chip);
|
||||
let mut olmcmap: Vec<Option<NodeIdx>> = vec![None; chip.num_olmcs()];
|
||||
|
||||
|
@ -108,13 +206,16 @@ pub fn graph_convert(graph: &Graph, pcf: PcfFile, chip: Chip) -> anyhow::Result<
|
|||
if let Some(olmcrow) = chip.pin_to_olmc(pin as usize) {
|
||||
if port.direction == PortDirection::Input {
|
||||
olmcmap[olmcrow] = Some(NodeIdx(usize::MAX));
|
||||
} // otherwise we do not care!
|
||||
} // otherwise we do not care at this point!
|
||||
}
|
||||
} else {
|
||||
return Err(MappingError::Unknown.into());
|
||||
// we don't have a constraint for this port
|
||||
return Err(MappingError::MissingConstraint(port.clone()).into());
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Graph adj list is {:?}", graph.adjlist);
|
||||
|
||||
// phase one: OLMC mapping
|
||||
// start by finding the constraints.
|
||||
|
||||
|
@ -123,16 +224,21 @@ pub fn graph_convert(graph: &Graph, pcf: PcfFile, chip: Chip) -> anyhow::Result<
|
|||
// For all the OLMCs in the graph, we either map it directly since it's constrained to a pin,
|
||||
// or we defer it to later.
|
||||
for o in graph.get_olmc_idx() {
|
||||
debug!("Processing OLMC {o}");
|
||||
debug!("Value = {:?}", graph.get_node(&o));
|
||||
// find all the
|
||||
let others: Vec<Net> = graph
|
||||
.get_node_port_conns(o, "Y")
|
||||
.iter()
|
||||
.map(|adj| adj.net.clone())
|
||||
.collect();
|
||||
|
||||
let n: &Net;
|
||||
if let Some(Node::Olmc(olmc)) = graph.get_node(&o) {
|
||||
n = &olmc.connections.get("Y").ok_or(MappingError::Unknown)?[0];
|
||||
} else {
|
||||
warn!("Could not find output net! Silently skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
// if it's got a port we map it now else we defer it.
|
||||
|
||||
let port = graph.find_port(&others[0]);
|
||||
let port = graph.find_port(n);
|
||||
|
||||
match port {
|
||||
Some(port) => {
|
||||
|
@ -164,8 +270,8 @@ pub fn graph_convert(graph: &Graph, pcf: PcfFile, chip: Chip) -> anyhow::Result<
|
|||
let num_mapped = olmcmap.iter().filter(|x| x.is_some()).count();
|
||||
info!("Mapped {num_mapped} OLMCS, {} deferred", deferrals.len());
|
||||
|
||||
// to map remainders, we need to find the smallest SOP.
|
||||
//
|
||||
// to map the deferred ones, we need to find the smallest SOP that is still large enough for
|
||||
// it.
|
||||
// Vec<(olmc_index,size)>
|
||||
let mut unused_rows = olmcmap
|
||||
.iter()
|
||||
|
@ -175,8 +281,10 @@ pub fn graph_convert(graph: &Graph, pcf: PcfFile, chip: Chip) -> anyhow::Result<
|
|||
.collect();
|
||||
|
||||
// find the smallest row that fits.
|
||||
info!("Starting deferred mapping process");
|
||||
for olmc in deferrals {
|
||||
let row = map_remaining_olmc(graph, olmc, &unused_rows)?;
|
||||
debug!("Found a mapping for {olmc} in row {} size {}", row.0, row.1);
|
||||
// insert into the mapping
|
||||
olmcmap[row.0] = Some(olmc);
|
||||
// remove this row from the available rows
|
||||
|
@ -185,7 +293,32 @@ pub fn graph_convert(graph: &Graph, pcf: PcfFile, chip: Chip) -> anyhow::Result<
|
|||
}
|
||||
|
||||
// at this point, we have mapped every OLMC.
|
||||
// now use the blueprint to set the settings.
|
||||
// find the SOPs and for each sop, find
|
||||
info!("Deferred mapping complete, starting SOP mapping");
|
||||
for (idx, olmc) in olmcmap.iter().enumerate() {
|
||||
match olmc {
|
||||
Some(node) => {
|
||||
debug!("Mapping node {node} at row {idx}");
|
||||
let sop = get_sop_for_olmc(graph, node)?;
|
||||
debug!("Got SOP {:?} attached to node", sop);
|
||||
let term = make_term_from_sop(graph, sop, &pcf);
|
||||
debug!("Got term {:?}", term);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(bp)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use anyhow::Result;
|
||||
|
||||
#[test]
|
||||
fn test_sop_to_term() -> Result<()> {
|
||||
let pct = "set_io pinName 1";
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
pub mod pcf;
|
||||
pub mod yosys_parser;
|
||||
mod fitter;
|
||||
|
||||
use clap::{Parser, Subcommand, Args};
|
||||
use crate::pcf::parse_pcf;
|
||||
use crate::yosys_parser::{YosysDoc, Graph};
|
||||
use crate::fitter::graph_convert;
|
||||
use anyhow::{bail, Result};
|
||||
use serde_json::from_slice;
|
||||
use std::path::PathBuf;
|
||||
use galette::chips::Chip;
|
||||
use std::fs;
|
||||
use env_logger;
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Cli {
|
||||
|
@ -18,6 +23,7 @@ struct Cli {
|
|||
enum Commands {
|
||||
/// Validate a yosys netlist JSON file.
|
||||
Validate(ValidateArgs),
|
||||
Synth(SynthArgs),
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
@ -27,6 +33,15 @@ struct ValidateArgs {
|
|||
}
|
||||
|
||||
|
||||
#[derive(Args)]
|
||||
struct SynthArgs {
|
||||
#[arg(required = true, value_hint = clap::ValueHint::DirPath)]
|
||||
netlist: PathBuf,
|
||||
#[arg(required = true, value_hint = clap::ValueHint::DirPath)]
|
||||
constraints: PathBuf,
|
||||
}
|
||||
|
||||
|
||||
fn validate(v: ValidateArgs) -> Result<()>{
|
||||
let f = fs::read(v.file)?;
|
||||
|
||||
|
@ -44,9 +59,38 @@ fn validate(v: ValidateArgs) -> Result<()>{
|
|||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn synth(s: SynthArgs) -> Result<()> {
|
||||
let f = fs::read(s.netlist)?;
|
||||
|
||||
let data: YosysDoc = from_slice(f.as_slice())?;
|
||||
|
||||
let g = Graph::from(data);
|
||||
let res = g.validate().map_err(|x| x.to_string());
|
||||
if let Err(e) = res {
|
||||
bail!(e);
|
||||
}
|
||||
println!("Validation Complete!");
|
||||
println!("Stats:");
|
||||
println!("Nodes: {}", g.nodelist.len());
|
||||
println!("Edges: {}", g.adjlist.len());
|
||||
|
||||
// load the pcf
|
||||
let pcf_file = &fs::read(s.constraints)?;
|
||||
let pcf_string = std::str::from_utf8(pcf_file)?;
|
||||
let pcf = parse_pcf(pcf_string);
|
||||
|
||||
let res = graph_convert(&g, pcf, Chip::GAL16V8);
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<()>{
|
||||
let args = Cli::parse();
|
||||
env_logger::init();
|
||||
match args.command {
|
||||
Commands::Validate(v) => validate(v),
|
||||
Commands::Synth(s) => synth(s),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use log::info;
|
|||
use serde::{de::Error, Deserialize, Deserializer, Serialize};
|
||||
use serde_with::{serde_as, BoolFromInt};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::str;
|
||||
|
||||
#[derive(Debug, Serialize, Clone, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
|
@ -230,6 +231,12 @@ impl NamedPort {
|
|||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct NodeIdx(pub usize);
|
||||
|
||||
impl fmt::Display for NodeIdx {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "(Nodeindex: {})", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NetAdjPair {
|
||||
|
@ -273,13 +280,13 @@ impl NetAdjPair {
|
|||
pub fn uses_net(&self, net: &Net) -> bool {
|
||||
net == &self.net
|
||||
}
|
||||
pub fn uses_nodeport(&self, idx: NodeIdx, port: &str) -> bool {
|
||||
(self.idx1 == idx && self.port1 == port) || (self.idx2 == idx && self.port2 == port)
|
||||
pub fn uses_nodeport(&self, idx: &NodeIdx, port: &str) -> bool {
|
||||
(&self.idx1 == idx && self.port1 == port) || (&self.idx2 == idx && self.port2 == port)
|
||||
}
|
||||
pub fn get_other(&self, my_idx: NodeIdx) -> Option<(NodeIdx, &str)> {
|
||||
if my_idx == self.idx1 {
|
||||
pub fn get_other(&self, my_idx: &NodeIdx) -> Option<(NodeIdx, &str)> {
|
||||
if my_idx == &self.idx1 {
|
||||
Some((self.idx2, &self.port2))
|
||||
} else if my_idx == self.idx2 {
|
||||
} else if my_idx == &self.idx2 {
|
||||
Some((self.idx1, &self.port1))
|
||||
} else {
|
||||
None
|
||||
|
@ -396,12 +403,12 @@ impl Graph {
|
|||
|
||||
/// Retrieve a node from the node index
|
||||
/// TODO: make a newtype for the index.
|
||||
pub fn get_node(&self, idx: NodeIdx) -> Option<&Node> {
|
||||
pub fn get_node(&self, idx: &NodeIdx) -> Option<&Node> {
|
||||
self.nodelist.get(idx.0)
|
||||
}
|
||||
|
||||
// find the connections from the given node/port
|
||||
pub fn get_node_port_conns(&self, nodeidx: NodeIdx, port: &str) -> Vec<&NetAdjPair> {
|
||||
// find the connections from the given node/port ONLY WORKS FOR NON_PORT DEVICES.
|
||||
pub fn get_node_port_conns(&self, nodeidx: &NodeIdx, port: &str) -> Vec<&NetAdjPair> {
|
||||
self.adjlist
|
||||
.iter()
|
||||
.filter(|adj| adj.uses_nodeport(nodeidx, port))
|
||||
|
@ -527,12 +534,61 @@ type Parameters = HashMap<String, String>;
|
|||
|
||||
pub trait Cell {
|
||||
fn name(&self) -> &str;
|
||||
fn connections(&self) -> &Connections;
|
||||
fn params(&self) -> &Parameters;
|
||||
fn ctype(&self) -> CellType;
|
||||
|
||||
fn get_connection(&self, conn: &str) -> Option<&Vec<Net>>;
|
||||
fn get_param(&self, param: &str) -> Option<&String>;
|
||||
fn nets(&self) -> Vec<Net>;
|
||||
fn uses_net(&self, net: Net) -> bool;
|
||||
fn net_on_port(&self, net: Net) -> Option<String>;
|
||||
}
|
||||
|
||||
pub struct GalCell {
|
||||
name: Option<String>,
|
||||
ctype: CellType,
|
||||
connections: Connections,
|
||||
params: Parameters,
|
||||
}
|
||||
|
||||
impl GalCell {
|
||||
pub fn name(&self) -> String {
|
||||
self.name.clone().unwrap_or("unnamed".to_string())
|
||||
}
|
||||
|
||||
pub fn get_connection(&self, conn: &str) -> Option<&Vec<Net>> {
|
||||
self.connections.get(conn)
|
||||
}
|
||||
|
||||
/// Access the parameter from the cell. Note that this does not
|
||||
/// have.
|
||||
pub fn get_param(&self, param: &str) -> Option<&String> {
|
||||
self.params.get(param)
|
||||
}
|
||||
|
||||
/// Get all of the nets that this cell uses
|
||||
pub fn nets(&self) -> Vec<&Net> {
|
||||
self.connections.iter().flat_map(|x| x.1).collect()
|
||||
}
|
||||
|
||||
/// Returns true if the net is used at all by the cell
|
||||
pub fn uses_net(&self, net: &Net) -> bool {
|
||||
self.nets().contains(&net)
|
||||
}
|
||||
|
||||
/// Returns the port that uses the given net, if any.
|
||||
pub fn get_port_for_net(&self, net: &Net) -> Option<&str> {
|
||||
for (port, nets) in self.connections.iter() {
|
||||
if nets.contains(net) {
|
||||
return Some(port)
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the underlying Cell type, used to differentiate available
|
||||
/// connections
|
||||
pub fn ctype(&self) -> &CellType {
|
||||
&self.ctype
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue