sftool_lib/common/
sifli_debug.rs

1use crate::{Error, Result, SifliTool};
2use probe_rs::architecture::arm::armv8m::Dcrdr;
3use probe_rs::{MemoryMappedRegister, memory_mapped_bitfield_register};
4use std::cmp::{max, min};
5use std::fmt;
6use std::io::{BufReader, BufWriter, Read, Write};
7use std::time::{Duration, Instant};
8
9pub const START_WORD: [u8; 2] = [0x7E, 0x79];
10pub const DEFUALT_RECV_TIMEOUT: Duration = Duration::from_secs(3);
11
12#[derive(Debug)]
13pub enum SifliUartCommand<'a> {
14    Enter,
15    Exit,
16    MEMRead { addr: u32, len: u16 },
17    MEMWrite { addr: u32, data: &'a [u32] },
18}
19
20#[derive(Debug)]
21pub enum SifliUartResponse {
22    Enter,
23    Exit,
24    MEMRead { data: Vec<u8> },
25    MEMWrite,
26}
27
28#[derive(Debug)]
29pub enum RecvError {
30    Timeout,
31    InvalidHeaderLength,
32    InvalidHeaderChannel,
33    ReadError(std::io::Error),
34    InvalidResponse(u8),
35}
36
37impl From<RecvError> for Error {
38    fn from(err: RecvError) -> Self {
39        match err {
40            RecvError::Timeout => Error::timeout("receiving UART frame"),
41            RecvError::InvalidHeaderLength => Error::protocol("invalid frame length"),
42            RecvError::InvalidHeaderChannel => Error::protocol("invalid frame channel information"),
43            RecvError::ReadError(e) => Error::from(e),
44            RecvError::InvalidResponse(code) => {
45                Error::protocol(format!("invalid response code: {:#04X}", code))
46            }
47        }
48    }
49}
50
51impl fmt::Display for SifliUartCommand<'_> {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        match self {
54            SifliUartCommand::Enter => write!(f, "Enter"),
55            SifliUartCommand::Exit => write!(f, "Exit"),
56            SifliUartCommand::MEMRead { addr, len } => {
57                write!(f, "MEMRead {{ addr: {:#X}, len: {:#X} }}", addr, len)
58            }
59            SifliUartCommand::MEMWrite { addr, data } => {
60                write!(f, "MEMWrite {{ addr: {:#X}, data: [", addr)?;
61                for (i, d) in data.iter().enumerate() {
62                    if i > 0 {
63                        write!(f, ", ")?;
64                    }
65                    write!(f, "{:#X}", d)?;
66                }
67                write!(f, "] }}")
68            }
69        }
70    }
71}
72
73impl fmt::Display for SifliUartResponse {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        match self {
76            SifliUartResponse::Enter => write!(f, "Enter"),
77            SifliUartResponse::Exit => write!(f, "Exit"),
78            SifliUartResponse::MEMRead { data } => {
79                write!(f, "MEMRead {{ data: [")?;
80                for (i, byte) in data.iter().enumerate() {
81                    if i > 0 {
82                        write!(f, ", ")?;
83                    }
84                    write!(f, "{:#04X}", byte)?;
85                }
86                write!(f, "] }}")
87            }
88            SifliUartResponse::MEMWrite => write!(f, "MEMWrite"),
89        }
90    }
91}
92
93memory_mapped_bitfield_register! {
94    pub struct Dcrsr(u32);
95    0xE000_EDF4, "DCRSR",
96    impl From;
97    pub _, set_regwnr: 16;
98    // If the processor does not implement the FP extension the REGSEL field is bits `[4:0]`, and bits `[6:5]` are Reserved, SBZ.
99    pub _, set_regsel: 6,0;
100}
101
102memory_mapped_bitfield_register! {
103    pub struct Dhcsr(u32);
104    0xE000_EDF0, "DHCSR",
105    impl From;
106    pub s_reset_st, _: 25;
107    pub s_retire_st, _: 24;
108    pub s_lockup, _: 19;
109    pub s_sleep, _: 18;
110    pub s_halt, _: 17;
111    pub s_regrdy, _: 16;
112    pub c_maskints, set_c_maskints: 3;
113    pub c_step, set_c_step: 2;
114    pub c_halt, set_c_halt: 1;
115    pub c_debugen, set_c_debugen: 0;
116}
117
118impl Dhcsr {
119    /// This function sets the bit to enable writes to this register.
120    ///
121    /// C1.6.3 Debug Halting Control and Status Register, DHCSR:
122    /// Debug key:
123    /// Software must write 0xA05F to this field to enable write accesses to bits
124    /// `[15:0]`, otherwise the processor ignores the write access.
125    pub fn enable_write(&mut self) {
126        self.0 &= !(0xffff << 16);
127        self.0 |= 0xa05f << 16;
128    }
129}
130
131pub trait SifliDebug {
132    fn debug_command(&mut self, command: SifliUartCommand) -> Result<SifliUartResponse>;
133    fn debug_write_word32(&mut self, addr: u32, data: u32) -> Result<()>;
134    fn debug_read_word32(&mut self, addr: u32) -> Result<u32>;
135    fn debug_write_core_reg(&mut self, reg: u16, data: u32) -> Result<()>;
136    fn debug_write_memory(&mut self, addr: u32, data: &[u8]) -> Result<()>;
137    fn debug_run(&mut self) -> Result<()>;
138    fn debug_halt(&mut self) -> Result<()>;
139    fn debug_step(&mut self) -> Result<()>;
140}
141
142// Trait defining chip-specific frame formatting behavior
143pub trait ChipFrameFormat {
144    /// Create chip-specific header with appropriate endianness and fields
145    fn create_header(len: u16) -> Vec<u8>;
146
147    /// Parse received frame header and return payload size
148    fn parse_frame_header(
149        reader: &mut BufReader<Box<dyn Read + Send>>,
150    ) -> std::result::Result<usize, RecvError>;
151
152    /// Encode command data with chip-specific endianness
153    fn encode_command_data(command: &SifliUartCommand) -> Vec<u8>;
154
155    /// Decode response data with chip-specific endianness
156    fn decode_response_data(data: &[u8]) -> u32;
157
158    /// Apply chip-specific address mapping (default: no mapping)
159    fn map_address(addr: u32) -> u32 {
160        addr
161    }
162}
163
164// Common implementation for communication
165pub fn send_command<F: ChipFrameFormat>(
166    writer: &mut BufWriter<Box<dyn Write + Send>>,
167    command: &SifliUartCommand,
168) -> Result<()> {
169    let send_data = F::encode_command_data(command);
170    let header = F::create_header(send_data.len() as u16);
171
172    writer.write_all(&header)?;
173    writer.write_all(&send_data)?;
174    writer.flush()?;
175    Ok(())
176}
177
178pub fn recv_response<F: ChipFrameFormat>(
179    reader: &mut BufReader<Box<dyn Read + Send>>,
180) -> Result<SifliUartResponse> {
181    let start_time = Instant::now();
182    let mut temp: Vec<u8> = vec![];
183
184    // 步骤1: 找到帧起始标记 (START_WORD)
185    tracing::debug!("Waiting for frame start marker...");
186    let mut buffer = vec![];
187
188    loop {
189        if start_time.elapsed() >= DEFUALT_RECV_TIMEOUT {
190            tracing::warn!(
191                "Receive timeout: {} seconds",
192                DEFUALT_RECV_TIMEOUT.as_secs()
193            );
194            return Err(RecvError::Timeout.into());
195        }
196
197        let mut byte = [0; 1];
198        match reader.read_exact(&mut byte) {
199            Ok(_) => {
200                // 处理帧检测逻辑
201                if byte[0] == START_WORD[0] || (buffer.len() == 1 && byte[0] == START_WORD[1]) {
202                    buffer.push(byte[0]);
203
204                    // 检查是否找到完整的START_WORD
205                    if buffer.ends_with(&START_WORD) {
206                        tracing::debug!("Frame start marker found: {:02X?}", START_WORD);
207                        break;
208                    }
209                } else {
210                    // 重置缓冲区
211                    buffer.clear();
212                }
213
214                // 缓冲区超过2个字节但没有匹配START_WORD,清除旧数据
215                if buffer.len() > 2 {
216                    buffer.clear();
217                }
218            }
219            Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
220                // 对于非阻塞IO,继续尝试
221                continue;
222            }
223            Err(e) => {
224                tracing::error!("Error reading frame start marker: {}", e);
225                continue; // 继续尝试读取下一个字节
226            }
227        }
228    }
229
230    temp.extend_from_slice(&buffer);
231
232    // 步骤2: 使用芯片特定的帧头解析
233    let payload_size = F::parse_frame_header(reader)?;
234    tracing::debug!("Received packet length: {} bytes", payload_size);
235
236    // 步骤3: 读取有效载荷数据
237    tracing::debug!("Reading payload data ({} bytes)...", payload_size);
238    let mut recv_data = vec![];
239
240    while recv_data.len() < payload_size {
241        let mut byte = [0; 1];
242        match reader.read_exact(&mut byte) {
243            Ok(_) => {
244                recv_data.push(byte[0]);
245            }
246            Err(e) => {
247                tracing::error!("Failed to read payload data: {}", e);
248                return Err(RecvError::ReadError(e).into());
249            }
250        }
251    }
252
253    temp.extend_from_slice(&recv_data);
254
255    // 步骤4: 解析响应数据
256    if recv_data.is_empty() {
257        tracing::error!("Received empty payload data");
258        return Err(RecvError::InvalidResponse(0).into());
259    }
260
261    let response_code = recv_data[0];
262    match response_code {
263        0xD1 => {
264            tracing::info!("Received Enter command response");
265            Ok(SifliUartResponse::Enter)
266        }
267        0xD0 => {
268            tracing::info!("Received Exit command response");
269            Ok(SifliUartResponse::Exit)
270        }
271        0xD2 => {
272            // 提取数据部分,跳过响应代码和最后的校验字节
273            let data = if recv_data.len() > 1 {
274                recv_data[1..recv_data.len() - 1].to_vec()
275            } else {
276                Vec::new()
277            };
278            tracing::info!(
279                "Received memory read response, data length: {} bytes",
280                data.len()
281            );
282            Ok(SifliUartResponse::MEMRead { data })
283        }
284        0xD3 => {
285            tracing::info!("Received memory write response");
286            Ok(SifliUartResponse::MEMWrite)
287        }
288        _ => {
289            tracing::error!("Received unknown response code: {:#04X}", response_code);
290            Err(RecvError::InvalidResponse(response_code).into())
291        }
292    }
293}
294
295// Common helper functions that implement shared debug operations
296pub mod common_debug {
297    use super::*;
298
299    /// Common implementation for debug_command
300    pub fn debug_command_impl<T: SifliTool, F: ChipFrameFormat>(
301        tool: &mut T,
302        command: SifliUartCommand,
303    ) -> Result<SifliUartResponse> {
304        tracing::info!("Command: {}", command);
305        let writer: Box<dyn Write + Send> = tool.port().try_clone()?;
306        let mut buf_writer = BufWriter::new(writer);
307
308        let reader: Box<dyn Read + Send> = tool.port().try_clone()?;
309        let mut buf_reader = BufReader::new(reader);
310
311        let ret = send_command::<F>(&mut buf_writer, &command);
312        if let Err(e) = ret {
313            tracing::error!("Command send error: {:?}", e);
314            return Err(e);
315        }
316
317        match command {
318            SifliUartCommand::Exit => Ok(SifliUartResponse::Exit),
319            _ => recv_response::<F>(&mut buf_reader),
320        }
321    }
322
323    /// Common implementation for debug_read_word32
324    pub fn debug_read_word32_impl<T: SifliTool, F: ChipFrameFormat>(
325        tool: &mut T,
326        addr: u32,
327    ) -> Result<u32> {
328        let mapped_addr = F::map_address(addr);
329        let command = SifliUartCommand::MEMRead {
330            addr: mapped_addr,
331            len: 1,
332        };
333
334        // Call debug_command_impl directly instead of using the trait method
335        match debug_command_impl::<T, F>(tool, command) {
336            Ok(SifliUartResponse::MEMRead { data }) => {
337                if data.len() != 4 {
338                    return Err(Error::invalid_input("invalid response length"));
339                }
340                let value = F::decode_response_data(&data);
341                Ok(value)
342            }
343            Ok(_) => Err(Error::invalid_input("invalid response")),
344            Err(e) => Err(e),
345        }
346    }
347
348    /// Common implementation for debug_write_word32
349    pub fn debug_write_word32_impl<T: SifliTool, F: ChipFrameFormat>(
350        tool: &mut T,
351        addr: u32,
352        data: u32,
353    ) -> Result<()> {
354        let mapped_addr = F::map_address(addr);
355        let command = SifliUartCommand::MEMWrite {
356            addr: mapped_addr,
357            data: &[data],
358        };
359        match debug_command_impl::<T, F>(tool, command) {
360            Ok(SifliUartResponse::MEMWrite) => Ok(()),
361            Ok(_) => Err(Error::invalid_input("invalid response")),
362            Err(e) => Err(e),
363        }
364    }
365
366    /// Common implementation for debug_write_memory with chip-specific mapping
367    pub fn debug_write_memory_impl<T: SifliTool, F: ChipFrameFormat>(
368        tool: &mut T,
369        address: u32,
370        data: &[u8],
371    ) -> Result<()> {
372        if data.is_empty() {
373            return Ok(());
374        }
375
376        // Apply chip-specific address mapping first
377        let mut mapped_address = F::map_address(address);
378
379        // Then apply the existing mapping logic (common to all chips)
380        mapped_address = if (mapped_address & 0xff000000) == 0x12000000 {
381            (mapped_address & 0x00ffffff) | 0x62000000
382        } else {
383            mapped_address
384        };
385
386        let addr_usize = mapped_address as usize;
387        // Calculate the start address and end address after alignment
388        let start_aligned = addr_usize - (addr_usize % 4);
389        let end_aligned = (addr_usize + data.len()).div_ceil(4) * 4;
390        let total_bytes = end_aligned - start_aligned;
391        let total_words = total_bytes / 4;
392
393        let mut buffer = vec![0u8; total_bytes];
394
395        for i in 0..total_words {
396            let block_addr = start_aligned + i * 4;
397            let block_end = block_addr + 4;
398
399            // Determine if the current 4-byte block is 'completely overwritten' by the new data written to it
400            // If the block is completely in the new data area, then copy the new data directly
401            if block_addr >= addr_usize && block_end <= addr_usize + data.len() {
402                let offset_in_data = block_addr - addr_usize;
403                buffer[i * 4..i * 4 + 4].copy_from_slice(&data[offset_in_data..offset_in_data + 4]);
404            } else {
405                // For the rest of the cases (header or tail incomplete overwrite):
406                // Call MEMRead first to read out the original 4-byte block.
407                let resp = debug_command_impl::<T, F>(
408                    tool,
409                    SifliUartCommand::MEMRead {
410                        addr: block_addr as u32,
411                        len: 1,
412                    },
413                )?;
414                let mut block: [u8; 4] = match resp {
415                    SifliUartResponse::MEMRead { data: d } if d.len() == 4 => {
416                        // Apply chip-specific decoding for proper endianness
417                        let value = F::decode_response_data(&d);
418                        value.to_le_bytes()
419                    }
420                    _ => {
421                        return Err(Error::invalid_input("invalid response length"));
422                    }
423                };
424                // Calculate the overlap of the block with the new data area
425                let overlap_start = max(block_addr, addr_usize);
426                let overlap_end = min(block_end, addr_usize + data.len());
427                if overlap_start < overlap_end {
428                    let in_block_offset = overlap_start - block_addr;
429                    let in_data_offset = overlap_start - addr_usize;
430                    let overlap_len = overlap_end - overlap_start;
431                    block[in_block_offset..in_block_offset + overlap_len]
432                        .copy_from_slice(&data[in_data_offset..in_data_offset + overlap_len]);
433                }
434                buffer[i * 4..i * 4 + 4].copy_from_slice(&block);
435            }
436        }
437
438        let words: Vec<u32> = buffer
439            .chunks_exact(4)
440            .map(|chunk| u32::from_le_bytes(chunk.try_into().expect("chunk length is 4")))
441            .collect();
442
443        // Write the entire alignment area at once
444        debug_command_impl::<T, F>(
445            tool,
446            SifliUartCommand::MEMWrite {
447                addr: start_aligned as u32,
448                data: &words,
449            },
450        )?;
451
452        Ok(())
453    }
454
455    /// Common implementation for debug_write_core_reg
456    pub fn debug_write_core_reg_impl<T: SifliTool, F: ChipFrameFormat>(
457        tool: &mut T,
458        addr: u16,
459        value: u32,
460    ) -> Result<()> {
461        debug_write_word32_impl::<T, F>(tool, Dcrdr::get_mmio_address() as u32, value)?;
462
463        let mut dcrsr_val = Dcrsr(0);
464        dcrsr_val.set_regwnr(true); // Perform a write.
465        dcrsr_val.set_regsel(addr.into()); // The address of the register to write.
466
467        debug_write_word32_impl::<T, F>(tool, Dcrsr::get_mmio_address() as u32, dcrsr_val.into())?;
468
469        // self.wait_for_core_register_transfer(Duration::from_millis(100))?;
470        std::thread::sleep(Duration::from_millis(10));
471        Ok(())
472    }
473
474    /// Common implementation for debug_step
475    pub fn debug_step_impl<T: SifliTool, F: ChipFrameFormat>(tool: &mut T) -> Result<()> {
476        // 这里我们忽略了很多必要的检查,请参考probe-rs源码
477        let mut value = Dhcsr(0);
478        // Leave halted state.
479        // Step one instruction.
480        value.set_c_step(true);
481        value.set_c_halt(false);
482        value.set_c_debugen(true);
483        value.set_c_maskints(true);
484        value.enable_write();
485
486        debug_write_word32_impl::<T, F>(tool, Dhcsr::get_mmio_address() as u32, value.into())?;
487
488        std::thread::sleep(Duration::from_millis(10));
489        Ok(())
490    }
491
492    /// Common implementation for debug_run
493    pub fn debug_run_impl<T: SifliTool, F: ChipFrameFormat>(tool: &mut T) -> Result<()> {
494        debug_step_impl::<T, F>(tool)?;
495        std::thread::sleep(Duration::from_millis(100));
496        let mut value = Dhcsr(0);
497        value.set_c_halt(false);
498        value.set_c_debugen(true);
499        value.enable_write();
500
501        debug_write_word32_impl::<T, F>(tool, Dhcsr::get_mmio_address() as u32, value.into())?;
502
503        std::thread::sleep(Duration::from_millis(100));
504        Ok(())
505    }
506
507    /// Common implementation for debug_halt
508    pub fn debug_halt_impl<T: SifliTool, F: ChipFrameFormat>(tool: &mut T) -> Result<()> {
509        let mut value = Dhcsr(0);
510        value.set_c_halt(true);
511        value.set_c_debugen(true);
512        value.enable_write();
513
514        debug_write_word32_impl::<T, F>(tool, Dhcsr::get_mmio_address() as u32, value.into())?;
515        std::thread::sleep(Duration::from_millis(10));
516        Ok(())
517    }
518}