debugging BOOST mode glitch issue

Here's an arcane bug for you:

When powering down, the Spartan 7 3.3V I/O drivers will
output a roughly 800us wide glitch as 3.3V falls below around
1.7V, if the 0.95V core voltage comes down first (which it does
because we have an active discharge on 0.95V, but not on 3.3V).

In this case, it's just wide and high enough to cause the
BOOST mode line to engage, which takes the BOOST output
and ties it to VBUS, which is just enough for the system to
think you're powering it back on again. Which is not what
you want when powering down.

The ultimate fix was to put a heftier pull-down on the BOOST
line so that it can't trip the BOOST output into the VBUS.
Another option could be more active discharge FETs, but that
would require a full board spin and we are already at certification,
so we'll stick with the stronger pull-down on BOOST. It seems to
be a robust fix in the worst corner cases we can find.

The downside is that when powering an external USB device,
you are burning an extra 30mW of power overcoming the pull-down,
but typically, any external USB devices plugged into Precursor
will burn far in excess of that, so the delta should be negligible.
This commit is contained in:
bunnie 2021-04-23 15:33:14 +08:00
parent 8192c68a63
commit 061284ec5f
9 changed files with 90 additions and 12 deletions

View file

@ -50,10 +50,10 @@ pub(crate) enum Opcode {
//BattFullCapacity,
/// Turn Boost Mode On
//BoostOn,
BoostOn,
/// Turn Boost Mode Off
//BoostOff,
BoostOff,
/// Read the current accelerations off the IMU
//ImuAccelRead,

View file

@ -181,6 +181,14 @@ impl Com {
Err(xous::Error::InternalError)
}
}
pub fn set_boost(&self, on: bool) -> Result<(), xous::Error> {
if on {
send_message(self.conn, Message::new_scalar(Opcode::BoostOn.to_usize().unwrap(), 0, 0, 0, 0,)).map(|_| ())
} else {
send_message(self.conn, Message::new_scalar(Opcode::BoostOff.to_usize().unwrap(), 0, 0, 0, 0,)).map(|_| ())
}
}
// note to future self: add other event listener registrations (such as network events) here
}

View file

@ -328,6 +328,12 @@ fn xmain() -> ! {
com.txrx(ComState::POWER_OFF.verb);
com.wait_txrx(ComState::LINK_READ.verb, Some(STD_TIMEOUT)); // consume the obligatory return value, even if not used
}
Some(Opcode::BoostOff) => {
com.txrx(ComState::CHG_BOOST_OFF.verb);
}
Some(Opcode::BoostOn) => {
com.txrx(ComState::CHG_BOOST_ON.verb);
}
Some(Opcode::BattStats) => {
info!("batt stats request received");
let stats = com.get_battstats();

View file

@ -293,6 +293,18 @@ impl Llio {
Message::new_scalar(Opcode::SelfDestruct.to_usize().unwrap(), code, 0, 0, 0)
).map(|_| ())
}
pub fn boost_on(&self, ena: bool) -> Result<(), xous::Error> {
let arg = if ena { 1 } else { 0 };
send_message(self.conn,
Message::new_scalar(Opcode::PowerBoostMode.to_usize().unwrap(), arg, 0, 0, 0)
).map(|_| ())
}
pub fn audio_on(&self, ena: bool) -> Result<(), xous::Error> {
let arg = if ena { 1 } else { 0 };
send_message(self.conn,
Message::new_scalar(Opcode::PowerAudio.to_usize().unwrap(), arg, 0, 0, 0)
).map(|_| ())
}
}

View file

