Skip to main content

ops/
upgrade.rs

1//! Installing firmware by the route the card supports: upload the whole image
2//! into SDRAM, then ask the card to erase and program itself.
3//!
4//! Direct writes to the program area are silently ignored, so this is the
5//! only path.
6
7use 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
13/// Ask the card what image it expects and how it can be upgraded.
14///
15/// # Errors
16/// Fails if the card does not answer or the reply cannot be decoded.
17pub 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
28/// Print what the card reports.
29///
30/// # Errors
31/// Fails if the card does not answer.
32pub 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/// Install a firmware image through SDRAM staging and wait for the card to
53/// report done. `image` is a manifest name or a path (`crate::firmware`).
54///
55/// # Errors
56/// Fails if the image is not what the manifest says, does not match what the
57/// card expects, if the card does not support SDRAM staging, or if
58/// programming does not complete in time.
59#[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        // Chunks are not acknowledged; pacing is the only flow control. Sent
120        // too fast, runs of chunks drop silently and stale SDRAM gets programmed.
121        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        // Once the erase is sent the card must finish; a cancel only stops
141        // the waiting.
142        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}