Merge pull request 'smaller improvements and fixes' (#34) from smaller-improvements-fixes into main
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good

Reviewed-on: #34
This commit is contained in:
Robin Müller 2024-09-30 13:50:52 +02:00
commit bea5a852a2
7 changed files with 209 additions and 206 deletions

View File

@ -11,10 +11,10 @@ The bootloader uses the following memory map:
| ------ | ---- | ---- |
| 0x0 | Bootloader start | code up to 0x3FFC bytes |
| 0x3FFC | Bootloader CRC | word |
| 0x4000 | App image A start | code up to 0x1DFFC (~120K) bytes |
| 0x4000 | App image A start | code up to 0x1DFF8 (~120K) bytes |
| 0x21FF8 | App image A CRC check length | word |
| 0x21FFC | App image A CRC check value | word |
| 0x22000 | App image B start | code up to 0x1DFFC (~120K) bytes |
| 0x22000 | App image B start | code up to 0x1DFF8 (~120K) bytes |
| 0x3FFF8 | App image B CRC check length | word |
| 0x3FFFC | App image B CRC check value | word |
| 0x40000 | End of NVM | end |

View File

@ -53,7 +53,7 @@ const APP_A_START_ADDR: u32 = BOOTLOADER_END_ADDR;
const APP_A_SIZE_ADDR: u32 = APP_B_END_ADDR - 8;
// 0x21FFC
const APP_A_CRC_ADDR: u32 = APP_B_END_ADDR - 4;
pub const APP_A_END_ADDR: u32 = APP_B_END_ADDR - BOOTLOADER_END_ADDR / 2;
pub const APP_A_END_ADDR: u32 = BOOTLOADER_END_ADDR + APP_IMG_SZ;
// 0x22000
const APP_B_START_ADDR: u32 = APP_A_END_ADDR;

View File

@ -86,7 +86,7 @@ async fn main(spawner: Spawner) {
&clocks,
);
let (mut tx, rx) = uart0.split();
let mut rx = rx.to_rx_with_irq();
let mut rx = rx.into_rx_with_irq();
rx.start();
RX.lock(|static_rx| {
static_rx.borrow_mut().replace(rx);

View File

@ -193,7 +193,7 @@ mod app {
Mono::start(cx.core.SYST, clocks.sysclk().raw());
CLOCKS.set(clocks).unwrap();
let mut rx = rx.to_rx_with_irq();
let mut rx = rx.into_rx_with_irq();
let mut rx_context = IrqContextTimeoutOrMaxSize::new(MAX_TC_FRAME_SIZE);
rx.read_fixed_len_or_timeout_based_using_irq(&mut rx_context)
.expect("initiating UART RX failed");

View File

@ -1,7 +1,7 @@
/* Special linker script for application slot A with an offset at address 0x4000 */
MEMORY
{
FLASH : ORIGIN = 0x00004000, LENGTH = 256K
FLASH : ORIGIN = 0x00004000, LENGTH = 0x1DFF8
/* RAM is a mandatory region. This RAM refers to the SRAM_0 */
RAM : ORIGIN = 0x1FFF8000, LENGTH = 32K
SRAM_1 : ORIGIN = 0x20000000, LENGTH = 32K

View File

@ -1,7 +1,7 @@
/* Special linker script for application slot B with an offset at address 0x22000 */
MEMORY
{
FLASH : ORIGIN = 0x00022000, LENGTH = 256K
FLASH : ORIGIN = 0x00022000, LENGTH = 0x1DFF8
/* RAM is a mandatory region. This RAM refers to the SRAM_0 */
RAM : ORIGIN = 0x1FFF8000, LENGTH = 32K
SRAM_1 : ORIGIN = 0x20000000, LENGTH = 32K

View File

@ -94,6 +94,36 @@ impl From<RxError> for Error {
}
}
impl embedded_io::Error for Error {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}
impl embedded_io::Error for RxError {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}
impl embedded_hal_nb::serial::Error for RxError {
fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
match self {
RxError::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
RxError::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,
RxError::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
}
}
}
impl embedded_hal_nb::serial::Error for Error {
fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
match self {
Error::Rx(rx_error) => embedded_hal_nb::serial::Error::kind(rx_error),
Error::BreakCondition => embedded_hal_nb::serial::ErrorKind::Other,
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Event {
@ -305,6 +335,50 @@ enum IrqReceptionMode {
Pending,
}
#[derive(Default, Debug, Copy, Clone)]
pub struct IrqUartError {
overflow: bool,
framing: bool,
parity: bool,
other: bool,
}
impl IrqUartError {
#[inline(always)]
pub fn overflow(&self) -> bool {
self.overflow
}
#[inline(always)]
pub fn framing(&self) -> bool {
self.framing
}
#[inline(always)]
pub fn parity(&self) -> bool {
self.parity
}
#[inline(always)]
pub fn other(&self) -> bool {
self.other
}
}
impl IrqUartError {
#[inline(always)]
pub fn error(&self) -> bool {
self.overflow || self.framing || self.parity
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct BufferTooShortError {
found: usize,
expected: usize,
}
//==================================================================================================
// UART peripheral wrapper
//==================================================================================================
@ -510,6 +584,40 @@ impl<Uart: Instance> UartBase<Uart> {
}
}
impl<UartInstance> embedded_io::ErrorType for UartBase<UartInstance> {
type Error = Error;
}
impl<UartInstance> embedded_hal_nb::serial::ErrorType for UartBase<UartInstance> {
type Error = Error;
}
impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for UartBase<Uart> {
fn read(&mut self) -> nb::Result<u8, Self::Error> {
self.rx.read().map_err(|e| e.map(Error::Rx))
}
}
impl<Uart: Instance> embedded_hal_nb::serial::Write<u8> for UartBase<Uart> {
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
self.tx.write(word).map_err(|e| {
if let nb::Error::Other(_) = e {
unreachable!()
}
nb::Error::WouldBlock
})
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
self.tx.flush().map_err(|e| {
if let nb::Error::Other(_) = e {
unreachable!()
}
nb::Error::WouldBlock
})
}
}
/// Serial abstraction. Entry point to create a new UART
pub struct Uart<UartInstance, Pins> {
inner: UartBase<UartInstance>,
@ -620,9 +728,7 @@ impl<Uart: Instance> Rx<Uart> {
fn new(uart: Uart) -> Self {
Self(uart)
}
}
impl<Uart: Instance> Rx<Uart> {
/// Direct access to the peripheral structure.
///
/// # Safety
@ -674,7 +780,7 @@ impl<Uart: Instance> Rx<Uart> {
self.0.data().read().bits()
}
pub fn to_rx_with_irq(self) -> RxWithIrq<Uart> {
pub fn into_rx_with_irq(self) -> RxWithIrq<Uart> {
RxWithIrq(self)
}
@ -683,18 +789,69 @@ impl<Uart: Instance> Rx<Uart> {
}
}
impl<Uart> embedded_io::ErrorType for Rx<Uart> {
type Error = RxError;
}
impl<Uart> embedded_hal_nb::serial::ErrorType for Rx<Uart> {
type Error = RxError;
}
impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for Rx<Uart> {
fn read(&mut self) -> nb::Result<u8, Self::Error> {
let uart = unsafe { &(*Uart::ptr()) };
let status_reader = uart.rxstatus().read();
let err = if status_reader.rxovr().bit_is_set() {
Some(RxError::Overrun)
} else if status_reader.rxfrm().bit_is_set() {
Some(RxError::Framing)
} else if status_reader.rxpar().bit_is_set() {
Some(RxError::Parity)
} else {
None
};
if let Some(err) = err {
// The status code is always related to the next bit for the framing
// and parity status bits. We have to read the DATA register
// so that the next status reflects the next DATA word
// For overrun error, we read as well to clear the peripheral
self.read_fifo_unchecked();
return Err(err.into());
}
self.read_fifo().map(|val| (val & 0xff) as u8).map_err(|e| {
if let nb::Error::Other(_) = e {
unreachable!()
}
nb::Error::WouldBlock
})
}
}
impl<Uart: Instance> embedded_io::Read for Rx<Uart> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
return Ok(0);
}
for byte in buf.iter_mut() {
let w = nb::block!(<Self as embedded_hal_nb::serial::Read<u8>>::read(self))?;
*byte = w;
}
Ok(buf.len())
}
}
/// Serial transmitter
///
/// Can be created by using the [Uart::split] or [UartBase::split] API.
pub struct Tx<Uart>(Uart);
impl<Uart> Tx<Uart> {
impl<Uart: Instance> Tx<Uart> {
fn new(uart: Uart) -> Self {
Self(uart)
}
}
impl<Uart: Instance> Tx<Uart> {
/// Direct access to the peripheral structure.
///
/// # Safety
@ -746,48 +903,47 @@ impl<Uart: Instance> Tx<Uart> {
}
}
#[derive(Default, Debug, Copy, Clone)]
pub struct IrqUartError {
overflow: bool,
framing: bool,
parity: bool,
other: bool,
impl<Uart> embedded_io::ErrorType for Tx<Uart> {
type Error = Infallible;
}
impl IrqUartError {
#[inline(always)]
pub fn overflow(&self) -> bool {
self.overflow
impl<Uart> embedded_hal_nb::serial::ErrorType for Tx<Uart> {
type Error = Infallible;
}
#[inline(always)]
pub fn framing(&self) -> bool {
self.framing
impl<Uart: Instance> embedded_hal_nb::serial::Write<u8> for Tx<Uart> {
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
self.write_fifo(word as u32)
}
#[inline(always)]
pub fn parity(&self) -> bool {
self.parity
fn flush(&mut self) -> nb::Result<(), Self::Error> {
// SAFETY: Only TX related registers are used.
let reader = unsafe { &(*Uart::ptr()) }.txstatus().read();
if reader.wrbusy().bit_is_set() {
return Err(nb::Error::WouldBlock);
}
#[inline(always)]
pub fn other(&self) -> bool {
self.other
Ok(())
}
}
impl IrqUartError {
#[inline(always)]
pub fn error(&self) -> bool {
self.overflow || self.framing || self.parity
}
impl<Uart: Instance> embedded_io::Write for Tx<Uart> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
return Ok(0);
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct BufferTooShortError {
found: usize,
expected: usize,
for byte in buf.iter() {
nb::block!(<Self as embedded_hal_nb::serial::Write<u8>>::write(
self, *byte
))?;
}
Ok(buf.len())
}
fn flush(&mut self) -> Result<(), Self::Error> {
nb::block!(<Self as embedded_hal_nb::serial::Write<u8>>::flush(self))
}
}
/// Serial receiver, using interrupts to offload reading to the hardware.
@ -1063,165 +1219,12 @@ impl<Uart: Instance> RxWithIrq<Uart> {
context.rx_idx = 0;
}
pub fn release(self) -> Uart {
/// # Safety
///
/// This API allows creating multiple UART instances when releasing the TX structure as well.
/// The user must ensure that these instances are not used to create multiple overlapping
/// UART drivers.
pub unsafe fn release(self) -> Uart {
self.0.release()
}
}
impl embedded_io::Error for Error {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}
impl embedded_io::Error for RxError {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}
impl embedded_hal_nb::serial::Error for Error {
fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
embedded_hal_nb::serial::ErrorKind::Other
}
}
impl embedded_hal_nb::serial::Error for RxError {
fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
match self {
RxError::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
RxError::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,
RxError::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
}
}
}
impl<Uart> embedded_io::ErrorType for Rx<Uart> {
type Error = RxError;
}
impl<Uart> embedded_hal_nb::serial::ErrorType for Rx<Uart> {
type Error = RxError;
}
impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for Rx<Uart> {
fn read(&mut self) -> nb::Result<u8, Self::Error> {
let uart = unsafe { &(*Uart::ptr()) };
let status_reader = uart.rxstatus().read();
let err = if status_reader.rxovr().bit_is_set() {
Some(RxError::Overrun)
} else if status_reader.rxfrm().bit_is_set() {
Some(RxError::Framing)
} else if status_reader.rxpar().bit_is_set() {
Some(RxError::Parity)
} else {
None
};
if let Some(err) = err {
// The status code is always related to the next bit for the framing
// and parity status bits. We have to read the DATA register
// so that the next status reflects the next DATA word
// For overrun error, we read as well to clear the peripheral
self.read_fifo_unchecked();
return Err(err.into());
}
self.read_fifo().map(|val| (val & 0xff) as u8).map_err(|e| {
if let nb::Error::Other(_) = e {
unreachable!()
}
nb::Error::WouldBlock
})
}
}
impl<Uart: Instance> embedded_io::Read for Rx<Uart> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
return Ok(0);
}
for byte in buf.iter_mut() {
let w = nb::block!(<Self as embedded_hal_nb::serial::Read<u8>>::read(self))?;
*byte = w;
}
Ok(buf.len())
}
}
impl<Uart> embedded_io::ErrorType for Tx<Uart> {
type Error = Infallible;
}
impl<Uart> embedded_hal_nb::serial::ErrorType for Tx<Uart> {
type Error = Infallible;
}
impl<Uart: Instance> embedded_hal_nb::serial::Write<u8> for Tx<Uart> {
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
self.write_fifo(word as u32)
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
// SAFETY: Only TX related registers are used.
let reader = unsafe { &(*Uart::ptr()) }.txstatus().read();
if reader.wrbusy().bit_is_set() {
return Err(nb::Error::WouldBlock);
}
Ok(())
}
}
impl<Uart: Instance> embedded_io::Write for Tx<Uart> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
return Ok(0);
}
for byte in buf.iter() {
nb::block!(<Self as embedded_hal_nb::serial::Write<u8>>::write(
self, *byte
))?;
}
Ok(buf.len())
}
fn flush(&mut self) -> Result<(), Self::Error> {
nb::block!(<Self as embedded_hal_nb::serial::Write<u8>>::flush(self))
}
}
impl<UartInstance> embedded_io::ErrorType for UartBase<UartInstance> {
type Error = Error;
}
impl<UartInstance> embedded_hal_nb::serial::ErrorType for UartBase<UartInstance> {
type Error = Error;
}
impl<Uart: Instance> embedded_hal_nb::serial::Read<u8> for UartBase<Uart> {
fn read(&mut self) -> nb::Result<u8, Self::Error> {
self.rx.read().map_err(|e| e.map(Error::Rx))
}
}
impl<Uart: Instance> embedded_hal_nb::serial::Write<u8> for UartBase<Uart> {
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
self.tx.write(word).map_err(|e| {
if let nb::Error::Other(_) = e {
unreachable!()
}
nb::Error::WouldBlock
})
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
self.tx.flush().map_err(|e| {
if let nb::Error::Other(_) = e {
unreachable!()
}
nb::Error::WouldBlock
})
}
}