Skip to content

Commit

Permalink
add shit xc9500 assembler / disassembler
Browse files Browse the repository at this point in the history
  • Loading branch information
wanda-phi committed Dec 15, 2023
1 parent fa3d777 commit 4ddd468
Show file tree
Hide file tree
Showing 3 changed files with 653 additions and 0 deletions.
1 change: 1 addition & 0 deletions prjcombine_xc9500/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ serde.workspace = true
bitvec.workspace = true
zstd.workspace = true
bincode.workspace = true
clap.workspace = true

[lints]
workspace = true
319 changes: 319 additions & 0 deletions prjcombine_xc9500/src/bin/xc9500_as.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
use std::{
error::Error,
fs::{read_to_string, File},
io::Write,
path::{Path, PathBuf},
};

use bitvec::vec::BitVec;
use clap::Parser;
use prjcombine_xc9500::{Database, Device, DeviceKind, FbBitCoord, GlobalBitCoord, Tile, TileItem};

struct Bitstream {
fbs: Vec<Vec<[u8; 15]>>,
uim: Vec<Vec<Vec<[u8; 5]>>>,
}

impl Bitstream {
fn new(dev: &Device) -> Self {
let rows = if dev.kind == DeviceKind::Xc9500 {
72
} else {
108
};
let fbs = (0..dev.fbs)
.map(|_| {
vec![
if dev.kind == DeviceKind::Xc9500 {
[
0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0, 0, 0, 0, 0, 0,
]
} else {
[0; 15]
};
rows
]
})
.collect();
let uim = if dev.kind == DeviceKind::Xc9500 {
(0..dev.fbs)
.map(|_| (0..dev.fbs).map(|_| vec![[0; 5]; 18]).collect())
.collect()
} else {
vec![]
};
Bitstream { fbs, uim }
}

fn to_jed(&self) -> BitVec {
let mut res = BitVec::new();
if !self.uim.is_empty() {
for fb in 0..self.fbs.len() {
for row in 0..72 {
for col in 0..15 {
let sz = if col < 9 { 8 } else { 6 };
for j in 0..sz {
res.push((self.fbs[fb][row][col] >> j & 1) != 0);
}
}
}
for sfb in 0..self.fbs.len() {
for row in 0..18 {
for col in 0..5 {
let sz = if col == 0 { 8 } else { 7 };
for j in 0..sz {
res.push((self.uim[fb][sfb][row][col] >> j & 1) != 0);
}
}
}
}
}
} else {
for row in 0..108 {
for col in 0..15 {
for fb in 0..self.fbs.len() {
let sz = if col < 9 { 8 } else { 6 };
for j in 0..sz {
res.push((self.fbs[fb][row][col] >> j & 1) != 0);
}
}
}
}
}
res
}

fn put_bit(&mut self, fb: usize, row: usize, col: usize, bit: usize, val: bool) {
if val {
self.fbs[fb][row][col] |= 1 << bit;
} else {
self.fbs[fb][row][col] &= !(1 << bit);
}
}

fn put_global(&mut self, crd: GlobalBitCoord, val: bool) {
self.put_bit(
crd.fb as usize,
crd.row as usize,
crd.column as usize,
crd.bit as usize,
val,
);
}

fn put_fb(&mut self, fb: usize, crd: FbBitCoord, val: bool) {
self.put_bit(
fb,
crd.row as usize,
crd.column as usize,
crd.bit as usize,
val,
);
}

fn put_mc(&mut self, fb: usize, mc: usize, row: usize, val: bool) {
self.put_bit(fb, row, mc % 9, 6 + mc / 9, val);
}

fn put_pt(&mut self, fb: usize, mc: usize, pt: usize, imux: usize, pol: bool, val: bool) {
self.put_bit(
fb,
imux * 2 + usize::from(pol),
pt + (mc % 3) * 5,
mc / 3,
val,
);
}

fn put_uim(&mut self, fb: usize, sfb: usize, imux: usize, mc: usize, val: bool) {
if val {
self.uim[fb][sfb][mc][imux % 5] |= 1 << (imux / 5);
} else {
self.uim[fb][sfb][mc][imux % 5] &= !(1 << (imux / 5));
}
}
}

