pub struct Layout {
pub name: &'static str,
pub opcode: u8,
pub fields: &'static [(&'static str, u8, u8)],
}
pub const LAYOUTS: &[Layout] = &[
Layout { name: "INCRWC", opcode: 0x38, fields: &[("rwc_a", 9, 6), ("rwc_b", 13, 10), ("rwc_cr", 20, 18), ("rwc_d", 17, 14)] },
Layout {
name: "PACR",
opcode: 0x41,
fields: &[
("addr_cnt_context", 14, 13),
("addr_mode", 16, 15),
("cfg_context", 22, 21),
("concat", 4, 4),
("ctxt_ctrl", 3, 2),
("dst_access_mode", 17, 17),
("flush", 1, 1),
("last", 0, 0),
("ovrd_thread_id", 7, 7),
("read_intf_sel", 11, 8),
("row_pad_zero", 20, 18),
("zero_write", 12, 12),
],
},
Layout { name: "SETADCXX", opcode: 0x5e, fields: &[("cnt_set_mask", 23, 21), ("x_end2", 19, 10), ("x_start", 9, 0)] },
Layout { name: "SETADCXY", opcode: 0x51, fields: &[("bit_mask", 3, 0), ("ch0_x", 8, 6), ("ch0_y", 11, 9), ("ch1_x", 14, 12), ("ch1_y", 17, 15), ("cnt_set_mask", 23, 21)] },
Layout { name: "SETADCZW", opcode: 0x54, fields: &[("bit_mask", 3, 0), ("ch0_x", 8, 6), ("ch0_y", 11, 9), ("ch1_x", 14, 12), ("ch1_y", 17, 15), ("cnt_set_mask", 23, 21)] },
Layout { name: "SETC16", opcode: 0xb2, fields: &[("setc16_reg", 23, 16), ("setc16_value", 15, 0)] },
Layout { name: "SETGPR", opcode: 0x45, fields: &[("payload_sig_sel", 21, 8), ("payload_sig_sel_size", 23, 22), ("reg_index_16b", 6, 0), ("set_signals_mode", 7, 7)] },
Layout { name: "SETRWC", opcode: 0x37, fields: &[("bit_mask", 3, 0), ("clear_ab_vld", 23, 22), ("rwc_a", 9, 6), ("rwc_b", 13, 10), ("rwc_cr", 21, 18), ("rwc_d", 17, 14)] },
Layout { name: "SFPAND", opcode: 0x7e, fields: &[("imm12_math", 15, 12), ("instr_mod1", 3, 0), ("lreg_c", 11, 8), ("lreg_dest", 7, 4)] },
Layout { name: "SFPCONFIG", opcode: 0x91, fields: &[("config_dest", 7, 4), ("imm16_math", 23, 8), ("instr_mod1", 3, 0)] },
Layout { name: "SFPIADD", opcode: 0x79, fields: &[("imm12_math", 23, 12), ("instr_mod1", 3, 0), ("lreg_c", 11, 8), ("lreg_dest", 7, 4)] },
Layout { name: "SFPLOAD", opcode: 0x70, fields: &[("dest_reg_addr", 9, 0), ("instr_mod0", 19, 16), ("lreg_ind", 23, 20), ("sfpu_addr_mode", 15, 13)] },
Layout { name: "SFPLOADI", opcode: 0x71, fields: &[("imm16", 15, 0), ("instr_mod0", 19, 16), ("lreg_ind", 23, 20)] },
Layout {
name: "SFPMUL24",
opcode: 0x98,
fields: &[("instr_mod1", 3, 0), ("lreg_dest", 7, 4), ("lreg_src_a", 19, 16), ("lreg_src_b", 15, 12), ("lreg_src_c", 11, 8)],
},
Layout { name: "SFPNOP", opcode: 0x8f, fields: &[] },
Layout { name: "SFPOR", opcode: 0x7f, fields: &[("imm12_math", 15, 12), ("instr_mod1", 3, 0), ("lreg_c", 11, 8), ("lreg_dest", 7, 4)] },
Layout { name: "SFPSHFT", opcode: 0x7a, fields: &[("imm12_math", 23, 12), ("instr_mod1", 3, 0), ("lreg_c", 11, 8), ("lreg_dest", 7, 4)] },
Layout { name: "SFPSTORE", opcode: 0x72, fields: &[("dest_reg_addr", 9, 0), ("instr_mod0", 19, 16), ("lreg_ind", 23, 20), ("sfpu_addr_mode", 15, 13)] },
Layout { name: "STALLWAIT", opcode: 0xa2, fields: &[("stall_res", 23, 15), ("wait_res", 12, 0)] },
Layout {
name: "UNPACR",
opcode: 0x42,
fields: &[
("addr_cnt_context_id", 9, 8),
("addr_mode", 22, 15),
("auto_inc_context_id", 3, 3),
("cfg_context_cnt_inc", 13, 13),
("cfg_context_id", 12, 10),
("last", 0, 0),
("ovrd_thread_id", 7, 7),
("row_search", 2, 2),
("search_cache_flush", 1, 1),
("set_dat_valid", 6, 6),
("srcb_bcast", 5, 5),
("unpack_block_selection", 23, 23),
("zero_write2", 4, 4),
],
},
];
pub fn word(name: &str, fields: &[(&str, u32)]) -> u32 {
let l = LAYOUTS.iter().find(|l| l.name == name).unwrap_or_else(|| panic!("no instruction {name}"));
let mut w = u32::from(l.opcode) << 24;
for &(f, v) in fields {
let &(_, hi, lo) = l.fields.iter().find(|x| x.0 == f).unwrap_or_else(|| panic!("{name} has no field {f}"));
let width = u32::from(hi - lo + 1);
assert!(u64::from(v) < (1u64 << width), "{name}.{f} = {v} does not fit {width} bits");
w |= v << lo;
}
w
}
pub fn inline(word: u32) -> u32 {
word.rotate_left(2)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn words_match_an_independent_reading_of_the_table() {
let checks: [(u32, u32); 12] = [
(0x5420_000F, word("SETADCZW", &[("bit_mask", 0xF), ("cnt_set_mask", 1)])),
(0x5E23_FC00, word("SETADCXX", &[("x_end2", 255), ("cnt_set_mask", 1)])),
(0x4208_8081, word("UNPACR", &[("last", 1), ("ovrd_thread_id", 1), ("addr_mode", 0b0001_0001)])),
(0xA280_0002, word("STALLWAIT", &[("stall_res", 1 << 8), ("wait_res", 1 << 1)])),
(0x7004_0000, word("SFPLOAD", &[("instr_mod0", 4)])),
(0x7264_0000, word("SFPSTORE", &[("lreg_ind", 6), ("instr_mod0", 4)])),
(0x3800_8000, word("INCRWC", &[("rwc_d", 2)])),
(0x3700_0004, word("SETRWC", &[("bit_mask", 4)])),
(0x4100_0001, word("PACR", &[("last", 1)])),
(0x4500_017E, word("SETGPR", &[("reg_index_16b", 126), ("payload_sig_sel", 1)])),
(0x9100_00C0, word("SFPCONFIG", &[("config_dest", 12)])),
(0x9802_3940, word("SFPMUL24", &[("lreg_src_a", 2), ("lreg_src_b", 3), ("lreg_src_c", 9), ("lreg_dest", 4)])),
];
for (want, got) in checks {
assert_eq!(got, want, "{got:#010x} vs {want:#010x}");
}
for l in LAYOUTS {
assert!(l.opcode < 0xc0);
assert_ne!(inline(u32::from(l.opcode) << 24) & 3, 3, "{}", l.name);
}
assert_eq!(inline(0xA280_0002).rotate_right(2), 0xA280_0002);
}
#[test]
fn layouts_are_the_simulators_table() {
let Some(path) = std::env::var_os("WAI_QUANTUM_TENSIX_ISA") else { return };
let table: serde_json::Value = serde_json::from_slice(&std::fs::read(&path).expect("the table reads")).expect("the table is JSON");
for l in LAYOUTS {
let t = &table[l.name];
assert_eq!(t["opcode"].as_u64(), Some(u64::from(l.opcode)), "{} opcode", l.name);
let args = t["args"].as_object().unwrap_or_else(|| panic!("{} has no args", l.name));
assert_eq!(args.len(), l.fields.len(), "{} field count", l.name);
for &(f, hi, lo) in l.fields {
assert_eq!(args.get(f).and_then(|v| v.as_str()), Some(format!("{hi}:{lo}").as_str()), "{}.{f}", l.name);
}
}
println!("tensix layouts: {} instructions match the table", LAYOUTS.len());
}
}