Fix buffer overruns

This commit is contained in:
Christian Perez Llamas 2022-11-17 00:19:22 +01:00
parent 4fe834db2f
commit 1ed260b105
2 changed files with 294 additions and 221 deletions

View File

@ -3,6 +3,7 @@
//! I2S
use core::future::poll_fn;
use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
@ -10,7 +11,6 @@ use embassy_cortex_m::interrupt::{InterruptExt, Priority};
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
//use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::interrupt::Interrupt;
use crate::pac::i2s::{RegisterBlock, CONFIG, PSEL};
@ -31,9 +31,9 @@ pub const SRAM_UPPER: usize = 0x3000_0000;
pub enum Error {
BufferTooLong,
BufferZeroLength,
DMABufferNotInDataMemory,
BufferNotInDataMemory,
BufferMisaligned,
// TODO: add other error variants.
BufferLengthMisaligned,
}
pub const MODE_MASTER_8000: Mode = Mode::Master {
@ -122,12 +122,12 @@ pub enum MckFreq {
}
impl MckFreq {
const REGISTER_VALUES: &[u32] = &[
const REGISTER_VALUES: &'static [u32] = &[
0x20000000, 0x18000000, 0x16000000, 0x11000000, 0x10000000, 0x0C000000, 0x0B000000, 0x08800000, 0x08400000,
0x08000000, 0x06000000, 0x04100000, 0x020C0000,
];
const FREQUENCIES: &[u32] = &[
const FREQUENCIES: &'static [u32] = &[
4000000, 3200000, 2909090, 2133333, 2000000, 1523809, 1391304, 1066666, 1032258, 1000000, 761904, 507936,
256000,
];
@ -162,7 +162,7 @@ pub enum Ratio {
}
impl Ratio {
const RATIOS: &[u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512];
const RATIOS: &'static [u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512];
pub fn to_divisor(&self) -> u32 {
Self::RATIOS[u8::from(*self) as usize]
@ -234,23 +234,10 @@ impl From<Channels> for u8 {
}
}
/// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload.
/// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload.
///
/// For more details about EasyDMA, consult the module documentation.
pub struct I2S<'d, T: Instance> {
output: I2sOutput<'d, T>,
input: I2sInput<'d, T>,
}
/// Transmitter interface to the UARTE peripheral obtained
/// via [Uarte]::split.
pub struct I2sOutput<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
/// Receiver interface to the UARTE peripheral obtained
/// via [Uarte]::split.
pub struct I2sInput<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
@ -298,78 +285,19 @@ impl<'d, T: Instance> I2S<'d, T> {
r.enable.write(|w| w.enable().enabled());
Self {
output: I2sOutput {
_p: unsafe { i2s.clone_unchecked() },
},
input: I2sInput { _p: i2s },
}
Self { _p: i2s }
}
/// Enables the I2S module.
#[inline(always)]
pub fn enable(&self) -> &Self {
let r = T::regs();
r.enable.write(|w| w.enable().enabled());
self
pub fn output(self) -> Output<'d, T> {
Output { _p: self._p }
}
/// Disables the I2S module.
#[inline(always)]
pub fn disable(&self) -> &Self {
let r = T::regs();
r.enable.write(|w| w.enable().disabled());
self
pub fn input(self) -> Input<'d, T> {
Input { _p: self._p }
}
/// Starts I2S transfer.
#[inline(always)]
pub fn start(&self) -> &Self {
let r = T::regs();
self.enable();
trace!("START");
r.tasks_start.write(|w| unsafe { w.bits(1) });
self
}
/// Stops the I2S transfer and waits until it has stopped.
#[inline(always)]
pub async fn stop(&self) {
todo!()
}
/// Enables/disables I2S transmission (TX).
#[inline(always)]
pub fn set_tx_enabled(&self, enabled: bool) -> &Self {
let r = T::regs();
r.config.txen.write(|w| w.txen().bit(enabled));
self
}
/// Enables/disables I2S reception (RX).
#[inline(always)]
pub fn set_rx_enabled(&self, enabled: bool) -> &Self {
let r = T::regs();
r.config.rxen.write(|w| w.rxen().bit(enabled));
self
}
/// Transmits the given `buffer`.
/// Buffer address must be 4 byte aligned and located in RAM.
pub async fn tx<B>(&mut self, buffer: B) -> Result<(), Error>
where
B: Buffer,
{
self.output.tx(buffer).await
}
/// Receives data into the given `buffer` until it's filled.
/// Buffer address must be 4 byte aligned and located in RAM.
pub async fn rx<B>(&mut self, buffer: B) -> Result<(), Error>
where
B: Buffer,
{
self.input.rx(buffer).await
pub fn full_duplex(self) -> FullDuplex<'d, T> {
FullDuplex { _p: self._p }
}
fn apply_config(c: &CONFIG, config: &Config) {
@ -433,103 +361,94 @@ impl<'d, T: Instance> I2S<'d, T> {
irq.unpend();
irq.enable();
r.intenclr.write(|w| w.rxptrupd().clear());
r.intenclr.write(|w| w.txptrupd().clear());
let device = Device::<T>::new();
device.disable_tx_ptr_interrupt();
device.disable_rx_ptr_interrupt();
r.events_rxptrupd.reset();
r.events_txptrupd.reset();
device.reset_tx_ptr_event();
device.reset_rx_ptr_event();
r.intenset.write(|w| w.rxptrupd().set());
r.intenset.write(|w| w.txptrupd().set());
device.enable_tx_ptr_interrupt();
device.enable_rx_ptr_interrupt();
}
fn on_interrupt(_: *mut ()) {
let r = T::regs();
let device = Device::<T>::new();
let s = T::state();
if r.events_txptrupd.read().bits() != 0 {
trace!("[{}] INT", s.seq.load(Ordering::Relaxed));
if device.is_tx_ptr_updated() {
trace!("TX INT");
s.tx_waker.wake();
r.intenclr.write(|w| w.txptrupd().clear());
device.disable_tx_ptr_interrupt();
}
if r.events_rxptrupd.read().bits() != 0 {
if device.is_rx_ptr_updated() {
trace!("RX INT");
s.rx_waker.wake();
r.intenclr.write(|w| w.rxptrupd().clear());
device.disable_rx_ptr_interrupt();
}
s.overruns.fetch_add(1, Ordering::Relaxed);
}
}
impl<'d, T: Instance> I2sOutput<'d, T> {
/// Transmits the given `buffer`.
/// Buffer address must be 4 byte aligned and located in RAM.
#[allow(unused_mut)]
pub async fn tx<B>(&mut self, buffer: B) -> Result<(), Error>
pub struct Output<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> Output<'d, T> {
/// Starts I2S transfer.
#[inline(always)]
pub fn start<B>(&self, buffer: B) -> Result<(), Error>
where
B: Buffer,
{
let ptr = buffer.bytes_ptr();
let len = buffer.bytes_len();
// TODO what to do if it is started already?
if ptr as u32 % 4 != 0 {
return Err(Error::BufferMisaligned);
}
if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER {
return Err(Error::DMABufferNotInDataMemory);
}
let maxcnt = ((len + core::mem::size_of::<u32>() - 1) / core::mem::size_of::<u32>()) as u32;
if maxcnt > MAX_DMA_MAXCNT {
return Err(Error::BufferTooLong);
let device = Device::<T>::new();
device.enable();
device.set_tx_buffer(buffer)?;
device.enable_tx();
device.start();
Ok(())
}
let r = T::regs();
let s = T::state();
let seq = s.seq.fetch_add(1, Ordering::Relaxed);
if r.events_txptrupd.read().bits() != 0 && seq > 1 {
warn!("XRUN!");
loop {}
/// Stops the I2S transfer and waits until it has stopped.
#[inline(always)]
pub async fn stop(&self) {
todo!()
}
let drop = OnDrop::new(move || {
trace!("write drop: stopping");
/// Transmits the given `buffer`.
/// Buffer address must be 4 byte aligned and located in RAM.
#[allow(unused_mut)]
pub async fn send<B>(&mut self, buffer: B) -> Result<(), Error>
where
B: Buffer,
{
trace!("SEND: {}", buffer.bytes_ptr() as u32);
r.intenclr.write(|w| w.txptrupd().clear());
r.events_txptrupd.reset();
r.config.txen.write(|w| w.txen().disabled());
// TX is stopped almost instantly, spinning is fine.
while r.events_txptrupd.read().bits() == 0 {}
trace!("write drop: stopped");
});
trace!("[{}] PTR", s.seq.load(Ordering::Relaxed));
r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });
r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
let device = Device::<T>::new();
let drop = device.on_tx_drop();
compiler_fence(Ordering::SeqCst);
poll_fn(|cx| {
s.tx_waker.register(cx.waker());
if r.events_txptrupd.read().bits() != 0 || seq == 0 {
trace!("[{}] POLL Ready", s.seq.load(Ordering::Relaxed));
r.events_txptrupd.reset();
r.intenset.write(|w| w.txptrupd().set());
let overruns = s.overruns.fetch_sub(1, Ordering::Relaxed);
if overruns != 0 {
warn!("XRUN: {}", overruns);
s.overruns.store(0, Ordering::Relaxed)
}
T::state().tx_waker.register(cx.waker());
if device.is_tx_ptr_updated() {
trace!("TX POLL: Ready");
device.reset_tx_ptr_event();
device.enable_tx_ptr_interrupt();
Poll::Ready(())
} else {
trace!("[{}] POLL Pending", s.seq.load(Ordering::Relaxed));
trace!("TX POLL: Pending");
Poll::Pending
}
})
.await;
device.set_tx_buffer(buffer)?;
compiler_fence(Ordering::SeqCst);
drop.defuse();
@ -537,40 +456,180 @@ impl<'d, T: Instance> I2sOutput<'d, T> {
}
}
impl<'d, T: Instance> I2sInput<'d, T> {
/// Receives into the given `buffer`.
/// Buffer address must be 4 byte aligned and located in RAM.
#[allow(unused_mut)]
pub async fn rx<B>(&mut self, buffer: B) -> Result<(), Error>
pub struct Input<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> Input<'d, T> {
// TODO
}
pub struct FullDuplex<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> FullDuplex<'d, T> {
// TODO
}
struct Device<T>(&'static RegisterBlock, PhantomData<T>);
impl<T: Instance> Device<T> {
fn new() -> Self {
Self(T::regs(), PhantomData)
}
#[inline(always)]
pub fn enable(&self) {
trace!("ENABLED");
self.0.enable.write(|w| w.enable().enabled());
}
#[inline(always)]
pub fn disable(&self) {
trace!("DISABLED");
self.0.enable.write(|w| w.enable().disabled());
}
#[inline(always)]
fn enable_tx(&self) {
trace!("TX ENABLED");
self.0.config.txen.write(|w| w.txen().enabled());
}
#[inline(always)]
fn disable_tx(&self) {
trace!("TX DISABLED");
self.0.config.txen.write(|w| w.txen().disabled());
}
#[inline(always)]
fn enable_rx(&self) {
trace!("RX ENABLED");
self.0.config.rxen.write(|w| w.rxen().enabled());
}
#[inline(always)]
fn disable_rx(&self) {
trace!("RX DISABLED");
self.0.config.rxen.write(|w| w.rxen().disabled());
}
#[inline(always)]
fn start(&self) {
trace!("START");
self.0.tasks_start.write(|w| unsafe { w.bits(1) });
}
#[inline]
fn set_tx_buffer<B>(&self, buffer: B) -> Result<(), Error>
where
B: Buffer,
{
let ptr = buffer.bytes_ptr();
let len = buffer.bytes_len();
if ptr as u32 % 4 != 0 {
return Err(Error::BufferMisaligned);
}
if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER {
return Err(Error::DMABufferNotInDataMemory);
}
let maxcnt = ((len + core::mem::size_of::<u32>() - 1) / core::mem::size_of::<u32>()) as u32;
if maxcnt > MAX_DMA_MAXCNT {
return Err(Error::BufferTooLong);
}
let r = T::regs();
let _s = T::state();
// TODO we can not progress until the last buffer written in RXD.PTR
// has started the transmission.
// We can use some sync primitive from `embassy-sync`.
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });
let (ptr, maxcnt) = Self::validate_buffer(buffer)?;
self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });
self.0.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr) });
Ok(())
}
#[inline]
fn set_rx_buffer<B>(&self, buffer: B) -> Result<(), Error>
where
B: Buffer,
{
let (ptr, maxcnt) = Self::validate_buffer(buffer)?;
self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });
self.0.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr) });
Ok(())
}
#[inline(always)]
fn is_tx_ptr_updated(&self) -> bool {
self.0.events_txptrupd.read().bits() != 0
}
#[inline(always)]
fn is_rx_ptr_updated(&self) -> bool {
self.0.events_rxptrupd.read().bits() != 0
}
#[inline(always)]
fn reset_tx_ptr_event(&self) {
trace!("TX PTR EVENT: Reset");
self.0.events_txptrupd.reset();
}
#[inline(always)]
fn reset_rx_ptr_event(&self) {
trace!("RX PTR EVENT: Reset");
self.0.events_rxptrupd.reset();
}
#[inline(always)]
fn disable_tx_ptr_interrupt(&self) {
trace!("TX PTR INTERRUPT: Disabled");
self.0.intenclr.write(|w| w.txptrupd().clear());
}
#[inline(always)]
fn disable_rx_ptr_interrupt(&self) {
trace!("RX PTR INTERRUPT: Disabled");
self.0.intenclr.write(|w| w.rxptrupd().clear());
}
#[inline(always)]
fn enable_tx_ptr_interrupt(&self) {
trace!("TX PTR INTERRUPT: Enabled");
self.0.intenset.write(|w| w.txptrupd().set());
}
#[inline(always)]
fn enable_rx_ptr_interrupt(&self) {
trace!("RX PTR INTERRUPT: Enabled");
self.0.intenclr.write(|w| w.rxptrupd().clear());
}
#[inline]
fn on_tx_drop(&self) -> OnDrop<fn()> {
OnDrop::new(move || {
trace!("TX DROP: Stopping");
let device = Device::<T>::new();
device.disable_tx_ptr_interrupt();
device.reset_tx_ptr_event();
device.disable_tx();
// TX is stopped almost instantly, spinning is fine.
while !device.is_tx_ptr_updated() {}
trace!("TX DROP: Stopped");
})
}
fn validate_buffer<B>(buffer: B) -> Result<(u32, u32), Error>
where
B: Buffer,
{
let ptr = buffer.bytes_ptr() as u32;
let len = buffer.bytes_len();
let maxcnt = ((len + core::mem::size_of::<u32>() - 1) / core::mem::size_of::<u32>()) as u32;
trace!("PTR={}, MAXCNT={}", ptr, maxcnt);
// TODO can we avoid repeating all those runtime checks for the same buffer again and again?
if ptr % 4 != 0 {
Err(Error::BufferMisaligned)
} else if len % 4 != 0 {
Err(Error::BufferLengthMisaligned)
} else if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER {
Err(Error::BufferNotInDataMemory)
} else if maxcnt > MAX_DMA_MAXCNT {
Err(Error::BufferTooLong)
} else {
Ok((ptr, maxcnt))
}
}
}
pub trait Buffer: Sized {
@ -578,10 +637,10 @@ pub trait Buffer: Sized {
fn bytes_len(&self) -> usize;
}
impl Buffer for &[u8] {
impl Buffer for &[i8] {
#[inline]
fn bytes_ptr(&self) -> *const u8 {
self.as_ptr()
self.as_ptr() as *const u8
}
#[inline]
@ -610,7 +669,7 @@ impl Buffer for &[i32] {
#[inline]
fn bytes_len(&self) -> usize {
self.len() * core::mem::size_of::<i16>()
self.len() * core::mem::size_of::<i32>()
}
}
@ -624,8 +683,6 @@ pub(crate) mod sealed {
pub struct State {
pub rx_waker: AtomicWaker,
pub tx_waker: AtomicWaker,
pub overruns: AtomicI32,
pub seq: AtomicI32,
}
impl State {
@ -633,8 +690,6 @@ pub(crate) mod sealed {
Self {
rx_waker: AtomicWaker::new(),
tx_waker: AtomicWaker::new(),
overruns: AtomicI32::new(0),
seq: AtomicI32::new(0),
}
}
}
@ -654,7 +709,7 @@ pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
macro_rules! impl_i2s {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::i2s::sealed::Instance for peripherals::$type {
fn regs() -> &'static pac::i2s::RegisterBlock {
fn regs() -> &'static crate::pac::i2s::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::i2s::sealed::State {

View File

@ -1,14 +1,13 @@
// Example inspired by RTIC's I2S demo: https://github.com/nrf-rs/nrf-hal/blob/master/examples/i2s-controller-demo/src/main.rs
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use core::f32::consts::PI;
use defmt::{error, info};
use defmt::{error, info, trace};
use embassy_executor::Spawner;
use embassy_nrf::i2s::{MckFreq, Mode, Ratio, MODE_MASTER_16000, MODE_MASTER_8000, Channels};
use embassy_nrf::gpio::{Input, Pin, Pull};
use embassy_nrf::i2s::{Channels, MckFreq, Mode, Ratio, SampleWidth, MODE_MASTER_32000};
use embassy_nrf::pac::ficr::info;
use embassy_nrf::{i2s, interrupt};
use {defmt_rtt as _, panic_probe as _};
@ -32,60 +31,79 @@ impl<T> AsMut<T> for AlignedBuffer<T> {
async fn main(_spawner: Spawner) {
let p = embassy_nrf::init(Default::default());
let mut config = i2s::Config::default();
// config.mode = MODE_MASTER_16000;
config.mode = Mode::Master {
freq: MckFreq::_32MDiv10,
ratio: Ratio::_256x,
}; // 12500 Hz
config.mode = MODE_MASTER_32000;
// config.mode = Mode::Master {
// freq: MckFreq::_32MDiv10,
// ratio: Ratio::_256x,
// }; // 12500 Hz
config.channels = Channels::Left;
config.swidth = SampleWidth::_16bit;
let sample_rate = config.mode.sample_rate().expect("I2S Master");
let inv_sample_rate = 1.0 / sample_rate as f32;
info!("Sample rate: {}", sample_rate);
let irq = interrupt::take!(I2S);
let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config);
// Wait for a button press
// let mut btn1 = Input::new(p.P1_00.degrade(), Pull::Up);
// btn1.wait_for_low().await;
const BUF_SAMPLES: usize = 500;
const BUF_SIZE: usize = BUF_SAMPLES;
let mut buf = AlignedBuffer([0i16; BUF_SIZE]);
let irq = interrupt::take!(I2S);
let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config).output();
type Sample = i16;
const MAX_UNIPOLAR_VALUE: Sample = (1 << 15) as Sample;
const NUM_SAMPLES: usize = 2000;
let mut buffers: [AlignedBuffer<[Sample; NUM_SAMPLES]>; 3] = [
AlignedBuffer([0; NUM_SAMPLES]),
AlignedBuffer([0; NUM_SAMPLES]),
AlignedBuffer([0; NUM_SAMPLES]),
];
let mut carrier = SineOsc::new();
carrier.set_frequency(16.0, inv_sample_rate);
// let mut modulator = SineOsc::new();
// modulator.set_frequency(0.01, inv_sample_rate);
// modulator.set_amplitude(0.2);
let mut freq_mod = SineOsc::new();
freq_mod.set_frequency(8.0, inv_sample_rate);
freq_mod.set_amplitude(1.0);
let mut generate = |buf: &mut [i16]| {
for sample in buf.as_mut() {
let mut amp_mod = SineOsc::new();
amp_mod.set_frequency(4.0, inv_sample_rate);
amp_mod.set_amplitude(0.5);
let mut generate = |buf: &mut [Sample]| {
let ptr = buf as *const [Sample] as *const Sample as u32;
trace!("GEN: {}", ptr);
for sample in &mut buf.as_mut().chunks_mut(1) {
let signal = carrier.generate();
// let modulation = bipolar_to_unipolar(modulator.generate());
// carrier.set_frequency(200.0 + 100.0 * modulation, inv_sample_rate);
// carrier.set_amplitude((modulation);
let value = (i16::MAX as f32 * signal) as i16;
*sample = value;
let freq_modulation = bipolar_to_unipolar(freq_mod.generate());
carrier.set_frequency(220.0 + 220.0 * freq_modulation, inv_sample_rate);
let amp_modulation = bipolar_to_unipolar(amp_mod.generate());
carrier.set_amplitude(amp_modulation);
let value = (MAX_UNIPOLAR_VALUE as f32 * signal) as Sample;
sample[0] = value;
}
};
generate(buf.as_mut().as_mut_slice());
generate(buffers[0].as_mut().as_mut_slice());
generate(buffers[1].as_mut().as_mut_slice());
if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await {
error!("{}", err);
}
i2s.set_tx_enabled(true);
i2s.start();
i2s.start(buffers[0].as_ref().as_slice()).expect("I2S Start");
let mut index = 1;
loop {
generate(buf.as_mut().as_mut_slice());
if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await {
if let Err(err) = i2s.send(buffers[index].as_ref().as_slice()).await {
error!("{}", err);
}
index += 1;
if index >= 3 {
index = 0;
}
generate(buffers[index].as_mut().as_mut_slice());
}
}
#[derive(Clone)]
struct SineOsc {
amplitude: f32,
modulo: f32,