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]]
|
||||
name = "thiserror"
|
||||
version = "1.0.58"
|
||||
version = "1.0.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
|
||||
checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.58"
|
||||
version = "1.0.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
|
||||
checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -682,6 +682,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -14,6 +14,7 @@ regex = "1.10.4"
|
|||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
serde_json = "1.0.115"
|
||||
serde_with = { version = "3.7.0", features = ["json"] }
|
||||
thiserror = "1.0.59"
|
||||
|
||||
|
||||
[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 pcf;
|
||||
pub mod fitter;
|
||||
|
|
|
@ -188,7 +188,7 @@ pub enum PortDirection {
|
|||
/// NamedPort is our internal representation of a port.
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||
pub struct NamedPort {
|
||||
name: String,
|
||||
pub name: String,
|
||||
net: Net,
|
||||
direction: PortDirection,
|
||||
}
|
||||
|
@ -221,16 +221,16 @@ impl NamedPort {
|
|||
}
|
||||
}
|
||||
/// 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.
|
||||
// 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)]
|
||||
pub struct NetAdjPair {
|
||||
net: Net,
|
||||
pub net: Net,
|
||||
idx1: usize,
|
||||
port1: String,
|
||||
port2: String,
|
||||
|
@ -267,11 +267,20 @@ impl PartialEq for NetAdjPair {
|
|||
impl Eq for NetAdjPair {}
|
||||
|
||||
impl NetAdjPair {
|
||||
fn uses_net(&self, net: &Net) -> bool {
|
||||
pub fn uses_net(&self, net: &Net) -> bool {
|
||||
net == &self.net
|
||||
}
|
||||
fn uses_nodeport(&self, idx: usize, port: &str) -> bool {
|
||||
self.idx1 == idx && self.port1 == port || self.idx2 == idx && self.port2 == port
|
||||
pub fn uses_nodeport(&self, idx: usize, port: &str) -> bool {
|
||||
(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(),
|
||||
// }
|
||||
// }
|
||||
fn get_ports(&self) -> Iter<'_, String, Vec<Net>> {
|
||||
pub fn get_ports(&self) -> HashMap<String, Vec<Net>> {
|
||||
match self {
|
||||
Self::Olmc(ol) => ol.connections.iter(),
|
||||
Self::Input(i) => i.connections.iter(),
|
||||
Self::Sop(s) => s.connections.iter(),
|
||||
Self::Olmc(ol) => ol.connections.clone(),
|
||||
Self::Input(i) => i.connections.clone(),
|
||||
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() {
|
||||
if nets.contains(net) {
|
||||
return Some(port.to_string());
|
||||
|
@ -318,8 +327,9 @@ impl Node {
|
|||
}
|
||||
None
|
||||
}
|
||||
fn get_nets(&self) -> Vec<Net> {
|
||||
pub fn get_nets(&self) -> Vec<Net> {
|
||||
self.get_ports()
|
||||
.iter()
|
||||
.flat_map(|(_, nets)| nets.clone())
|
||||
.collect()
|
||||
}
|
||||
|
@ -387,9 +397,51 @@ impl Graph {
|
|||
}
|
||||
res
|
||||
}
|
||||
/// find the port that uses the current net, if any
|
||||
fn find_port(&self, net: &Net) -> Option<&NamedPort> {
|
||||
self.ports.iter().find(|p| p.net == *net)
|
||||
|
||||
/// Retrieve a node from the node index
|
||||
/// 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.
|
||||
|
@ -397,12 +449,11 @@ impl Graph {
|
|||
/// by the yosys script is what we expected. Mainly a tool for debugging the Yosys outputs.
|
||||
pub fn validate(&self) -> Result<(), &str> {
|
||||
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),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let olmc_clock = all_olmc.filter_map(|o| o.connections.get("C"));
|
||||
let olmc_clock = olmc.filter_map(|o| o.connections.get("C"));
|
||||
let test = olmc_clock.clone().all(|v| v.len() == 1);
|
||||
if !test {
|
||||
return Err("OLMC has more than one clock input!");
|
||||
|
@ -415,7 +466,8 @@ impl Graph {
|
|||
if !test {
|
||||
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
|
||||
.clone()
|
||||
.flatten()
|
||||
|
|
Loading…
Reference in a new issue