1use crate::util::{await_any_frame, has_lattice_header, open};
8use crate::{check, protocol, Ctx, Progress};
9use anyhow::{Context, Result};
10use protocol::upgrade::{self, Descriptor, Partition};
11use std::time::{Duration, Instant};
12
13pub fn describe(ctx: &Ctx, wait: u64) -> Result<Descriptor> {
18 let mut dev = open(ctx)?;
19 dev.send(&protocol::upgrade_info())?;
20 await_any_frame(
21 &mut dev,
22 Duration::from_secs(wait),
23 upgrade::parse_descriptor,
24 )?
25 .with_context(|| format!("no firmware descriptor from the card within {wait}s"))
26}
27
28pub fn info(ctx: &Ctx, wait: u64, p: &mut dyn Progress) -> Result<()> {
33 let d = describe(ctx, wait)?;
34 p.out(&format!("image start 0x{:06x}", d.start));
35 p.out(&format!(
36 "image length 0x{:06x} ({} bytes)",
37 d.image_len, d.image_len
38 ));
39 p.out(&format!(
40 "file length 0x{:06x} ({} bytes)",
41 d.file_len, d.file_len
42 ));
43 p.out(&format!("chunks {}", d.chunks()));
44 p.out(&format!("flash op type 0x{:02x}", d.flash_op_type));
45 p.out(&format!("sdram staging {}", d.supports_sdram()));
46 p.out(&format!("golden bank {}", d.has_golden()));
47 p.out(&format!("partition sel {}", d.supports_select_part()));
48 p.out(&format!("golden upgrade {}", d.supports_golden_upgrade()));
49 Ok(())
50}
51
52#[allow(clippy::too_many_arguments)]
60pub fn install(
61 ctx: &Ctx,
62 image: &str,
63 commit: bool,
64 partition: Partition,
65 timeout_s: u64,
66 chunk_delay_us: u64,
67 wait: u64,
68 p: &mut dyn Progress,
69) -> Result<()> {
70 let loaded = crate::firmware::load(image, p)?;
71 let checked = crate::firmware::checked(&loaded);
72 let (image_path, img) = (loaded.path.as_str(), loaded.bytes.as_slice());
73 anyhow::ensure!(
74 has_lattice_header(img),
75 "{image_path} does not look like a Lattice bitstream"
76 );
77
78 let d = describe(ctx, wait)?;
79 anyhow::ensure!(
80 img.len() as u32 == d.file_len,
81 "{image_path} is {} bytes but the card expects exactly {}",
82 img.len(),
83 d.file_len
84 );
85 anyhow::ensure!(
86 d.supports_sdram(),
87 "this card does not stage via SDRAM, and the direct-write path is not implemented"
88 );
89 if partition == Partition::Golden {
90 anyhow::ensure!(
91 d.supports_golden_upgrade(),
92 "this card does not accept upgrades aimed at the golden bank"
93 );
94 }
95
96 let staged = &img[..d.image_len as usize];
97 p.err(&format!(
98 "upgrade: {image_path} ({checked}) -> {} image, {} chunks of {} bytes {chunk_delay_us}us apart, ~{:.1}s to program",
99 match partition {
100 Partition::Primary => "primary",
101 Partition::Golden => "golden",
102 },
103 d.chunks(),
104 upgrade::CHUNK,
105 d.estimated_ms() as f64 / 1000.0
106 ));
107 if !commit {
108 p.out("dry run: nothing sent (add --commit)");
109 return Ok(());
110 }
111
112 let mut dev = open(ctx)?;
113 let sel = protocol::BROADCAST;
114
115 for (n, chunk) in staged.chunks(upgrade::CHUNK).enumerate() {
116 check(p)?;
117 let offset = (n * upgrade::CHUNK) as u32;
118 dev.send(&upgrade::sdram_chunk(sel, offset, chunk))?;
119 std::thread::sleep(Duration::from_micros(chunk_delay_us));
122 if n.is_multiple_of(128) {
123 p.err(&format!("upgrade: chunk {n}/{}", d.chunks()));
124 }
125 }
126 std::thread::sleep(Duration::from_millis(1));
127
128 dev.send(&upgrade::sdram_erase(sel, partition, d.image_len))?;
129 std::thread::sleep(Duration::from_millis(1));
130
131 dev.send(&upgrade::sdram_program(sel, partition, d.image_len))?;
132 std::thread::sleep(Duration::from_millis(1));
133
134 p.err("upgrade: programming, do not power off");
135 std::thread::sleep(Duration::from_millis(d.first_poll_ms()));
136
137 let deadline = Instant::now() + Duration::from_secs(timeout_s);
138 let mut polls = 0u32;
139 while Instant::now() < deadline {
140 check(p)?;
143 dev.send(&protocol::upgrade_info())?;
144 let done = await_any_frame(&mut dev, Duration::from_millis(600), |f| {
145 upgrade::programming_finished(f).then_some(())
146 })?;
147 if done.is_some() {
148 p.err("upgrade: programming complete; power-cycle the card to load it");
149 return Ok(());
150 }
151 polls += 1;
152 if polls.is_multiple_of(5) {
153 p.err(&format!("upgrade: still programming ({polls}s)"));
154 }
155 std::thread::sleep(Duration::from_millis(400));
156 }
157
158 anyhow::bail!(
159 "no completion report within {timeout_s}s; the card may still be programming, \
160 do not power it off; check with: rxp firmware info"
161 )
162}