Make flashload COM more reliable #25
@ -1,12 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
from spacepackets.ecss import RequestId
|
||||
from spacepackets.ecss.defs import PusService
|
||||
from spacepackets.ecss.tm import PusTm
|
||||
import toml
|
||||
import struct
|
||||
import logging
|
||||
import argparse
|
||||
import threading
|
||||
import time
|
||||
import enum
|
||||
from tmtccmd.com.serial_base import SerialCfg
|
||||
@ -45,6 +43,7 @@ ACTION_SERVICE = 8
|
||||
|
||||
RAW_MEMORY_WRITE_SUBSERVICE = 2
|
||||
BOOT_NVM_MEMORY_ID = 1
|
||||
PING_PAYLOAD_SIZE = 0
|
||||
|
||||
|
||||
class ActionId(enum.IntEnum):
|
||||
@ -104,6 +103,29 @@ def main() -> int:
|
||||
com_if = SerialCobsComIF(serial_cfg)
|
||||
com_if.open()
|
||||
file_path = None
|
||||
if args.ping:
|
||||
_LOGGER.info("Sending ping command")
|
||||
ping_tc = PusTc(
|
||||
apid=0x00,
|
||||
service=PusService.S17_TEST,
|
||||
subservice=1,
|
||||
seq_count=SEQ_PROVIDER.get_and_increment(),
|
||||
app_data=bytes(PING_PAYLOAD_SIZE),
|
||||
)
|
||||
verificator.add_tc(ping_tc)
|
||||
com_if.send(ping_tc.pack())
|
||||
|
||||
data_available = com_if.data_available(0.4)
|
||||
if not data_available:
|
||||
_LOGGER.warning("no ping reply received")
|
||||
for reply in com_if.receive():
|
||||
result = verificator.add_tm(
|
||||
Service1Tm.from_tm(PusTm.unpack(reply, 0), UnpackParams(0))
|
||||
)
|
||||
if result is not None and result.completed:
|
||||
_LOGGER.info("received ping completion reply")
|
||||
if not args.target:
|
||||
return 0
|
||||
if args.target:
|
||||
if not args.corrupt:
|
||||
if not args.path:
|
||||
@ -113,15 +135,6 @@ def main() -> int:
|
||||
if not file_path.exists():
|
||||
_LOGGER.error("File does not exist")
|
||||
return -1
|
||||
if args.ping:
|
||||
_LOGGER.info("Sending ping command")
|
||||
ping_tc = PusTc(
|
||||
apid=0x00,
|
||||
service=PusService.S17_TEST,
|
||||
subservice=1,
|
||||
seq_count=SEQ_PROVIDER.get_and_increment(),
|
||||
)
|
||||
com_if.send(ping_tc.pack())
|
||||
if args.corrupt:
|
||||
if not args.target:
|
||||
_LOGGER.error("target for corruption command required")
|
||||
@ -254,7 +267,7 @@ def main() -> int:
|
||||
):
|
||||
done = True
|
||||
# Still keep a small delay
|
||||
time.sleep(0.01)
|
||||
# time.sleep(0.05)
|
||||
verificator.remove_completed_entries()
|
||||
if done:
|
||||
break
|
||||
|
@ -18,13 +18,11 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use embedded_hal_nb::serial::Read;
|
||||
use once_cell::sync::OnceCell;
|
||||
use panic_rtt_target as _;
|
||||
use va416xx_hal::{clock::Clocks, edac, pac, time::Hertz, wdt::Wdt};
|
||||
|
||||
const EXTCLK_FREQ: u32 = 40_000_000;
|
||||
const COBS_FRAME_SEPARATOR: u8 = 0x0;
|
||||
|
||||
const MAX_TC_SIZE: usize = 1024;
|
||||
const MAX_TC_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TC_SIZE);
|
||||
@ -33,10 +31,8 @@ const MAX_TM_SIZE: usize = 128;
|
||||
const MAX_TM_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TM_SIZE);
|
||||
|
||||
const UART_BAUDRATE: u32 = 115200;
|
||||
const SERIAL_RX_WIRETAPPING: bool = false;
|
||||
const COBS_RX_DEBUGGING: bool = false;
|
||||
|
||||
const BOOT_NVM_MEMORY_ID: u8 = 1;
|
||||
const RX_DEBUGGING: bool = false;
|
||||
|
||||
pub enum ActionId {
|
||||
CorruptImageA = 128,
|
||||
@ -62,13 +58,24 @@ use ringbuf::{
|
||||
CachingCons, StaticProd, StaticRb,
|
||||
};
|
||||
|
||||
const BUF_RB_SIZE_TX: usize = 1024;
|
||||
const SIZES_RB_SIZE_TX: usize = 16;
|
||||
// Larger buffer for TC to be able to hold the possibly large memory write packets.
|
||||
const BUF_RB_SIZE_TC: usize = 2048;
|
||||
const SIZES_RB_SIZE_TC: usize = 16;
|
||||
|
||||
static mut BUF_RB_TX: Lazy<StaticRb<u8, BUF_RB_SIZE_TX>> =
|
||||
Lazy::new(StaticRb::<u8, BUF_RB_SIZE_TX>::default);
|
||||
static mut SIZES_RB_TX: Lazy<StaticRb<usize, SIZES_RB_SIZE_TX>> =
|
||||
Lazy::new(StaticRb::<usize, SIZES_RB_SIZE_TX>::default);
|
||||
const BUF_RB_SIZE_TM: usize = 512;
|
||||
const SIZES_RB_SIZE_TM: usize = 16;
|
||||
|
||||
// Ring buffers to handling variable sized telemetry
|
||||
static mut BUF_RB_TM: Lazy<StaticRb<u8, BUF_RB_SIZE_TM>> =
|
||||
Lazy::new(StaticRb::<u8, BUF_RB_SIZE_TM>::default);
|
||||
static mut SIZES_RB_TM: Lazy<StaticRb<usize, SIZES_RB_SIZE_TM>> =
|
||||
Lazy::new(StaticRb::<usize, SIZES_RB_SIZE_TM>::default);
|
||||
|
||||
// Ring buffers to handling variable sized telecommands
|
||||
static mut BUF_RB_TC: Lazy<StaticRb<u8, BUF_RB_SIZE_TC>> =
|
||||
Lazy::new(StaticRb::<u8, BUF_RB_SIZE_TC>::default);
|
||||
static mut SIZES_RB_TC: Lazy<StaticRb<usize, SIZES_RB_SIZE_TC>> =
|
||||
Lazy::new(StaticRb::<usize, SIZES_RB_SIZE_TC>::default);
|
||||
|
||||
pub struct DataProducer<const BUF_SIZE: usize, const SIZES_LEN: usize> {
|
||||
pub buf_prod: StaticProd<'static, u8, BUF_SIZE>,
|
||||
@ -91,15 +98,10 @@ pub const APP_B_END_ADDR: u32 = 0x40000;
|
||||
mod app {
|
||||
use super::*;
|
||||
use cortex_m::asm;
|
||||
use embedded_hal_nb::nb;
|
||||
use embedded_io::Write;
|
||||
use panic_rtt_target as _;
|
||||
use rtic::Mutex;
|
||||
use rtic_monotonics::systick::prelude::*;
|
||||
use rtic_sync::{
|
||||
channel::{Receiver, Sender},
|
||||
make_channel,
|
||||
};
|
||||
use rtt_target::rprintln;
|
||||
use satrs::pus::verification::VerificationReportCreator;
|
||||
use spacepackets::ecss::PusServiceId;
|
||||
@ -127,26 +129,24 @@ mod app {
|
||||
|
||||
#[local]
|
||||
struct Local {
|
||||
uart_rx: uart::Rx<pac::Uart0>,
|
||||
uart_rx: uart::RxWithIrq<pac::Uart0>,
|
||||
uart_tx: uart::Tx<pac::Uart0>,
|
||||
cobs_reader_state: CobsReaderStates,
|
||||
tc_tx: TcTx,
|
||||
tc_rx: TcRx,
|
||||
rom_spi: Option<pac::Spi3>,
|
||||
tx_cons: DataConsumer<BUF_RB_SIZE_TX, SIZES_RB_SIZE_TX>,
|
||||
// We handle all TM in one task.
|
||||
tm_cons: DataConsumer<BUF_RB_SIZE_TM, SIZES_RB_SIZE_TM>,
|
||||
// We consume all TC in one task.
|
||||
tc_cons: DataConsumer<BUF_RB_SIZE_TC, SIZES_RB_SIZE_TC>,
|
||||
// We produce all TC in one task.
|
||||
tc_prod: DataProducer<BUF_RB_SIZE_TC, SIZES_RB_SIZE_TC>,
|
||||
verif_reporter: VerificationReportCreator,
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct Shared {
|
||||
decode_buffer_busy: bool,
|
||||
decode_buf: [u8; MAX_TC_SIZE],
|
||||
tx_prod: DataProducer<BUF_RB_SIZE_TX, SIZES_RB_SIZE_TX>,
|
||||
// Having this shared allows multiple tasks to generate telemetry.
|
||||
tm_prod: DataProducer<BUF_RB_SIZE_TM, SIZES_RB_SIZE_TM>,
|
||||
}
|
||||
|
||||
pub type TcTx = Sender<'static, usize, 2>;
|
||||
pub type TcRx = Receiver<'static, usize, 2>;
|
||||
|
||||
rtic_monotonics::systick_monotonic!(Mono, 10_000);
|
||||
|
||||
#[init]
|
||||
@ -176,38 +176,45 @@ mod app {
|
||||
&mut cx.device.sysconfig,
|
||||
&clocks,
|
||||
);
|
||||
let (tx, rx) = uart0.split();
|
||||
let (tc_tx, tc_rx) = make_channel!(usize, 2);
|
||||
let (tx, mut rx, _) = uart0.split_with_irq();
|
||||
|
||||
let verif_reporter = VerificationReportCreator::new(0).unwrap();
|
||||
|
||||
let (buf_prod, buf_cons) = unsafe { BUF_RB_TX.split_ref() };
|
||||
let (sizes_prod, sizes_cons) = unsafe { SIZES_RB_TX.split_ref() };
|
||||
let (buf_prod_tm, buf_cons_tm) = unsafe { BUF_RB_TM.split_ref() };
|
||||
let (sizes_prod_tm, sizes_cons_tm) = unsafe { SIZES_RB_TM.split_ref() };
|
||||
|
||||
let (buf_prod_tc, buf_cons_tc) = unsafe { BUF_RB_TC.split_ref() };
|
||||
let (sizes_prod_tc, sizes_cons_tc) = unsafe { SIZES_RB_TC.split_ref() };
|
||||
|
||||
Mono::start(cx.core.SYST, clocks.sysclk().raw());
|
||||
CLOCKS.set(clocks).unwrap();
|
||||
|
||||
rx.read_fixed_len_using_irq(MAX_TC_FRAME_SIZE, true)
|
||||
.expect("initiating UART RX failed");
|
||||
pus_tc_handler::spawn().unwrap();
|
||||
uart_reader_task::spawn().unwrap();
|
||||
pus_tm_tx_handler::spawn().unwrap();
|
||||
(
|
||||
Shared {
|
||||
decode_buffer_busy: false,
|
||||
decode_buf: [0; MAX_TC_SIZE],
|
||||
tx_prod: DataProducer {
|
||||
buf_prod,
|
||||
sizes_prod,
|
||||
tm_prod: DataProducer {
|
||||
buf_prod: buf_prod_tm,
|
||||
sizes_prod: sizes_prod_tm,
|
||||
},
|
||||
},
|
||||
Local {
|
||||
uart_rx: rx,
|
||||
uart_tx: tx,
|
||||
cobs_reader_state: CobsReaderStates::default(),
|
||||
tc_tx,
|
||||
tc_rx,
|
||||
rom_spi: Some(cx.device.spi3),
|
||||
tx_cons: DataConsumer {
|
||||
buf_cons,
|
||||
sizes_cons,
|
||||
tm_cons: DataConsumer {
|
||||
buf_cons: buf_cons_tm,
|
||||
sizes_cons: sizes_cons_tm,
|
||||
},
|
||||
tc_cons: DataConsumer {
|
||||
buf_cons: buf_cons_tc,
|
||||
sizes_cons: sizes_cons_tc,
|
||||
},
|
||||
tc_prod: DataProducer {
|
||||
buf_prod: buf_prod_tc,
|
||||
sizes_prod: sizes_prod_tc,
|
||||
},
|
||||
verif_reporter,
|
||||
},
|
||||
@ -223,120 +230,62 @@ mod app {
|
||||
}
|
||||
|
||||
#[task(
|
||||
priority = 4,
|
||||
local=[
|
||||
read_buf: [u8;MAX_TC_FRAME_SIZE] = [0; MAX_TC_FRAME_SIZE],
|
||||
binds = UART0_RX,
|
||||
local = [
|
||||
cnt: u32 = 0,
|
||||
rx_buf: [u8; MAX_TC_FRAME_SIZE] = [0; MAX_TC_FRAME_SIZE],
|
||||
uart_rx,
|
||||
cobs_reader_state,
|
||||
tc_tx
|
||||
tc_prod
|
||||
],
|
||||
shared=[decode_buffer_busy, decode_buf]
|
||||
)]
|
||||
async fn uart_reader_task(mut cx: uart_reader_task::Context) {
|
||||
let mut current_idx = 0;
|
||||
loop {
|
||||
match cx.local.uart_rx.read() {
|
||||
Ok(byte) => {
|
||||
if SERIAL_RX_WIRETAPPING {
|
||||
log::debug!("RX Byte: 0x{:x?}", byte);
|
||||
}
|
||||
handle_single_rx_byte(&mut cx, byte, &mut current_idx)
|
||||
fn uart_rx_irq(cx: uart_rx_irq::Context) {
|
||||
match cx.local.uart_rx.irq_handler(cx.local.rx_buf) {
|
||||
Ok(result) => {
|
||||
if RX_DEBUGGING {
|
||||
log::debug!("RX Info: {:?}", cx.local.uart_rx.irq_info());
|
||||
log::debug!("RX Result: {:?}", result);
|
||||
}
|
||||
Err(e) => {
|
||||
match e {
|
||||
nb::Error::Other(e) => {
|
||||
log::warn!("UART error: {:?}", e);
|
||||
match e {
|
||||
uart::Error::Overrun => {
|
||||
cx.local.uart_rx.clear_fifo();
|
||||
}
|
||||
uart::Error::FramingError => (),
|
||||
uart::Error::ParityError => (),
|
||||
uart::Error::BreakCondition => (),
|
||||
uart::Error::TransferPending => (),
|
||||
uart::Error::BufferTooShort => (),
|
||||
if result.complete() {
|
||||
// Check frame validity (must have COBS format) and decode the frame.
|
||||
// Currently, we expect a full frame or a frame received through a timeout
|
||||
// to be one COBS frame. We could parse for multiple COBS packets in one
|
||||
// frame, but the additional complexity is not necessary here..
|
||||
if cx.local.rx_buf[0] == 0 && cx.local.rx_buf[result.bytes_read - 1] == 0 {
|
||||
let decoded_size =
|
||||
cobs::decode_in_place(&mut cx.local.rx_buf[1..result.bytes_read]);
|
||||
if decoded_size.is_err() {
|
||||
log::warn!("COBS decoding failed");
|
||||
} else {
|
||||
let decoded_size = decoded_size.unwrap();
|
||||
if cx.local.tc_prod.sizes_prod.vacant_len() >= 1
|
||||
&& cx.local.tc_prod.buf_prod.vacant_len() >= decoded_size
|
||||
{
|
||||
// Should never fail, we checked there is enough space.
|
||||
cx.local.tc_prod.sizes_prod.try_push(decoded_size).unwrap();
|
||||
cx.local
|
||||
.tc_prod
|
||||
.buf_prod
|
||||
.push_slice(&cx.local.rx_buf[1..1 + decoded_size]);
|
||||
} else {
|
||||
log::warn!("COBS TC queue full");
|
||||
}
|
||||
}
|
||||
nb::Error::WouldBlock => {
|
||||
// Delay for a short period before polling again.
|
||||
Mono::delay(400.micros()).await;
|
||||
}
|
||||
} else {
|
||||
log::warn!("COBS frame with invalid format, start and end bytes are not 0");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_single_rx_byte(
|
||||
cx: &mut uart_reader_task::Context,
|
||||
byte: u8,
|
||||
current_idx: &mut usize,
|
||||
) {
|
||||
match cx.local.cobs_reader_state {
|
||||
CobsReaderStates::WaitingForStart => {
|
||||
if byte == COBS_FRAME_SEPARATOR {
|
||||
if COBS_RX_DEBUGGING {
|
||||
log::debug!("COBS start marker detected");
|
||||
}
|
||||
*cx.local.cobs_reader_state = CobsReaderStates::WatingForEnd;
|
||||
*current_idx = 0;
|
||||
// Initiate next transfer.
|
||||
cx.local
|
||||
.uart_rx
|
||||
.read_fixed_len_using_irq(MAX_TC_FRAME_SIZE, true)
|
||||
.expect("read operation failed");
|
||||
}
|
||||
if result.error() {
|
||||
log::warn!("UART error: {:?}", result.error());
|
||||
}
|
||||
}
|
||||
CobsReaderStates::WatingForEnd => {
|
||||
if byte == COBS_FRAME_SEPARATOR {
|
||||
if COBS_RX_DEBUGGING {
|
||||
log::debug!("COBS end marker detected");
|
||||
}
|
||||
let mut sending_failed = false;
|
||||
let mut decoding_error = false;
|
||||
let mut decode_buffer_busy = false;
|
||||
cx.shared.decode_buffer_busy.lock(|busy| {
|
||||
if *busy {
|
||||
decode_buffer_busy = true;
|
||||
} else {
|
||||
cx.shared.decode_buf.lock(|buf| {
|
||||
match cobs::decode(&cx.local.read_buf[..*current_idx], buf) {
|
||||
Ok(packet_len) => {
|
||||
if COBS_RX_DEBUGGING {
|
||||
log::debug!(
|
||||
"COBS decoded packet with length {}",
|
||||
packet_len
|
||||
);
|
||||
}
|
||||
if cx.local.tc_tx.try_send(packet_len).is_err() {
|
||||
sending_failed = true;
|
||||
}
|
||||
*busy = true;
|
||||
}
|
||||
Err(_) => {
|
||||
decoding_error = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if sending_failed {
|
||||
log::warn!("sending TC packet failed, queue full");
|
||||
}
|
||||
if decoding_error {
|
||||
log::warn!("decoding error");
|
||||
}
|
||||
if decode_buffer_busy {
|
||||
log::warn!("decode buffer busy. data arriving too fast");
|
||||
}
|
||||
*cx.local.cobs_reader_state = CobsReaderStates::WaitingForStart;
|
||||
} else if *current_idx >= cx.local.read_buf.len() {
|
||||
*cx.local.cobs_reader_state = CobsReaderStates::FrameOverflow;
|
||||
} else {
|
||||
cx.local.read_buf[*current_idx] = byte;
|
||||
*current_idx += 1;
|
||||
}
|
||||
}
|
||||
CobsReaderStates::FrameOverflow => {
|
||||
if byte == COBS_FRAME_SEPARATOR {
|
||||
*cx.local.cobs_reader_state = CobsReaderStates::WaitingForStart;
|
||||
*current_idx = 0;
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("UART error: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -344,149 +293,167 @@ mod app {
|
||||
#[task(
|
||||
priority = 2,
|
||||
local=[
|
||||
read_buf: [u8;MAX_TC_FRAME_SIZE] = [0; MAX_TC_FRAME_SIZE],
|
||||
tc_buf: [u8; MAX_TC_SIZE] = [0; MAX_TC_SIZE],
|
||||
src_data_buf: [u8; 16] = [0; 16],
|
||||
verif_buf: [u8; 32] = [0; 32],
|
||||
tc_rx,
|
||||
tc_cons,
|
||||
rom_spi,
|
||||
verif_reporter
|
||||
],
|
||||
shared=[decode_buffer_busy, decode_buf, tx_prod]
|
||||
shared=[tm_prod]
|
||||
)]
|
||||
async fn pus_tc_handler(mut cx: pus_tc_handler::Context) {
|
||||
loop {
|
||||
let packet_len = cx.local.tc_rx.recv().await.expect("all senders down");
|
||||
// Try to read a TC from the ring buffer.
|
||||
let packet_len = cx.local.tc_cons.sizes_cons.try_pop();
|
||||
if packet_len.is_none() {
|
||||
// Small delay, TCs might arrive very quickly.
|
||||
Mono::delay(20.millis()).await;
|
||||
continue;
|
||||
}
|
||||
let packet_len = packet_len.unwrap();
|
||||
log::info!(target: "TC Handler", "received packet with length {}", packet_len);
|
||||
// We still copy the data to a local buffer, so the exchange buffer can already be used
|
||||
// for the next packet / decode process.
|
||||
cx.shared
|
||||
.decode_buf
|
||||
.lock(|buf| cx.local.read_buf[0..buf.len()].copy_from_slice(buf));
|
||||
cx.shared.decode_buffer_busy.lock(|busy| *busy = false);
|
||||
match PusTcReader::new(cx.local.read_buf) {
|
||||
Ok((pus_tc, _)) => {
|
||||
let mut write_and_send = |tm: &PusTmCreator| {
|
||||
let written_size = tm.write_to_bytes(cx.local.verif_buf).unwrap();
|
||||
cx.shared.tx_prod.lock(|prod| {
|
||||
prod.sizes_prod.try_push(tm.len_written()).unwrap();
|
||||
prod.buf_prod
|
||||
.push_slice(&cx.local.verif_buf[0..written_size]);
|
||||
});
|
||||
};
|
||||
let token = cx.local.verif_reporter.add_tc(&pus_tc);
|
||||
let (tm, accepted_token) = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.acceptance_success(cx.local.src_data_buf, token, 0, 0, &[])
|
||||
.expect("acceptance success failed");
|
||||
write_and_send(&tm);
|
||||
assert_eq!(
|
||||
cx.local
|
||||
.tc_cons
|
||||
.buf_cons
|
||||
.pop_slice(&mut cx.local.tc_buf[0..packet_len]),
|
||||
packet_len
|
||||
);
|
||||
// Read a telecommand, now handle it.
|
||||
handle_valid_pus_tc(&mut cx);
|
||||
}
|
||||
}
|
||||
|
||||
let (tm, started_token) = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.start_success(cx.local.src_data_buf, accepted_token, 0, 0, &[])
|
||||
.expect("acceptance success failed");
|
||||
write_and_send(&tm);
|
||||
fn handle_valid_pus_tc(cx: &mut pus_tc_handler::Context) {
|
||||
let pus_tc = PusTcReader::new(cx.local.tc_buf);
|
||||
if pus_tc.is_err() {
|
||||
log::warn!("PUS TC error: {}", pus_tc.unwrap_err());
|
||||
return;
|
||||
}
|
||||
let (pus_tc, _) = pus_tc.unwrap();
|
||||
let mut write_and_send = |tm: &PusTmCreator| {
|
||||
let written_size = tm.write_to_bytes(cx.local.verif_buf).unwrap();
|
||||
cx.shared.tm_prod.lock(|prod| {
|
||||
prod.sizes_prod.try_push(tm.len_written()).unwrap();
|
||||
prod.buf_prod
|
||||
.push_slice(&cx.local.verif_buf[0..written_size]);
|
||||
});
|
||||
};
|
||||
let token = cx.local.verif_reporter.add_tc(&pus_tc);
|
||||
let (tm, accepted_token) = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.acceptance_success(cx.local.src_data_buf, token, 0, 0, &[])
|
||||
.expect("acceptance success failed");
|
||||
write_and_send(&tm);
|
||||
|
||||
if pus_tc.service() == PusServiceId::Action as u8 {
|
||||
let mut corrupt_image = |base_addr: u32| {
|
||||
// Safety: We only use this for NVM handling and we only do NVM
|
||||
// handling here.
|
||||
let mut sys_cfg = unsafe { pac::Sysconfig::steal() };
|
||||
let nvm = Nvm::new(
|
||||
&mut sys_cfg,
|
||||
cx.local.rom_spi.take().unwrap(),
|
||||
CLOCKS.get().as_ref().unwrap(),
|
||||
);
|
||||
let mut buf = [0u8; 4];
|
||||
nvm.read_data(base_addr + 32, &mut buf);
|
||||
buf[0] += 1;
|
||||
nvm.write_data(base_addr + 32, &buf);
|
||||
*cx.local.rom_spi = Some(nvm.release(&mut sys_cfg));
|
||||
let tm = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||
.expect("completion success failed");
|
||||
write_and_send(&tm);
|
||||
};
|
||||
if pus_tc.subservice() == ActionId::CorruptImageA as u8 {
|
||||
rprintln!("corrupting App Image A");
|
||||
corrupt_image(APP_A_START_ADDR);
|
||||
}
|
||||
if pus_tc.subservice() == ActionId::CorruptImageB as u8 {
|
||||
rprintln!("corrupting App Image B");
|
||||
corrupt_image(APP_B_START_ADDR);
|
||||
}
|
||||
}
|
||||
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
|
||||
log::info!(target: "TC Handler", "received ping TC");
|
||||
} else if pus_tc.service() == PusServiceId::MemoryManagement as u8 {
|
||||
let tm = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.step_success(
|
||||
cx.local.src_data_buf,
|
||||
&started_token,
|
||||
0,
|
||||
0,
|
||||
&[],
|
||||
EcssEnumU8::new(0),
|
||||
)
|
||||
.expect("step success failed");
|
||||
write_and_send(&tm);
|
||||
// Raw memory write TC
|
||||
if pus_tc.subservice() == 2 {
|
||||
let app_data = pus_tc.app_data();
|
||||
if app_data.len() < 10 {
|
||||
log::warn!(
|
||||
target: "TC Handler",
|
||||
"app data for raw memory write is too short: {}",
|
||||
app_data.len()
|
||||
);
|
||||
}
|
||||
let memory_id = app_data[0];
|
||||
if memory_id != BOOT_NVM_MEMORY_ID {
|
||||
log::warn!(target: "TC Handler", "memory ID {} not supported", memory_id);
|
||||
// TODO: Error reporting
|
||||
return;
|
||||
}
|
||||
let offset = u32::from_be_bytes(app_data[2..6].try_into().unwrap());
|
||||
let data_len = u32::from_be_bytes(app_data[6..10].try_into().unwrap());
|
||||
if 10 + data_len as usize > app_data.len() {
|
||||
log::warn!(
|
||||
target: "TC Handler",
|
||||
"invalid data length {} for raw mem write detected",
|
||||
data_len
|
||||
);
|
||||
// TODO: Error reporting
|
||||
return;
|
||||
}
|
||||
let data = &app_data[10..10 + data_len as usize];
|
||||
log::info!("writing {} bytes at offset {} to NVM", data_len, offset);
|
||||
// Safety: We only use this for NVM handling and we only do NVM
|
||||
// handling here.
|
||||
let mut sys_cfg = unsafe { pac::Sysconfig::steal() };
|
||||
let nvm = Nvm::new(
|
||||
&mut sys_cfg,
|
||||
cx.local.rom_spi.take().unwrap(),
|
||||
CLOCKS.get().as_ref().unwrap(),
|
||||
);
|
||||
nvm.write_data(offset, data);
|
||||
*cx.local.rom_spi = Some(nvm.release(&mut sys_cfg));
|
||||
let tm = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||
.expect("completion success failed");
|
||||
write_and_send(&tm);
|
||||
log::info!("NVM operation done");
|
||||
}
|
||||
}
|
||||
let (tm, started_token) = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.start_success(cx.local.src_data_buf, accepted_token, 0, 0, &[])
|
||||
.expect("acceptance success failed");
|
||||
write_and_send(&tm);
|
||||
|
||||
if pus_tc.service() == PusServiceId::Action as u8 {
|
||||
let mut corrupt_image = |base_addr: u32| {
|
||||
// Safety: We only use this for NVM handling and we only do NVM
|
||||
// handling here.
|
||||
let mut sys_cfg = unsafe { pac::Sysconfig::steal() };
|
||||
let nvm = Nvm::new(
|
||||
&mut sys_cfg,
|
||||
cx.local.rom_spi.take().unwrap(),
|
||||
CLOCKS.get().as_ref().unwrap(),
|
||||
);
|
||||
let mut buf = [0u8; 4];
|
||||
nvm.read_data(base_addr + 32, &mut buf);
|
||||
buf[0] += 1;
|
||||
nvm.write_data(base_addr + 32, &buf);
|
||||
*cx.local.rom_spi = Some(nvm.release(&mut sys_cfg));
|
||||
let tm = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||
.expect("completion success failed");
|
||||
write_and_send(&tm);
|
||||
};
|
||||
if pus_tc.subservice() == ActionId::CorruptImageA as u8 {
|
||||
rprintln!("corrupting App Image A");
|
||||
corrupt_image(APP_A_START_ADDR);
|
||||
}
|
||||
if pus_tc.subservice() == ActionId::CorruptImageB as u8 {
|
||||
rprintln!("corrupting App Image B");
|
||||
corrupt_image(APP_B_START_ADDR);
|
||||
}
|
||||
}
|
||||
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
|
||||
log::info!(target: "TC Handler", "received ping TC");
|
||||
let tm = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||
.expect("completion success failed");
|
||||
write_and_send(&tm);
|
||||
} else if pus_tc.service() == PusServiceId::MemoryManagement as u8 {
|
||||
let tm = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.step_success(
|
||||
cx.local.src_data_buf,
|
||||
&started_token,
|
||||
0,
|
||||
0,
|
||||
&[],
|
||||
EcssEnumU8::new(0),
|
||||
)
|
||||
.expect("step success failed");
|
||||
write_and_send(&tm);
|
||||
// Raw memory write TC
|
||||
if pus_tc.subservice() == 2 {
|
||||
let app_data = pus_tc.app_data();
|
||||
if app_data.len() < 10 {
|
||||
log::warn!(
|
||||
target: "TC Handler",
|
||||
"app data for raw memory write is too short: {}",
|
||||
app_data.len()
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("PUS TC error: {}", e);
|
||||
let memory_id = app_data[0];
|
||||
if memory_id != BOOT_NVM_MEMORY_ID {
|
||||
log::warn!(target: "TC Handler", "memory ID {} not supported", memory_id);
|
||||
// TODO: Error reporting
|
||||
return;
|
||||
}
|
||||
let offset = u32::from_be_bytes(app_data[2..6].try_into().unwrap());
|
||||
let data_len = u32::from_be_bytes(app_data[6..10].try_into().unwrap());
|
||||
if 10 + data_len as usize > app_data.len() {
|
||||
log::warn!(
|
||||
target: "TC Handler",
|
||||
"invalid data length {} for raw mem write detected",
|
||||
data_len
|
||||
);
|
||||
// TODO: Error reporting
|
||||
return;
|
||||
}
|
||||
let data = &app_data[10..10 + data_len as usize];
|
||||
log::info!("writing {} bytes at offset {} to NVM", data_len, offset);
|
||||
// Safety: We only use this for NVM handling and we only do NVM
|
||||
// handling here.
|
||||
let mut sys_cfg = unsafe { pac::Sysconfig::steal() };
|
||||
let nvm = Nvm::new(
|
||||
&mut sys_cfg,
|
||||
cx.local.rom_spi.take().unwrap(),
|
||||
CLOCKS.get().as_ref().unwrap(),
|
||||
);
|
||||
nvm.write_data(offset, data);
|
||||
*cx.local.rom_spi = Some(nvm.release(&mut sys_cfg));
|
||||
let tm = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||
.expect("completion success failed");
|
||||
write_and_send(&tm);
|
||||
log::info!("NVM operation done");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -497,16 +464,16 @@ mod app {
|
||||
read_buf: [u8;MAX_TM_SIZE] = [0; MAX_TM_SIZE],
|
||||
encoded_buf: [u8;MAX_TM_FRAME_SIZE] = [0; MAX_TM_FRAME_SIZE],
|
||||
uart_tx,
|
||||
tx_cons,
|
||||
tm_cons
|
||||
],
|
||||
shared=[]
|
||||
)]
|
||||
async fn pus_tm_tx_handler(cx: pus_tm_tx_handler::Context) {
|
||||
loop {
|
||||
while cx.local.tx_cons.sizes_cons.occupied_len() > 0 {
|
||||
let next_size = cx.local.tx_cons.sizes_cons.try_pop().unwrap();
|
||||
while cx.local.tm_cons.sizes_cons.occupied_len() > 0 {
|
||||
let next_size = cx.local.tm_cons.sizes_cons.try_pop().unwrap();
|
||||
cx.local
|
||||
.tx_cons
|
||||
.tm_cons
|
||||
.buf_cons
|
||||
.pop_slice(&mut cx.local.read_buf[0..next_size]);
|
||||
cx.local.encoded_buf[0] = 0;
|
||||
|
@ -17,12 +17,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
- Added `va41620`, `va41630`, `va41628` and `va41629` device features. A device now has to be
|
||||
selected for HAL compilation to work properly
|
||||
- Adaptions for the UART IRQ feature which are now only implemented for the RX part of the UART.
|
||||
|
||||
## Fixed
|
||||
|
||||
- Small fixes and improvements for ADC drivers
|
||||
- Fixes for the SPI implementation where the clock divider values were not calculated
|
||||
correctly
|
||||
- Fixes for UART IRQ handler implementation
|
||||
|
||||
## Added
|
||||
|
||||
|
@ -20,7 +20,7 @@ embedded-io = "0.6"
|
||||
num_enum = { version = "0.7", default-features = false }
|
||||
typenum = "1"
|
||||
bitflags = "2"
|
||||
bitfield = "0.15"
|
||||
bitfield = "0.17"
|
||||
defmt = { version = "0.3", optional = true }
|
||||
fugit = "0.3"
|
||||
delegate = "0.12"
|
||||
|
@ -197,66 +197,36 @@ impl From<Hertz> for Config {
|
||||
// IRQ Definitions
|
||||
//==================================================================================================
|
||||
|
||||
struct IrqInfo {
|
||||
#[derive(Debug)]
|
||||
pub struct IrqInfo {
|
||||
rx_len: usize,
|
||||
rx_idx: usize,
|
||||
mode: IrqReceptionMode,
|
||||
}
|
||||
|
||||
pub enum IrqResultMask {
|
||||
Complete = 0,
|
||||
Overflow = 1,
|
||||
FramingError = 2,
|
||||
ParityError = 3,
|
||||
Break = 4,
|
||||
Timeout = 5,
|
||||
Addr9 = 6,
|
||||
/// Should not happen
|
||||
Unknown = 7,
|
||||
}
|
||||
|
||||
/// This struct is used to return the default IRQ handler result to the user
|
||||
#[derive(Debug, Default)]
|
||||
pub struct IrqResult {
|
||||
raw_res: u32,
|
||||
complete: bool,
|
||||
timeout: bool,
|
||||
pub errors: IrqUartError,
|
||||
pub bytes_read: usize,
|
||||
}
|
||||
|
||||
impl IrqResult {
|
||||
pub const fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
IrqResult {
|
||||
raw_res: 0,
|
||||
complete: false,
|
||||
timeout: false,
|
||||
errors: IrqUartError::default(),
|
||||
bytes_read: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IrqResult {
|
||||
#[inline]
|
||||
pub fn raw_result(&self) -> u32 {
|
||||
self.raw_res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn clear_result(&mut self) {
|
||||
self.raw_res = 0;
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn set_result(&mut self, flag: IrqResultMask) {
|
||||
self.raw_res |= 1 << flag as u32;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn complete(&self) -> bool {
|
||||
if ((self.raw_res >> IrqResultMask::Complete as u32) & 0x01) == 0x01 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn error(&self) -> bool {
|
||||
if self.overflow_error() || self.framing_error() || self.parity_error() {
|
||||
if self.errors.overflow || self.errors.parity || self.errors.framing {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
@ -264,34 +234,27 @@ impl IrqResult {
|
||||
|
||||
#[inline]
|
||||
pub fn overflow_error(&self) -> bool {
|
||||
if ((self.raw_res >> IrqResultMask::Overflow as u32) & 0x01) == 0x01 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
self.errors.overflow
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn framing_error(&self) -> bool {
|
||||
if ((self.raw_res >> IrqResultMask::FramingError as u32) & 0x01) == 0x01 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
self.errors.framing
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn parity_error(&self) -> bool {
|
||||
if ((self.raw_res >> IrqResultMask::ParityError as u32) & 0x01) == 0x01 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
self.errors.parity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn timeout(&self) -> bool {
|
||||
if ((self.raw_res >> IrqResultMask::Timeout as u32) & 0x01) == 0x01 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
self.timeout
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn complete(&self) -> bool {
|
||||
self.complete
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,41 +280,27 @@ pub struct Uart<UartInstance, Pins> {
|
||||
pins: Pins,
|
||||
}
|
||||
|
||||
/// UART using the IRQ capabilities of the peripheral. Can be created with the
|
||||
/// [`Uart::into_uart_with_irq`] function. Currently, only the RX side for IRQ based reception
|
||||
/// is implemented.
|
||||
pub struct UartWithIrq<Uart, Pins> {
|
||||
base: UartWithIrqBase<Uart>,
|
||||
pins: Pins,
|
||||
}
|
||||
/// Serial receiver
|
||||
pub struct Rx<Uart>(Uart);
|
||||
|
||||
/// Type-erased UART using the IRQ capabilities of the peripheral. Can be created with the
|
||||
/// [`UartWithIrq::downgrade`] function. Currently, only the RX side for IRQ based reception
|
||||
/// is implemented.
|
||||
pub struct UartWithIrqBase<UART> {
|
||||
pub inner: UartBase<UART>,
|
||||
// Serial receiver, using interrupts to offload reading to the hardware.
|
||||
pub struct RxWithIrq<Uart> {
|
||||
inner: Rx<Uart>,
|
||||
irq_info: IrqInfo,
|
||||
}
|
||||
|
||||
/// Serial receiver
|
||||
pub struct Rx<Uart> {
|
||||
uart: Uart,
|
||||
}
|
||||
|
||||
/// Serial transmitter
|
||||
pub struct Tx<Uart> {
|
||||
uart: Uart,
|
||||
}
|
||||
pub struct Tx<Uart>(Uart);
|
||||
|
||||
impl<Uart: Instance> Rx<Uart> {
|
||||
fn new(uart: Uart) -> Self {
|
||||
Self { uart }
|
||||
Self(uart)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Uart> Tx<Uart> {
|
||||
fn new(uart: Uart) -> Self {
|
||||
Self { uart }
|
||||
Self(uart)
|
||||
}
|
||||
}
|
||||
|
||||
@ -602,20 +551,30 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
|
||||
}
|
||||
|
||||
/// If the IRQ capabilities of the peripheral are used, the UART needs to be converted
|
||||
/// with this function
|
||||
pub fn into_uart_with_irq(self) -> UartWithIrq<UartInstance, (TxPinInst, RxPinInst)> {
|
||||
/// with this function. Currently, IRQ abstractions are only implemented for the RX part
|
||||
/// of the UART, so this function will release a TX and RX handle as well as the pin
|
||||
/// instances.
|
||||
pub fn split_with_irq(
|
||||
self,
|
||||
) -> (
|
||||
Tx<UartInstance>,
|
||||
RxWithIrq<UartInstance>,
|
||||
(TxPinInst, RxPinInst),
|
||||
) {
|
||||
let (inner, pins) = self.downgrade_internal();
|
||||
UartWithIrq {
|
||||
pins,
|
||||
base: UartWithIrqBase {
|
||||
inner,
|
||||
let (tx, rx) = inner.split();
|
||||
(
|
||||
tx,
|
||||
RxWithIrq {
|
||||
inner: rx,
|
||||
irq_info: IrqInfo {
|
||||
rx_len: 0,
|
||||
rx_idx: 0,
|
||||
mode: IrqReceptionMode::Idle,
|
||||
},
|
||||
},
|
||||
}
|
||||
pins,
|
||||
)
|
||||
}
|
||||
|
||||
delegate::delegate! {
|
||||
@ -673,11 +632,26 @@ impl<Uart: Instance> Rx<Uart> {
|
||||
///
|
||||
/// You must ensure that only registers related to the operation of the RX side are used.
|
||||
pub unsafe fn uart(&self) -> &Uart {
|
||||
&self.uart
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_fifo(&self) {
|
||||
self.uart.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||
self.0.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable(&mut self) {
|
||||
self.0.enable().modify(|_, w| w.rxenable().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable(&mut self) {
|
||||
self.0.enable().modify(|_, w| w.rxenable().clear_bit());
|
||||
}
|
||||
|
||||
pub fn release(self) -> Uart {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -688,11 +662,22 @@ impl<Uart: Instance> Tx<Uart> {
|
||||
///
|
||||
/// You must ensure that only registers related to the operation of the TX side are used.
|
||||
pub unsafe fn uart(&self) -> &Uart {
|
||||
&self.uart
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_fifo(&self) {
|
||||
self.uart.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||
self.0.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable(&mut self) {
|
||||
self.0.enable().modify(|_, w| w.txenable().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable(&mut self) {
|
||||
self.0.enable().modify(|_, w| w.txenable().clear_bit());
|
||||
}
|
||||
}
|
||||
|
||||
@ -701,6 +686,7 @@ pub struct IrqUartError {
|
||||
overflow: bool,
|
||||
framing: bool,
|
||||
parity: bool,
|
||||
other: bool,
|
||||
}
|
||||
|
||||
impl IrqUartError {
|
||||
@ -715,7 +701,7 @@ pub enum IrqError {
|
||||
Uart(IrqUartError),
|
||||
}
|
||||
|
||||
impl<Uart: Instance> UartWithIrqBase<Uart> {
|
||||
impl<Uart: Instance> RxWithIrq<Uart> {
|
||||
/// This initializes a non-blocking read transfer using the IRQ capabilities of the UART
|
||||
/// peripheral.
|
||||
///
|
||||
@ -735,8 +721,7 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
||||
self.irq_info.mode = IrqReceptionMode::Pending;
|
||||
self.irq_info.rx_idx = 0;
|
||||
self.irq_info.rx_len = max_len;
|
||||
self.inner.enable_rx();
|
||||
self.inner.enable_tx();
|
||||
self.inner.enable();
|
||||
self.enable_rx_irq_sources(enb_timeout_irq);
|
||||
unsafe { enable_interrupt(Uart::IRQ_RX) };
|
||||
Ok(())
|
||||
@ -744,7 +729,7 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
||||
|
||||
#[inline]
|
||||
fn enable_rx_irq_sources(&mut self, timeout: bool) {
|
||||
self.inner.uart.irq_enb().modify(|_, w| {
|
||||
self.inner.0.irq_enb().modify(|_, w| {
|
||||
if timeout {
|
||||
w.irq_rx_to().set_bit();
|
||||
}
|
||||
@ -755,30 +740,24 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
||||
|
||||
#[inline]
|
||||
fn disable_rx_irq_sources(&mut self) {
|
||||
self.inner.uart.irq_enb().modify(|_, w| {
|
||||
self.inner.0.irq_enb().modify(|_, w| {
|
||||
w.irq_rx_to().clear_bit();
|
||||
w.irq_rx_status().clear_bit();
|
||||
w.irq_rx().clear_bit()
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable_tx(&mut self) {
|
||||
self.inner.enable_tx()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable_tx(&mut self) {
|
||||
self.inner.disable_tx()
|
||||
}
|
||||
|
||||
pub fn cancel_transfer(&mut self) {
|
||||
self.disable_rx_irq_sources();
|
||||
self.inner.clear_tx_fifo();
|
||||
self.inner.clear_fifo();
|
||||
self.irq_info.rx_idx = 0;
|
||||
self.irq_info.rx_len = 0;
|
||||
}
|
||||
|
||||
pub fn uart(&self) -> &Uart {
|
||||
&self.inner.0
|
||||
}
|
||||
|
||||
/// Default IRQ handler which can be used to read the packets arriving on the UART peripheral.
|
||||
///
|
||||
/// If passed buffer is equal to or larger than the specified maximum length, an
|
||||
@ -791,104 +770,102 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
||||
});
|
||||
}
|
||||
let mut res = IrqResult::default();
|
||||
let mut possible_error = IrqUartError::default();
|
||||
|
||||
let rx_status = self.inner.uart.rxstatus().read();
|
||||
res.raw_res = rx_status.bits();
|
||||
let irq_end = self.inner.uart.irq_end().read();
|
||||
let enb_status = self.inner.uart.enable().read();
|
||||
let irq_end = self.inner.0.irq_end().read();
|
||||
let enb_status = self.inner.0.enable().read();
|
||||
let rx_enabled = enb_status.rxenable().bit_is_set();
|
||||
let _tx_enabled = enb_status.txenable().bit_is_set();
|
||||
let read_handler = |res: &mut IrqResult,
|
||||
possible_error: &mut IrqUartError,
|
||||
read_res: nb::Result<u8, Error>|
|
||||
-> Option<u8> {
|
||||
match read_res {
|
||||
Ok(byte) => Some(byte),
|
||||
Err(nb::Error::WouldBlock) => None,
|
||||
Err(nb::Error::Other(e)) => {
|
||||
match e {
|
||||
Error::Overrun => {
|
||||
possible_error.overflow = true;
|
||||
}
|
||||
Error::FramingError => {
|
||||
possible_error.framing = true;
|
||||
}
|
||||
Error::ParityError => {
|
||||
possible_error.parity = true;
|
||||
}
|
||||
_ => {
|
||||
res.set_result(IrqResultMask::Unknown);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Half-Full interrupt. We have a guaranteed amount of data we can read.
|
||||
if irq_end.irq_rx().bit_is_set() {
|
||||
// Determine the number of bytes to read, ensuring we leave 1 byte in the FIFO.
|
||||
// We use this trick/hack because the timeout feature of the peripheral relies on data
|
||||
// being in the RX FIFO. If data continues arriving, another half-full IRQ will fire.
|
||||
// If not, the last byte(s) is/are emptied by the timeout interrupt.
|
||||
let available_bytes = self.inner.0.rxfifoirqtrg().read().bits() as usize;
|
||||
|
||||
let bytes_to_read = core::cmp::min(
|
||||
available_bytes.saturating_sub(1),
|
||||
self.irq_info.rx_len - self.irq_info.rx_idx,
|
||||
);
|
||||
|
||||
// If this interrupt bit is set, the trigger level is available at the very least.
|
||||
// Read everything as fast as possible
|
||||
for _ in 0..core::cmp::min(
|
||||
self.inner.uart.rxfifoirqtrg().read().bits() as usize,
|
||||
self.irq_info.rx_len,
|
||||
) {
|
||||
buf[self.irq_info.rx_idx] = (self.inner.uart.data().read().bits() & 0xff) as u8;
|
||||
for _ in 0..bytes_to_read {
|
||||
buf[self.irq_info.rx_idx] = (self.inner.0.data().read().bits() & 0xff) as u8;
|
||||
self.irq_info.rx_idx += 1;
|
||||
}
|
||||
|
||||
// On high-baudrates, data might be available immediately, and we possible have to
|
||||
// read continuosly? Then again, the CPU should always be faster than that. I'd rather
|
||||
// rely on the hardware firing another IRQ. I have not tried baudrates higher than
|
||||
// 115200 so far.
|
||||
}
|
||||
let read_handler =
|
||||
|possible_error: &mut IrqUartError, read_res: nb::Result<u8, Error>| -> Option<u8> {
|
||||
match read_res {
|
||||
Ok(byte) => Some(byte),
|
||||
Err(nb::Error::WouldBlock) => None,
|
||||
Err(nb::Error::Other(e)) => {
|
||||
match e {
|
||||
Error::Overrun => {
|
||||
possible_error.overflow = true;
|
||||
}
|
||||
Error::FramingError => {
|
||||
possible_error.framing = true;
|
||||
}
|
||||
Error::ParityError => {
|
||||
possible_error.parity = true;
|
||||
}
|
||||
_ => {
|
||||
possible_error.other = true;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
// Timeout, empty the FIFO completely.
|
||||
if irq_end.irq_rx_to().bit_is_set() {
|
||||
// While there is data in the FIFO, write it into the reception buffer
|
||||
loop {
|
||||
if self.irq_info.rx_idx == self.irq_info.rx_len {
|
||||
self.irq_completion_handler(&mut res);
|
||||
return Ok(res);
|
||||
break;
|
||||
}
|
||||
if let Some(byte) = read_handler(&mut res, &mut possible_error, self.inner.read()) {
|
||||
if let Some(byte) = read_handler(&mut res.errors, self.inner.read()) {
|
||||
buf[self.irq_info.rx_idx] = byte;
|
||||
self.irq_info.rx_idx += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.irq_completion_handler(&mut res);
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
// RX transfer not complete, check for RX errors
|
||||
if (self.irq_info.rx_idx < self.irq_info.rx_len) && rx_enabled {
|
||||
// Read status register again, might have changed since reading received data
|
||||
let rx_status = self.inner.uart.rxstatus().read();
|
||||
res.raw_res = rx_status.bits();
|
||||
let rx_status = self.inner.0.rxstatus().read();
|
||||
if rx_status.rxovr().bit_is_set() {
|
||||
possible_error.overflow = true;
|
||||
res.errors.overflow = true;
|
||||
}
|
||||
if rx_status.rxfrm().bit_is_set() {
|
||||
possible_error.framing = true;
|
||||
res.errors.framing = true;
|
||||
}
|
||||
if rx_status.rxpar().bit_is_set() {
|
||||
possible_error.parity = true;
|
||||
}
|
||||
if rx_status.rxto().bit_is_set() {
|
||||
// A timeout has occured but there might be some leftover data in the FIFO,
|
||||
// so read that data as well
|
||||
while let Some(byte) =
|
||||
read_handler(&mut res, &mut possible_error, self.inner.read())
|
||||
{
|
||||
buf[self.irq_info.rx_idx] = byte;
|
||||
self.irq_info.rx_idx += 1;
|
||||
}
|
||||
self.irq_completion_handler(&mut res);
|
||||
res.set_result(IrqResultMask::Timeout);
|
||||
return Ok(res);
|
||||
res.errors.parity = true;
|
||||
}
|
||||
|
||||
// If it is not a timeout, it's an error
|
||||
if possible_error.error() {
|
||||
if res.error() {
|
||||
self.disable_rx_irq_sources();
|
||||
return Err(IrqError::Uart(possible_error));
|
||||
return Err(IrqError::Uart(res.errors));
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the interrupt status bits
|
||||
self.inner
|
||||
.uart
|
||||
.0
|
||||
.irq_clr()
|
||||
.write(|w| unsafe { w.bits(irq_end.bits()) });
|
||||
Ok(res)
|
||||
@ -896,48 +873,23 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
||||
|
||||
fn irq_completion_handler(&mut self, res: &mut IrqResult) {
|
||||
self.disable_rx_irq_sources();
|
||||
self.inner.disable_rx();
|
||||
self.inner.disable();
|
||||
res.bytes_read = self.irq_info.rx_idx;
|
||||
res.clear_result();
|
||||
res.set_result(IrqResultMask::Complete);
|
||||
res.complete = true;
|
||||
self.irq_info.mode = IrqReceptionMode::Idle;
|
||||
self.irq_info.rx_idx = 0;
|
||||
self.irq_info.rx_len = 0;
|
||||
}
|
||||
|
||||
pub fn irq_info(&self) -> &IrqInfo {
|
||||
&self.irq_info
|
||||
}
|
||||
|
||||
pub fn release(self) -> Uart {
|
||||
self.inner.release()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Uart: Instance, Pins> UartWithIrq<Uart, Pins> {
|
||||
/// See [`UartWithIrqBase::read_fixed_len_using_irq`] doc
|
||||
pub fn read_fixed_len_using_irq(
|
||||
&mut self,
|
||||
max_len: usize,
|
||||
enb_timeout_irq: bool,
|
||||
) -> Result<(), Error> {
|
||||
self.base.read_fixed_len_using_irq(max_len, enb_timeout_irq)
|
||||
}
|
||||
|
||||
pub fn cancel_transfer(&mut self) {
|
||||
self.base.cancel_transfer()
|
||||
}
|
||||
|
||||
/// See [`UartWithIrqBase::irq_handler`] doc
|
||||
pub fn irq_handler(&mut self, buf: &mut [u8]) -> Result<IrqResult, IrqError> {
|
||||
self.base.irq_handler(buf)
|
||||
}
|
||||
|
||||
pub fn release(self) -> (Uart, Pins) {
|
||||
(self.base.release(), self.pins)
|
||||
}
|
||||
|
||||
pub fn downgrade(self) -> (UartWithIrqBase<Uart>, Pins) {
|
||||
(self.base, self.pins)
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_io::Error for Error {
|
||||
fn kind(&self) -> embedded_io::ErrorKind {
|
||||
embedded_io::ErrorKind::Other
|
||||
|
Loading…
Reference in New Issue
Block a user