diff --git a/prjcombine_spartan6/src/bscan.rs b/prjcombine_spartan6/src/bscan.rs new file mode 100644 index 0000000..4ac78dd --- /dev/null +++ b/prjcombine_spartan6/src/bscan.rs @@ -0,0 +1,90 @@ +use std::collections::BTreeMap; + +use prjcombine_int::grid::{EdgeIoCoord, TileIobId}; +use prjcombine_types::bscan::{BScanBuilder, BScanPin}; +use unnamed_entity::EntityId; + +use crate::{ + bond::CfgPin, + grid::{ColumnIoKind, Grid}, +}; + +#[derive(Debug)] +pub struct BScan { + pub bits: usize, + pub io: BTreeMap, + pub cfg: BTreeMap, +} + +impl Grid { + pub fn get_bscan(&self) -> BScan { + let mut io = BTreeMap::new(); + let mut cfg = BTreeMap::new(); + let mut builder = BScanBuilder::new(); + // TIO + for (col, &cd) in self.columns.iter().rev() { + if cd.tio == ColumnIoKind::None { + continue; + } + for (iob, unused) in [ + // inner + (0, cd.tio == ColumnIoKind::Outer), + (1, cd.tio == ColumnIoKind::Outer), + // outer + (2, cd.tio == ColumnIoKind::Inner), + (3, cd.tio == ColumnIoKind::Inner), + ] { + if !unused { + let crd = EdgeIoCoord::T(col, TileIobId::from_idx(iob)); + io.insert(crd, builder.get_toi()); + } + } + } + // LIO + for (row, &rd) in self.rows.iter().rev() { + if rd.lio { + for iob in [0, 1] { + let crd = EdgeIoCoord::L(row, TileIobId::from_idx(iob)); + io.insert(crd, builder.get_toi()); + } + } + } + cfg.insert(CfgPin::ProgB, builder.get_toi()); + // BIO + for (col, &cd) in &self.columns { + if cd.bio == ColumnIoKind::None { + continue; + } + for (iob, unused) in [ + // inner + (0, cd.bio == ColumnIoKind::Outer), + (1, cd.bio == ColumnIoKind::Outer), + // outer + (2, cd.bio == ColumnIoKind::Inner), + (3, cd.bio == ColumnIoKind::Inner), + ] { + if !unused { + let crd = EdgeIoCoord::B(col, TileIobId::from_idx(iob)); + io.insert(crd, builder.get_toi()); + } + } + } + cfg.insert(CfgPin::Done, builder.get_toi()); + cfg.insert(CfgPin::CmpCsB, builder.get_toi()); + cfg.insert(CfgPin::Suspend, builder.get_toi()); + // RIO + for (row, &rd) in &self.rows { + if rd.rio { + for iob in [0, 1] { + let crd = EdgeIoCoord::R(row, TileIobId::from_idx(iob)); + io.insert(crd, builder.get_toi()); + } + } + } + BScan { + bits: builder.bits, + io, + cfg, + } + } +} diff --git a/prjcombine_spartan6/src/expand.rs b/prjcombine_spartan6/src/expand.rs index 49797fb..d6f06f3 100644 --- a/prjcombine_spartan6/src/expand.rs +++ b/prjcombine_spartan6/src/expand.rs @@ -1,8 +1,6 @@ use enum_map::EnumMap; use prjcombine_int::db::{Dir, IntDb}; -use prjcombine_int::grid::{ - ColId, Coord, EdgeIoCoord, ExpandedDieRefMut, ExpandedGrid, Rect, RowId, TileIobId, -}; +use prjcombine_int::grid::{ColId, Coord, ExpandedDieRefMut, ExpandedGrid, Rect, RowId}; use prjcombine_virtex_bitstream::{ BitstreamGeom, DeviceKind, DieBitstreamGeom, FrameAddr, FrameInfo, }; @@ -22,7 +20,6 @@ struct Expander<'a, 'b> { frame_info: Vec, bram_frame_info: Vec, iob_frame_len: usize, - io: Vec, col_frame: EntityVec>, col_width: EntityVec, spine_frame: EntityVec, @@ -203,7 +200,7 @@ impl Expander<'_, '_> { for (row, &rd) in &self.grid.rows { if rd.lio { self.fill_ioi((col, row)); - self.fill_iob((col, row)); + self.die.add_xnode((col, row), "IOB", &[]); } else { self.die.add_xnode((col, row), "INTF", &[(col, row)]); if row == self.grid.row_bio_outer() { @@ -245,7 +242,7 @@ impl Expander<'_, '_> { for (row, &rd) in self.grid.rows.iter().rev() { if rd.rio { self.fill_ioi((col, row)); - self.fill_iob((col, row)); + self.die.add_xnode((col, row), "IOB", &[]); } else { self.die.add_xnode((col, row), "INTF", &[(col, row)]); if row == self.grid.row_bio_outer() { @@ -301,7 +298,7 @@ impl Expander<'_, '_> { ] { self.fill_ioi((col, row)); if !unused { - self.fill_iob((col, row)); + self.die.add_xnode((col, row), "IOB", &[]); } } let row = self.grid.row_tio_outer(); @@ -331,7 +328,7 @@ impl Expander<'_, '_> { ] { self.fill_ioi((col, row)); if !unused { - self.fill_iob((col, row)); + self.die.add_xnode((col, row), "IOB", &[]); } } let row = self.grid.row_bio_outer(); @@ -816,44 +813,6 @@ impl Expander<'_, '_> { self.die.add_xnode(crd, kind, &[crd]); } - fn fill_iob(&mut self, crd: Coord) { - self.die.add_xnode(crd, "IOB", &[]); - let (crd_p, crd_n) = if crd.0 == self.grid.col_lio() { - ( - EdgeIoCoord::L(crd.1, TileIobId::from_idx(1)), - EdgeIoCoord::L(crd.1, TileIobId::from_idx(0)), - ) - } else if crd.0 == self.grid.col_rio() { - ( - EdgeIoCoord::R(crd.1, TileIobId::from_idx(1)), - EdgeIoCoord::R(crd.1, TileIobId::from_idx(0)), - ) - } else if crd.1 == self.grid.row_bio_outer() { - ( - EdgeIoCoord::B(crd.0, TileIobId::from_idx(3)), - EdgeIoCoord::B(crd.0, TileIobId::from_idx(2)), - ) - } else if crd.1 == self.grid.row_bio_inner() { - ( - EdgeIoCoord::B(crd.0, TileIobId::from_idx(1)), - EdgeIoCoord::B(crd.0, TileIobId::from_idx(0)), - ) - } else if crd.1 == self.grid.row_tio_outer() { - ( - EdgeIoCoord::T(crd.0, TileIobId::from_idx(3)), - EdgeIoCoord::T(crd.0, TileIobId::from_idx(2)), - ) - } else if crd.1 == self.grid.row_tio_inner() { - ( - EdgeIoCoord::T(crd.0, TileIobId::from_idx(1)), - EdgeIoCoord::T(crd.0, TileIobId::from_idx(0)), - ) - } else { - unreachable!() - }; - self.io.extend([crd_p, crd_n]); - } - fn fill_intf_rterm(&mut self, crd: Coord) { self.die.fill_term(crd, "TERM.E"); self.die.add_xnode(crd, "INTF", &[crd]); @@ -1019,7 +978,6 @@ impl Grid { frame_info: vec![], bram_frame_info: vec![], iob_frame_len: 0, - io: vec![], col_frame: EntityVec::new(), col_width: EntityVec::new(), spine_frame: EntityVec::new(), @@ -1050,8 +1008,6 @@ impl Grid { expander.fill_frame_info(); expander.fill_iob_frame_info(); - let io = expander.io; - let die_bs_geom = DieBitstreamGeom { frame_len: 1040, frame_info: expander.frame_info, @@ -1081,7 +1037,6 @@ impl Grid { egrid, site_holes, bs_geom, - io, col_frame, col_width, spine_frame, diff --git a/prjcombine_spartan6/src/expanded.rs b/prjcombine_spartan6/src/expanded.rs index f34f252..7673ed3 100644 --- a/prjcombine_spartan6/src/expanded.rs +++ b/prjcombine_spartan6/src/expanded.rs @@ -1,7 +1,7 @@ use enum_map::EnumMap; use prjcombine_int::{ db::Dir, - grid::{ColId, DieId, EdgeIoCoord, ExpandedGrid, Rect, RowId}, + grid::{ColId, DieId, ExpandedGrid, Rect, RowId}, }; use prjcombine_virtex_bitstream::{BitTile, BitstreamGeom}; use std::collections::{BTreeSet, HashMap}; @@ -15,7 +15,6 @@ pub struct ExpandedDevice<'a> { pub egrid: ExpandedGrid<'a>, pub site_holes: Vec, pub bs_geom: BitstreamGeom, - pub io: Vec, pub col_frame: EntityVec>, pub col_width: EntityVec, pub spine_frame: EntityVec, diff --git a/prjcombine_spartan6/src/grid.rs b/prjcombine_spartan6/src/grid.rs index 56a057c..8cff6fa 100644 --- a/prjcombine_spartan6/src/grid.rs +++ b/prjcombine_spartan6/src/grid.rs @@ -339,6 +339,63 @@ impl Grid { } } + pub fn get_bonded_ios(&self) -> Vec { + let mut res = vec![]; + // TIO + for (col, &cd) in &self.columns { + if cd.tio == ColumnIoKind::None { + continue; + } + for (iob, unused) in [ + // outer + (3, cd.tio == ColumnIoKind::Inner), + (2, cd.tio == ColumnIoKind::Inner), + // inner + (1, cd.tio == ColumnIoKind::Outer), + (0, cd.tio == ColumnIoKind::Outer), + ] { + if !unused { + res.push(EdgeIoCoord::T(col, TileIobId::from_idx(iob))); + } + } + } + // RIO + for (row, &rd) in self.rows.iter().rev() { + if rd.rio { + for iob in [1, 0] { + res.push(EdgeIoCoord::R(row, TileIobId::from_idx(iob))); + } + } + } + // BIO + for (col, &cd) in self.columns.iter().rev() { + if cd.bio == ColumnIoKind::None { + continue; + } + for (iob, unused) in [ + // outer + (3, cd.bio == ColumnIoKind::Inner), + (2, cd.bio == ColumnIoKind::Inner), + // inner + (1, cd.bio == ColumnIoKind::Outer), + (0, cd.bio == ColumnIoKind::Outer), + ] { + if !unused { + res.push(EdgeIoCoord::B(col, TileIobId::from_idx(iob))); + } + } + } + // LIO + for (row, &rd) in &self.rows { + if rd.lio { + for iob in [1, 0] { + res.push(EdgeIoCoord::L(row, TileIobId::from_idx(iob))); + } + } + } + res + } + pub fn to_json(&self) -> serde_json::Value { json!({ "columns": Vec::from_iter(self.columns.values().map(|column| { diff --git a/prjcombine_spartan6/src/lib.rs b/prjcombine_spartan6/src/lib.rs index fab8046..b8cae60 100644 --- a/prjcombine_spartan6/src/lib.rs +++ b/prjcombine_spartan6/src/lib.rs @@ -1,4 +1,5 @@ pub mod bond; +pub mod bscan; pub mod db; mod expand; pub mod expanded; diff --git a/prjcombine_spartan6_naming/src/lib.rs b/prjcombine_spartan6_naming/src/lib.rs index c0b2bd5..884261e 100644 --- a/prjcombine_spartan6_naming/src/lib.rs +++ b/prjcombine_spartan6_naming/src/lib.rs @@ -1666,7 +1666,7 @@ pub fn name_device<'a>(edev: &'a ExpandedDevice<'a>, ndb: &'a NamingDb) -> Expan } let mut pad_cnt = 1; - for &io in &edev.io { + for io in edev.grid.get_bonded_ios() { let (col, row, bel) = grid.get_io_loc(io); let layer = edev .egrid diff --git a/prjcombine_spartan6_rd2db/src/bond.rs b/prjcombine_spartan6_rd2db/src/bond.rs index 0befb4e..1b249ba 100644 --- a/prjcombine_spartan6_rd2db/src/bond.rs +++ b/prjcombine_spartan6_rd2db/src/bond.rs @@ -11,10 +11,10 @@ pub fn make_bond(endev: &ExpandedNamedDevice, pins: &[PkgPin]) -> Bond { let mut io_banks = BTreeMap::new(); let mut vref = BTreeSet::new(); let io_lookup: HashMap<_, _> = endev - .edev - .io - .iter() - .map(|&io| (endev.get_io_name(io), io)) + .grid + .get_bonded_ios() + .into_iter() + .map(|io| (endev.get_io_name(io), io)) .collect(); let mut gt_lookup: HashMap<_, (String, u32, GtPin)> = HashMap::new(); for gt in endev.get_gts() { diff --git a/prjcombine_types/src/bscan.rs b/prjcombine_types/src/bscan.rs new file mode 100644 index 0000000..c284708 --- /dev/null +++ b/prjcombine_types/src/bscan.rs @@ -0,0 +1,46 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub enum BScanPin { + Input(usize), + Output(usize), + OutputTristate(usize, usize), + OutputEnable(usize, usize), + InputOutputTristate(usize, usize, usize), + InputOutputEnable(usize, usize, usize), +} + +#[derive(Debug, Default)] +pub struct BScanBuilder { + pub bits: usize, +} + +impl BScanBuilder { + pub fn new() -> Self { + Self { bits: 0 } + } + + pub fn get_toi(&mut self) -> BScanPin { + let res = BScanPin::InputOutputTristate(self.bits + 2, self.bits + 1, self.bits); + self.bits += 3; + res + } + + pub fn get_to(&mut self) -> BScanPin { + let res = BScanPin::OutputTristate(self.bits + 1, self.bits); + self.bits += 2; + res + } + + pub fn get_o(&mut self) -> BScanPin { + let res = BScanPin::Output(self.bits); + self.bits += 1; + res + } + + pub fn get_i(&mut self) -> BScanPin { + let res = BScanPin::Input(self.bits); + self.bits += 1; + res + } +} diff --git a/prjcombine_types/src/lib.rs b/prjcombine_types/src/lib.rs index d6cde66..8675424 100644 --- a/prjcombine_types/src/lib.rs +++ b/prjcombine_types/src/lib.rs @@ -3,6 +3,7 @@ use core::fmt::Debug; use serde::{Deserialize, Serialize}; use unnamed_entity::entity_id; +pub mod bscan; pub mod tiledb; entity_id! { diff --git a/prjcombine_virtex/src/bond.rs b/prjcombine_virtex/src/bond.rs index 732dd8c..ebd5757 100644 --- a/prjcombine_virtex/src/bond.rs +++ b/prjcombine_virtex/src/bond.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use serde_json::json; use std::collections::{BTreeMap, BTreeSet}; -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub enum CfgPin { Tck, Tdi, @@ -18,7 +18,7 @@ pub enum CfgPin { M2, } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub enum BondPin { Clk(u32), Io(EdgeIoCoord), @@ -32,7 +32,7 @@ pub enum BondPin { Dxp, } -#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Bond { pub pins: BTreeMap, // device bank -> pkg bank diff --git a/prjcombine_virtex/src/bscan.rs b/prjcombine_virtex/src/bscan.rs new file mode 100644 index 0000000..778bb54 --- /dev/null +++ b/prjcombine_virtex/src/bscan.rs @@ -0,0 +1,86 @@ +use std::collections::BTreeMap; + +use prjcombine_int::grid::{EdgeIoCoord, TileIobId}; +use prjcombine_types::bscan::{BScanBuilder, BScanPin}; +use unnamed_entity::EntityId; + +use crate::{bond::CfgPin, grid::Grid}; + +#[derive(Debug)] +pub struct BScan { + pub bits: usize, + pub io: BTreeMap, + pub clk: BTreeMap, + pub cfg: BTreeMap, +} + +impl Grid { + pub fn get_bscan(&self) -> BScan { + let mut builder = BScanBuilder::new(); + let mut io = BTreeMap::new(); + let mut cfg = BTreeMap::new(); + let mut clk = BTreeMap::new(); + for col in self.columns().rev() { + if self.cols_bram.contains(&col) { + continue; + } + if col == self.col_lio() || col == self.col_rio() { + continue; + } + for iob in [1, 2] { + let crd = EdgeIoCoord::T(col, TileIobId::from_idx(iob)); + io.insert(crd, builder.get_toi()); + } + if col == self.col_clk() { + clk.insert(1, builder.get_i()); + clk.insert(0, builder.get_i()); + } + } + for row in self.rows().rev() { + if row == self.row_bio() || row == self.row_tio() { + continue; + } + for iob in [1, 2, 3] { + let crd = EdgeIoCoord::L(row, TileIobId::from_idx(iob)); + io.insert(crd, builder.get_toi()); + } + } + for pin in [CfgPin::M1, CfgPin::M0, CfgPin::M2] { + cfg.insert(pin, builder.get_i()); + } + for col in self.columns() { + if self.cols_bram.contains(&col) { + continue; + } + if col == self.col_lio() || col == self.col_rio() { + continue; + } + if col == self.col_clk() { + clk.insert(5, builder.get_i()); + clk.insert(4, builder.get_i()); + } + for iob in [2, 1] { + let crd = EdgeIoCoord::B(col, TileIobId::from_idx(iob)); + io.insert(crd, builder.get_toi()); + } + } + cfg.insert(CfgPin::Done, builder.get_toi()); + cfg.insert(CfgPin::ProgB, builder.get_i()); + for row in self.rows() { + if row == self.row_bio() || row == self.row_tio() { + continue; + } + for iob in [3, 2, 1] { + let crd = EdgeIoCoord::R(row, TileIobId::from_idx(iob)); + io.insert(crd, builder.get_toi()); + } + } + cfg.insert(CfgPin::Cclk, builder.get_toi()); + BScan { + bits: builder.bits, + io, + clk, + cfg, + } + } +} diff --git a/prjcombine_virtex/src/lib.rs b/prjcombine_virtex/src/lib.rs index fab8046..b8cae60 100644 --- a/prjcombine_virtex/src/lib.rs +++ b/prjcombine_virtex/src/lib.rs @@ -1,4 +1,5 @@ pub mod bond; +pub mod bscan; pub mod db; mod expand; pub mod expanded; diff --git a/prjcombine_virtex2/src/bscan.rs b/prjcombine_virtex2/src/bscan.rs new file mode 100644 index 0000000..92a3307 --- /dev/null +++ b/prjcombine_virtex2/src/bscan.rs @@ -0,0 +1,186 @@ +use std::collections::BTreeMap; + +use prjcombine_int::grid::{EdgeIoCoord, TileIobId}; +use prjcombine_types::bscan::{BScanBuilder, BScanPin}; +use unnamed_entity::EntityId; + +use crate::{ + bond::CfgPin, + grid::{ColumnKind, Grid, GridKind}, + iob::IobKind, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum FcCfgPin { + Init, + DoneO, + DoneI, + PorEnB, + Dout(u8), + Din(u8), + WriteB, + CsB, + Cclk, +} + +#[derive(Debug)] +pub struct BScan { + pub bits: usize, + pub io: BTreeMap, + pub cfg: BTreeMap, + pub fc_cfg: BTreeMap, +} + +impl Grid { + pub fn get_bscan(&self) -> BScan { + let mut io = BTreeMap::new(); + let mut cfg = BTreeMap::new(); + let mut fc_cfg = BTreeMap::new(); + let mut builder = BScanBuilder::new(); + if self.kind == GridKind::FpgaCore { + for col in self.columns.ids().rev() { + if self.columns[col].kind == ColumnKind::Clb { + for i in 0..4 { + let crd = EdgeIoCoord::T(col, TileIobId::from_idx(4 + i)); + io.insert(crd, builder.get_o()); + let crd = EdgeIoCoord::T(col, TileIobId::from_idx(i)); + io.insert(crd, builder.get_i()); + } + } + } + cfg.insert(CfgPin::ProgB, builder.get_i()); + for row in self.rows.ids().rev() { + if row != self.row_bot() && row != self.row_top() { + for i in (0..4).rev() { + let crd = EdgeIoCoord::L(row, TileIobId::from_idx(4 + i)); + io.insert(crd, builder.get_o()); + let crd = EdgeIoCoord::L(row, TileIobId::from_idx(i)); + io.insert(crd, builder.get_i()); + } + } + } + for pin in [CfgPin::M1, CfgPin::M0, CfgPin::M2] { + cfg.insert(pin, builder.get_i()); + } + for col in self.columns.ids() { + if self.columns[col].kind == ColumnKind::Clb { + for i in 0..4 { + let crd = EdgeIoCoord::B(col, TileIobId::from_idx(4 + i)); + io.insert(crd, builder.get_o()); + let crd = EdgeIoCoord::B(col, TileIobId::from_idx(i)); + io.insert(crd, builder.get_i()); + } + } + } + for i in (0..8).rev() { + fc_cfg.insert(FcCfgPin::Dout(i), builder.get_o()); + } + fc_cfg.insert(FcCfgPin::Init, builder.get_o()); + fc_cfg.insert(FcCfgPin::DoneO, builder.get_o()); + fc_cfg.insert(FcCfgPin::PorEnB, builder.get_i()); + for i in (0..8).rev() { + fc_cfg.insert(FcCfgPin::Din(i), builder.get_i()); + } + fc_cfg.insert(FcCfgPin::WriteB, builder.get_i()); + fc_cfg.insert(FcCfgPin::DoneI, builder.get_i()); + fc_cfg.insert(FcCfgPin::CsB, builder.get_i()); + fc_cfg.insert(FcCfgPin::Cclk, builder.get_i()); + for row in self.rows.ids() { + if row != self.row_bot() && row != self.row_top() { + for i in 0..4 { + let crd = EdgeIoCoord::R(row, TileIobId::from_idx(4 + i)); + io.insert(crd, builder.get_o()); + let crd = EdgeIoCoord::R(row, TileIobId::from_idx(i)); + io.insert(crd, builder.get_i()); + } + } + } + } else { + for col in self.columns.ids().rev() { + let row = self.row_top(); + if let Some((data, tidx)) = self.get_iob_data((col, row)) { + for &iob in data.iobs.iter().rev() { + if iob.tile == tidx { + let crd = EdgeIoCoord::T(col, TileIobId::from_idx(iob.bel.to_idx())); + if iob.kind == IobKind::Iob { + io.insert(crd, builder.get_toi()); + } else { + io.insert(crd, builder.get_i()); + }; + } + } + } + } + if !self.kind.is_spartan3ea() { + cfg.insert(CfgPin::HswapEn, builder.get_i()); + } + cfg.insert(CfgPin::ProgB, builder.get_i()); + for row in self.rows.ids().rev() { + let col = self.col_left(); + if let Some((data, tidx)) = self.get_iob_data((col, row)) { + for &iob in data.iobs.iter().rev() { + if iob.tile == tidx { + let crd = EdgeIoCoord::L(row, TileIobId::from_idx(iob.bel.to_idx())); + if iob.kind == IobKind::Iob { + io.insert(crd, builder.get_toi()); + } else { + io.insert(crd, builder.get_i()); + }; + } + } + } + } + if !self.kind.is_spartan3ea() { + for pin in [CfgPin::M1, CfgPin::M0, CfgPin::M2] { + cfg.insert(pin, builder.get_i()); + } + } + for col in self.columns.ids() { + let row = self.row_bot(); + if let Some((data, tidx)) = self.get_iob_data((col, row)) { + for &iob in data.iobs.iter().rev() { + if iob.tile == tidx { + let crd = EdgeIoCoord::B(col, TileIobId::from_idx(iob.bel.to_idx())); + if iob.kind == IobKind::Iob { + io.insert(crd, builder.get_toi()); + } else { + io.insert(crd, builder.get_i()); + }; + } + } + } + } + cfg.insert(CfgPin::Done, builder.get_toi()); + if self.kind.is_virtex2() { + cfg.insert(CfgPin::PwrdwnB, builder.get_i()); + } + if !self.kind.is_spartan3ea() { + cfg.insert(CfgPin::Cclk, builder.get_toi()); + } + if self.kind.is_spartan3a() { + cfg.insert(CfgPin::Suspend, builder.get_i()); + } + for row in self.rows.ids() { + let col = self.col_right(); + if let Some((data, tidx)) = self.get_iob_data((col, row)) { + for &iob in data.iobs.iter().rev() { + if iob.tile == tidx { + let crd = EdgeIoCoord::R(row, TileIobId::from_idx(iob.bel.to_idx())); + if iob.kind == IobKind::Iob { + io.insert(crd, builder.get_toi()); + } else { + io.insert(crd, builder.get_i()); + }; + } + } + } + } + } + BScan { + bits: builder.bits, + io, + cfg, + fc_cfg, + } + } +} diff --git a/prjcombine_virtex2/src/lib.rs b/prjcombine_virtex2/src/lib.rs index d65604b..d3f17f5 100644 --- a/prjcombine_virtex2/src/lib.rs +++ b/prjcombine_virtex2/src/lib.rs @@ -1,4 +1,5 @@ pub mod bond; +pub mod bscan; pub mod db; mod expand; pub mod expanded; diff --git a/prjcombine_virtex2_rd2db/src/grid.rs b/prjcombine_virtex2_rd2db/src/grid.rs index 77694ae..095c3db 100644 --- a/prjcombine_virtex2_rd2db/src/grid.rs +++ b/prjcombine_virtex2_rd2db/src/grid.rs @@ -1,6 +1,9 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; -use prjcombine_int::{db::BelId, grid::{ColId, RowId}}; +use prjcombine_int::{ + db::BelId, + grid::{ColId, RowId}, +}; use prjcombine_rawdump::{Coord, Part, TkSiteSlot}; use prjcombine_virtex2::grid::{ Column, ColumnIoKind, ColumnKind, Dcms, Grid, GridKind, RowIoKind, SharedCfgPin, @@ -424,7 +427,7 @@ fn handle_spec_io(rd: &Part, grid: &mut Grid, int: &IntGrid) { grid.get_io_crd( int.lookup_column(crd.x.into()), int.lookup_row(crd.y.into()), - BelId::from_idx(idx as usize) + BelId::from_idx(idx as usize), ), ); } diff --git a/prjcombine_virtex_rd2db/src/grid.rs b/prjcombine_virtex_rd2db/src/grid.rs index 1970313..4bc0ec7 100644 --- a/prjcombine_virtex_rd2db/src/grid.rs +++ b/prjcombine_virtex_rd2db/src/grid.rs @@ -1,6 +1,9 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; -use prjcombine_int::{db::BelId, grid::{ColId, EdgeIoCoord}}; +use prjcombine_int::{ + db::BelId, + grid::{ColId, EdgeIoCoord}, +}; use prjcombine_rawdump::{Coord, Part, TkSiteSlot}; use prjcombine_virtex::grid::{DisabledPart, Grid, GridKind, SharedCfgPin}; use unnamed_entity::EntityId; diff --git a/prjcombine_xact_dump/src/xc4000.rs b/prjcombine_xact_dump/src/xc4000.rs index 0335a0b..377b2a7 100644 --- a/prjcombine_xact_dump/src/xc4000.rs +++ b/prjcombine_xact_dump/src/xc4000.rs @@ -6,7 +6,7 @@ use prjcombine_int::{ BelId, BelInfo, BelPin, Dir, IntDb, NodeKind, NodeTileId, PinDir, TermInfo, TermKind, WireKind, }, - grid::{DieId, LayerId, EdgeIoCoord}, + grid::{DieId, EdgeIoCoord, LayerId}, }; use prjcombine_xact_data::die::Die; use prjcombine_xact_naming::db::{NamingDb, NodeNaming}; diff --git a/prjcombine_xact_dump/src/xc5200.rs b/prjcombine_xact_dump/src/xc5200.rs index a1c0083..992b07a 100644 --- a/prjcombine_xact_dump/src/xc5200.rs +++ b/prjcombine_xact_dump/src/xc5200.rs @@ -6,7 +6,7 @@ use prjcombine_int::{ BelId, BelInfo, BelPin, Dir, IntDb, NodeKind, NodeTileId, PinDir, TermInfo, TermKind, WireKind, }, - grid::{DieId, LayerId, EdgeIoCoord}, + grid::{DieId, EdgeIoCoord, LayerId}, }; use prjcombine_xact_data::die::Die; use prjcombine_xact_naming::db::{NamingDb, NodeNaming}; diff --git a/prjcombine_xact_hammer/src/fbuild.rs b/prjcombine_xact_hammer/src/fbuild.rs index 05fc3e3..c77bd7f 100644 --- a/prjcombine_xact_hammer/src/fbuild.rs +++ b/prjcombine_xact_hammer/src/fbuild.rs @@ -9,7 +9,9 @@ use prjcombine_int::{ use crate::{ backend::{Key, Value, XactBackend}, fgen::{ - BaseBelConfig, BaseBelMode, BaseBelMutex, BaseRaw, BondedIo, ExtraTile, FuzzBelConfig, FuzzBelConfigDiff, FuzzBelMode, FuzzBelPipBufg, FuzzBelPipPin, FuzzEquate, FuzzEquateFixed, FuzzRaw, PinMutexExclusive, Prop, XactFuzzerGen + BaseBelConfig, BaseBelMode, BaseBelMutex, BaseRaw, BondedIo, ExtraTile, FuzzBelConfig, + FuzzBelConfigDiff, FuzzBelMode, FuzzBelPipBufg, FuzzBelPipPin, FuzzEquate, FuzzEquateFixed, + FuzzRaw, PinMutexExclusive, Prop, XactFuzzerGen, }, }; diff --git a/prjcombine_xc2000/src/bond.rs b/prjcombine_xc2000/src/bond.rs index e2cec9c..ea842c3 100644 --- a/prjcombine_xc2000/src/bond.rs +++ b/prjcombine_xc2000/src/bond.rs @@ -5,7 +5,7 @@ use prjcombine_int::grid::EdgeIoCoord; use serde::{Deserialize, Serialize}; use serde_json::json; -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum CfgPin { Cclk, Done, @@ -18,7 +18,7 @@ pub enum CfgPin { M2, } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum BondPin { Io(EdgeIoCoord), Gnd, diff --git a/prjcombine_xc2000/src/bscan.rs b/prjcombine_xc2000/src/bscan.rs new file mode 100644 index 0000000..a3ab866 --- /dev/null +++ b/prjcombine_xc2000/src/bscan.rs @@ -0,0 +1,106 @@ +use std::collections::BTreeMap; + +use prjcombine_int::grid::{EdgeIoCoord, TileIobId}; +use prjcombine_types::bscan::{BScanBuilder, BScanPin}; +use unnamed_entity::EntityId; + +use crate::{ + bond::CfgPin, + grid::{Grid, GridKind}, +}; + +#[derive(Debug)] +pub struct BScan { + pub bits: usize, + pub io: BTreeMap, + pub cfg: BTreeMap, + pub upd: usize, +} + +impl Grid { + pub fn get_bscan(&self) -> BScan { + if self.kind.is_xc3000() || self.kind == GridKind::Xc2000 { + panic!("no boundary scan on XC2000/XC3000"); + } + let iobs: &[_] = if self.kind == GridKind::Xc5200 { + &[3, 2, 1, 0] + } else if self.kind == GridKind::Xc4000H { + &[0, 1, 2, 3] + } else { + &[0, 1] + }; + let mut io = BTreeMap::new(); + let mut cfg = BTreeMap::new(); + let mut builder = BScanBuilder::new(); + if self.kind.is_xc4000() { + cfg.insert(CfgPin::Tdo, builder.get_to()); + } + // top edge, right-to-left + for col in self.columns() { + if col == self.col_lio() || col == self.col_rio() { + continue; + } + for iob in iobs.iter().copied() { + let crd = EdgeIoCoord::T(col, TileIobId::from_idx(iob)); + if self.unbonded_io.contains(&crd) { + continue; + } + io.insert(crd, builder.get_toi()); + } + } + // left edge, top-to-bottom + for row in self.rows().rev() { + if row == self.row_bio() || row == self.row_tio() { + continue; + } + for iob in iobs.iter().copied() { + let crd = EdgeIoCoord::R(row, TileIobId::from_idx(iob)); + if self.unbonded_io.contains(&crd) { + continue; + } + io.insert(crd, builder.get_toi()); + } + } + if self.kind.is_xc4000() { + cfg.insert(CfgPin::M1, builder.get_toi()); + cfg.insert(CfgPin::M0, builder.get_i()); + if self.kind != GridKind::SpartanXl { + cfg.insert(CfgPin::M2, builder.get_i()); + } + } + // bottom edge, left-to-right + for col in self.columns().rev() { + if col == self.col_lio() || col == self.col_rio() { + continue; + } + for iob in iobs.iter().copied().rev() { + let crd = EdgeIoCoord::B(col, TileIobId::from_idx(iob)); + if self.unbonded_io.contains(&crd) { + continue; + } + io.insert(crd, builder.get_toi()); + } + } + // right edge, bottom-to-top + for row in self.rows() { + if row == self.row_bio() || row == self.row_tio() { + continue; + } + for iob in iobs.iter().copied().rev() { + let crd = EdgeIoCoord::L(row, TileIobId::from_idx(iob)); + if self.unbonded_io.contains(&crd) { + continue; + } + io.insert(crd, builder.get_toi()); + } + } + let upd = builder.bits; + builder.bits += 1; + BScan { + bits: builder.bits, + io, + cfg, + upd, + } + } +} diff --git a/prjcombine_xc2000/src/lib.rs b/prjcombine_xc2000/src/lib.rs index fab8046..b8cae60 100644 --- a/prjcombine_xc2000/src/lib.rs +++ b/prjcombine_xc2000/src/lib.rs @@ -1,4 +1,5 @@ pub mod bond; +pub mod bscan; pub mod db; mod expand; pub mod expanded;