Skip to main content

armv8_single_step_pcsr/
armv8-single-step-pcsr.rs

1use std::cell::RefCell;
2use std::rc::Rc;
3use std::ops::DerefMut;
4use std::num::ParseIntError;
5use std::time::Instant;
6
7use jtag_taps::cable::{self, Cable};
8use jtag_taps::statemachine::JtagSM;
9use jtag_taps::taps::Taps;
10
11use jtag_adi::{ArmDebugInterface, MemAP};
12
13use clap::Parser;
14
15#[derive(Parser, Debug)]
16#[command(author, version, about, long_about = None)]
17struct Args {
18    #[arg(short, long)]
19    cable: String,
20    #[arg(short, long)]
21    baud: u32,
22    #[arg(short, long, default_value_t = 0)]
23    /// Which JTAG TAP to use
24    tap_index: usize,
25    #[arg(short, long, default_value_t = 0)]
26    /// Which access port to use
27    ap_num: u32,
28    #[arg(long)]
29    cpu_base: String,
30    #[arg(long)]
31    cti_base: String,
32    command: Option<String>,
33}
34
35fn cpu_halt<T,U>(mem: &mut MemAP<T>, cti_base: u32)
36    where T: DerefMut<Target=U>,
37          U: Cable + ?Sized
38{
39    // Gate all
40    mem.write(cti_base + 0x140, 0).expect("write ctigate");
41
42    // Enable CTIOUTEN for channel 0
43    mem.write(cti_base + 0x0a0, 1).expect("write ctiouten");
44
45    // Generate HALT to core 0
46    mem.write(cti_base + 0x01c, 1).expect("write ctiouten");
47
48    // ACK the halt
49    mem.write(cti_base + 0x010, 3).expect("write ctiouten");
50    // Wait for ACK
51    while mem.read(cti_base + 0x134).unwrap() != 0 {}
52}
53
54fn parse_int(x: &str) -> Result<u32, ParseIntError> {
55    if x.starts_with("0x") {
56        let len = x.len();
57        u32::from_str_radix(&x[2..len], 16)
58    } else {
59        str::parse(&x)
60    }
61}
62
63fn main() {
64    let args = Args::parse();
65    let cable = cable::new_from_string(&args.cable, args.baud).expect("cable");
66    let jtag = JtagSM::new(cable);
67    let mut taps = Taps::new(jtag);
68    taps.detect();
69
70    // IDCODE instruction
71    let ir = vec![14];
72    taps.select_tap(0, &ir);
73    //let dr = taps.read_dr(32);
74    //let idcode = u32::from_le_bytes(dr.try_into().unwrap());
75    //assert_eq!(idcode, 0x6ba00477);
76
77    let adi = Rc::new(RefCell::new(ArmDebugInterface::new(taps)));
78    let mut mem = MemAP::new(adi.clone(), args.ap_num);
79
80    let cpu_base = parse_int(&args.cpu_base).expect("invalid cpu base");
81    let edprsr = mem.read(cpu_base + 0x314).expect("read edprsr");
82    //println!("edprsr {:x}", edprsr);
83    assert!(edprsr & 1 == 1);
84
85    // Clear OS lock
86    mem.write(cpu_base + 0x300, 0).expect("write oslar");
87
88    // Clear software lock lock
89    mem.write(cpu_base + 0xfb0, 0xC5ACCE55).expect("write oslar");
90    let oslar = mem.read(cpu_base + 0xfb4).expect("read oslar");
91    //println!("swlck {:x}", oslar);
92    assert_eq!(oslar & 2, 0);
93
94    // Enable halting debug
95    let mut edscr = mem.read(cpu_base + 0x088).expect("read edscr");
96    //println!("edscr {:x}", edscr);
97    edscr |= 1 << 14;
98    mem.write(cpu_base + 0x088, edscr).expect("write edscr");
99
100    //// Unlock CTI
101    let cti_base = parse_int(&args.cti_base).expect("invalid cti base");
102    mem.write(cti_base + 0xfb0, 0xC5ACCE55).expect("write cti");
103
104    //// Enable CTI
105    let mut cti = mem.read(cti_base).expect("read cti");
106    //println!("cti {:x}", cti);
107    cti |= 1;
108    mem.write(cti_base, cti).expect("write cti");
109    let cti = mem.read(cti_base).expect("read cti");
110    //println!("cti {:x}", cti);
111    assert_eq!(cti & 1, 1);
112
113    let eddevid = mem.read(cpu_base + 0xfc8).expect("read edscr");
114    if eddevid & 7 == 0 {
115        eprintln!("CPU must support EDPCSR!");
116        return;
117    }
118
119    // Must be in halt state
120    cpu_halt(&mut mem, cti_base);
121    // enable single step
122    mem.write(cpu_base + 0x024, 1 << 2).expect("write edecr");
123
124    // pull these writes out of the loop for performance
125    mem.write_nocheck(cti_base + 0x140, 0).expect("write ctigate");
126    mem.write_nocheck(cti_base + 0x0a4, 2).expect("write ctiouten");
127
128    let start = Instant::now();
129    let mut count = 0;
130    loop {
131        mem.queue_read(cpu_base + 0x0ac).expect("read edpcsr");
132        mem.queue_read(cpu_base + 0x0a0).expect("read edpcsr");
133        let pc_hi = mem.finish_read().expect("read edpcsr");
134        let pc_lo = mem.finish_read().expect("read edpcsr");
135        println!("pc {:x}{:x}", pc_hi, pc_lo);
136        count += 1;
137        if count % 1000 == 0 {
138            let delta = start.elapsed().as_millis();
139            eprintln!("IPS {}", count * 1000 / delta);
140        }
141
142        // resume the CPU so it can run one instruction
143        mem.write_nocheck(cti_base + 0x01c, 2).expect("write ctiouten");
144    }
145}