Skip to main content

sftool_lib/common/
sifli_debug.rs

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