sftool_lib/
ram_command.rs1use crate::ram_stub::CHIP_FILE_NAME;
2use crate::sifli_debug::{SifliUartCommand, SifliUartResponse};
3use crate::{Operation, SifliTool, ram_stub};
4use indicatif::{ProgressBar, ProgressStyle};
5use probe_rs::MemoryMappedRegister;
6use probe_rs::architecture::arm::core::registers::cortex_m::{PC, SP};
7use std::cmp::PartialEq;
8use std::io::{Error, Write};
9use std::str::FromStr;
10use std::time::Duration;
11use strum::{Display, EnumString};
12
13#[derive(EnumString, Display, Debug, Clone, PartialEq, Eq)]
14pub enum Command {
15 #[strum(to_string = "burn_erase_all 0x{address:08x}\r")]
16 EraseAll { address: u32 },
17
18 #[strum(to_string = "burn_verify 0x{address:08x} 0x{len:08x} 0x{crc:08x}\r")]
19 Verify { address: u32, len: u32, crc: u32 },
20
21 #[strum(to_string = "burn_erase_write 0x{address:08x} 0x{len:08x}\r")]
22 WriteAndErase { address: u32, len: u32 },
23
24 #[strum(to_string = "burn_write 0x{address:08x} 0x{len:08x}\r")]
25 Write { address: u32, len: u32 },
26
27 #[strum(to_string = "burn_reset\r")]
28 SoftReset,
29
30 #[strum(to_string = "burn_speed {baud} {delay}\r")]
31 SetBaud { baud: u32, delay: u32 },
32}
33
34#[derive(EnumString, Display, Debug, Clone, PartialEq, Eq)]
35pub enum Response {
36 #[strum(serialize = "OK")]
37 Ok,
38 #[strum(serialize = "Fail")]
39 Fail,
40 #[strum(serialize = "RX_WAIT")]
41 RxWait,
42}
43
44const RESPONSE_STR_TABLE: [&str; 3] = ["OK", "Fail", "RX_WAIT"];
45
46pub trait RamCommand {
47 fn command(&mut self, cmd: Command) -> Result<Response, std::io::Error>;
48 fn send_data(&mut self, data: &[u8]) -> Result<Response, std::io::Error>;
49}
50
51const TIMEOUT: u128 = 4000; impl RamCommand for SifliTool {
54 fn command(&mut self, cmd: Command) -> Result<Response, std::io::Error> {
55 self.port.write_all(cmd.to_string().as_bytes())?;
56 self.port.flush()?;
57 self.port.clear(serialport::ClearBuffer::All)?;
58
59 let timeout = match cmd {
60 Command::EraseAll { .. } => 30 * 1000,
61 _ => TIMEOUT,
62 };
63
64 if let Command::SetBaud { .. } = cmd {
65 return Ok(Response::Ok);
66 }
67
68 let mut buffer = Vec::new();
69 let now = std::time::SystemTime::now();
70 loop {
71 let elapsed = now.elapsed().unwrap().as_millis();
72 if elapsed > timeout {
73 return Err(std::io::Error::new(std::io::ErrorKind::TimedOut, "Timeout"));
74 }
75
76 let mut byte = [0];
77 let ret = self.port.read_exact(&mut byte);
78 if ret.is_err() {
79 continue;
80 }
81 buffer.push(byte[0]);
82
83 for response_str in RESPONSE_STR_TABLE.iter() {
84 let response_bytes = response_str.as_bytes();
85 let exists = buffer
88 .windows(response_bytes.len())
89 .any(|window| window == response_bytes);
90 if exists {
91 return Response::from_str(response_str).map_err(|e| {
92 std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string())
93 });
94 }
95 }
96 }
97 }
98
99 fn send_data(&mut self, data: &[u8]) -> Result<Response, Error> {
100 if !self.base.compat {
101 self.port.write_all(data)?;
102 self.port.flush()?;
103 } else {
104 for chunk in data.chunks(256) {
106 self.port.write_all(chunk)?;
107 self.port.flush()?;
108 std::thread::sleep(std::time::Duration::from_millis(10));
109 }
110 }
111
112 let mut buffer = Vec::new();
113 let now = std::time::SystemTime::now();
114 loop {
115 let elapsed = now.elapsed().unwrap().as_millis();
116 if elapsed > TIMEOUT {
117 return Err(std::io::Error::new(std::io::ErrorKind::TimedOut, "Timeout"));
118 }
119
120 let mut byte = [0];
121 let ret = self.port.read_exact(&mut byte);
122 if ret.is_err() {
123 continue;
124 }
125 buffer.push(byte[0]);
126
127 for response_str in RESPONSE_STR_TABLE.iter() {
129 let response_bytes = response_str.as_bytes();
130 let exists = buffer
131 .windows(response_bytes.len())
132 .any(|window| window == response_bytes);
133 if exists {
134 return Response::from_str(response_str).map_err(|e| {
135 std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string())
136 });
137 }
138 }
139 }
140 }
141}
142
143pub trait DownloadStub {
144 fn download_stub(&mut self) -> Result<(), std::io::Error>;
145}
146
147impl SifliTool {
148 fn download_stub(&mut self) -> Result<(), std::io::Error> {
149 let spinner = ProgressBar::new_spinner();
150 if !self.base.quiet {
151 spinner.enable_steady_tick(std::time::Duration::from_millis(100));
152 spinner.set_style(ProgressStyle::with_template("[{prefix}] {spinner} {msg}").unwrap());
153 spinner.set_prefix(format!("0x{:02X}", self.step));
154 spinner.set_message("Download stub...");
155 }
156 self.step = self.step.wrapping_add(1);
157
158 use probe_rs::architecture::arm::core::armv7m::Demcr;
161 let demcr = self.debug_read_word32(Demcr::get_mmio_address() as u32)?;
162 let mut demcr = Demcr(demcr);
163 demcr.set_vc_corereset(true);
164 self.debug_write_word32(Demcr::get_mmio_address() as u32, demcr.into())?;
165 use probe_rs::architecture::arm::core::armv7m::Aircr;
167
168 let mut aircr = Aircr(0);
169 aircr.vectkey();
170 aircr.set_sysresetreq(true);
171 self.debug_write_word32(Aircr::get_mmio_address() as u32, aircr.into())?;
172
173 let _ = self.debug_command(SifliUartCommand::Enter)?;
175
176 self.debug_halt()?;
178
179 let demcr = self.debug_read_word32(Demcr::get_mmio_address() as u32)?;
181 let mut demcr = Demcr(demcr);
182 demcr.set_vc_corereset(false);
183 self.debug_write_word32(Demcr::get_mmio_address() as u32, demcr.into())?;
184
185 std::thread::sleep(std::time::Duration::from_millis(100));
186 let stub = ram_stub::RamStubFile::get(
188 CHIP_FILE_NAME
189 .get(format!("{}_{}", self.base.chip, self.base.memory_type).as_str())
190 .expect("REASON"),
191 );
192 let Some(stub) = stub else {
193 if !self.base.quiet {
194 spinner.finish_with_message("No stub file found for the given chip and memory type");
195 }
196 return Err(std::io::Error::new(
197 std::io::ErrorKind::NotFound,
198 "No stub file found for the given chip and memory type",
199 ));
200 };
201
202 let packet_size = if self.base.compat { 256 } else { 64 * 1024 };
203
204 let mut addr = 0x2005_A000;
205 let mut data = &stub.data[..];
206 while !data.is_empty() {
207 let chunk = &data[..std::cmp::min(data.len(), packet_size)];
208 self.debug_write_memory(addr, chunk)?;
209 addr += chunk.len() as u32;
210 data = &data[chunk.len()..];
211 }
212
213 let sp = u32::from_le_bytes(
216 stub.data[0..4]
217 .try_into()
218 .expect("slice with exactly 4 bytes"),
219 );
220 let pc = u32::from_le_bytes(
221 stub.data[4..8]
222 .try_into()
223 .expect("slice with exactly 4 bytes"),
224 );
225 self.debug_write_core_reg(PC.id, pc)?;
226 self.debug_write_core_reg(SP.id, sp)?;
227
228 self.debug_run()?;
230
231 if !self.base.quiet {
232 spinner.finish_with_message("Download stub success!");
233 }
234
235 Ok(())
236 }
237
238 fn attempt_connect(&mut self) -> Result<(), std::io::Error> {
239 let infinite_attempts = self.base.connect_attempts <= 0;
240 let mut remaining_attempts = if infinite_attempts {
241 None
242 } else {
243 Some(self.base.connect_attempts)
244 };
245 loop {
246 if self.base.before == Operation::DefaultReset {
247 self.port.write_request_to_send(true)?;
249 std::thread::sleep(Duration::from_millis(100));
250 self.port.write_request_to_send(false)?;
251 std::thread::sleep(Duration::from_millis(100));
252 }
253 let value = match self.debug_command(SifliUartCommand::Enter) {
254 Ok(SifliUartResponse::Enter) => Ok(()),
255 _ => Err(std::io::Error::new(
256 std::io::ErrorKind::Other,
257 "Failed to enter debug mode",
258 )),
259 };
260 if let Some(ref mut attempts) = remaining_attempts {
262 if *attempts == 0 {
263 break; }
265 *attempts -= 1;
266 }
267
268 let spinner = ProgressBar::new_spinner();
269 if !self.base.quiet {
270 spinner.enable_steady_tick(Duration::from_millis(100));
271 spinner
272 .set_style(ProgressStyle::with_template("[{prefix}] {spinner} {msg}").unwrap());
273 spinner.set_prefix(format!("0x{:02X}", self.step));
274 self.step = self.step.wrapping_add(1);
275 spinner.set_message("Connecting to chip...");
276 }
277
278 match value {
280 Ok(_) => {
281 if !self.base.quiet {
282 spinner.finish_with_message("Connected success!");
283 }
284 return Ok(());
285 }
286 Err(_) => {
287 if !self.base.quiet {
288 spinner.finish_with_message("Failed to connect to the chip, retrying...");
289 }
290 std::thread::sleep(Duration::from_millis(500));
291 }
292 }
293 }
294 Err(std::io::Error::new(
295 std::io::ErrorKind::Other,
296 "Failed to connect to the chip",
297 ))
298 }
299}
300
301impl DownloadStub for SifliTool {
302 fn download_stub(&mut self) -> Result<(), std::io::Error> {
303 self.attempt_connect()?;
304 self.download_stub()?;
305
306 std::thread::sleep(std::time::Duration::from_millis(100));
307 self.port.clear(serialport::ClearBuffer::All)?;
308
309 let mut buffer = Vec::new();
311 let mut now = std::time::SystemTime::now();
312 const RETRY: u32 = 5;
313 let mut retry_count = 0;
314 self.port.write_all(b"\r\n")?;
315 self.port.flush()?;
316 loop {
317 let elapsed = now.elapsed().unwrap().as_millis();
318 if elapsed > 200 {
319 retry_count += 1;
320 now = std::time::SystemTime::now();
321 self.port.write_all(b"\r\n")?;
322 self.port.flush()?;
323 buffer.clear();
324 }
325 if retry_count > RETRY {
326 return Err(std::io::Error::new(std::io::ErrorKind::TimedOut, "Timeout"));
327 }
328
329 let mut byte = [0];
330 let ret = self.port.read_exact(&mut byte);
331 if ret.is_err() {
332 continue;
333 }
334 buffer.push(byte[0]);
335
336 if buffer.windows(5).any(|window| window == b"msh >") {
338 break;
339 }
340 }
341 Ok(())
342 }
343}