1use anyhow::{Context, Result, bail};
6use clap::Args;
7use std::convert::From;
8use std::fs;
9use std::path::PathBuf;
10use std::str::FromStr;
11
12use crate::app::TransportWrapper;
13use crate::io::fpga_backdoor::{
14 Backdoor, BackdoorParams, BackdoorTarget, BackdoorTargetInfo, enter_backdoor_loader,
15};
16use crate::io::jtag::JtagParams;
17use crate::util::vmem::{Section, Vmem, Word};
18
19pub fn enter_backdoor(transport: &TransportWrapper, jtag_params: &JtagParams) -> Result<Backdoor> {
21 enter_backdoor_loader(transport)?;
22 let backdoor_params = BackdoorParams {
23 jtag: jtag_params.clone(),
24 };
25 backdoor_params.create(transport)?.connect(true)
26}
27
28fn normalize_word(word: &mut Word, bits_per_word: usize) {
30 let bytes_per_word = bits_per_word.div_ceil(8);
31 if word.bytes.len() > bytes_per_word {
32 let start = word.bytes.len() - bytes_per_word;
33 word.bytes.drain(..start);
34 } else if word.bytes.len() < bytes_per_word {
35 let mut padded = vec![0u8; bytes_per_word - word.bytes.len()];
36 padded.append(&mut word.bytes);
37 word.bytes = padded;
38 }
39
40 let extra_bits = bits_per_word % 8;
41 if extra_bits > 0 {
42 word.bytes[0] &= 0xFF >> (8 - extra_bits);
43 }
44}
45
46pub fn verify_readback(
48 input: &mut [Word],
49 readback: &mut [Word],
50 bits_per_word: usize,
51 mut offset: u32,
52) -> Result<()> {
53 for (write_word, read_word) in readback.iter_mut().zip(input) {
54 normalize_word(write_word, bits_per_word);
55 normalize_word(read_word, bits_per_word);
56 if write_word != read_word {
57 bail!(
58 "Read verification at word {} failed. Expected: {}, Got: {}",
59 offset,
60 hex::encode(write_word.bytes.clone()),
61 hex::encode(read_word.bytes.clone()),
62 );
63 }
64
65 offset += 1;
66 }
67
68 Ok(())
69}
70
71pub fn write_to_target(
72 target: &mut BackdoorTarget,
73 target_id: &str,
74 data: Vec<Section>,
75 verify: bool,
76) -> Result<()> {
77 log::info!("Writing to the {}...", target_id);
79 for mut section in data {
80 log::debug!(
81 "Writing section of size {} to word {} of target {}",
82 section.data.len(),
83 section.addr,
84 target_id
85 );
86
87 let first_word = §ion.data.first().clone();
90 if section.addr == 0
91 && section.data.len() == target.info.depth as usize
92 && section.data.iter().all(|w| w == first_word.unwrap())
93 {
94 log::debug!(
95 "Clearing target {} with word {}",
96 target_id,
97 hex::encode(first_word.unwrap().bytes.clone())
98 );
99 target.clear(first_word.unwrap(), verify)?;
100 } else {
101 target.write(section.addr, §ion.data, false, verify)?;
102 }
103
104 if verify {
106 let mut readback = target.read(section.addr, section.data.len() as u32, false)?;
107 verify_readback(
108 &mut section.data,
109 &mut readback,
110 target.info.width as usize,
111 section.addr,
112 )?;
113 }
114 }
115
116 Ok(())
117}
118
119#[derive(Debug, Clone)]
120pub struct TargetWrite {
121 pub target: String,
122 pub path: PathBuf,
123 pub offset: Option<u32>,
124}
125
126impl FromStr for TargetWrite {
127 type Err = anyhow::Error;
128
129 fn from_str(s: &str) -> Result<Self, Self::Err> {
130 let (target_path, offset) = s
131 .split_once('@')
132 .map(|(x, y)| (x, y.parse::<u32>().ok()))
133 .unwrap_or((s, None));
134
135 let (target, path) = target_path
136 .split_once('=')
137 .context("expected input like TARGET=FILE[@OFFSET], but no '=' was seen")?;
138 if target.is_empty() {
139 bail!("target name cannot be empty");
140 }
141 if path.is_empty() {
142 bail!("file path cannot be empty");
143 }
144
145 Ok(TargetWrite {
146 target: target.to_string(),
147 path: PathBuf::from(path),
148 offset,
149 })
150 }
151}
152
153impl TargetWrite {
154 pub fn load_data(&self) -> Result<Vec<Section>> {
155 log::info!("Loading VMEM file: {}", self.path.display());
156 let vmem_content = fs::read_to_string(&self.path)?;
157 let mut vmem = Vmem::from_str(&vmem_content, None)?;
158 vmem.merge_sections(None);
159 let mut sections: Vec<Section> = vmem.sections().cloned().collect();
160
161 if let Some(offset) = self.offset {
163 for section in &mut sections {
164 section.addr += offset;
165 }
166 }
167
168 Ok(sections)
169 }
170
171 pub fn backdoor_write(&self, backdoor: &mut Backdoor, verify: bool) -> Result<()> {
172 let mut target = backdoor
173 .target_by_id_str(&self.target)?
174 .context(format!("FPGA target '{}' not found", &self.target))?;
175
176 let data = self.load_data()?;
177 write_to_target(&mut target, &self.target, data, verify)
178 }
179}
180
181#[derive(Debug, Clone)]
182pub struct TargetClear {
183 pub target: String,
184 pub num_words: Option<u32>,
185 pub offset: Option<u32>,
186}
187
188impl FromStr for TargetClear {
189 type Err = anyhow::Error;
190
191 fn from_str(s: &str) -> Result<Self, Self::Err> {
192 let (target_words, offset) = s
193 .split_once('@')
194 .map(|(x, y)| (x, y.parse::<u32>().ok()))
195 .unwrap_or((s, None));
196
197 let (target, num_words) = target_words
198 .split_once('=')
199 .context("expected input like TARGET=NUM_WORDS[@OFFSET], but no '=' was seen")?;
200 if target.is_empty() {
201 bail!("target name cannot be empty");
202 }
203 if num_words.is_empty() {
204 bail!("num_words cannot be empty");
205 }
206 let num_words = if num_words.to_lowercase() == "all" {
207 None
208 } else {
209 Some(num_words.parse::<u32>()?)
210 };
211
212 Ok(TargetClear {
213 target: target.to_string(),
214 num_words,
215 offset,
216 })
217 }
218}
219
220impl TargetClear {
221 pub fn as_sections(&self, target_info: &BackdoorTargetInfo) -> Vec<Section> {
222 let num_bytes = target_info.width.div_ceil(8) as usize;
223 let remaining_words = target_info.depth - self.offset.unwrap_or(0);
224 let num_words = self.num_words.unwrap_or(remaining_words);
225 let data = vec![Word::new(vec![0x00; num_bytes]); num_words as usize];
226 vec![Section {
227 addr: self.offset.unwrap_or(0),
228 data,
229 }]
230 }
231
232 pub fn backdoor_write(&self, backdoor: &mut Backdoor, verify: bool) -> Result<()> {
233 let mut target = backdoor
234 .target_by_id_str(&self.target)?
235 .context(format!("FPGA target '{}' not found", &self.target))?;
236
237 let data = self.as_sections(&target.info);
238 write_to_target(&mut target, &self.target, data, verify)
239 }
240}
241
242#[derive(Debug, Args)]
244pub struct LoadMemories {
245 #[arg(long = "clear-memory", value_name = "TARGET=NUM_WORDS[@OFFSET]")]
247 pub target_clears: Vec<TargetClear>,
248
249 #[arg(long = "load-memory", value_name = "TARGET=FILE[@OFFSET]")]
251 pub target_writes: Vec<TargetWrite>,
252}
253
254impl LoadMemories {
255 pub fn init(&self, transport: &TransportWrapper, jtag_params: &JtagParams) -> Result<()> {
256 if self.target_clears.is_empty() && self.target_writes.is_empty() {
257 return Ok(());
258 }
259
260 let mut backdoor = enter_backdoor(transport, jtag_params)?;
261 self.target_clears
262 .iter()
263 .try_for_each(|t| t.backdoor_write(&mut backdoor, false))?;
264 self.target_writes
265 .iter()
266 .try_for_each(|t| t.backdoor_write(&mut backdoor, false))?;
267 backdoor.set_done()?;
268
269 Ok(())
270 }
271}