From f1410444018640de57c913c146d00c59a1e8ca41 Mon Sep 17 00:00:00 2001 From: Wanda Date: Mon, 16 Dec 2024 22:10:25 +0000 Subject: [PATCH] versal: deal with SLL. --- prjcombine_rawdump/src/bin/dump_sll.rs | 78 +++++ prjcombine_ultrascale/src/grid.rs | 7 + prjcombine_versal/src/expand.rs | 364 ++++++++++++++++++++-- prjcombine_versal/src/expanded.rs | 19 +- prjcombine_versal/src/grid.rs | 43 ++- prjcombine_versal_naming/src/lib.rs | 32 +- prjcombine_versal_rd2db/src/grid.rs | 50 ++- prjcombine_versal_rd2db/src/int.rs | 98 ++++-- prjcombine_versal_rdverify/src/lib.rs | 54 +++- prjcombine_virtex4/src/grid.rs | 9 + prjcombine_xilinx_geom/src/bin/xgprint.rs | 3 +- prjcombine_xilinx_geom/src/lib.rs | 11 + 12 files changed, 656 insertions(+), 112 deletions(-) create mode 100644 prjcombine_rawdump/src/bin/dump_sll.rs diff --git a/prjcombine_rawdump/src/bin/dump_sll.rs b/prjcombine_rawdump/src/bin/dump_sll.rs new file mode 100644 index 00000000..1f96008b --- /dev/null +++ b/prjcombine_rawdump/src/bin/dump_sll.rs @@ -0,0 +1,78 @@ +use clap::Parser; +use prjcombine_rawdump::{Coord, Part, TkWire}; +use std::collections::{hash_map, HashMap}; +use std::error::Error; +use std::path::PathBuf; + +#[derive(Debug, Parser)] +#[command(name = "dump_noc", about = "Dump Versal NOC structure from rawdump.")] +struct Args { + file: PathBuf, +} + +fn main() -> Result<(), Box> { + let args = Args::parse(); + let rd = Part::from_file(args.file)?; + println!( + "PART {} {} {:?} {}×{}", + rd.part, rd.family, rd.source, rd.width, rd.height + ); + let mut n2w: HashMap<_, Vec<_>> = HashMap::new(); + let mut pairs = vec![]; + for tkn in ["SLL", "SLL2"] { + for &crd in rd.tiles_by_kind_name(tkn) { + let tile = &rd.tiles[&crd]; + let tk = &rd.tile_kinds[tile.kind]; + for wn in ["UBUMP0", "UBUMP1", "UBUMP2", "UBUMP3", "UBUMP4", "UBUMP5"] { + if rd.wires.get(wn).is_none() { + println!("OOPS {tkn} {wn}"); + } + let wni = rd.wires.get(wn).unwrap(); + if tk.wires.get(&wni).is_none() { + println!("OOPS {tkn} {wn}"); + } + let w = tk.wires.get(&wni).unwrap().1; + if let TkWire::Connected(cwi) = *w { + if let Some(ni) = tile.conn_wires.get(cwi) { + match n2w.entry(ni) { + hash_map::Entry::Occupied(mut entry) => { + let list = entry.get_mut(); + assert_eq!(list.len(), 1); + pairs.push((Some(list[0]), Some((crd, wn)))); + pairs.push((Some((crd, wn)), Some(list[0]))); + list.push((crd, wn)); + } + hash_map::Entry::Vacant(entry) => { + entry.insert(vec![(crd, wn)]); + } + } + } + } + } + } + } + for list in n2w.values() { + if list.len() == 1 { + pairs.push((Some(list[0]), None)); + } + } + pairs.sort_by_key(|&(pi, po)| { + ( + pi.map(|(Coord { x, y }, w)| (y, x, w)), + po.map(|(Coord { x, y }, w)| (y, x, w)), + ) + }); + for (pi, po) in pairs { + match pi { + None => { + print!("[NONE] <= ") + } + Some((ci, wi)) => print!("{ti:17} {wi:6} <=> ", ti = rd.tiles[&ci].name), + } + match po { + None => println!("[NONE]"), + Some((co, wo)) => println!("{to:17} {wo}", to = rd.tiles[&co].name), + } + } + Ok(()) +} diff --git a/prjcombine_ultrascale/src/grid.rs b/prjcombine_ultrascale/src/grid.rs index fcba512f..ef180269 100644 --- a/prjcombine_ultrascale/src/grid.rs +++ b/prjcombine_ultrascale/src/grid.rs @@ -545,3 +545,10 @@ impl std::fmt::Display for Grid { Ok(()) } } + +impl std::fmt::Display for Interposer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "\tPRIMARY: D{}", self.primary)?; + Ok(()) + } +} diff --git a/prjcombine_versal/src/expand.rs b/prjcombine_versal/src/expand.rs index a37ff976..418536b7 100644 --- a/prjcombine_versal/src/expand.rs +++ b/prjcombine_versal/src/expand.rs @@ -1,10 +1,10 @@ use prjcombine_int::db::IntDb; use prjcombine_int::grid::{ColId, DieId, ExpandedGrid, RowId}; -use std::collections::BTreeSet; -use unnamed_entity::{EntityId, EntityVec}; +use std::collections::{BTreeSet, HashMap}; +use unnamed_entity::{EntityBitVec, EntityId, EntityIds, EntityVec}; -use crate::expanded::ExpandedDevice; -use crate::grid::{ColumnKind, DisabledPart, Grid, HardRowKind, Interposer, RightKind}; +use crate::expanded::{ExpandedDevice, SllConns, UbumpId}; +use crate::grid::{CleKind, ColumnKind, DisabledPart, Grid, HardRowKind, Interposer, RightKind}; struct DieInfo { col_cfrm: ColId, @@ -21,7 +21,8 @@ struct Expander<'a> { impl Expander<'_> { fn fill_die(&mut self) { for (_, &grid) in &self.grids { - self.egrid.add_die(grid.columns.len(), grid.regs * 48); + self.egrid + .add_die(grid.columns.len(), grid.regs * Grid::ROWS_PER_REG); self.die.push(DieInfo { col_cfrm: grid .columns @@ -57,13 +58,13 @@ impl Expander<'_> { continue; } die.fill_tile((col, row), "INT"); - if row.to_idx() % 48 == 0 && grid.is_reg_top(reg) { + if row.to_idx() % Grid::ROWS_PER_REG == 0 && grid.is_reg_top(reg) { die.add_xnode((col, row), "RCLK", &[(col, row)]); } } } - if di.ps_height != grid.regs * 48 { + if di.ps_height != grid.regs * Grid::ROWS_PER_REG { let row_t = RowId::from_idx(di.ps_height); for dx in 0..ps_width { let col = ColId::from_idx(dx); @@ -113,7 +114,25 @@ impl Expander<'_> { } else { false }; - die.add_xnode((col, row), "CLE_BC", &[(col, row), (col + 1, row)]); + let kind = match cd.r { + ColumnKind::Cle(CleKind::Plain) => "CLE_BC", + ColumnKind::Cle(CleKind::Sll) => { + if has_bli_r { + "CLE_BC" + } else { + "CLE_BC.SLL" + } + } + ColumnKind::Cle(CleKind::Sll2) => { + if has_bli_r { + "CLE_BC" + } else { + "CLE_BC.SLL2" + } + } + _ => unreachable!(), + }; + die.add_xnode((col, row), kind, &[(col, row), (col + 1, row)]); if has_bli_r { die.fill_term_pair( (col, row), @@ -125,7 +144,7 @@ impl Expander<'_> { die.fill_term_pair((col, row), (col + 1, row), "CLE.E", "CLE.W"); } let reg = grid.row_to_reg(row); - if row.to_idx() % 48 == 0 && grid.is_reg_top(reg) { + if row.to_idx() % Grid::ROWS_PER_REG == 0 && grid.is_reg_top(reg) { if reg.to_idx() % 2 == 1 { die.add_xnode( (col + 1, row), @@ -230,7 +249,7 @@ impl Expander<'_> { ); } let reg = grid.row_to_reg(row); - if row.to_idx() % 48 == 0 && grid.is_reg_top(reg) { + if row.to_idx() % Grid::ROWS_PER_REG == 0 && grid.is_reg_top(reg) { if !matches!(cd.l, ColumnKind::Cle(_) | ColumnKind::None) { if reg.to_idx() % 2 == 1 { die.add_xnode( @@ -302,7 +321,7 @@ impl Expander<'_> { die.add_xnode( (col, row), if grid.is_vr { "CLE_R.VR" } else { "CLE_R" }, - &[(col, row)], + &[(col, row), (col + 1, row)], ); die.add_xnode( (col + 1, row), @@ -409,7 +428,7 @@ impl Expander<'_> { let reg = grid.row_to_reg(row); die.add_xnode( (col, row), - if grid.is_reg_top(reg) && row.to_idx() % 48 == 44 { + if grid.is_reg_top(reg) && row.to_idx() % Grid::ROWS_PER_REG == 44 { "URAM_DELAY" } else { "URAM" @@ -456,7 +475,11 @@ impl Expander<'_> { }; let row = grid.row_reg_bot(reg); let mut crd = vec![]; - let height = if is_high { 96 } else { 48 }; + let height = if is_high { + Grid::ROWS_PER_REG * 2 + } else { + Grid::ROWS_PER_REG + }; for i in 0..height { crd.push((hc.col - 1, row + i)); } @@ -488,10 +511,10 @@ impl Expander<'_> { } let row = grid.row_reg_bot(reg); let mut crd = vec![]; - for i in 0..48 { + for i in 0..Grid::ROWS_PER_REG { crd.push((col - 1, row + i)); } - for i in 0..48 { + for i in 0..Grid::ROWS_PER_REG { crd.push((col, row + i)); } match cd.l { @@ -522,15 +545,12 @@ impl Expander<'_> { for col in die.cols() { for row in die.rows() { - let crow = RowId::from_idx( - if grid.regs % 2 == 1 && row.to_idx() >= (grid.regs - 1) * 48 { - row.to_idx() / 48 * 48 - } else if row.to_idx() % 96 < 48 { - row.to_idx() / 96 * 96 + 47 - } else { - row.to_idx() / 96 * 96 + 48 - }, - ); + let reg = grid.row_to_reg(row); + let crow = if grid.is_reg_top(reg) { + grid.row_reg_hclk(reg) + } else { + grid.row_reg_hclk(reg) - 1 + }; die[(col, row)].clkroot = (col, crow); } } @@ -568,11 +588,307 @@ pub fn expand_grid<'a>( let col_cfrm = expander.die.map_values(|die| die.col_cfrm); + let mut sll = HashMap::new(); + match interposer.kind { + crate::grid::InterposerKind::Single => (), + crate::grid::InterposerKind::Column => { + fill_sll_column(interposer, grids, &mut sll); + } + crate::grid::InterposerKind::MirrorSquare => { + fill_sll_mirror_square(interposer, grids, &mut sll); + } + } + ExpandedDevice { grids: expander.grids, interposer, egrid: expander.egrid, disabled: expander.disabled, col_cfrm, + sll, + } +} + +fn fill_sll_column( + interposer: &Interposer, + grids: &EntityVec, + sll: &mut HashMap<(DieId, ColId, RowId), SllConns>, +) { + let mut curse_queue = vec![]; + for (die, cols) in &interposer.sll_columns { + let grid = grids[die]; + let all_rows = grid.rows(); + let has_link_bot = die != grids.first_id().unwrap(); + let has_link_top = die != grids.last_id().unwrap(); + for (cidx, &col) in cols.iter().enumerate() { + let start = if grid.columns[col].has_bli_bot_l { + assert!(!has_link_bot); + 4 + } else { + 0 + }; + let end = if grid.columns[col].has_bli_top_l { + assert!(!has_link_top); + all_rows.len() - 4 + } else { + all_rows.len() + }; + let rows: EntityIds = EntityIds::new_range(start, end); + for row in rows { + let mut conns = SllConns { + conns: (0..6).map(|_| None).collect(), + cursed: EntityBitVec::repeat(false, 6), + }; + if has_link_bot && row.to_idx() < start + 75 { + let odie = die - 1; + let orow = RowId::from_idx(grids[odie].rows().len() - 75 + row.to_idx()); + let ocol = interposer.sll_columns[odie][cidx]; + for (bump, obump) in [(0, 0), (1, 1), (3, 2), (4, 4), (5, 5)] { + let bump = UbumpId::from_idx(bump); + let obump = UbumpId::from_idx(obump); + conns.conns[bump] = Some((odie, ocol, orow, obump)); + } + let bump = UbumpId::from_idx(2); + let obump = UbumpId::from_idx(3); + let orow = row + 75; + conns.conns[bump] = Some((die, col, orow, obump)); + } else if has_link_top && row.to_idx() >= end - 75 { + let odie = die + 1; + let orow = RowId::from_idx(row.to_idx() - (end - 75)); + let ocol = interposer.sll_columns[odie][cidx]; + for (bump, obump) in [(0, 0), (1, 1), (2, 3), (4, 4), (5, 5)] { + let bump = UbumpId::from_idx(bump); + let obump = UbumpId::from_idx(obump); + conns.conns[bump] = Some((odie, ocol, orow, obump)); + } + let bump = UbumpId::from_idx(3); + let obump = UbumpId::from_idx(2); + let orow = row - 75; + conns.conns[bump] = Some((die, col, orow, obump)); + } else { + if row.to_idx() < start + 75 { + let bump = UbumpId::from_idx(3); + let triad = (row.to_idx() - start) / 3; + let sub = (row.to_idx() - start) % 3; + let orow = RowId::from_idx(start + (24 - triad) * 3 + sub); + if orow != row { + conns.conns[bump] = Some((die, col, orow, bump)); + } + } else { + let bump = UbumpId::from_idx(3); + let obump = UbumpId::from_idx(2); + let orow = row - 75; + conns.conns[bump] = Some((die, col, orow, obump)); + } + if row.to_idx() >= end - 75 { + let bump = UbumpId::from_idx(2); + let triad = (row.to_idx() - (end - 75)) / 3; + let sub = (row.to_idx() - (end - 75)) % 3; + let orow = RowId::from_idx(end - 75 + (24 - triad) * 3 + sub); + if orow != row { + conns.conns[bump] = Some((die, col, orow, bump)); + } + } else { + let bump = UbumpId::from_idx(2); + let obump = UbumpId::from_idx(3); + let orow = row + 75; + conns.conns[bump] = Some((die, col, orow, obump)); + } + if cidx < 10 { + for bump in [1, 5] { + let bump = UbumpId::from_idx(bump); + let ocol = cols[9 - cidx]; + conns.conns[bump] = Some((die, ocol, row, bump)); + } + } else { + for (bump, obump) in [(1, 0), (5, 4)] { + let bump = UbumpId::from_idx(bump); + let obump = UbumpId::from_idx(obump); + let ocol = cols[cidx - 10]; + conns.conns[bump] = Some((die, ocol, row, obump)); + } + } + if cidx >= cols.len() - 10 { + for bump in [0, 4] { + let bump = UbumpId::from_idx(bump); + let ocol = cols[cols.len() - 10 + (9 - (cidx - (cols.len() - 10)))]; + conns.conns[bump] = Some((die, ocol, row, bump)); + } + } else { + for (bump, obump) in [(0, 1), (4, 5)] { + let bump = UbumpId::from_idx(bump); + let obump = UbumpId::from_idx(obump); + let ocol = cols[cidx + 10]; + conns.conns[bump] = Some((die, ocol, row, obump)); + } + } + } + sll.insert((die, col, row), conns); + } + curse_queue.push((die, col, RowId::from_idx(start))); + if has_link_top { + curse_queue.push((die, col, RowId::from_idx(end - 75))); + } + if has_link_bot { + curse_queue.push((die, col, RowId::from_idx(start + 75))); + } + } + } + for (die, col, row) in curse_queue { + let conns = sll.get_mut(&(die, col, row)).unwrap(); + for mut val in conns.cursed.values_mut() { + *val = true; + } + for (odie, ocol, orow, ubump) in conns.conns.clone().into_values().flatten() { + let conns = sll.get_mut(&(odie, ocol, orow)).unwrap(); + conns.cursed.set(ubump, true); + } + } +} + +fn fill_sll_mirror_square( + interposer: &Interposer, + grids: &EntityVec, + sll: &mut HashMap<(DieId, ColId, RowId), SllConns>, +) { + let mut curse_queue = vec![]; + for (die, cols) in &interposer.sll_columns { + let grid = grids[die]; + let all_rows = grid.rows(); + let col_cfrm = grid + .columns + .iter() + .find(|(_, c)| c.l == ColumnKind::Cfrm) + .unwrap() + .0; + let ps_height = grid.get_ps_height(); + let cidx_ps = cols.binary_search(&col_cfrm).unwrap_err(); + for (cidx, &col) in cols.iter().enumerate() { + let start = if col < col_cfrm { + ps_height + } else if grid.columns[col].has_bli_bot_l { + 4 + } else { + 0 + }; + let end = all_rows.len(); + let rows: EntityIds = EntityIds::new_range(start, end); + for row in rows.clone() { + let cidx_l = if row.to_idx() < ps_height { cidx_ps } else { 0 }; + let mut conns = SllConns { + conns: (0..6).map(|_| None).collect(), + cursed: EntityBitVec::repeat(false, 6), + }; + if row == RowId::from_idx(end - 63) { + // do nothing + } else if cidx < cols.len() - 10 { + if row < RowId::from_idx(end - 63) { + if row.to_idx() < start + 75 { + let bump = UbumpId::from_idx(3); + let triad = (row.to_idx() - start) / 3; + let sub = (row.to_idx() - start) % 3; + let orow = RowId::from_idx(start + (24 - triad) * 3 + sub); + if orow != row { + conns.conns[bump] = Some((die, col, orow, bump)); + } + } else { + let bump = UbumpId::from_idx(3); + let obump = UbumpId::from_idx(2); + let orow = row - 75; + conns.conns[bump] = Some((die, col, orow, obump)); + } + if row.to_idx() >= end - 63 - 75 { + // nothing + } else { + let bump = UbumpId::from_idx(2); + let obump = UbumpId::from_idx(3); + let orow = row + 75; + conns.conns[bump] = Some((die, col, orow, obump)); + } + if cidx < cidx_l + 10 { + for bump in [1, 5] { + let bump = UbumpId::from_idx(bump); + let ocol = cols[cidx_l + 9 - (cidx - cidx_l)]; + conns.conns[bump] = Some((die, ocol, row, bump)); + } + } else { + for (bump, obump) in [(1, 0), (5, 4)] { + let bump = UbumpId::from_idx(bump); + let obump = UbumpId::from_idx(obump); + let ocol = cols[cidx - 10]; + conns.conns[bump] = Some((die, ocol, row, obump)); + } + } + for (bump, obump) in [(0, 1), (4, 5)] { + let bump = UbumpId::from_idx(bump); + let obump = UbumpId::from_idx(obump); + let ocol = cols[cidx + 10]; + conns.conns[bump] = Some((die, ocol, row, obump)); + } + } else { + for bump in 0..6 { + let bump = UbumpId::from_idx(bump); + let odie = DieId::from_idx(die.to_idx() ^ 1); + let orow = RowId::from_idx(end - 62 + (end - 1 - row.to_idx())); + conns.conns[bump] = Some((odie, col, orow, bump)); + } + } + } else { + if !(46..50).contains(&(row.to_idx() % (Grid::ROWS_PER_REG * 2))) { + for bump in [0, 2, 4] { + let bump = UbumpId::from_idx(bump); + let odie = DieId::from_idx(die.to_idx() ^ 3); + let ocol = cols[cols.len() - 10 + (cols.len() - 1 - cidx)]; + conns.conns[bump] = Some((odie, ocol, row, bump)); + } + } + if row.to_idx() % (Grid::ROWS_PER_REG * 2) == 50 { + for bump in [0, 2, 4] { + let bump = UbumpId::from_idx(bump); + curse_queue.push((die, col, row, bump)); + } + } + if row < RowId::from_idx(end - 63) { + for (bump, obump) in [(1, 0), (5, 4)] { + let bump = UbumpId::from_idx(bump); + let obump = UbumpId::from_idx(obump); + let ocol = cols[cidx - 10]; + conns.conns[bump] = Some((die, ocol, row, obump)); + } + if row.to_idx() % (Grid::ROWS_PER_REG * 2) == 50 { + for bump in [1, 5] { + let bump = UbumpId::from_idx(bump); + curse_queue.push((die, col, row, bump)); + } + } + } + } + sll.insert((die, col, row), conns); + } + for bump in 0..6 { + let bump = UbumpId::from_idx(bump); + if cidx < cols.len() - 10 || bump.to_idx() != 3 { + curse_queue.push((die, col, RowId::from_idx(start), bump)); + } + if cidx < cols.len() - 10 || matches!(bump.to_idx(), 0 | 2 | 4) { + curse_queue.push((die, col, RowId::from_idx(end - 63), bump)); + } + } + if cidx < 10 { + let row = RowId::from_idx(ps_height); + for bump in [1, 5] { + let bump = UbumpId::from_idx(bump); + curse_queue.push((die, col, row, bump)); + } + } + } + } + for (die, col, row, bump) in curse_queue { + let conns = sll.get_mut(&(die, col, row)).unwrap(); + conns.cursed.set(bump, true); + if let Some((odie, ocol, orow, obump)) = conns.conns[bump] { + let conns = sll.get_mut(&(odie, ocol, orow)).unwrap(); + conns.cursed.set(obump, true); + } } } diff --git a/prjcombine_versal/src/expanded.rs b/prjcombine_versal/src/expanded.rs index 7d74fb87..be24fbc7 100644 --- a/prjcombine_versal/src/expanded.rs +++ b/prjcombine_versal/src/expanded.rs @@ -1,9 +1,21 @@ -use prjcombine_int::grid::{ColId, DieId, ExpandedGrid}; -use std::collections::BTreeSet; -use unnamed_entity::EntityVec; +use prjcombine_int::grid::{ColId, DieId, ExpandedGrid, RowId}; +use std::collections::{BTreeSet, HashMap}; +use unnamed_entity::{entity_id, EntityBitVec, EntityVec}; + +entity_id! { + pub id UbumpId u8; +} + +pub type UbumpLoc = (DieId, ColId, RowId, UbumpId); use crate::grid::{DisabledPart, Grid, Interposer}; +#[derive(Debug)] +pub struct SllConns { + pub conns: EntityVec>, + pub cursed: EntityBitVec, +} + #[derive(Debug)] pub struct ExpandedDevice<'a> { pub grids: EntityVec, @@ -11,4 +23,5 @@ pub struct ExpandedDevice<'a> { pub interposer: &'a Interposer, pub disabled: BTreeSet, pub col_cfrm: EntityVec, + pub sll: HashMap<(DieId, ColId, RowId), SllConns>, } diff --git a/prjcombine_versal/src/grid.rs b/prjcombine_versal/src/grid.rs index 15e54d3e..a2780e27 100644 --- a/prjcombine_versal/src/grid.rs +++ b/prjcombine_versal/src/grid.rs @@ -27,6 +27,7 @@ pub struct Grid { #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum InterposerKind { + Single, Column, MirrorSquare, } @@ -34,6 +35,7 @@ pub enum InterposerKind { #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct Interposer { pub kind: InterposerKind, + pub sll_columns: EntityVec>, } #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] @@ -190,12 +192,19 @@ pub enum DisabledPart { } impl Grid { + pub const ROWS_PER_REG: usize = 48; + pub fn row_to_reg(&self, row: RowId) -> RegId { - RegId::from_idx(row.to_idx() / 48) + RegId::from_idx(row.to_idx() / Self::ROWS_PER_REG) } pub fn row_reg_bot(&self, reg: RegId) -> RowId { - RowId::from_idx(reg.to_idx() * 48) + RowId::from_idx(reg.to_idx() * Self::ROWS_PER_REG) + } + + pub fn row_reg_hclk(&self, reg: RegId) -> RowId { + let reg = if self.is_reg_top(reg) { reg } else { reg + 1 }; + self.row_reg_bot(reg) } pub fn is_reg_top(&self, reg: RegId) -> bool { @@ -206,17 +215,21 @@ impl Grid { EntityIds::new(self.regs) } + pub fn rows(&self) -> EntityIds { + EntityIds::new(self.regs * Self::ROWS_PER_REG) + } + pub fn get_col_hard(&self, col: ColId) -> Option<&HardColumn> { self.cols_hard.iter().find(|x| x.col == col) } pub fn get_ps_height(&self) -> usize { match (self.ps, self.cpm) { - (PsKind::Ps9, CpmKind::None) => 48 * 2, - (PsKind::Ps9, CpmKind::Cpm4) => 48 * 3, - (PsKind::Ps9, CpmKind::Cpm5) => 48 * 6, - (PsKind::PsX, CpmKind::Cpm5N) => 48 * 9, - (PsKind::PsXc, CpmKind::None) => 48 * 6, + (PsKind::Ps9, CpmKind::None) => Self::ROWS_PER_REG * 2, + (PsKind::Ps9, CpmKind::Cpm4) => Self::ROWS_PER_REG * 3, + (PsKind::Ps9, CpmKind::Cpm5) => Self::ROWS_PER_REG * 6, + (PsKind::PsX, CpmKind::Cpm5N) => Self::ROWS_PER_REG * 9, + (PsKind::PsXc, CpmKind::None) => Self::ROWS_PER_REG * 6, _ => unreachable!(), } } @@ -350,3 +363,19 @@ impl std::fmt::Display for Grid { Ok(()) } } + +impl std::fmt::Display for Interposer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "\tKIND: {:?}", self.kind)?; + for (die, die_sll_columns) in &self.sll_columns { + if !die_sll_columns.is_empty() { + write!(f, "\tSLL COLUMNS D{die}:")?; + for &col in die_sll_columns { + write!(f, " X{col}")?; + } + writeln!(f)?; + } + } + Ok(()) + } +} diff --git a/prjcombine_versal_naming/src/lib.rs b/prjcombine_versal_naming/src/lib.rs index 9c9ffa09..735c355d 100644 --- a/prjcombine_versal_naming/src/lib.rs +++ b/prjcombine_versal_naming/src/lib.rs @@ -525,31 +525,11 @@ pub fn name_device<'a>( )], ); } - "CLE_BC" => { - let cd = &grid.columns[col]; - let has_bli_r = if row.to_idx() < 4 { - cd.has_bli_bot_r - } else if row.to_idx() >= die.rows().len() - 4 { - cd.has_bli_top_r - } else { - false - }; - let tk = match cd.r { - ColumnKind::Cle(CleKind::Plain) => "CLE_BC_CORE", - ColumnKind::Cle(CleKind::Sll) => { - if has_bli_r { - "CLE_BC_CORE" - } else { - "SLL" - } - } - ColumnKind::Cle(CleKind::Sll2) => { - if has_bli_r { - "CLE_BC_CORE" - } else { - "SLL2" - } - } + "CLE_BC" | "CLE_BC.SLL" | "CLE_BC.SLL2" => { + let tk = match &kind[..] { + "CLE_BC" => "CLE_BC_CORE", + "CLE_BC.SLL" => "SLL", + "CLE_BC.SLL2" => "SLL2", _ => unreachable!(), }; let bump_cur = col >= edev.col_cfrm[die.die] @@ -560,7 +540,7 @@ pub fn name_device<'a>( && grid.cols_vbrk.contains(&col); ngrid.name_node( nloc, - "CLE_BC", + kind, [int_grid.name( &if bump_prev && !bump_cur { format!("{tk}_1") diff --git a/prjcombine_versal_rd2db/src/grid.rs b/prjcombine_versal_rd2db/src/grid.rs index 23fae1b8..e28296cc 100644 --- a/prjcombine_versal_rd2db/src/grid.rs +++ b/prjcombine_versal_rd2db/src/grid.rs @@ -1,5 +1,5 @@ use prjcombine_int::grid::{ColId, DieId}; -use prjcombine_rawdump::{Coord, Part, Tile, TkSiteSlot}; +use prjcombine_rawdump::{Coord, NodeOrWire, Part, Tile, TkSiteSlot}; use prjcombine_versal::grid::{ BotKind, BramKind, CleKind, Column, ColumnKind, CpmKind, DisabledPart, Grid, GtRowKind, HardColumn, HardRowKind, Interposer, InterposerKind, PsKind, RegId, RightKind, TopKind, @@ -424,6 +424,7 @@ fn get_grid( int: &IntGrid<'_>, disabled: &mut BTreeSet, is_vnoc2_scan_offset: &mut bool, + sll_columns: &mut EntityVec>, ) -> (Grid, DieNaming) { let mut naming = DieNaming { hdio: BTreeMap::new(), @@ -479,6 +480,26 @@ fn get_grid( right, }; get_vnoc_naming(int, &mut naming, is_vnoc2_scan_offset); + let mut die_sll_columns = BTreeSet::new(); + for (x, y) in int.find_tiles(&["SLL", "SLL2"]) { + let crd = Coord { + x: x as u16, + y: y as u16, + }; + let Some(nw) = int.rd.lookup_wire(crd, "UBUMP2") else { + continue; + }; + let NodeOrWire::Node(node) = nw else { + continue; + }; + let node = &int.rd.nodes[node]; + let templ = &int.rd.templates[node.template]; + if templ.len() > 1 { + let col = int.lookup_column_inter(x); + die_sll_columns.insert(col); + } + } + sll_columns.push(Vec::from_iter(die_sll_columns)); (grid, naming) } @@ -493,7 +514,7 @@ pub fn make_grids( let mut disabled = BTreeSet::new(); let crd = rd.tiles_by_kind_name("INT").first().unwrap(); let tile = &rd.tiles[crd]; - let ikind = if tile.name.contains("_S") { + let mut ikind = if tile.name.contains("_S") { InterposerKind::MirrorSquare } else { InterposerKind::Column @@ -501,18 +522,28 @@ pub fn make_grids( let mut grids = EntityVec::new(); let mut namings = EntityVec::new(); let mut is_vnoc2_scan_offset = false; + let mut sll_columns = EntityVec::new(); if ikind == InterposerKind::Column { let mut rows_slr_split: BTreeSet<_> = find_rows(rd, &["NOC_TNOC_BRIDGE_BOT_CORE"]) .into_iter() .map(|r| r as u16) .collect(); + if rows_slr_split.is_empty() { + ikind = InterposerKind::Single; + } rows_slr_split.insert(0); rows_slr_split.insert(rd.height); let rows_slr_split: Vec<_> = rows_slr_split.iter().collect(); for (dieid, w) in rows_slr_split.windows(2).enumerate() { let int = extract_int_slr_column(rd, &["INT"], &[], *w[0], *w[1]); let die = DieId::from_idx(dieid); - let (grid, naming) = get_grid(die, &int, &mut disabled, &mut is_vnoc2_scan_offset); + let (grid, naming) = get_grid( + die, + &int, + &mut disabled, + &mut is_vnoc2_scan_offset, + &mut sll_columns, + ); grids.push(grid); namings.push(naming); } @@ -567,7 +598,13 @@ pub fn make_grids( .enumerate() { let die = DieId::from_idx(dieid); - let (grid, naming) = get_grid(die, &int, &mut disabled, &mut is_vnoc2_scan_offset); + let (grid, naming) = get_grid( + die, + &int, + &mut disabled, + &mut is_vnoc2_scan_offset, + &mut sll_columns, + ); grids.push(grid); namings.push(naming); } @@ -717,7 +754,10 @@ pub fn make_grids( col_gt_r[RegId::from_idx(13)] = GtRowKind::Gtm; } let is_dsp_v2 = rd.wires.contains("DSP_DSP58_4_CLK"); - let interposer = Interposer { kind: ikind }; + let interposer = Interposer { + kind: ikind, + sll_columns, + }; ( grids, interposer, diff --git a/prjcombine_versal_rd2db/src/int.rs b/prjcombine_versal_rd2db/src/int.rs index d87f5516..ef63d0a0 100644 --- a/prjcombine_versal_rd2db/src/int.rs +++ b/prjcombine_versal_rd2db/src/int.rs @@ -156,6 +156,7 @@ pub fn make_int_db(rd: &Part, dev_naming: &DeviceNaming) -> (IntDb, NamingDb) { let w = builder.branch(w_b, Dir::S, format!("{name}.{bwd}.{ew_b}.{i}.0.S"), &[""]); builder.extra_name_tile_sub("CLE_BC_CORE", "BNODE_TAP0", 1, w); + builder.extra_name_tile_sub("CLE_BC_CORE_MX", "BNODE_TAP0", 1, w); builder.extra_name_tile_sub("SLL", "BNODE_TAP0", 1, w); builder.extra_name_tile_sub("SLL2", "BNODE_TAP0", 1, w); } @@ -374,6 +375,12 @@ pub fn make_int_db(rd: &Part, dev_naming: &DeviceNaming) -> (IntDb, NamingDb) { } } + for i in 0..6 { + let w = builder.mux_out(format!("IMUX.LAG{i}"), &[""]); + builder.extra_name_tile_sub("SLL", format!("LAG_CASCOUT_TXI{i}"), 1, w); + builder.extra_name_tile_sub("SLL2", format!("LAG_CASCOUT_TXI{i}"), 1, w); + } + let mut bnodes = Vec::new(); for dir in [Dir::E, Dir::W] { for i in 0..64 { @@ -407,6 +414,7 @@ pub fn make_int_db(rd: &Part, dev_naming: &DeviceNaming) -> (IntDb, NamingDb) { let cw = builder.wire(format!("CLE.OUT.{ew}.{i}"), WireKind::Branch(ew), &[""]); builder.test_mux_pass(cw); builder.extra_name_tile_sub("CLE_BC_CORE", format!("LOGIC_OUTS_{we}{i}"), sub, cw); + builder.extra_name_tile_sub("CLE_BC_CORE_MX", format!("LOGIC_OUTS_{we}{i}"), sub, cw); builder.extra_name_tile_sub("SLL", format!("LOGIC_OUTS_{we}{i}"), sub, cw); builder.extra_name_tile_sub("SLL2", format!("LOGIC_OUTS_{we}{i}"), sub, cw); if ew == Dir::E { @@ -417,6 +425,14 @@ pub fn make_int_db(rd: &Part, dev_naming: &DeviceNaming) -> (IntDb, NamingDb) { } } + for i in 0..6 { + let w = builder.logic_out(format!("OUT.LAG{i}"), &[""]); + builder.extra_name_tile_sub("CLE_BC_CORE", format!("VCC_WIRE{i}"), 1, w); + builder.extra_name_tile_sub("CLE_BC_CORE_MX", format!("VCC_WIRE{i}"), 1, w); + builder.extra_name_tile_sub("SLL", format!("LAG_OUT{i}"), 1, w); + builder.extra_name_tile_sub("SLL2", format!("LAG_OUT{i}"), 1, w); + } + for ew in [Dir::E, Dir::W] { let w = builder.test_out(format!("TEST.{ew}.TMR_DFT"), &[""]); @@ -643,7 +659,12 @@ pub fn make_int_db(rd: &Part, dev_naming: &DeviceNaming) -> (IntDb, NamingDb) { builder.node_type("INT", "INT", "INT"); - for tkn in ["CLE_BC_CORE", "SLL", "SLL2"] { + for (kind, tkn) in [ + ("CLE_BC", "CLE_BC_CORE"), + ("CLE_BC", "CLE_BC_CORE_MX"), + ("CLE_BC.SLL", "SLL"), + ("CLE_BC.SLL2", "SLL2"), + ] { for &xy in rd.tiles_by_kind_name(tkn) { let td = &rd.tiles[&builder.delta(xy, 0, -1)]; if rd.tile_kinds.key(td.kind) != tkn { @@ -655,7 +676,18 @@ pub fn make_int_db(rd: &Part, dev_naming: &DeviceNaming) -> (IntDb, NamingDb) { } let xy_l = builder.walk_to_int(xy, Dir::W, false).unwrap(); let xy_r = builder.walk_to_int(xy, Dir::E, false).unwrap(); - builder.extract_xnode("CLE_BC", xy, &[], &[xy_l, xy_r], "CLE_BC", &[], &[]); + let mut bels = vec![]; + if kind != "CLE_BC" { + let mut bel = builder.bel_virtual("LAGUNA"); + for i in 0..6 { + bel = bel + .extra_int_in(format!("IN{i}"), &[format!("LAG_CASCOUT_TXI{i}")]) + .extra_int_out(format!("OUT{i}"), &[format!("LAG_OUT{i}")]) + .extra_wire(format!("UBUMP{i}"), &[format!("UBUMP{i}")]); + } + bels.push(bel); + } + builder.extract_xnode(kind, xy, &[], &[xy_l, xy_r], kind, &bels, &[]); let tile = &rd.tiles[&xy]; let tk = &rd.tile_kinds[tile.kind]; let naming = builder.ndb.get_node_naming("CLE_BC"); @@ -840,37 +872,37 @@ pub fn make_int_db(rd: &Part, dev_naming: &DeviceNaming) -> (IntDb, NamingDb) { } else { builder.walk_to_int(xy, Dir::E, false).unwrap() }; - builder.extract_xnode_bels( - kind, - xy, - &[], - &[int_xy], - kind, - &[ - builder - .bel_xy(key0, "SLICE", 0, 0) - .pin_name_only("LAG_W1", 1) - .pin_name_only("LAG_W2", 1) - .pin_name_only("LAG_E1", 1) - .pin_name_only("LAG_E2", 1) - .pin_name_only("LAG_S", 1) - .pin_name_only("LAG_N", 1) - .pin_name_only("CIN", 1) - .pin_name_only("COUT", 1), - builder - .bel_xy(key1, "SLICE", 1, 0) - .pin_name_only("LAG_W1", 1) - .pin_name_only("LAG_W2", 1) - .pin_name_only("LAG_E1", 1) - .pin_name_only("LAG_E2", 1) - .pin_name_only("LAG_S", 1) - .pin_name_only("LAG_N", 1) - .pin_name_only("SRL_IN_B", 1) - .pin_name_only("SRL_OUT_B", 1) - .pin_name_only("CIN", 1) - .pin_name_only("COUT", 1), - ], - ); + let bel_slicel = builder + .bel_xy(key0, "SLICE", 0, 0) + .pin_name_only("CIN", 1) + .pin_name_only("COUT", 1); + let bel_slicem = builder + .bel_xy(key1, "SLICE", 1, 0) + .pin_name_only("SRL_IN_B", 1) + .pin_name_only("SRL_OUT_B", 1) + .pin_name_only("CIN", 1) + .pin_name_only("COUT", 1); + let cle_bc_xy = builder.delta(xy, if kind.starts_with("CLE_R") { 1 } else { -1 }, 0); + let cle_bc_kind = rd.tiles[&cle_bc_xy].kind; + let cle_bc_naming = match &rd.tile_kinds.key(cle_bc_kind)[..] { + "CLE_BC_CORE" | "CLE_BC_CORE_MX" => "CLE_BC", + "SLL" => "CLE_BC.SLL", + "SLL2" => "CLE_BC.SLL2", + _ => unreachable!(), + }; + let cle_bc_naming = builder.ndb.get_node_naming(cle_bc_naming); + let mut xn = builder + .xnode(kind, kind, xy) + .num_tiles(if kind.starts_with("CLE_R") { 2 } else { 1 }) + .bel(bel_slicel) + .bel(bel_slicem) + .ref_int(int_xy, 0); + if kind.starts_with("CLE_R") { + xn = xn.ref_xlat(cle_bc_xy, &[None, Some(1)], cle_bc_naming); + } else { + xn = xn.ref_xlat(cle_bc_xy, &[None, Some(0)], cle_bc_naming); + } + xn.extract(); } } diff --git a/prjcombine_versal_rdverify/src/lib.rs b/prjcombine_versal_rdverify/src/lib.rs index 0f017d25..17aa6184 100644 --- a/prjcombine_versal_rdverify/src/lib.rs +++ b/prjcombine_versal_rdverify/src/lib.rs @@ -1,6 +1,6 @@ use prjcombine_rawdump::Part; use prjcombine_rdverify::{verify, BelContext, SitePinDir, Verifier}; -use prjcombine_versal::grid::DisabledPart; +use prjcombine_versal::{expanded::UbumpId, grid::DisabledPart}; use prjcombine_versal_naming::ExpandedNamedDevice; use unnamed_entity::EntityId; @@ -10,16 +10,7 @@ fn verify_slice(vrf: &mut Verifier, bel: &BelContext<'_>) { } else { "SLICEL" }; - let mut pins = vec![ - ("CIN", SitePinDir::In), - ("COUT", SitePinDir::Out), - ("LAG_E1", SitePinDir::In), - ("LAG_E2", SitePinDir::In), - ("LAG_W1", SitePinDir::In), - ("LAG_W2", SitePinDir::In), - ("LAG_S", SitePinDir::In), - ("LAG_N", SitePinDir::In), - ]; + let mut pins = vec![("CIN", SitePinDir::In), ("COUT", SitePinDir::Out)]; if kind == "SLICEM" { pins.extend([("SRL_IN_B", SitePinDir::In), ("SRL_OUT_B", SitePinDir::Out)]); } @@ -50,7 +41,45 @@ fn verify_slice(vrf: &mut Verifier, bel: &BelContext<'_>) { vrf.claim_node(&[bel.fwire_far("SRL_IN_B")]); } } - // XXX LAG_* +} + +fn verify_laguna(endev: &ExpandedNamedDevice, vrf: &mut Verifier, bel: &BelContext<'_>) { + let edev = endev.edev; + for i in 0..6 { + vrf.claim_pip( + bel.crd(), + bel.wire(&format!("OUT{i}")), + bel.wire(&format!("UBUMP{i}")), + ); + vrf.claim_pip( + bel.crd(), + bel.wire(&format!("UBUMP{i}")), + bel.wire(&format!("IN{i}")), + ); + let bump = UbumpId::from_idx(i); + if let Some(conns) = edev.sll.get(&(bel.die, bel.col + 1, bel.row)) { + if !conns.cursed[bump] { + if let Some((odie, ocol, orow, obump)) = conns.conns[bump] { + let obel = vrf.find_bel(odie, (ocol - 1, orow), "LAGUNA").unwrap(); + if (bel.die, bel.col + 1, bel.row, bump) < (odie, ocol, orow, obump) { + vrf.claim_node(&[ + bel.fwire(&format!("UBUMP{i}")), + obel.fwire(&format!("UBUMP{obump}")), + ]); + } else { + vrf.verify_node(&[ + bel.fwire(&format!("UBUMP{i}")), + obel.fwire(&format!("UBUMP{obump}")), + ]); + } + } else { + vrf.claim_node(&[bel.fwire(&format!("UBUMP{i}"))]); + } + } + } else { + vrf.claim_node(&[bel.fwire(&format!("UBUMP{i}"))]); + } + } } fn verify_dsp(vrf: &mut Verifier, bel: &BelContext<'_>) { @@ -891,6 +920,7 @@ fn verify_vcc(vrf: &mut Verifier, bel: &BelContext<'_>) { fn verify_bel(endev: &ExpandedNamedDevice, vrf: &mut Verifier, bel: &BelContext<'_>) { match bel.key { + "LAGUNA" => verify_laguna(endev, vrf, bel), "DSP0" | "DSP1" => verify_dsp(vrf, bel), "DSP_CPLX" => verify_dsp_cplx(vrf, bel), "BRAM_L_F" | "BRAM_R_F" => verify_bram_f(vrf, bel), diff --git a/prjcombine_virtex4/src/grid.rs b/prjcombine_virtex4/src/grid.rs index c40751f2..22ce9d80 100644 --- a/prjcombine_virtex4/src/grid.rs +++ b/prjcombine_virtex4/src/grid.rs @@ -408,3 +408,12 @@ impl std::fmt::Display for Grid { Ok(()) } } + +impl std::fmt::Display for Interposer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "\tPRIMARY: D{}", self.primary)?; + writeln!(f, "\tGTZ BOT: {}", self.gtz_bot)?; + writeln!(f, "\tGTZ TOP: {}", self.gtz_top)?; + Ok(()) + } +} diff --git a/prjcombine_xilinx_geom/src/bin/xgprint.rs b/prjcombine_xilinx_geom/src/bin/xgprint.rs index 081e439b..5546950f 100644 --- a/prjcombine_xilinx_geom/src/bin/xgprint.rs +++ b/prjcombine_xilinx_geom/src/bin/xgprint.rs @@ -65,8 +65,7 @@ fn main() -> Result<(), Box> { } println!(); if args.grids { - // XXX pretty - println!("{ip:#?}"); + print!("{ip}"); } } } diff --git a/prjcombine_xilinx_geom/src/lib.rs b/prjcombine_xilinx_geom/src/lib.rs index 2ffcb37e..269f3950 100644 --- a/prjcombine_xilinx_geom/src/lib.rs +++ b/prjcombine_xilinx_geom/src/lib.rs @@ -75,6 +75,17 @@ pub enum Interposer { Versal(prjcombine_versal::grid::Interposer), } +impl std::fmt::Display for Interposer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Interposer::None => write!(f, "\t[NONE]"), + Interposer::Virtex4(ip) => write!(f, "{ip}"), + Interposer::Ultrascale(ip) => write!(f, "{ip}"), + Interposer::Versal(ip) => write!(f, "{ip}"), + } + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Device { pub name: String,