wip: fitter

This commit is contained in:
saji 2024-05-02 12:38:57 -05:00
parent 9d2befc29b
commit 62180c852f
5 changed files with 187 additions and 24 deletions

9
compiler/Cargo.lock generated
View file

@ -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]]

View file

@ -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
View 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)
}

View file

@ -1,2 +1,3 @@
pub mod yosys_parser;
pub mod pcf;
pub mod fitter;

View file

@ -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()