use std::cell::RefCell;
use std::ops::DerefMut;
use std::rc::Rc;
use jtag_taps::cable::Cable;
use jtag_taps::taps::Taps;
pub enum Port {
DP = 10,
AP = 11,
}
pub enum DPReg {
Abort = 0,
CtrlStat = 1,
Select = 2,
Rdbuff = 3,
}
pub struct ArmDebugInterface<T> {
taps: Taps<T>,
lastbank: u32,
lastir: Vec<u8>,
}
impl<T, U> ArmDebugInterface<T>
where
T: DerefMut<Target = U>,
U: Cable + ?Sized,
{
pub fn new(taps: Taps<T>) -> Self {
let mut adi = Self {
taps,
lastbank: 0,
lastir: vec![],
};
adi.write_adi_nobank(
Port::DP,
DPReg::CtrlStat as u8,
1 << 30 | 1 << 28 | 1 << 5 | 1 << 1,
true,
)
.expect("clear errors");
adi
}
fn write_ir(&mut self, ir: &[u8]) {
if self.lastir != ir {
self.taps.write_ir(ir);
self.lastir = ir.to_vec();
}
}
fn parse_ack(mut dr: Vec<u8>) -> Result<u32, u8> {
dr.push(0);
dr.push(0);
dr.push(0);
let val = u64::from_le_bytes(dr.try_into().unwrap());
let val = val & ((1 << 35) - 1);
let ack = val & 7;
if ack != 2 {
return Err(ack as u8);
}
Ok((val >> 3) as u32)
}
pub fn read_adi_nobank(&mut self, port: Port, reg: u8) -> Result<u32, u8> {
let ir = [port as u8];
self.write_ir(&ir);
let buf = [(reg << 1) | 1, 0, 0, 0, 0];
self.taps.write_dr(&buf, 3);
let mut dr = self.taps.read_dr(35);
dr.push(0);
dr.push(0);
dr.push(0);
let val = u64::from_le_bytes(dr.try_into().unwrap());
let val = val & ((1 << 35) - 1);
let ack = val & 7;
if ack != 2 {
return Err(ack as u8);
}
let val = (val >> 3) as u32;
Ok(val)
}
pub fn write_adi_nobank(
&mut self,
port: Port,
reg: u8,
val: u32,
check: bool,
) -> Result<(), u8> {
let ir = [port as u8];
let mut val = val as u64;
val <<= 3;
val |= (reg << 1) as u64;
let bytes = val.to_le_bytes();
loop {
self.write_ir(&ir);
self.taps.write_dr(&bytes[0..5], 3);
if !check {
return Ok(());
} else {
let mut dr = self.taps.read_dr(35);
dr.push(0);
dr.push(0);
dr.push(0);
let val = u64::from_le_bytes(dr.try_into().unwrap());
let val = val & ((1 << 35) - 1);
let ack = val & 7;
if ack == 2 {
return Ok(());
}
if ack == 1 {
continue;
}
return Err(ack as u8);
}
}
}
pub fn bank_select(&mut self, apsel: u32, apbank: u32, dpbank: u32) {
let val = (apsel << 24) | (apbank << 4) | dpbank;
if val != self.lastbank {
self.write_adi_nobank(Port::DP, DPReg::Select as u8, val, true)
.expect("bank sel");
self.lastbank = val;
}
}
pub fn read_adi(&mut self, apsel: u32, port: Port, mut reg: u8) -> Result<u32, u8> {
let bank = reg >> 2;
reg &= 3;
self.bank_select(apsel, bank as u32, 0);
self.read_adi_nobank(port, reg)
}
pub fn write_adi(&mut self, apsel: u32, port: Port, mut reg: u8, val: u32) -> Result<(), u8> {
let bank = reg >> 2;
reg &= 3;
self.bank_select(apsel, bank as u32, bank as u32);
self.write_adi_nobank(port, reg, val, true)
}
pub fn write_adi_nocheck(
&mut self,
apsel: u32,
port: Port,
mut reg: u8,
val: u32,
) -> Result<(), u8> {
let bank = reg >> 2;
reg &= 3;
self.bank_select(apsel, bank as u32, bank as u32);
self.write_adi_nobank(port, reg, val, false)
}
pub fn read_adi_pipelined(
&mut self,
apsel: u32,
port: Port,
reg: &[u8],
) -> Result<Vec<u32>, u8> {
let bank = reg[0] >> 2;
self.bank_select(apsel, bank as u32, 0);
let ir = [port as u8];
self.write_ir(&ir);
let buf = [((reg[0] & 3) << 1) | 1, 0, 0, 0, 0];
self.taps.write_dr(&buf, 3);
let mut result = vec![];
for r in ®[1..] {
assert_eq!(r >> 2, reg[0] >> 2);
let buf = [((r & 3) << 1) | 1, 0, 0, 0, 0];
let dr = self.taps.read_write_dr(&buf, 3);
result.push(Self::parse_ack(dr)?);
}
let dr = self.taps.read_dr(35);
result.push(Self::parse_ack(dr)?);
Ok(result)
}
pub fn write_adi_pipelined(
&mut self,
apsel: u32,
port: Port,
reg: &[(u8, u32)],
) -> Result<(), u8> {
let bank = reg[0].0 >> 2;
self.bank_select(apsel, bank as u32, 0);
let ir = [port as u8];
self.write_ir(&ir);
for (r, val) in reg {
assert_eq!(r >> 2, reg[0].0 >> 2);
let mut val = *val as u64;
val <<= 3;
val |= ((r & 3) << 1) as u64;
let bytes = val.to_le_bytes();
self.taps.write_dr(&bytes[0..5], 3);
}
Ok(())
}
}
#[allow(clippy::upper_case_acronyms)]
enum MemAPReg {
CSW = 0,
TAR = 1,
DRW = 3,
}
pub struct MemAP<T> {
adi: Rc<RefCell<ArmDebugInterface<T>>>,
apsel: u32,
csw: u32,
}
impl<T, U> MemAP<T>
where
T: DerefMut<Target = U>,
U: Cable + ?Sized,
{
pub fn new(adi: Rc<RefCell<ArmDebugInterface<T>>>, apsel: u32) -> Self {
let csw = adi
.borrow_mut()
.read_adi(apsel, Port::AP, MemAPReg::CSW as u8)
.expect("read csw");
Self { adi, apsel, csw }
}
pub fn write_csw(&mut self, csw: u32) -> Result<(), u8> {
if csw != self.csw {
self.adi
.borrow_mut()
.write_adi(self.apsel, Port::AP, MemAPReg::CSW as u8, csw)?;
self.csw = csw;
}
Ok(())
}
pub fn read(&mut self, addr: u32) -> Result<u32, u8> {
self.adi
.borrow_mut()
.write_adi(self.apsel, Port::AP, MemAPReg::TAR as u8, addr)?;
let val = self
.adi
.borrow_mut()
.read_adi(self.apsel, Port::AP, MemAPReg::DRW as u8)?;
let stat = self
.adi
.borrow_mut()
.read_adi(self.apsel, Port::DP, DPReg::CtrlStat as u8)?;
if stat & 5 != 0 {
return Err(5);
}
Ok(val)
}
pub fn write(&mut self, addr: u32, value: u32) -> Result<(), u8> {
self.adi
.borrow_mut()
.write_adi(self.apsel, Port::AP, MemAPReg::TAR as u8, addr)?;
self.adi
.borrow_mut()
.write_adi(self.apsel, Port::AP, MemAPReg::DRW as u8, value)?;
let stat = self
.adi
.borrow_mut()
.read_adi(self.apsel, Port::DP, DPReg::CtrlStat as u8)?;
if stat & 5 != 0 {
return Err(5);
}
Ok(())
}
pub fn read_dcc(&mut self, debug_base: u32, check_fifo: bool) -> Result<Option<u32>, u8> {
self.write_csw(self.csw | (1 << 4))?;
let mut adi = self.adi.borrow_mut();
adi.write_adi_nocheck(self.apsel, Port::AP, MemAPReg::TAR as u8, debug_base + 0x88)
.expect("write tar");
let port = Port::AP;
let mut drw = MemAPReg::DRW as u8;
let bank = drw >> 2;
drw &= 3;
adi.bank_select(1, bank as u32, 0);
let ir = [port as u8];
adi.write_ir(&ir);
let buf = [(drw << 1) | 1, 0, 0, 0, 0];
adi.taps.write_dr(&buf, 3);
if check_fifo {
let dr = adi.taps.read_write_dr(&buf, 3);
let dscr = ArmDebugInterface::<T>::parse_ack(dr)?;
if dscr & (1 << 29) == 0 {
return Ok(None);
}
} else {
adi.taps.write_dr(&buf, 3);
}
let dr = adi.taps.read_dr(35);
Ok(Some(ArmDebugInterface::<T>::parse_ack(dr)?))
}
pub fn read_block(
&mut self,
addr: u32,
len: usize,
check_status: bool,
) -> Result<Vec<u32>, u8> {
self.write_csw(self.csw | (1 << 4))?;
self.adi
.borrow_mut()
.write_adi(self.apsel, Port::AP, MemAPReg::TAR as u8, addr)?;
let reg = vec![MemAPReg::DRW as u8; len];
let val = self
.adi
.borrow_mut()
.read_adi_pipelined(self.apsel, Port::AP, ®)?;
if check_status {
let stat =
self.adi
.borrow_mut()
.read_adi(self.apsel, Port::DP, DPReg::CtrlStat as u8)?;
if stat & 5 != 0 {
return Err(5);
}
}
Ok(val)
}
pub fn write_block(&mut self, addr: u32, data: &[u32], check_status: bool) -> Result<(), u8> {
self.write_csw(self.csw | (1 << 4))?;
self.adi
.borrow_mut()
.write_adi(self.apsel, Port::AP, MemAPReg::TAR as u8, addr)?;
let reg: Vec<(u8, u32)> = data.iter().map(|x| (MemAPReg::DRW as u8, *x)).collect();
self.adi
.borrow_mut()
.write_adi_pipelined(self.apsel, Port::AP, ®)?;
if check_status {
let stat =
self.adi
.borrow_mut()
.read_adi(self.apsel, Port::DP, DPReg::CtrlStat as u8)?;
if stat & 5 != 0 {
return Err(5);
}
}
Ok(())
}
}