mirror of
https://github.com/annoyatron255/yosys4gal.git
synced 2024-12-22 10:42:24 +00:00
wip: fitter
This commit is contained in:
parent
9d2befc29b
commit
62180c852f
9
compiler/Cargo.lock
generated
9
compiler/Cargo.lock
generated
|
@ -597,18 +597,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.58"
|
version = "1.0.59"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
|
checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.58"
|
version = "1.0.59"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
|
checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -682,6 +682,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -14,6 +14,7 @@ regex = "1.10.4"
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
serde_json = "1.0.115"
|
serde_json = "1.0.115"
|
||||||
serde_with = { version = "3.7.0", features = ["json"] }
|
serde_with = { version = "3.7.0", features = ["json"] }
|
||||||
|
thiserror = "1.0.59"
|
||||||
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|
108
compiler/src/fitter.rs
Normal file
108
compiler/src/fitter.rs
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
use crate::pcf::PcfFile;
|
||||||
|
use crate::yosys_parser::{GalOLMC, Graph, NamedPort, Net};
|
||||||
|
use galette::blueprint::Blueprint;
|
||||||
|
use galette::chips::Chip;
|
||||||
|
use log::info;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum MappingError {
|
||||||
|
#[error("OLMC missing output: {0}")]
|
||||||
|
OLMCMissingOutput(String),
|
||||||
|
|
||||||
|
#[error("Could not find constraint for port {}", .0.name)]
|
||||||
|
MissingConstraint(NamedPort),
|
||||||
|
|
||||||
|
#[error("Could not find the SOP input")]
|
||||||
|
MissingSOP,
|
||||||
|
|
||||||
|
#[error("Unknown error")]
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to map graph into blueprint
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct GALMapEntry(GalOLMC, usize); // data and the entry in the graph
|
||||||
|
|
||||||
|
pub fn graph_convert(graph: &Graph, pcf: PcfFile, chip: Chip) -> anyhow::Result<Blueprint> {
|
||||||
|
let mut bp = Blueprint::new(chip);
|
||||||
|
|
||||||
|
// phase one: OLMC mapping
|
||||||
|
// start by finding the constraints.
|
||||||
|
//
|
||||||
|
let mut olmcmap: Vec<Option<usize>> = vec![None; chip.num_olmcs()];
|
||||||
|
|
||||||
|
let mut deferrals: Vec<usize> = Vec::new();
|
||||||
|
|
||||||
|
for o in graph.get_olmc_idx() {
|
||||||
|
// find all the nodes named
|
||||||
|
let others: Vec<Net> = graph
|
||||||
|
.get_node_port_conns(o, "Y")
|
||||||
|
.iter()
|
||||||
|
.map(|adj| adj.net.clone())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// if it's got a port we map it now else we defer it.
|
||||||
|
|
||||||
|
let port = graph.find_port(&others[0]);
|
||||||
|
|
||||||
|
match port {
|
||||||
|
Some(port) => {
|
||||||
|
info!("Found a port, performing port lookup");
|
||||||
|
let pin = port
|
||||||
|
.lookup(&pcf)
|
||||||
|
.ok_or(MappingError::MissingConstraint(port.clone()))?;
|
||||||
|
let olmc_row = chip
|
||||||
|
.pin_to_olmc(pin.try_into()?)
|
||||||
|
.ok_or(MappingError::Unknown)?;
|
||||||
|
info!("Found a real pin to map: Mapping node {o} onto row {olmc_row}");
|
||||||
|
olmcmap[olmc_row] = Some(o);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
info!("No port found, deferring placement for {o}");
|
||||||
|
deferrals.push(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// at this point, we should have mapped
|
||||||
|
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.
|
||||||
|
//
|
||||||
|
// Vec<(olmc_index,size)>
|
||||||
|
let unused_rows =
|
||||||
|
olmcmap
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(i, x)| if !x.is_some() { Some(i) } else { None })
|
||||||
|
.map(|i| (i, chip.num_rows_for_olmc(i)));
|
||||||
|
// find the smallest row that fits.
|
||||||
|
|
||||||
|
let olmc = deferrals[0];
|
||||||
|
let mut chosen_row: Option<(usize, usize)> = None;
|
||||||
|
// FIXME: implement.
|
||||||
|
let sopsize = 0;
|
||||||
|
|
||||||
|
for (olmc_idx, size) in unused_rows {
|
||||||
|
|
||||||
|
match chosen_row {
|
||||||
|
None => {
|
||||||
|
if size > sopsize {
|
||||||
|
chosen_row = Some((olmc_idx, size));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(r) => {
|
||||||
|
// we do the comparison (size > SOP Size)
|
||||||
|
if size < r.1 && size > sopsize {
|
||||||
|
chosen_row = Some((olmc_idx, size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Ok(bp)
|
||||||
|
}
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod yosys_parser;
|
pub mod yosys_parser;
|
||||||
pub mod pcf;
|
pub mod pcf;
|
||||||
|
pub mod fitter;
|
||||||
|
|
|
@ -188,7 +188,7 @@ pub enum PortDirection {
|
||||||
/// NamedPort is our internal representation of a port.
|
/// NamedPort is our internal representation of a port.
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||||
pub struct NamedPort {
|
pub struct NamedPort {
|
||||||
name: String,
|
pub name: String,
|
||||||
net: Net,
|
net: Net,
|
||||||
direction: PortDirection,
|
direction: PortDirection,
|
||||||
}
|
}
|
||||||
|
@ -221,16 +221,16 @@ impl NamedPort {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Retrieves the port mapping for this port, given a PCF file.
|
/// Retrieves the port mapping for this port, given a PCF file.
|
||||||
pub fn lookup(&self, pcf: &PcfFile) -> u32 {
|
pub fn lookup(&self, pcf: &PcfFile) -> Option<u32> {
|
||||||
//NOTE: since NamedPort is exactly (1) pin, we always use the pin case.
|
//NOTE: since NamedPort is exactly (1) pin, we always use the pin case.
|
||||||
// When constructing, if we have a port with multiple bits, we split it (see `new_split`)
|
// When constructing, if we have a port with multiple bits, we split it (see `new_split`)
|
||||||
pcf.pin(&self.name).expect("missing constraint")
|
pcf.pin(&self.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct NetAdjPair {
|
pub struct NetAdjPair {
|
||||||
net: Net,
|
pub net: Net,
|
||||||
idx1: usize,
|
idx1: usize,
|
||||||
port1: String,
|
port1: String,
|
||||||
port2: String,
|
port2: String,
|
||||||
|
@ -267,11 +267,20 @@ impl PartialEq for NetAdjPair {
|
||||||
impl Eq for NetAdjPair {}
|
impl Eq for NetAdjPair {}
|
||||||
|
|
||||||
impl NetAdjPair {
|
impl NetAdjPair {
|
||||||
fn uses_net(&self, net: &Net) -> bool {
|
pub fn uses_net(&self, net: &Net) -> bool {
|
||||||
net == &self.net
|
net == &self.net
|
||||||
}
|
}
|
||||||
fn uses_nodeport(&self, idx: usize, port: &str) -> bool {
|
pub fn uses_nodeport(&self, idx: usize, port: &str) -> bool {
|
||||||
self.idx1 == idx && self.port1 == port || self.idx2 == idx && self.port2 == port
|
(self.idx1 == idx && self.port1 == port) || (self.idx2 == idx && self.port2 == port)
|
||||||
|
}
|
||||||
|
pub fn get_other(&self, my_idx: usize) -> (usize, &str) {
|
||||||
|
if my_idx == self.idx1 {
|
||||||
|
(self.idx2, &self.port2)
|
||||||
|
} else if my_idx == self.idx2 {
|
||||||
|
(self.idx1, &self.port1)
|
||||||
|
} else {
|
||||||
|
(usize::MAX, "")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,14 +312,14 @@ impl Node {
|
||||||
// Self::Olmc(go) => go.connections.input.to_vec(),
|
// Self::Olmc(go) => go.connections.input.to_vec(),
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
fn get_ports(&self) -> Iter<'_, String, Vec<Net>> {
|
pub fn get_ports(&self) -> HashMap<String, Vec<Net>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Olmc(ol) => ol.connections.iter(),
|
Self::Olmc(ol) => ol.connections.clone(),
|
||||||
Self::Input(i) => i.connections.iter(),
|
Self::Input(i) => i.connections.clone(),
|
||||||
Self::Sop(s) => s.connections.iter(),
|
Self::Sop(s) => s.connections.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn port_for_net(&self, net: &Net) -> Option<String> {
|
pub fn port_for_net(&self, net: &Net) -> Option<String> {
|
||||||
for (port, nets) in self.get_ports() {
|
for (port, nets) in self.get_ports() {
|
||||||
if nets.contains(net) {
|
if nets.contains(net) {
|
||||||
return Some(port.to_string());
|
return Some(port.to_string());
|
||||||
|
@ -318,8 +327,9 @@ impl Node {
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn get_nets(&self) -> Vec<Net> {
|
pub fn get_nets(&self) -> Vec<Net> {
|
||||||
self.get_ports()
|
self.get_ports()
|
||||||
|
.iter()
|
||||||
.flat_map(|(_, nets)| nets.clone())
|
.flat_map(|(_, nets)| nets.clone())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -387,9 +397,51 @@ impl Graph {
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
/// find the port that uses the current net, if any
|
|
||||||
fn find_port(&self, net: &Net) -> Option<&NamedPort> {
|
/// Retrieve a node from the node index
|
||||||
self.ports.iter().find(|p| p.net == *net)
|
/// TODO: make a newtype for the index.
|
||||||
|
pub fn get_node(&self, idx: usize) -> Option<&Node> {
|
||||||
|
self.nodelist.get(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the connections from the given node/port
|
||||||
|
pub fn get_node_port_conns(&self, nodeidx: usize, port: &str) -> Vec<&NetAdjPair> {
|
||||||
|
self.adjlist
|
||||||
|
.iter()
|
||||||
|
.filter(|adj| adj.uses_nodeport(nodeidx, port))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: get rid of this or refactor somehow?????
|
||||||
|
// VERY BAD
|
||||||
|
pub fn get_olmc_idx(&self) -> Vec<usize> {
|
||||||
|
self.nodelist
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(idx, node)| match node {
|
||||||
|
Node::Olmc(_) => Some(idx),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// find the port that uses the current net, if any.
|
||||||
|
/// Ports are the input/output of a module. They are handled separately.
|
||||||
|
pub fn find_port(&self, net: &Net) -> Option<&NamedPort> {
|
||||||
|
match net {
|
||||||
|
Net::N(_) => self.ports.iter().find(|p| p.net == *net),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_olmc(&self) -> Vec<&Node> {
|
||||||
|
self.nodelist
|
||||||
|
.iter()
|
||||||
|
.filter_map(|node| match node {
|
||||||
|
Node::Olmc(_) => Some(node),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validate that the graph has valid invariants.
|
/// Validate that the graph has valid invariants.
|
||||||
|
@ -397,12 +449,11 @@ impl Graph {
|
||||||
/// by the yosys script is what we expected. Mainly a tool for debugging the Yosys outputs.
|
/// by the yosys script is what we expected. Mainly a tool for debugging the Yosys outputs.
|
||||||
pub fn validate(&self) -> Result<(), &str> {
|
pub fn validate(&self) -> Result<(), &str> {
|
||||||
info!("Checking OLMC blocks");
|
info!("Checking OLMC blocks");
|
||||||
let all_olmc = self.nodelist.iter().filter_map(|node| match node {
|
let olmc = self.nodelist.iter().filter_map(|node| match node {
|
||||||
Node::Olmc(o) => Some(o),
|
Node::Olmc(o) => Some(o),
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
|
let olmc_clock = olmc.filter_map(|o| o.connections.get("C"));
|
||||||
let olmc_clock = all_olmc.filter_map(|o| o.connections.get("C"));
|
|
||||||
let test = olmc_clock.clone().all(|v| v.len() == 1);
|
let test = olmc_clock.clone().all(|v| v.len() == 1);
|
||||||
if !test {
|
if !test {
|
||||||
return Err("OLMC has more than one clock input!");
|
return Err("OLMC has more than one clock input!");
|
||||||
|
@ -415,7 +466,8 @@ impl Graph {
|
||||||
if !test {
|
if !test {
|
||||||
return Err("invalid clock pin");
|
return Err("invalid clock pin");
|
||||||
}
|
}
|
||||||
// for the ones connected to a net, extract the net nubmer and Vec it.
|
// for the ones connected to a net, extract the net number so we can make sure they're all
|
||||||
|
// the same clock.
|
||||||
let olmc_clocked: Vec<u32> = olmc_clock
|
let olmc_clocked: Vec<u32> = olmc_clock
|
||||||
.clone()
|
.clone()
|
||||||
.flatten()
|
.flatten()
|
||||||
|
|
Loading…
Reference in a new issue