diff --git a/Cargo.lock b/Cargo.lock index 5d6371d45..7232df761 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -142,6 +142,8 @@ dependencies = [ "heapless", "log", "log-server", + "ticktimer-server", + "typenum", "utralib", "xous", ] diff --git a/emulation/renode.svd b/emulation/renode.svd index 740dff62c..10bbc3176 100644 --- a/emulation/renode.svd +++ b/emulation/renode.svd @@ -12,68 +12,9 @@ 0xFFFFFFFF - - CTRL - 0xF0000000 - CTRL - - - RESET - - 0x0000 - 0x00 - 32 - - - reset - 0 - [0:0] - 0 - - - - - SCRATCH - - 0x0004 - 0x12345678 - 32 - - - scratch - 31 - [31:0] - 0 - - - - - BUS_ERRORS - - 0x0008 - 0x00 - 32 - - - bus_errors - 31 - [31:0] - 0 - - - - - - 0 - 0xc - registers - - IDENTIFIER_MEM - 0xF0002000 + 0xF0001000 IDENTIFIER_MEM @@ -100,7 +41,7 @@ to the Wishbone/CSR bus are working correctly. The initial reset value of TIMER0 - 0xF0003000 + 0xF0002000 TIMER0 @@ -246,17 +187,19 @@ register to disable individual events.]]> REBOOT - 0xF0004000 + 0xF0003000 REBOOT - CTRL + SOC_RESET + 0x0000 0x00 32 - ctrl + soc_reset 7 [7:0] 0 @@ -265,6 +208,7 @@ register to disable individual events.]]> ADDR + 0x0004 0x00 32 @@ -277,16 +221,32 @@ register to disable individual events.]]> + + CPU_RESET + + 0x0008 + 0x00 + 32 + + + cpu_reset + 0 + [0:0] + 0 + + + 0 - 0x8 + 0xc registers CRG - 0xF0005000 + 0xF0004000 CRG @@ -410,7 +370,7 @@ register to disable individual events.]]> GPIO - 0xF0006000 + 0xF0005000 GPIO @@ -724,7 +684,7 @@ register to disable individual events.]]> UART_PHY - 0xF0007000 + 0xF0006000 UART_PHY @@ -750,7 +710,7 @@ register to disable individual events.]]> UART - 0xF0008000 + 0xF0007000 UART @@ -908,7 +868,7 @@ register to disable individual events.]]> CONSOLE_PHY - 0xF0009000 + 0xF0008000 CONSOLE_PHY @@ -934,7 +894,7 @@ register to disable individual events.]]> CONSOLE - 0xF000A000 + 0xF0009000 CONSOLE @@ -1092,7 +1052,7 @@ register to disable individual events.]]> SERVER0_PHY - 0xF000B000 + 0xF000A000 SERVER0_PHY @@ -1118,7 +1078,7 @@ register to disable individual events.]]> SERVER0 - 0xF000C000 + 0xF000B000 SERVER0 @@ -1276,7 +1236,7 @@ register to disable individual events.]]> SERVER1_PHY - 0xF000D000 + 0xF000C000 SERVER1_PHY @@ -1302,7 +1262,7 @@ register to disable individual events.]]> SERVER1 - 0xF000E000 + 0xF000D000 SERVER1 @@ -1460,7 +1420,7 @@ register to disable individual events.]]> INFO - 0xF000F000 + 0xF000E000 INFO @@ -1856,7 +1816,7 @@ VCCBRAM (V) = ``Value`` x 3 / 4096.]]> SRAM_EXT - 0xF0010000 + 0xF000F000 SRAM_EXT @@ -1898,7 +1858,7 @@ VCCBRAM (V) = ``Value`` x 3 / 4096.]]> MEMLCD - 0xF0011000 + 0xF0010000 MEMLCD @@ -2014,7 +1974,7 @@ register to disable individual events.]]> COM - 0xF0012000 + 0xF0011000 COM @@ -2171,10 +2131,14 @@ register to disable individual events.]]> 0x1c registers + + com + 6 + I2C - 0xF0013000 + 0xF0012000 I2C @@ -2422,12 +2386,12 @@ register to disable individual events.]]> i2c - 6 + 7 BTEVENTS - 0xF0014000 + 0xF0013000 BTEVENTS @@ -2511,12 +2475,12 @@ register to disable individual events.]]> btevents - 7 + 8 MESSIBLE - 0xF0015000 + 0xF0014000 MESSIBLE @@ -2582,7 +2546,7 @@ register advances the read pointer automatically.]]> MESSIBLE2 - 0xF0016000 + 0xF0015000 MESSIBLE2 @@ -2648,7 +2612,7 @@ register advances the read pointer automatically.]]> TICKTIMER - 0xF0017000 + 0xF0016000 TICKTIMER @@ -2793,12 +2757,12 @@ register to disable individual events.]]> ticktimer - 8 + 9 POWER - 0xF0018000 + 0xF0017000 POWER @@ -2958,7 +2922,7 @@ reset fashion]]> SPINOR - 0xF0019000 + 0xF0018000 SPINOR @@ -3239,7 +3203,7 @@ checked]]> KEYBOARD - 0xF001A000 + 0xF0019000 KEYBOARD @@ -3449,19 +3413,19 @@ again until the interrupt is serviced.]]> keyboard - 9 + 10 SEED - 0xF001B000 + 0xF001A000 SEED SEED1 0x0000 - 0x20483d3b + 0x2c552c3f 32 @@ -3476,7 +3440,7 @@ again until the interrupt is serviced.]]> SEED0 0x0004 - 0x1febda0d + 0x3e20093a 32 @@ -3496,7 +3460,7 @@ again until the interrupt is serviced.]]> ROMTEST - 0xF001C000 + 0xF001B000 ROMTEST @@ -3538,7 +3502,7 @@ again until the interrupt is serviced.]]> AUDIO - 0xF001D000 + 0xF001C000 AUDIO @@ -3905,12 +3869,12 @@ I2S_FORMAT.I2S_STANDARD is I2S standard]]> audio - 10 + 11 TRNG_OSC - 0xF001E000 + 0xF001D000 TRNG_OSC @@ -3996,7 +3960,7 @@ reading the `rand` register]]> AES - 0xF001F000 + 0xF001E000 AES @@ -4459,7 +4423,7 @@ automatically on data and IV ready]]> SHA512 - 0xF0020000 + 0xF001F000 SHA512 @@ -4949,12 +4913,12 @@ in bits]]> sha512 - 11 + 12 ENGINE - 0xF0021000 + 0xF0020000 ENGINE @@ -5178,12 +5142,12 @@ exactly reflects the binary layout of an Engine instruction.]]> engine - 12 + 13 JTAG - 0xF0022000 + 0xF0021000 JTAG diff --git a/services/com/Cargo.toml b/services/com/Cargo.toml index f87c066cb..3c4b7fea9 100644 --- a/services/com/Cargo.toml +++ b/services/com/Cargo.toml @@ -9,9 +9,12 @@ description = "COM interface to the EC" xous = { path = "../../xous-rs" } heapless = "0.5" log-server = { path = "../log-server" } +ticktimer-server = { path = "../ticktimer-server" } log = "0.4" com_rs = { path = "com_rs" } -#[target.'cfg(not(any(windows,unix)))'.dependencies] +typenum = "1.12" + +[target.'cfg(not(any(windows,unix)))'.dependencies] utralib = { path = "../../utralib"} [target.'cfg(any(windows,unix))'.dependencies] diff --git a/services/com/src/api.rs b/services/com/src/api.rs index 60570d545..b6b0e5320 100644 --- a/services/com/src/api.rs +++ b/services/com/src/api.rs @@ -4,12 +4,44 @@ use xous::{Message, ScalarMessage}; // It's just a convenient abuse of already-defined constants. However, it's intended that // the COM server on the SoC side abstracts much of the EC bus complexity away. use com_rs::*; +#[derive(Debug, Default)] +pub struct BattStats { + /// instantaneous voltage in mV + pub voltage: u16, + /// state of charge in %, as inferred by impedance tracking + pub soc: u8, + /// instantaneous current draw in mA + pub current: i16, + /// remaining capacity in mA, as measured by coulomb counting + pub remaining_capacity: u16, +} + +impl From<[usize; 2]> for BattStats { + fn from(a: [usize; 2]) -> BattStats { + BattStats { + voltage: (a[0] & 0xFFFF) as u16, + soc: ((a[0] >> 16) & 0xFF) as u8, + current: ((a[1] >> 16) & 0xFFFF) as i16, + remaining_capacity: (a[1] & 0xFFFF) as u16, + } + } +} + +impl Into<[usize; 2]> for BattStats { + fn into(self) -> [usize; 2] { + [ (self.voltage as usize & 0xffff) | ((self.soc as usize) << 16) & 0xFF_0000, + (self.remaining_capacity as usize & 0xffff) | ((self.current as usize) << 16) & 0xffff_0000 ] + } +} #[derive(Debug)] pub enum Opcode { /// Battery stats BattStats, + /// Query Full charge capacity of the battery + BattFullCapacity, + /// Turn Boost Mode On BoostOn, diff --git a/services/com/src/lib.rs b/services/com/src/lib.rs index cffcdd08b..8071ec2a1 100644 --- a/services/com/src/lib.rs +++ b/services/com/src/lib.rs @@ -1,13 +1,27 @@ #![cfg_attr(target_os = "none", no_std)] +/// This is the API that other servers use to call the COM. Read this code as if you +/// are calling these functions inside a different process. + pub mod api; +use api::BattStats; use xous::{send_message, Error, CID}; pub fn power_off_soc(cid: CID) -> Result<(), xous::Error> { send_message(cid, api::Opcode::PowerOffSoc.into()).map(|_| ()) } +pub fn get_batt_stats(cid: CID) -> Result { + let response = send_message(cid, api::Opcode::BattStats.into())?; + if let xous::Result::Scalar2(upper, lower) = response { + let raw_stats: [usize; 2] = [lower, upper]; + Ok(raw_stats.into()) + } else { + panic!("unexpected return value: {:#?}", response); + } +} + /* pub fn elapsed_ms(cid: CID) -> Result { let response = send_message(cid, api::Opcode::ElapsedMs.into())?; diff --git a/services/com/src/main.rs b/services/com/src/main.rs index e7e3a5159..f0c8d4c93 100644 --- a/services/com/src/main.rs +++ b/services/com/src/main.rs @@ -10,28 +10,39 @@ use api::Opcode; use core::convert::TryFrom; -/* -use heapless::binary_heap::{BinaryHeap, Min}; -use heapless::Vec; -use heapless::consts::*; -type U1280 = UInt, B0>, B1>, B0>, B0>, B0>, B0>, B0>, B0>, B0>, B0>; -*/ - use log::{error, info}; use com_rs::*; -//#[cfg(target_os = "none")] +#[cfg(target_os = "none")] mod implementation { + use crate::api::BattStats; + use ticktimer_server::*; + use com_rs::*; use utralib::generated::*; + use xous::CID; + + /* + #[macro_use] + use heapless::Vec; + + use typenum::{UInt, UTerm}; + use typenum::bit::{B0, B1}; + type U1280 = UInt, B0>, B1>, B0>, B0>, B0>, B0>, B0>, B0>, B0>, B0>; + */ + + const STD_TIMEOUT: u32 = 100; pub struct XousCom { csr: utralib::CSR, + ticktimer: CID, } fn handle_irq(_irq_no: usize, arg: *mut usize) { - let _xc = unsafe { &mut *(arg as *mut XousCom) }; + let xc = unsafe { &mut *(arg as *mut XousCom) }; println!("COM IRQ"); + // just clear the pending request, as this is used as a "wait" until request function + xc.csr.wo(utra::com::EV_PENDING, xc.csr.r(utra::com::EV_PENDING)); } impl XousCom { @@ -44,8 +55,15 @@ mod implementation { ) .expect("couldn't map COM CSR range"); + let ticktimer_server_id = xous::SID::from_bytes(b"ticktimer-server").unwrap(); + let ticktimer_conn = xous::connect(ticktimer_server_id).unwrap(); + let mut xc = XousCom { csr: CSR::new(csr.as_mut_ptr() as *mut u32), + ticktimer: ticktimer_conn, + //tx_queue: Vec::new(), + //rx_queue: Vec::new(), + //in_progress: false, }; xous::claim_interrupt( @@ -68,12 +86,53 @@ mod implementation { // grab the RX value and return it self.csr.rf(utra::com::RX_RX) as u16 } + + pub fn wait_txrx(&mut self, tx: u16, timeout: Option) -> u16 { + self.csr.wfo(utra::com::EV_ENABLE_SPI_HOLD, 1); + if timeout.is_some() { + let curtime = ticktimer_server::elapsed_ms(self.ticktimer).expect("couldn't connect to ticktimer"); + let mut timed_out = false; + let to = timeout.unwrap() as u64; + // timeout after 0.25 second to avoid hangs in case of a bug in the protocol + while self.csr.rf(utra::com::STATUS_HOLD) == 1 && !timed_out { + if (ticktimer_server::elapsed_ms(self.ticktimer).expect("couldn't connect to ticktimer") - curtime) > to { + timed_out = true; + } + xous::wait_event(); + } + } else { + while self.csr.rf(utra::com::STATUS_HOLD) == 1 { + xous::wait_event(); + } + } + self.csr.wfo(utra::com::EV_ENABLE_SPI_HOLD, 0); + + self.txrx(tx) + } + + pub fn get_battstats(&mut self) -> BattStats { + let mut stats = BattStats::default(); + + self.txrx(ComState::GAS_GAUGE.verb); + stats.current = self.wait_txrx(ComState::LINK_READ.verb, Some(STD_TIMEOUT)) as i16; + self.wait_txrx(ComState::LINK_READ.verb, Some(100)); // stby_current, not used here + stats.voltage = self.wait_txrx(ComState::LINK_READ.verb, Some(STD_TIMEOUT)); + self.wait_txrx(ComState::LINK_READ.verb, Some(100)); // power register value, not used + + self.txrx(ComState::GG_SOC.verb); + stats.soc = self.wait_txrx(ComState::LINK_READ.verb, Some(STD_TIMEOUT)) as u8; + self.txrx(ComState::GG_REMAINING.verb); + stats.remaining_capacity = self.wait_txrx(ComState::LINK_READ.verb, Some(STD_TIMEOUT)); + + stats + } } } // a stub to try to avoid breaking hosted mode for as long as possible. #[cfg(not(target_os = "none"))] mod implementation { + use crate::api::BattStats; pub struct XousCom { } @@ -83,9 +142,18 @@ mod implementation { } } - pub fn txrx(tx: u16) -> u16 { + pub fn txrx(&mut self, _tx: u16) -> u16 { 0xDEAD as u16 } + + pub fn get_battstats(&mut self) -> BattStats { + BattStats { + voltage: 3700, + current: -150, + soc: 50, + remaining_capacity: 750, + } + } } } @@ -113,6 +181,17 @@ fn xmain() -> ! { info!("COM: power off called"); com.txrx(ComState::POWER_OFF.verb); } + Opcode::BattStats => { + info!("COM: batt stats request received"); + let stats = com.get_battstats(); + let raw_stats: [usize; 2] = stats.into(); + xous::return_scalar2( + envelope.sender, + raw_stats[1], + raw_stats[0] + ).expect("COM: couldn't return batt stats request"); + info!("COM: done returning batt stats request"); + } _ => error!("unknown opcode"), } } else { @@ -120,5 +199,3 @@ fn xmain() -> ! { } } } - -// xous::wait_event() diff --git a/services/log-server/Cargo.toml b/services/log-server/Cargo.toml index 9341019dc..c0d5faf5a 100644 --- a/services/log-server/Cargo.toml +++ b/services/log-server/Cargo.toml @@ -14,5 +14,5 @@ utralib = { path = "../../utralib"} [features] debugprint = [] -default = [] -#default = ["debugprint"] +#default = [] +default = ["debugprint"] diff --git a/services/shell/src/main.rs b/services/shell/src/main.rs index f7db2c346..6108aa067 100644 --- a/services/shell/src/main.rs +++ b/services/shell/src/main.rs @@ -12,6 +12,8 @@ use log::{error, info}; use xous::String; use graphics_server::Point; use graphics_server::GlyphSet; +use com::api::BattStats; +use com::*; // fn print_and_yield(index: *mut usize) -> ! { // let num = index as usize; @@ -148,8 +150,6 @@ fn shell_main() -> ! { let screensize = graphics_server::screen_size(graphics_conn).expect("Couldn't get screen size"); - // let mut counter: usize = 0; - let mut ls = logstr::LogStr::new(); let dark = graphics_server::Color::from(0); let light = graphics_server::Color::from(!0); let mut bouncyball = Bounce::new(14, @@ -157,8 +157,10 @@ fn shell_main() -> ! { Point::new(screensize.x as _, screensize.y as i16 - 1))); bouncyball.update(); + let mut batt_stats: BattStats = BattStats::default(); + #[cfg(baremetal)] - { + { // use this to select which UART to monitor in the main loop use utralib::generated::*; let gpio_base = xous::syscall::map_memory( xous::MemoryAddress::new(utra::gpio::HW_GPIO_BASE), @@ -183,40 +185,19 @@ fn shell_main() -> ! { ticktimer_server::reset(ticktimer_conn).unwrap(); let mut string_buffer = String::new(4096); loop { - // a message passing demo -- checking time + // update battery status periodically if let Ok(elapsed_time) = ticktimer_server::elapsed_ms(ticktimer_conn) { info!("SHELL: {}ms", elapsed_time); - if elapsed_time - last_time > 40 { + if elapsed_time - last_time > 500 { last_time = elapsed_time; - /* - xous::try_send_message(log_conn, - xous::Message::Scalar(xous::ScalarMessage{id:256, arg1: elapsed_time as usize, arg2: 257, arg3: 258, arg4: 259})); - */ - info!("Preparing a mutable borrow message"); - - ls.clear(); - write!( - ls, - "Hello, Server! This memory is borrowed from another process. Elapsed: {}", - elapsed_time as usize - ) - .expect("couldn't send hello message"); - - let mm = ls - .as_memory_message(0) - .expect("couldn't form memory message"); - - info!("Sending a mutable borrow message"); - - xous::syscall::send_message(log_conn, xous::Message::MutableBorrow(mm)) - .expect("couldn't send memory message"); + batt_stats = get_batt_stats(com_conn).expect("Can't get battery stats from COM"); } } else { error!("error requesting ticktimer!") } string_buffer.clear(); - write!(&mut string_buffer, "Uptime: {:.2}s", last_time as f32 / 1000f32).expect("Can't write"); + write!(&mut string_buffer, "{}mV Uptime: {:.2}s", batt_stats.voltage, last_time as f32 / 1000f32).expect("Can't write"); graphics_server::set_glyph(graphics_conn, GlyphSet::Small).expect("unable to set glyph"); let (_, h) = graphics_server::query_glyph(graphics_conn).expect("unable to query glyph"); graphics_server::clear_region(graphics_conn, 0, 0, screensize.x as usize - 1, h) @@ -225,9 +206,6 @@ fn shell_main() -> ! { graphics_server::draw_string(graphics_conn, &string_buffer).expect("unable to draw string"); // ticktimer_server::sleep_ms(ticktimer_conn, 500).expect("couldn't sleep"); - if last_time > 10_000 { // after 10 seconds issue a shutdown command - com::power_off_soc(com_conn).expect("Couldn't issue powerdown command to COM"); - } // draw the ball bouncyball.update();