From 14a647867a123097e9a2f7da1b656aa3e8c76ed8 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 7 Dec 2020 10:55:09 +0100 Subject: [PATCH] Updating after review --- ad9959/src/lib.rs | 27 +++++++++++---------------- src/hrtimer.rs | 17 ++++++++++++++--- src/main.rs | 16 ++++++++++------ src/pounder/dds_output.rs | 4 ++++ 4 files changed, 39 insertions(+), 25 deletions(-) diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index 8501e3a..d5de1ec 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -124,7 +124,8 @@ impl Ad9959 { reset_pin.set_high().or(Err(Error::Pin))?; // Delay for at least 1 SYNC_CLK period for the reset to occur. The SYNC_CLK is guaranteed - // to be at least 250KHz (1/4 of 1MHz minimum REF_CLK). + // to be at least 250KHz (1/4 of 1MHz minimum REF_CLK). We use 5uS instead of 4uS to + // guarantee conformance with datasheet requirements. delay.delay_us(5); reset_pin.set_low().or(Err(Error::Pin))?; @@ -143,7 +144,8 @@ impl Ad9959 { io_update.set_high().or(Err(Error::Pin))?; // Delay for at least 1 SYNC_CLK period for the update to occur. The SYNC_CLK is guaranteed - // to be at least 250KHz (1/4 of 1MHz minimum REF_CLK). + // to be at least 250KHz (1/4 of 1MHz minimum REF_CLK). We use 5uS instead of 4uS to + // guarantee conformance with datasheet requirements. delay.delay_us(5); io_update.set_low().or(Err(Error::Pin))?; @@ -157,7 +159,8 @@ impl Ad9959 { // active. This is likely due to needing to wait at least 1 clock cycle of the DDS for the // interface update to occur. // Delay for at least 1 SYNC_CLK period for the update to occur. The SYNC_CLK is guaranteed - // to be at least 250KHz (1/4 of 1MHz minimum REF_CLK). + // to be at least 250KHz (1/4 of 1MHz minimum REF_CLK). We use 5uS instead of 4uS to + // guarantee conformance with datasheet requirements. delay.delay_us(5); // Read back the CSR to ensure it specifies the mode correctly. @@ -552,12 +555,6 @@ impl ProfileSerializer { pow: Option, acr: Option, ) { - // The user should have provided something to update. - assert!( - (ftw.is_some() || acr.is_some() || pow.is_some()) - && channels.len() > 0 - ); - let mut csr: u8 = *0u8.set_bits(1..3, self.mode as u8); for channel in channels.iter() { csr.set_bit(4 + *channel as usize, true); @@ -581,8 +578,6 @@ impl ProfileSerializer { /// Add a register write to the serialization data. fn add_write(&mut self, register: Register, value: &[u8]) { let data = &mut self.data[self.index..]; - assert!(value.len() + 1 <= data.len()); - data[0] = register as u8; data[1..][..value.len()].copy_from_slice(value); self.index += value.len() + 1; @@ -592,24 +587,24 @@ impl ProfileSerializer { /// /// # Note /// The serialized profile will be padded to the next 32-bit word boundary by adding dummy - /// writes to the CSR or FR2 registers. + /// writes to the CSR or LSRR registers. /// /// # Returns /// A slice of `u32` words representing the serialized profile. pub fn finalize<'a>(&'a mut self) -> &[u32] { - // Pad the buffer to 32-bit alignment by adding dummy writes to CSR and FR2. + // Pad the buffer to 32-bit alignment by adding dummy writes to CSR and LSRR. let padding = 4 - (self.index % 4); match padding { 0 => {} 1 => { // For a pad size of 1, we have to pad with 5 bytes to align things. self.add_write(Register::CSR, &[(self.mode as u8) << 1]); - self.add_write(Register::FR2, &[0, 0, 0]); + self.add_write(Register::LSRR, &[0, 0, 0]); } 2 => self.add_write(Register::CSR, &[(self.mode as u8) << 1]), - 3 => self.add_write(Register::FR2, &[0, 0, 0]), + 3 => self.add_write(Register::LSRR, &[0, 0, 0]), - _ => panic!("Invalid"), + _ => unreachable!(), } unsafe { core::slice::from_raw_parts::<'a, u32>( diff --git a/src/hrtimer.rs b/src/hrtimer.rs index 5ccb2d7..2831bbe 100644 --- a/src/hrtimer.rs +++ b/src/hrtimer.rs @@ -68,28 +68,39 @@ impl HighResTimerE { // Determine the clock divider, which may be 1, 2, or 4. We will choose a clock divider that // allows us the highest resolution per tick, so lower dividers are favored. - let divider: u8 = if source_cycles < 0xFFDF { + let setting: u8 = if source_cycles < 0xFFDF { 1 } else if (source_cycles / 2) < 0xFFDF { 2 } else if (source_cycles / 4) < 0xFFDF { - 4 + 3 } else { panic!("Unattainable timing parameters!"); }; + let divider = 1 << (setting - 1); + // The period register must be greater than or equal to 3 cycles. let period = (source_cycles / divider as u32) as u16; assert!(period > 2); // We now have the prescaler and the period registers. Configure the timer. + // Note(unsafe): The prescaler is guaranteed to be greater than or equal to 4 (minimum + // allowed value) due to the addition. The setting is always 1, 2, or 3, which represents + // all valid values. self.timer .timecr - .modify(|_, w| unsafe { w.ck_pscx().bits(divider + 4) }); + .modify(|_, w| unsafe { w.ck_pscx().bits(setting + 4) }); + + // Note(unsafe): The period register is guaranteed to be a 16-bit value, which will fit in + // this register. self.timer.perer.write(|w| unsafe { w.perx().bits(period) }); // Configure the comparator 1 level. let offset = (set_offset * source_frequency as f32) as u16; + // Note(unsafe): The offset is always a 16-bit value, so is always valid for values >= 3, as + // specified by the datasheet. + assert!(offset >= 3); self.timer .cmp1er .write(|w| unsafe { w.cmp1x().bits(offset) }); diff --git a/src/main.rs b/src/main.rs index 28c2ed9..f7b07e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -625,12 +625,16 @@ const APP: () = { ccdr.peripheral.HRTIM, ); - // IO_Update should be latched for 50ns after the QSPI profile write. Profile writes - // are always 16 bytes, with 2 cycles required per byte, coming out to a total of 32 - // QSPI clock cycles. The QSPI is configured for 40MHz, so this comes out to an - // offset of 800nS. We use 900ns to be safe - note that the timer is triggered after - // the QSPI write, which can take approximately 120nS, so there is additional - // margin. + // IO_Update should be latched for 4 SYNC_CLK cycles after the QSPI profile + // write. With pounder SYNC_CLK running at 100MHz (1/4 of the pounder reference + // clock of 400MHz), this corresponds to 40ns. To accomodate rounding errors, we + // use 50ns instead. + // + // Profile writes are always 16 bytes, with 2 cycles required per byte, coming + // out to a total of 32 QSPI clock cycles. The QSPI is configured for 40MHz, so + // this comes out to an offset of 800nS. We use 900ns to be safe - note that the + // timer is triggered after the QSPI write, which can take approximately 120nS, + // so there is additional margin. hrtimer.configure_single_shot( hrtimer::Channel::Two, 50_e-9, diff --git a/src/pounder/dds_output.rs b/src/pounder/dds_output.rs index a8fc432..418eb65 100644 --- a/src/pounder/dds_output.rs +++ b/src/pounder/dds_output.rs @@ -58,6 +58,10 @@ impl DdsOutput { // fashion. let regs = unsafe { &*hal::stm32::QUADSPI::ptr() }; + if regs.sr.read().flevel() != 0 { + warn!("QSPI stalling") + } + for word in profile.iter() { // Note(unsafe): We are writing to the SPI TX FIFO in a raw manner for performance. This // is safe because we know the data register is a valid address to write to.