From d1c6e8dfc1994d9d6de557d312d280210c77f266 Mon Sep 17 00:00:00 2001 From: Wanda Date: Mon, 29 Jan 2024 03:40:03 +0000 Subject: [PATCH] xc2c_as, xc2c_dis --- prjcombine_xc2c/Cargo.toml | 4 +- prjcombine_xc2c/src/bin/xc2c_as.rs | 254 ++++++++++++++++++++++++++ prjcombine_xc2c/src/bin/xc2c_dis.rs | 238 ++++++++++++++++++++++++ prjcombine_xpla3/Cargo.toml | 2 - prjcombine_xpla3/src/bin/xpla3_dis.rs | 4 +- 5 files changed, 497 insertions(+), 5 deletions(-) create mode 100644 prjcombine_xc2c/src/bin/xc2c_as.rs create mode 100644 prjcombine_xc2c/src/bin/xc2c_dis.rs diff --git a/prjcombine_xc2c/Cargo.toml b/prjcombine_xc2c/Cargo.toml index cb8ad274..802d099f 100644 --- a/prjcombine_xc2c/Cargo.toml +++ b/prjcombine_xc2c/Cargo.toml @@ -3,12 +3,12 @@ name = "prjcombine_xc2c" edition.workspace = true version.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] +bitvec.workspace = true serde.workspace = true bincode.workspace = true zstd.workspace = true +clap.workspace = true unnamed_entity.workspace = true prjcombine_types.workspace = true diff --git a/prjcombine_xc2c/src/bin/xc2c_as.rs b/prjcombine_xc2c/src/bin/xc2c_as.rs new file mode 100644 index 00000000..54c55b3e --- /dev/null +++ b/prjcombine_xc2c/src/bin/xc2c_as.rs @@ -0,0 +1,254 @@ +use std::{ + collections::BTreeMap, + error::Error, + fs::{read_to_string, File}, + io::Write, + path::{Path, PathBuf}, +}; + +use bitvec::vec::BitVec; +use clap::Parser; +use prjcombine_types::{FbId, FbMcId, IoId, Tile, TileItemKind}; +use prjcombine_xc2c::{BitCoord, Database, Device}; +use unnamed_entity::EntityId; + +struct Bitstream { + fbs: Vec, + globals: BTreeMap, +} + +struct FbData { + imux: BTreeMap, + mcs: [BTreeMap; 16], + pla_and: [PTermData; 56], + pla_or: [BitVec; 16], +} + +struct PTermData { + im_t: BitVec, + im_f: BitVec, +} + +fn init_tile(tile: &Tile) -> BTreeMap { + tile.items + .iter() + .map(|(k, v)| (k.clone(), BitVec::repeat(true, v.bits.len()))) + .collect() +} + +impl Bitstream { + fn new(device: &Device) -> Self { + let fbs = (0..(device.fb_rows as usize * device.fb_cols.len() * 2)) + .map(|_| FbData { + imux: init_tile(&device.imux_bits), + mcs: core::array::from_fn(|_| init_tile(&device.mc_bits)), + pla_and: core::array::from_fn(|_| PTermData { + im_t: BitVec::repeat(false, 40), + im_f: BitVec::repeat(false, 40), + }), + pla_or: core::array::from_fn(|_| BitVec::repeat(false, 56)), + }) + .collect(); + Bitstream { + fbs, + globals: init_tile(&device.global_bits), + } + } + + fn to_jed(&self, device: &Device, db: &Database) -> BitVec { + let mut res = BitVec::new(); + for (fb, fbd) in self.fbs.iter().enumerate() { + for i in 0..40 { + let n = format!("IM[{i}].MUX"); + let val = &fbd.imux[&n]; + res.extend(val); + } + for i in 0..56 { + let pt = &fbd.pla_and[i]; + for j in 0..40 { + res.push(!pt.im_t[j]); + res.push(!pt.im_f[j]); + } + } + for i in 0..56 { + for j in 0..16 { + res.push(!fbd.pla_or[j][i]); + } + } + for mc in 0..16 { + let iobful = device + .io + .contains_key(&IoId::Mc((FbId::from_idx(fb), FbMcId::from_idx(mc)))); + let mcd = &fbd.mcs[mc]; + let jed_bits = if !device.has_vref { + &db.jed_mc_bits_small + } else if iobful { + &db.jed_mc_bits_large_iob + } else { + &db.jed_mc_bits_large_buried + }; + for (bn, bi) in jed_bits { + res.push(mcd[bn][*bi]); + } + } + } + for (bn, bi) in &device.jed_global_bits { + res.push(self.globals[bn][*bi]); + } + res + } +} + +fn set_tile_item(data: &mut BTreeMap, tile: &Tile, item: &str) { + if let Some((name, val)) = item.split_once('=') { + let item = &tile.items[name]; + let val = match &item.kind { + TileItemKind::Enum { values } => values[val].clone(), + TileItemKind::BitVec { invert } => { + assert_eq!(val.len(), item.bits.len()); + val.chars() + .rev() + .map(|x| match x { + '0' => *invert, + '1' => !*invert, + _ => unreachable!(), + }) + .collect() + } + }; + data.insert(name.to_string(), val); + } else { + let (name, val) = if let Some(name) = item.strip_prefix('!') { + (name, false) + } else { + (item, true) + }; + let item = &tile.items[name]; + match &item.kind { + TileItemKind::Enum { .. } => unreachable!(), + TileItemKind::BitVec { invert } => { + assert_eq!(item.bits.len(), 1); + data.insert(name.to_string(), BitVec::repeat(val ^ *invert, 1)); + } + } + } +} + +#[derive(Parser)] +struct Args { + db: PathBuf, + src: PathBuf, + jed: PathBuf, +} + +fn write_jed(fname: impl AsRef, dev: &str, bits: &BitVec) -> Result<(), Box> { + let mut f = File::create(fname)?; + writeln!(f, "\x02QF{n}*", n = bits.len())?; + writeln!(f, "F0*")?; + writeln!(f, "N DEVICE {dev}*")?; + for (i, c) in bits.chunks(80).enumerate() { + write!(f, "L{ii:06} ", ii = i * 80)?; + for bit in c { + write!(f, "{x}", x = u32::from(*bit))?; + } + writeln!(f, "*")?; + } + writeln!(f, "\x030000")?; + Ok(()) +} + +pub fn main() -> Result<(), Box> { + let args = Args::parse(); + let src = read_to_string(args.src)?; + let mut lines = src.lines(); + let mut dev = None; + for mut line in &mut lines { + if let Some(pos) = line.find('#') { + line = &line[..pos]; + } + line = line.trim(); + if line.is_empty() { + continue; + } + let (pref, suf) = line.split_once(':').unwrap(); + let suf = suf.trim(); + assert_eq!(pref, "DEVICE"); + dev = Some(suf); + break; + } + let dev = dev.unwrap(); + let db = Database::from_file(args.db)?; + let mut part = None; + for p in &db.parts { + if p.name == dev { + part = Some(p); + break; + } + } + let Some(part) = part else { + eprintln!("Unknown device {dev}"); + return Ok(()); + }; + let device = &db.devices[part.device]; + let mut bs = Bitstream::new(device); + for mut line in lines { + if let Some(pos) = line.find('#') { + line = &line[..pos]; + } + line = line.trim(); + if line.is_empty() { + continue; + } + let (pref, suf) = line.split_once(':').unwrap(); + let pref: Vec<_> = pref.split_ascii_whitespace().collect(); + let suf: Vec<_> = suf.trim().split_ascii_whitespace().collect(); + match pref[..] { + ["GLOBAL"] => { + for item in suf { + set_tile_item(&mut bs.globals, &device.global_bits, item); + } + } + ["FB", fb] => { + let fb: usize = fb.parse()?; + for item in suf { + set_tile_item(&mut bs.fbs[fb].imux, &device.imux_bits, item); + } + } + ["MC", fb, mc] => { + let fb: usize = fb.parse()?; + let mc: usize = mc.parse()?; + for item in suf { + set_tile_item(&mut bs.fbs[fb].mcs[mc], &device.mc_bits, item); + } + } + ["PT", fb, pt] => { + let fb: usize = fb.parse()?; + let pt: usize = pt.parse()?; + let pt = &mut bs.fbs[fb].pla_and[pt]; + for item in suf { + if let Some(idx) = item.strip_prefix('!') { + let idx = idx.parse()?; + pt.im_f.set(idx, true); + } else { + let idx = item.parse()?; + pt.im_t.set(idx, true); + } + } + } + ["ST", fb, mc] => { + let fb: usize = fb.parse()?; + let mc: usize = mc.parse()?; + for item in suf { + let idx = item.parse()?; + bs.fbs[fb].pla_or[mc].set(idx, true); + } + } + + _ => panic!("weird line {line}"), + } + } + let fuses = bs.to_jed(device, &db); + write_jed(args.jed, dev, &fuses)?; + + Ok(()) +} diff --git a/prjcombine_xc2c/src/bin/xc2c_dis.rs b/prjcombine_xc2c/src/bin/xc2c_dis.rs new file mode 100644 index 00000000..36656bb3 --- /dev/null +++ b/prjcombine_xc2c/src/bin/xc2c_dis.rs @@ -0,0 +1,238 @@ +use std::{collections::BTreeMap, error::Error, path::PathBuf}; + +use bitvec::vec::BitVec; +use clap::Parser; +use prjcombine_types::{FbId, FbMcId, IoId, Tile, TileItemKind}; +use prjcombine_xc2c::{BitCoord, Database, Device}; +use unnamed_entity::EntityId; + +struct Bitstream { + fbs: Vec, + globals: BTreeMap, +} + +struct FbData { + imux: BTreeMap, + mcs: [BTreeMap; 16], + pla_and: [PTermData; 56], + pla_or: [BitVec; 16], +} + +struct PTermData { + im_t: BitVec, + im_f: BitVec, +} + +impl Bitstream { + fn from_jed(fuses: &BitVec, device: &Device, db: &Database) -> Self { + let mut fbs = vec![]; + let mut pos = 0; + for fb in 0..(device.fb_cols.len() * device.fb_rows as usize * 2) { + let mut fbd = FbData { + imux: BTreeMap::new(), + mcs: core::array::from_fn(|_| BTreeMap::new()), + pla_and: core::array::from_fn(|_| PTermData { + im_t: BitVec::new(), + im_f: BitVec::new(), + }), + pla_or: core::array::from_fn(|_| BitVec::new()), + }; + for i in 0..40 { + let n = format!("IM[{i}].MUX"); + let data = fuses[pos..(pos + device.imux_width as usize)].to_bitvec(); + pos += device.imux_width as usize; + fbd.imux.insert(n, data); + } + for i in 0..56 { + let pt = &mut fbd.pla_and[i]; + for _ in 0..40 { + pt.im_t.push(!fuses[pos]); + pos += 1; + pt.im_f.push(!fuses[pos]); + pos += 1; + } + } + for _ in 0..56 { + for j in 0..16 { + fbd.pla_or[j].push(!fuses[pos]); + pos += 1; + } + } + for mc in 0..16 { + let iobful = device + .io + .contains_key(&IoId::Mc((FbId::from_idx(fb), FbMcId::from_idx(mc)))); + let mcd = &mut fbd.mcs[mc]; + let jed_bits = if !device.has_vref { + &db.jed_mc_bits_small + } else if iobful { + &db.jed_mc_bits_large_iob + } else { + &db.jed_mc_bits_large_buried + }; + for (bn, bi) in jed_bits { + let bits = mcd.entry(bn.clone()).or_insert_with(|| { + BitVec::repeat(false, device.mc_bits.items[bn].bits.len()) + }); + bits.set(*bi, fuses[pos]); + pos += 1; + } + } + fbs.push(fbd); + } + let mut globals = BTreeMap::new(); + for (bn, bi) in &device.jed_global_bits { + let bits = globals + .entry(bn.clone()) + .or_insert_with(|| BitVec::repeat(false, device.global_bits.items[bn].bits.len())); + bits.set(*bi, fuses[pos]); + pos += 1; + } + assert_eq!(pos, fuses.len()); + Bitstream { fbs, globals } + } +} + +#[derive(Parser)] +struct Args { + db: PathBuf, + jed: PathBuf, +} + +fn parse_jed(jed: &str) -> (String, BitVec) { + let stx = jed.find('\x02').unwrap(); + let etx = jed.find('\x03').unwrap(); + let mut res = None; + let mut len = None; + let mut device = None; + for cmd in jed[stx + 1..etx].split('*') { + let cmd = cmd.trim(); + if let Some(arg) = cmd.strip_prefix("QF") { + assert!(len.is_none()); + let n: usize = arg.parse().unwrap(); + len = Some(n); + } else if let Some(arg) = cmd.strip_prefix("N DEVICE ") { + device = Some(arg.to_string()) + } else if let Some(arg) = cmd.strip_prefix('F') { + assert!(res.is_none()); + let x: u32 = arg.parse().unwrap(); + let x = match x { + 0 => false, + 1 => true, + _ => unreachable!(), + }; + res = Some(BitVec::repeat(x, len.unwrap())); + } else if let Some(arg) = cmd.strip_prefix('L') { + let sp = arg.find(' ').unwrap(); + let mut pos: usize = arg[..sp].parse().unwrap(); + let v = res.as_mut().unwrap(); + for c in arg[sp..].chars() { + match c { + '0' => { + v.set(pos, false); + pos += 1; + } + '1' => { + v.set(pos, true); + pos += 1; + } + ' ' => (), + _ => unreachable!(), + } + } + } + } + (device.unwrap(), res.unwrap()) +} + +fn print_tile(data: &BTreeMap, tile: &Tile) { + for (k, v) in data { + let item = &tile.items[k]; + print!(" {k}="); + match &item.kind { + TileItemKind::Enum { values } => { + let mut found = false; + for (vn, val) in values { + if val == v { + found = true; + print!("{vn}"); + } + } + if !found { + for bit in v.iter().rev() { + print!("{}", u8::from(*bit)); + } + } + } + TileItemKind::BitVec { invert } => { + for bit in v.iter().rev() { + print!("{}", u8::from(*bit ^ *invert)); + } + } + } + } + println!(); +} + +pub fn main() -> Result<(), Box> { + let args = Args::parse(); + let jed = std::fs::read_to_string(args.jed)?; + let (device, fuses) = parse_jed(&jed); + let device = device.to_ascii_lowercase(); + let dev = if let Some(pos) = device.find('-') { + &device[..pos] + } else { + &device[..] + }; + let db = Database::from_file(args.db)?; + let mut part = None; + for p in &db.parts { + if p.name == dev { + part = Some(p); + break; + } + } + let Some(part) = part else { + eprintln!("Unknown device {dev}"); + return Ok(()); + }; + let device = &db.devices[part.device]; + let bs = Bitstream::from_jed(&fuses, device, &db); + println!("DEVICE: {dev}"); + print!("GLOBAL:"); + print_tile(&bs.globals, &device.global_bits); + for (i, fbd) in bs.fbs.iter().enumerate() { + print!("FB {i}:"); + print_tile(&fbd.imux, &device.imux_bits); + for j in 0..16 { + print!("MC {i} {j}:"); + print_tile(&fbd.mcs[j], &device.mc_bits); + } + for (j, pt) in fbd.pla_and.iter().enumerate() { + if pt.im_t.any() || pt.im_f.any() { + print!("PT {i} {j}:"); + for k in 0..40 { + if pt.im_t[k] { + print!(" {k}"); + } + if pt.im_f[k] { + print!(" !{k}"); + } + } + println!(); + } + } + for (j, st) in fbd.pla_or.iter().enumerate() { + if st.any() { + print!("ST {i} {j}:"); + for k in 0..56 { + if st[k] { + print!(" {k}"); + } + } + println!(); + } + } + } + Ok(()) +} diff --git a/prjcombine_xpla3/Cargo.toml b/prjcombine_xpla3/Cargo.toml index 834d4fa2..375567a6 100644 --- a/prjcombine_xpla3/Cargo.toml +++ b/prjcombine_xpla3/Cargo.toml @@ -3,8 +3,6 @@ name = "prjcombine_xpla3" edition.workspace = true version.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] bitvec.workspace = true unnamed_entity.workspace = true diff --git a/prjcombine_xpla3/src/bin/xpla3_dis.rs b/prjcombine_xpla3/src/bin/xpla3_dis.rs index b824a2f6..42ca6825 100644 --- a/prjcombine_xpla3/src/bin/xpla3_dis.rs +++ b/prjcombine_xpla3/src/bin/xpla3_dis.rs @@ -249,7 +249,9 @@ pub fn main() -> Result<(), Box> { if st.any() { print!("ST {i} {j}:"); for k in 0..48 { - print!(" {k}"); + if st[k] { + print!(" {k}"); + } } println!(); }