@ -320,8 +320,8 @@ mod implementation {
self.power_csr.rmwf(utra::power::POWER_SELF, 0);
}
}
pub fn power_boost_mode(&mut self, power_on: bool) {
if power_on {
pub fn power_boost_mode(&mut self, boost_on: bool) {
if boost_on {
self.power_csr.rmwf(utra::power::POWER_BOOSTMODE, 1);
} else {
self.power_csr.rmwf(utra::power::POWER_BOOSTMODE, 0);

View file

@ -384,7 +384,7 @@ mod implementation {
self.blocking_i2c_write2([ABRTCMC_CONTROL2, control2]);
// turn on the timer proper -- the system will wakeup in 5..4..3....
let mut config = (Config::CLKOUT_DISABLE | Config::TIMER_B_ENABLE | Config::TIMERB_INT_PULSED).bits();
let mut config = (Config::CLKOUT_DISABLE | Config::TIMER_B_ENABLE).bits();
if self.rtc_alarm_enabled {
config |= (Config::TIMER_A_COUNTDWN | Config::TIMERA_SECONDS_INT_PULSED).bits();
}

View file

@ -49,6 +49,13 @@ impl<'a> ShellCmdApi<'a> for Sleep {
let mut tokens = args.as_str().unwrap().split(' ');
// try powering on the audio block to help to discharge the 3.3VA line
env.llio.audio_on(true).unwrap();
// in all cases, we want the boost to be off to ensure a clean shutdown
env.com.set_boost(false).unwrap();
env.llio.boost_on(false).unwrap();
if let Some(sub_cmd) = tokens.next() {
match sub_cmd {
"now" => {
@ -116,7 +123,7 @@ impl<'a> ShellCmdApi<'a> for Sleep {
env.ticktimer.sleep_ms(500).unwrap(); // let the screen redraw
// set a wakeup alarm a couple seconds from now -- this is the coldboot
self.rtc.set_wakeup_alarm(3).unwrap();
self.rtc.set_wakeup_alarm(4).unwrap();
// allow EC to snoop, so that it can wake up the system
env.llio.allow_ec_snoop(true).unwrap();

View file

@ -52,6 +52,51 @@ impl core::cmp::PartialEq for SleepRequest {
}
}
/*
// "suspend/resume u32"
struct SrU32 {
/// offset of the register
offset: crate::Register,
/// the saved value of the register
value: u32,
/// the order in which this register should be written. If none, it's written at any convenient time.
resume_order: Option<u8>,
/// if this register should be in the suspend/resume set
do_sr: bool,
}
/* sketching some structs for suspend/resume */
struct TicktimerSusres {
csr: utralib::CSR<u32>, // new(csr_base) will allocate this register, from the virtual CSR base given to us by the owning server
pub control: Option<u32>,
pub time1: Option<u32>,
pub time0: Option<u32>,
pub resume_time1: Option<u32>,
pub resume_time0: Option<u32>,
pub status: Option<u32>,
pub msleep_target1: Option<u32>,
pub msleep_target0: Option<u32>,
pub ev_status: Option<u32>,
pub ev_pending: Option<u32>,
pub ev_enable: Option<u32>,
pub ticktimer_irq: Option<bool>, // whether to enable or not after resume
pub wait_suspend: Option<fn(timeout: u32) -> bool>, // function to call to check if a block can suspend
}
pub trait SuspendResume {
pub fn suspend(&mut self) {
/*
go through each item of the structure, and if the item is Some(item), access the corresponding
register and save it into the register
*/
}
pub fn resume(&mut self) {
/*
go through each item of the stucture, and if the item is Some(item), unwrap the value and
poke it into the register
*/
}
}
*/
#[cfg(target_os = "none")]
mod implementation {
const TICKS_PER_MS: u64 = 1;

View file

@ -3,7 +3,7 @@
<device schemaVersion="1.1" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd" >
<vendor>litex</vendor>
<name>SOC</name>
<description><![CDATA[Litex SoC 2021-04-22 23:47:54]]></description>
<description><![CDATA[Litex SoC 2021-04-23 13:47:43]]></description>
<addressUnitBits>8</addressUnitBits>
<width>32</width>
@ -1070,10 +1070,10 @@ register to disable individual events.]]></description>
<register>
<name>GIT_GITREV</name>
<description><![CDATA[First 32-bits of the git revision. This documentation was built from git rev
``b9a1ecab``, so this value is 3114396843, which should be enough to check out
``4878d3c3``, so this value is 1215878083, which should be enough to check out
the exact git version used to build this firmware.]]></description>
<addressOffset>0x0014</addressOffset>
<resetValue>0xb9a1ecab</resetValue>
<resetValue>0x4878d3c3</resetValue>
<size>32</size>
<fields>
<field>
@ -1090,7 +1090,7 @@ the exact git version used to build this firmware.]]></description>
is ``1``, then the repository this was built from has one additional commit
beyond the tag indicated in `MAJOR`, `MINOR`, and `REVISION`.]]></description>
<addressOffset>0x0018</addressOffset>
<resetValue>0x1d</resetValue>
<resetValue>0x1f</resetValue>
<size>32</size>
<fields>
<field>
@ -3086,7 +3086,7 @@ inject too fast.]]></description>
<name>SEED1</name>
<description><![CDATA[Bits 32-63 of `SEED_SEED`. Seed used for the build]]></description>
<addressOffset>0x0000</addressOffset>
<resetValue>0x1976d1a8</resetValue>
<resetValue>0x5073fcc9</resetValue>
<size>32</size>
<fields>
<field>
@ -3101,7 +3101,7 @@ inject too fast.]]></description>
<name>SEED0</name>
<description><![CDATA[Bits 0-31 of `SEED_SEED`.]]></description>
<addressOffset>0x0004</addressOffset>
<resetValue>0x8576398</resetValue>
<resetValue>0x627a019a</resetValue>
<size>32</size>
<fields>
<field>