fn write_jed(fname: impl AsRef<Path>, dev: &str, bits: &BitVec) -> Result<(), Box<dyn Error>> {
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(())
}

fn set_tile_item<T: Copy>(
tile: &Tile<T>,
device: &Device,
item: &str,
mut put_bit: impl FnMut(T, bool),
) {
let is_large = device.io_special.contains_key("GOE2");
if let Some((name, val)) = item.split_once('=') {
let item = tile.items.get(name).unwrap_or_else(|| {
&tile.items[&format!("{}.{}", name, if is_large { "LARGE" } else { "SMALL" })]
});
match item {
TileItem::Enum(item) => {
let val = &item.values[val];
for (k, v) in item.bits.iter().zip(val.iter()) {
put_bit(*k, *v);
}
}
TileItem::BitVec(item) => {
assert_eq!(val.len(), item.bits.len());
for (k, v) in item.bits.iter().zip(val.chars().rev()) {
put_bit(
*k,
match v {
'0' => false,
'1' => true,
_ => unreachable!(),
} ^ item.invert,
)
}
}
}
} else {
let (name, val) = if let Some(name) = item.strip_prefix('!') {
(name, false)
} else {
(item, true)
};
let item = &tile.items[name];
match item {
TileItem::Enum(_) => unreachable!(),
TileItem::BitVec(item) => {
assert_eq!(item.bits.len(), 1);
put_bit(item.bits[0], val ^ item.invert);
}
}
}
}

#[derive(Parser)]
struct Args {
dbdir: PathBuf,
src: PathBuf,
jed: PathBuf,
}

pub fn main() -> Result<(), Box<dyn Error>> {
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 dbfn = if dev.ends_with("xv") {
args.dbdir.join("xc9500xv.zstd")
} else if dev.ends_with("xl") {
args.dbdir.join("xc9500xl.zstd")
} else {
args.dbdir.join("xc9500.zstd")
};
let db = Database::from_file(dbfn)?;
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();
let mut fb_bits = db.fb_bits.clone();
for (k, v) in device.imux_bits.clone().items {
fb_bits.items.insert(k, v);
}
match pref[..] {
["GLOBAL"] => {
for item in suf {
set_tile_item(&db.global_bits, device, item, |crd, val| {
bs.put_global(crd, val)
});
}
}
["FB", fb] => {
let fb: usize = fb.parse()?;
for item in suf {
set_tile_item(&fb_bits, device, item, |crd, val| bs.put_fb(fb, crd, val));
}
}
["MC", fb, mc] => {
let fb: usize = fb.parse()?;
let mc: usize = mc.parse()?;
for item in suf {
set_tile_item(&db.mc_bits, device, item, |crd, val| {
bs.put_mc(fb, mc, crd as usize, val)
});
}
}
["PT", fb, mc, pt] => {
let fb: usize = fb.parse()?;
let mc: usize = mc.parse()?;
let pt: usize = pt.parse()?;
for item in suf {
let (imux, pol) = if let Some(x) = item.strip_prefix('!') {
(x.parse()?, false)
} else {
(item.parse()?, true)
};
bs.put_pt(fb, mc, pt, imux, pol, true);
}
}
["UIM", fb, imux] => {
let fb: usize = fb.parse()?;
let imux: usize = imux.parse()?;
for item in suf {
let (sfb, mc) = item.split_once('.').unwrap();
let sfb: usize = sfb.parse()?;
let mc: usize = mc.parse()?;
bs.put_uim(fb, sfb, imux, mc, true);
}
}

_ => panic!("weird line {line}"),
}
}
let fuses = bs.to_jed();
write_jed(args.jed, dev, &fuses)?;

Ok(())
}
Loading

0 comments on commit 4ddd468

Please sign in to comment.