Skip to main content

embedded_debugger_mcp/flash/
manager.rs

1//! Flash programming manager - Real probe-rs integration
2
3use crate::error::{Result, DebugError};
4use std::path::Path;
5use std::time::Instant;
6use tracing::{debug, info, warn};
7
8// Probe-rs imports  
9use probe_rs::{flashing::{self, FlashProgress}, Session, MemoryInterface};
10
11/// Erase operation types
12#[derive(Debug, Clone)]
13pub enum EraseType {
14    /// Erase entire flash
15    All,
16    /// Erase specific sectors
17    Sectors { address: u64, size: usize },
18}
19
20/// File format types
21#[derive(Debug, Clone)]
22pub enum FileFormat {
23    Auto,
24    Elf,
25    Hex,
26    Bin,
27}
28
29/// Erase operation result
30#[derive(Debug)]
31pub struct EraseResult {
32    pub erase_time_ms: u64,
33    pub sectors_erased: Option<usize>,
34}
35
36/// Programming operation result
37#[derive(Debug)]
38pub struct ProgramResult {
39    pub bytes_programmed: usize,
40    pub programming_time_ms: u64,
41    pub verification_result: Option<bool>,
42}
43
44/// Verification result
45#[derive(Debug)]
46pub struct VerifyResult {
47    pub success: bool,
48    pub bytes_verified: usize,
49    pub mismatches: Vec<VerifyMismatch>,
50}
51
52/// Verification mismatch
53#[derive(Debug)]
54pub struct VerifyMismatch {
55    pub address: u64,
56    pub expected: u8,
57    pub actual: u8,
58}
59
60/// Flash manager for programming operations
61pub struct FlashManager;
62
63impl FlashManager {
64    /// Create a new flash manager
65    pub fn new() -> Self {
66        Self
67    }
68
69    /// Erase flash memory
70    pub async fn erase_flash(
71        session: &mut Session,
72        erase_type: EraseType,
73    ) -> Result<EraseResult> {
74        let start_time = Instant::now();
75        
76        match erase_type {
77            EraseType::All => {
78                debug!("Starting full flash erase");
79                flashing::erase_all(session, FlashProgress::empty())
80                    .map_err(|e| DebugError::FlashOperationFailed(format!("Full erase failed: {}", e)))?;
81                
82                info!("Full flash erase completed");
83                Ok(EraseResult {
84                    erase_time_ms: start_time.elapsed().as_millis() as u64,
85                    sectors_erased: None,
86                })
87            }
88            EraseType::Sectors { address, size } => {
89                debug!("Starting sector erase at 0x{:08X}, size: {} bytes", address, size);
90                
91                // Calculate sector range - this is target-specific, using approximation
92                let sector_size = 4096; // Common sector size, should be target-specific
93                let sector_count = (size + sector_size - 1) / sector_size;
94                
95                // Use probe-rs flashing API for sector erase
96                let mut core = session.core(0)
97                    .map_err(|e| DebugError::FlashOperationFailed(format!("Failed to get core: {}", e)))?;
98                
99                // For now, we'll use memory writes to simulate erase (0xFF)
100                // Real implementation should use target-specific flash algorithms
101                let erase_data = vec![0xFFu8; size];
102                core.write(address, &erase_data)
103                    .map_err(|e| DebugError::FlashOperationFailed(format!("Sector erase failed: {}", e)))?;
104                
105                info!("Sector erase completed: {} sectors", sector_count);
106                Ok(EraseResult {
107                    erase_time_ms: start_time.elapsed().as_millis() as u64,
108                    sectors_erased: Some(sector_count),
109                })
110            }
111        }
112    }
113
114    /// Program file to flash
115    pub async fn program_file(
116        session: &mut Session,
117        file_path: &Path,
118        format: FileFormat,
119        base_address: Option<u64>,
120    ) -> Result<ProgramResult> {
121        let start_time = Instant::now();
122        
123        // Check file existence
124        if !file_path.exists() {
125            return Err(DebugError::FlashOperationFailed(format!("File not found: {}", file_path.display())));
126        }
127
128        debug!("Programming file: {}", file_path.display());
129
130        // Determine format
131        let probe_format = match format {
132            FileFormat::Auto => {
133                // Auto-detect based on extension
134                match file_path.extension().and_then(|s| s.to_str()) {
135                    Some("elf") => flashing::Format::Elf,
136                    Some("hex") => flashing::Format::Hex, 
137                    Some("bin") => flashing::Format::Bin(probe_rs::flashing::BinOptions { base_address: None, skip: 0 }),
138                    _ => return Err(DebugError::FlashOperationFailed("Cannot auto-detect file format".to_string())),
139                }
140            }
141            FileFormat::Elf => flashing::Format::Elf,
142            FileFormat::Hex => flashing::Format::Hex,
143            FileFormat::Bin => flashing::Format::Bin(probe_rs::flashing::BinOptions { base_address, skip: 0 }),
144        };
145
146        // Setup download options - use default and override what we need
147        let mut options = flashing::DownloadOptions::default();
148        options.verify = true;
149        options.progress = None;
150
151        // Set base address for BIN files - this might need to be handled differently
152        if matches!(probe_format, flashing::Format::Bin(_)) {
153            if let Some(addr) = base_address {
154                // Note: probe-rs API may need different approach for base address
155                warn!("Base address specification for BIN files: 0x{:08X} - may require different API usage", addr);
156            }
157        }
158
159        // Execute programming
160        flashing::download_file_with_options(session, file_path, probe_format, options)
161            .map_err(|e| DebugError::FlashOperationFailed(format!("Programming failed: {}", e)))?;
162
163        let elapsed = start_time.elapsed().as_millis() as u64;
164        
165        info!("File programming completed in {}ms", elapsed);
166        
167        // Since we can't get exact bytes from probe-rs API, estimate from file size
168        let file_size = std::fs::metadata(file_path)
169            .map(|m| m.len() as usize)
170            .unwrap_or(0);
171        
172        Ok(ProgramResult {
173            bytes_programmed: file_size,
174            programming_time_ms: elapsed,
175            verification_result: Some(true), // probe-rs handles verification internally
176        })
177    }
178
179    /// Program binary data to flash
180    pub async fn program_data(
181        session: &mut Session,
182        data: &[u8],
183        base_address: u64,
184    ) -> Result<ProgramResult> {
185        let start_time = Instant::now();
186        
187        debug!("Programming {} bytes to address 0x{:08X}", data.len(), base_address);
188
189        // Use direct memory write for now - FlashLoader API requires memory map
190        let mut core = session.core(0)
191            .map_err(|e| DebugError::FlashOperationFailed(format!("Failed to get core: {}", e)))?;
192        
193        // Write data directly to flash memory
194        core.write(base_address, data)
195            .map_err(|e| DebugError::FlashOperationFailed(format!("Failed to write data: {}", e)))?;
196
197        let elapsed = start_time.elapsed().as_millis() as u64;
198        
199        info!("Data programming completed: {} bytes in {}ms", data.len(), elapsed);
200
201        Ok(ProgramResult {
202            bytes_programmed: data.len(),
203            programming_time_ms: elapsed,
204            verification_result: None, // Manual verification needed
205        })
206    }
207
208    /// Verify flash contents
209    pub async fn verify_flash(
210        session: &mut Session,
211        expected_data: &[u8],
212        address: u64,
213    ) -> Result<VerifyResult> {
214        debug!("Verifying {} bytes at address 0x{:08X}", expected_data.len(), address);
215
216        let mut core = session.core(0)
217            .map_err(|e| DebugError::FlashOperationFailed(format!("Failed to get core: {}", e)))?;
218        
219        // Read actual data from flash
220        let mut actual_data = vec![0u8; expected_data.len()];
221        core.read(address, &mut actual_data)
222            .map_err(|e| DebugError::FlashOperationFailed(format!("Failed to read flash: {}", e)))?;
223
224        // Compare data and find mismatches
225        let mut mismatches = Vec::new();
226        for (i, (expected, actual)) in expected_data.iter().zip(actual_data.iter()).enumerate() {
227            if expected != actual {
228                mismatches.push(VerifyMismatch {
229                    address: address + i as u64,
230                    expected: *expected,
231                    actual: *actual,
232                });
233            }
234        }
235
236        let success = mismatches.is_empty();
237        
238        if success {
239            info!("Flash verification successful: {} bytes", expected_data.len());
240        } else {
241            warn!("Flash verification failed: {} mismatches", mismatches.len());
242        }
243
244        Ok(VerifyResult {
245            success,
246            bytes_verified: expected_data.len(),
247            mismatches,
248        })
249    }
250}
251
252impl Default for FlashManager {
253    fn default() -> Self {
254        Self::new()
255    }
256}