Merge branch 'hardware-no-cb' into rkyv-refactor

Bring the two branches up to date, while also laying the groundwork
for XousServerName to be used as a native index type in xous-names
so we aren't burdening xous::String with a hasher
This commit is contained in:
bunnie 2021-03-17 16:06:06 +08:00
commit 8fbd900a4c
16 changed files with 457 additions and 176 deletions

4
Cargo.lock generated
View file

@ -654,7 +654,7 @@ dependencies = [
"heapless",
"log",
"log-server",
"rkyv 0.4.1",
"rkyv 0.3.1",
"ticktimer-server",
"utralib",
"xous",
@ -1589,8 +1589,6 @@ version = "0.1.2"
dependencies = [
"bitflags",
"compiler_builtins",
"hash32",
"hash32-derive",
"hex",
"lazy_static",
"log",

View file

@ -1328,10 +1328,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
``d77dca2f``, so this value is 3615345199, which should be enough to check out
``2a2abe5e``, so this value is 707444318, which should be enough to check out
the exact git version used to build this firmware.]]></description>
<addressOffset>0x0014</addressOffset>
<resetValue>0xd77dca2f</resetValue>
<resetValue>0x2a2abe5e</resetValue>
<size>32</size>
<fields>
<field>
@ -1348,7 +1348,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>0x0d</resetValue>
<resetValue>0x0e</resetValue>
<size>32</size>
<fields>
<field>
@ -1957,8 +1957,7 @@ upon the completion of one byte of data transfer.]]></description>
</register>
<register>
<name>EV_STATUS</name>
<description><![CDATA[This register contains the current raw level of the i2c_int event trigger.
Writes to this register have no effect.]]></description>
<description><![CDATA[Triggered on the falling edge of TIP]]></description>
<addressOffset>0x0018</addressOffset>
<resetValue>0x00</resetValue>
<size>32</size>
@ -1970,12 +1969,18 @@ Writes to this register have no effect.]]></description>
<lsb>0</lsb>
<description><![CDATA[Level of the ``i2c_int`` event]]></description>
</field>
<field>
<name>txrx_done</name>
<msb>1</msb>
<bitRange>[1:1]</bitRange>
<lsb>1</lsb>
<description><![CDATA[Level of the ``txrx_done`` event]]></description>
</field>
</fields>
</register>
<register>
<name>EV_PENDING</name>
<description><![CDATA[When a i2c_int event occurs, the corresponding bit will be set in this
register. To clear the Event, set the corresponding bit in this register.]]></description>
<description><![CDATA[Triggered on the falling edge of TIP]]></description>
<addressOffset>0x001c</addressOffset>
<resetValue>0x00</resetValue>
<size>32</size>
@ -1985,14 +1990,20 @@ register. To clear the Event, set the corresponding bit in this register.]]></d
<msb>0</msb>
<bitRange>[0:0]</bitRange>
<lsb>0</lsb>
<description><![CDATA[`1` if a `i2c_int` event occurred. This Event is triggered on a **rising** edge.]]></description>
<description><![CDATA[Triggered when arbitration is lost]]></description>
</field>
<field>
<name>txrx_done</name>
<msb>1</msb>
<bitRange>[1:1]</bitRange>
<lsb>1</lsb>
<description><![CDATA[Triggered on the falling edge of TIP]]></description>
</field>
</fields>
</register>
<register>
<name>EV_ENABLE</name>
<description><![CDATA[This register enables the corresponding i2c_int events. Write a ``0`` to this
register to disable individual events.]]></description>
<description><![CDATA[Triggered on the falling edge of TIP]]></description>
<addressOffset>0x0020</addressOffset>
<resetValue>0x00</resetValue>
<size>32</size>
@ -2004,6 +2015,13 @@ register to disable individual events.]]></description>
<lsb>0</lsb>
<description><![CDATA[Write a ``1`` to enable the ``i2c_int`` Event]]></description>
</field>
<field>
<name>txrx_done</name>
<msb>1</msb>
<bitRange>[1:1]</bitRange>
<lsb>1</lsb>
<description><![CDATA[Write a ``1`` to enable the ``txrx_done`` Event]]></description>
</field>
</fields>
</register>
</registers>
@ -3038,7 +3056,7 @@ again until the interrupt is serviced.]]></description>
<name>SEED1</name>
<description><![CDATA[Bits 32-63 of `SEED_SEED`. Seed used for the build]]></description>
<addressOffset>0x0000</addressOffset>
<resetValue>0x48f7dee1</resetValue>
<resetValue>0x1a7a2a66</resetValue>
<size>32</size>
<fields>
<field>
@ -3053,7 +3071,7 @@ again until the interrupt is serviced.]]></description>
<name>SEED0</name>
<description><![CDATA[Bits 0-31 of `SEED_SEED`.]]></description>
<addressOffset>0x0004</addressOffset>
<resetValue>0xc2db130</resetValue>
<resetValue>0x4b326b1e</resetValue>
<size>32</size>
<fields>
<field>

View file

@ -576,7 +576,12 @@ pub fn idle() -> bool {
crate::arch::process::set_current_pid(pid);
let mut process = Process::current();
let mut response_vec = Vec::new();
let mut capacity = 9 * core::mem::size_of::<usize>();
if let Some(mem) = response.memory() {
capacity += mem.len();
}
let mut response_vec = Vec::with_capacity(capacity);
response_vec.extend_from_slice(&thread_id.to_le_bytes());
for word in response.to_args().iter_mut() {
response_vec.extend_from_slice(&word.to_le_bytes());

View file

@ -21,6 +21,7 @@ const USER_AREA_END: usize = 0xff00_0000;
// and therefore are limited to 4 MB.
const EXCEPTION_STACK_TOP: usize = 0xffff_0000;
const KERNEL_LOAD_OFFSET: usize = 0xffd0_0000;
const KERNEL_STACK_PAGE_COUNT: usize = 1;
const KERNEL_ARGUMENT_OFFSET: usize = 0xffc0_0000;
const FLG_VALID: usize = 0x1;
@ -421,7 +422,7 @@ impl ProgramDescription {
allocator.change_owner(pid as XousPid, pt_addr as usize);
// Allocate stack pages.
for i in 0..STACK_PAGE_COUNT {
for i in 0..if is_kernel { KERNEL_STACK_PAGE_COUNT} else { STACK_PAGE_COUNT } {
let sp_page = allocator.alloc() as usize;
allocator.map_page(
satp,

View file

@ -12,11 +12,7 @@ log-server = { path = "../log-server" }
ticktimer-server = { path = "../ticktimer-server" }
xous-names = { path = "../xous-names" }
log = "0.4"
[dependencies.rkyv]
version = "0.4.1"
default-features = false
features = ["const_generics"]
rkyv = { version = "0.3.1", default_features = false }
[target.'cfg(not(any(windows,unix)))'.dependencies]
utralib = { path = "../../utralib"}

View file

@ -44,7 +44,7 @@ impl Into<u32> for UartType {
/////////////////////// I2C
// a small book-keeping struct used to report back to I2C requestors as to the status of a transaction
#[derive(Debug, Copy, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
#[derive(Debug, Copy, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Eq, PartialEq)]
pub enum I2cStatus {
/// used only as the default, should always be set to one of the below before sending
Uninitialized,
@ -67,15 +67,15 @@ pub enum I2cStatus {
}
#[derive(Debug, Copy, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub struct I2cTransaction {
bus_addr: u8,
pub bus_addr: u8,
// write address and read address are encoded in the packet field below
txbuf: Option<[u8; 258]>, // up to 258 bytes total packet length; note cost is "same" b/c these are sent via 4kiB page remaps
txlen: u32,
rxbuf: Option<[u8; 258]>,
rxlen: u32,
timeout_ms: u32,
pub txbuf: Option<[u8; 31]>, // limited by trait types available in Rkyv
pub txlen: u32,
pub rxbuf: Option<[u8; 31]>,
pub rxlen: u32,
pub timeout_ms: u32,
// response field to the calling server
status: I2cStatus,
pub status: I2cStatus,
}
impl I2cTransaction {
pub fn new() -> Self {

233
services/llio/src/i2c.rs Normal file
View file

@ -0,0 +1,233 @@
use heapless::Vec;
use heapless::consts::*;
use llio::api::*;
use llio::send_i2c_response;
#[cfg(target_os = "none")]
use utralib::*;
#[derive(Eq, PartialEq)]
enum I2cState {
Idle,
Write,
Read,
}
#[cfg(not(target_os = "none"))]
pub struct I2cStateMachine {
}
#[cfg(not(target_os = "none"))]
impl I2cStateMachine {
pub fn new(_ticktimer: xous::CID, _i2c_base: *mut u32) -> Self {
I2cStateMachine {}
}
pub fn initiate(&mut self, _transaction: I2cTransaction ) -> I2cStatus {
I2cStatus::ResponseInProgress
}
pub fn register_listener(&mut self, _listener: xous::CID) -> Result<(), xous::CID> {
Ok(())
}
pub fn handler(&mut self) {
}
}
#[cfg(target_os = "none")]
pub struct I2cStateMachine {
transaction: I2cTransaction,
state: I2cState,
index: u32, // index of the current buffer in the state machine
timestamp: u64, // timestamp of the last transaction
ticktimer: xous::CID, // a connection to the ticktimer so we can measure timeouts
i2c_csr: utralib::CSR<u32>,
listeners: Vec<xous::CID, U32>,
}
#[cfg(target_os = "none")]
impl I2cStateMachine {
pub fn new(ticktimer: xous::CID, i2c_base: *mut u32) -> Self {
I2cStateMachine {
transaction: I2cTransaction::new(),
state: I2cState::Idle,
timestamp: ticktimer_server::elapsed_ms(ticktimer).unwrap(),
ticktimer,
i2c_csr: CSR::new(i2c_base),
index: 0,
listeners: Vec::new(),
}
}
pub fn initiate(&mut self, transaction: I2cTransaction ) -> I2cStatus {
// sanity-check the bounds limits, currently imposed by trait implementations available in rkyv
if transaction.txlen > 31 || transaction.rxlen > 31 {
return I2cStatus::ResponseFormatError
}
let now = ticktimer_server::elapsed_ms(self.ticktimer).unwrap();
if self.state != I2cState::Idle && ((now - self.timestamp) < self.transaction.timeout_ms as u64) {
// we're in a transaction that hadn't timed out, can't accept a new one
I2cStatus::ResponseBusy
} else {
if self.state != I2cState::Idle {
// we're in a transaction, but previous transaction had timed out...
self.report_timeout();
// reset our state parameter
self.state = I2cState::Idle;
self.index = 0;
// now we're ready to move on and try a new transaction. We hope! Maybe the block should be reset?? TBD. Need to understand the nature of the timeouts better, if and when they do happen.
}
self.timestamp = now;
self.transaction = transaction.clone();
if self.transaction.status == I2cStatus::RequestIncoming {
self.transaction.status = I2cStatus::ResponseInProgress;
// now do the BusAddr stuff, so that the we can get the irq response
if let Some(_txbuf) = self.transaction.txbuf {
// initiate bus address with write bit set
self.state = I2cState::Write;
self.i2c_csr.wfo(utra::i2c::TXR_TXR, (self.transaction.bus_addr << 1 | 0) as u32);
self.index = 0;
self.i2c_csr.wo(utra::i2c::COMMAND,
self.i2c_csr.ms(utra::i2c::COMMAND_WR, 1) |
self.i2c_csr.ms(utra::i2c::COMMAND_STA, 1)
);
I2cStatus::ResponseInProgress
} else if let Some(_rxbuf) = self.transaction.rxbuf {
// initiate bus address with read bit set
self.state = I2cState::Read;
self.i2c_csr.wfo(utra::i2c::TXR_TXR, (self.transaction.bus_addr << 1 | 1) as u32);
self.index = 0;
self.i2c_csr.wo(utra::i2c::COMMAND,
self.i2c_csr.ms(utra::i2c::COMMAND_WR, 1) |
self.i2c_csr.ms(utra::i2c::COMMAND_STA, 1)
);
I2cStatus::ResponseInProgress
} else {
// no buffers specified, erase everything and go to idle
self.state = I2cState::Idle;
self.transaction = I2cTransaction::new();
I2cStatus::ResponseFormatError
}
} else {
I2cStatus::ResponseFormatError // the status field was not formatted correctly to accept the transaction
}
}
}
fn report_nack(&mut self) {
// report the NACK situation to all the listeners
let mut nack = I2cTransaction::new();
nack.status = I2cStatus::ResponseNack;
for &listener in self.listeners.iter() {
send_i2c_response(listener, nack).expect("LLIO|I2C: couldn't send NACK to listeners");
}
}
fn report_timeout(&mut self) {
let mut timeout = I2cTransaction::new();
timeout.status = I2cStatus::ResponseTimeout;
for &listener in self.listeners.iter() {
send_i2c_response(listener, timeout).expect("LLIO|I2c: couldn't send timeout error to liseners");
}
}
fn report_write_done(&mut self) {
// report the end of a write-only transaction to all the listeners
let mut ack = I2cTransaction::new();
ack.status = I2cStatus::ResponseWriteOk;
for &listener in self.listeners.iter() {
send_i2c_response(listener, ack).expect("LLIO|I2C: couldn't send write ACK to listeners");
}
}
fn report_read_done(&mut self) {
// report the result of a read transaction to all the listeners
self.transaction.status = I2cStatus::ResponseReadOk;
for &listener in self.listeners.iter() {
send_i2c_response(listener, self.transaction).expect("LLIO|I2C: couldn't send read response to listeners");
}
}
pub fn register_listener(&mut self, listener: xous::CID) -> Result<(), xous::CID> {
self.listeners.push(listener)
}
pub fn handler(&mut self) {
// check if the transaction had actually timed out
let now = ticktimer_server::elapsed_ms(self.ticktimer).unwrap();
if now - self.timestamp > self.transaction.timeout_ms as u64 {
// previous transaction had timed out...
self.report_timeout();
// reset our state parameter
self.state = I2cState::Idle;
self.index = 0;
self.timestamp = now;
return;
}
self.timestamp = now;
match self.state {
I2cState::Write => {
if let Some(txbuf) = self.transaction.txbuf {
// check ack bit
if self.i2c_csr.rf(utra::i2c::STATUS_RXACK) != 1 {
self.state = I2cState::Idle;
self.transaction = I2cTransaction::new();
self.report_nack();
}
// send next byte if there is one
if self.index < self.transaction.txlen {
self.i2c_csr.wfo(utra::i2c::TXR_TXR, txbuf[self.index as usize] as u32);
if self.index == (self.transaction.txlen - 1) && self.transaction.rxbuf.is_none() {
// send a stop bit if this is the very last in the series
self.i2c_csr.wo(utra::i2c::COMMAND,
self.i2c_csr.ms(utra::i2c::COMMAND_WR, 1) |
self.i2c_csr.ms(utra::i2c::COMMAND_STO, 1)
);
} else {
self.i2c_csr.wfo(utra::i2c::COMMAND_WR, 1);
}
self.index += 1;
} else {
if let Some(_rxbuf) = self.transaction.rxbuf {
// initiate bus address with read bit set
self.state = I2cState::Read;
self.i2c_csr.wfo(utra::i2c::TXR_TXR, (self.transaction.bus_addr << 1 | 1) as u32);
self.index = 0;
self.i2c_csr.wo(utra::i2c::COMMAND,
self.i2c_csr.ms(utra::i2c::COMMAND_WR, 1) |
self.i2c_csr.ms(utra::i2c::COMMAND_STA, 1)
);
} else {
self.report_write_done();
self.state = I2cState::Idle;
}
}
} else {
// we should never get here, because txbuf was checked as Some() by the setup routine
log::error!("LLIO|I2C: illegal write state");
}
},
I2cState::Read => {
if let Some(mut rxbuf) = self.transaction.rxbuf {
if self.index > 0 {
// we are re-entering from a previous call, store the read value from the previous call
rxbuf[self.index as usize - 1] = self.i2c_csr.rf(utra::i2c::RXR_RXR) as u8;
}
if self.index < self.transaction.rxlen {
if self.index == (self.transaction.rxlen - 1) {
self.i2c_csr.wo(utra::i2c::COMMAND,
self.i2c_csr.ms(utra::i2c::COMMAND_RD, 1) |
self.i2c_csr.ms(utra::i2c::COMMAND_STO, 1) |
self.i2c_csr.ms(utra::i2c::COMMAND_ACK, 1)
);
} else {
self.i2c_csr.wfo(utra::i2c::COMMAND_RD, 1);
}
self.index += 1;
} else {
self.report_read_done();
self.state = I2cState::Idle;
}
} else {
// we should never get here, because rxbuf was checked as Some() by the setup routine
log::error!("LLIO|I2C: illegal read state");
}
},
I2cState::Idle => {
log::error!("LLIO|I2C: received interrupt event when no transaciton pending!");
}
}
}
}

View file

@ -34,9 +34,8 @@ pub fn send_i2c_request(cid: CID, transaction: I2cTransaction) -> Result<I2cStat
let returned = unsafe { rkyv::archived_value::<api::Opcode>(buf.as_ref(), pos)};
if let rkyv::Archived::<api::Opcode>::I2cTxRx(result) = returned {
use core::ops::DerefMut;
transaction.deref_mut() = result.as_slice();
Ok(transaction.status)
let transaction = result.deserialise();
Ok(transaction.status())
} else {
log::info!("send_i2c_request saw an unhandled return type of {:?}", buf);
Err(xous::Error::InternalError)

View file

@ -2,22 +2,23 @@
#![cfg_attr(target_os = "none", no_main)]
mod api;
use api::*;
use llio::api::*;
mod i2c;
use i2c::*;
use core::convert::TryFrom;
use core::pin::Pin;
use rkyv::archived_value;
use rkyv::Unarchive;
use log::{error, info};
#[cfg(target_os = "none")]
mod implementation {
use crate::api::*;
use llio::api::*;
use log::{error, info};
use utralib::generated::*;
const STD_TIMEOUT: u32 = 100;
pub struct Llio {
reboot_csr: utralib::CSR<u32>,
crg_csr: utralib::CSR<u32>,
@ -25,7 +26,7 @@ mod implementation {
info_csr: utralib::CSR<u32>,
identifier_csr: utralib::CSR<u32>,
i2c_csr: utralib::CSR<u32>,
handler_conn: xous::CID,
handler_conn: Option<xous::CID>,
event_csr: utralib::CSR<u32>,
power_csr: utralib::CSR<u32>,
seed_csr: utralib::CSR<u32>,
@ -49,8 +50,8 @@ mod implementation {
// ASSUME: we are only ever handling txrx done interrupts. If implementing ARB interrupts, this needs to be refactored to read the source and dispatch accordingly.
fn handle_i2c_irq(_irq_no: usize, arg: *mut usize) {
let xl = unsafe { &mut *(arg as *mut Llio) };
if let Some(conn) = xl.i2c_conn {
xous::try_send_message(xl.handler_conn, Opcode::IrqI2cTxrxDone.into()).map(|_| ()).unwrap();
if let Some(conn) = xl.handler_conn {
xous::try_send_message(conn, Opcode::IrqI2cTxrxDone.into()).map(|_| ()).unwrap();
} else {
log::error!("LLIO|handle_i2c_irq: TXRX done interrupt, but no connection for notification!");
}
@ -59,7 +60,9 @@ mod implementation {
}
impl Llio {
pub fn new(local_conn: xous::CID) -> Llio {
pub fn get_i2c_base(&self) -> *mut u32 { self.i2c_csr.base }
pub fn new(handler_conn: xous::CID) -> Llio {
let reboot_csr = xous::syscall::map_memory(
xous::MemoryAddress::new(utra::reboot::HW_REBOOT_BASE),
None,
@ -102,7 +105,6 @@ mod implementation {
xous::MemoryFlags::R | xous::MemoryFlags::W,
)
.expect("couldn't map I2C CSR range");
let i2c_sid = xous::create_server_id().expect("LLIO: couldn't get random server ID for I2C");
let event_csr = xous::syscall::map_memory(
xous::MemoryAddress::new(utra::btevents::HW_BTEVENTS_BASE),
None,
@ -142,7 +144,7 @@ mod implementation {
info_csr: CSR::new(info_csr.as_mut_ptr() as *mut u32),
identifier_csr: CSR::new(identifier_csr.as_mut_ptr() as *mut u32),
i2c_csr: CSR::new(i2c_csr.as_mut_ptr() as *mut u32),
handler_conn, // connection for messages from IRQ handler
handler_conn: Some(handler_conn), // connection for messages from IRQ handler
event_csr: CSR::new(event_csr.as_mut_ptr() as *mut u32),
power_csr: CSR::new(power_csr.as_mut_ptr() as *mut u32),
seed_csr: CSR::new(seed_csr.as_mut_ptr() as *mut u32),
@ -176,8 +178,12 @@ mod implementation {
)
.expect("couldn't claim I2C irq");
// initialize i2c
i2c_init(&mut xl, utralib::LITEX_CONFIG_CLOCK_FREQUENCY);
// initialize i2c clocks
// set the prescale assuming 100MHz cpu operation: 100MHz / ( 5 * 100kHz ) - 1 = 199
let clkcode = (utralib::LITEX_CONFIG_CLOCK_FREQUENCY as u32) / (5 * 100_000) - 1;
xl.i2c_csr.wfo(utra::i2c::PRESCALE_PRESCALE, clkcode & 0xFFFF);
// enable the block
xl.i2c_csr.rmwf(utra::i2c::CONTROL_EN, 1);
// clear any interrupts pending, just in case something went pear-shaped during initialization
xl.i2c_csr.wo(utra::i2c::EV_PENDING, xl.i2c_csr.r(utra::i2c::EV_PENDING));
// now enable interrupts
@ -348,33 +354,24 @@ mod implementation {
pub fn xadc_gpio2(&self) -> u16 {
self.xadc_csr.rf(utra::trng::XADC_GPIO2_XADC_GPIO2) as u16
}
fn i2c_init(&mut self, clock_hz: u32) {
// set the prescale assuming 100MHz cpu operation: 100MHz / ( 5 * 100kHz ) - 1 = 199
let clkcode: u32 = clock_hz / (5 * 100_000) - 1;
self.i2c_csr.wfo(utra::i2c::PRESCALE_PRESCALE, clkcode & 0xFFFF);
// enable the block
self.i2c_csr.rmwf(utra::i2c::CONTROL_EN, 1);
}
}
}
// a stub to try to avoid breaking hosted mode for as long as possible.
#[cfg(not(target_os = "none"))]
mod implementation {
use crate::api::*;
use llio::api::*;
use log::{error, info};
pub struct Llio {
}
impl Llio {
pub fn new() -> Llio {
pub fn new(_handler_conn: xous::CID) -> Llio {
Llio {
}
}
pub fn get_i2c_base(&self) -> *mut u32 { 0 as *mut u32 }
pub fn reboot(&self, _reboot_soc: bool) {}
pub fn set_reboot_vector(&self, _vector: u32) {}
@ -435,8 +432,6 @@ mod implementation {
fn xmain() -> ! {
let debug1 = false;
use crate::implementation::Llio;
//use heapless::Vec;
//use heapless::consts::*;
log_server::init_wait().unwrap();
info!("LLIO: my PID is {}", xous::process::id());
@ -452,7 +447,7 @@ fn xmain() -> ! {
let ticktimer_server_id = xous::SID::from_bytes(b"ticktimer-server").unwrap();
let ticktimer_conn = xous::connect(ticktimer_server_id).unwrap();
// create an i2c state machine handler
let mut i2c_machine = I2cStateMachine::new(ticktimer_conn, llio.i2c_csr);
let mut i2c_machine = I2cStateMachine::new(ticktimer_conn, llio.get_i2c_base());
if debug1{info!("LLIO: starting main loop");}
let mut reboot_requested: bool = false;
@ -590,7 +585,7 @@ fn xmain() -> ! {
},
Opcode::IrqI2cTxrxDone => {
// I2C state machine handler irq received
ic2_machine.handler();
i2c_machine.handler();
},
_ => error!("LLIO: no handler for opcode"),
}
@ -602,7 +597,7 @@ fn xmain() -> ! {
};
match &*value {
rkyv::Archived::<Opcode>::I2cTxRx(rkyv_i2c) => {
let i2c_txrx: I2cTransaction = rkyv_i2c.deserialize();
let mut i2c_txrx: I2cTransaction = rkyv_i2c.deserialize();
let status = i2c_machine.initiate(i2c_txrx);
@ -610,19 +605,20 @@ fn xmain() -> ! {
// pack our data back into the buffer to return
use rkyv::Write;
let mut writer = rkyv::ArchiveBuffer::new(buf);
writer.archive(&api::Opcode::I2cTxRx(i2c_txrx)).expect("LLIO: couldn't re-archive return value to I2cTxRx");
writer.archive(&Opcode::I2cTxRx(i2c_txrx)).expect("LLIO: couldn't re-archive return value to I2cTxRx");
},
_ => panic!("LLIO: invalid MutableBorrow memory message")
};
} else if let xous::Message::Borrow(m) = &envelope.body {
let buf = unsafe { buffer::XousBuffer::from_memory_message(m) };
let buf = unsafe { xous::XousBuffer::from_memory_message(m) };
let bytes = Pin::new(buf.as_ref());
let value = unsafe {
archived_value::<api::Opcode>(&bytes, m.id as usize)
};
match &*value {
rkyv::Archived::<api::Opcode>::I2cSubscribe(registration) => {
let cid = xous_names::request_connection_blocking(registration.as_str()).expect("KBD: can't connect to requested listener for reporting events");
let reg: xous::String::<64> = registration.unarchive();
let cid = xous_names::request_connection_blocking(reg.to_str()).expect("KBD: can't connect to requested listener for reporting events");
i2c_machine.register_listener(cid).expect("LLIO: probably ran out of slots for I2C event reporting");
},
_ => panic!("LLIO: invalid Borrow memory message"),

View file

@ -1,15 +1,10 @@
use xous::{String, MessageEnvelope};
use xous::String;
use core::fmt::Write;
/////////////////////////// Common items to all commands
pub trait ShellCmdApi<'a> {
// user implemented:
// called to process the command with the remainder of the string attached
fn process(&mut self, args: String::<1024>, env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error>;
// called to process incoming messages that may have been origniated by the most recently issued command
fn callback(&mut self, msg: &MessageEnvelope, _env: &mut CommonEnv) -> Result<Option<String::<1024>>, xous::Error> {
log::info!("SHCH: received unhandled message {:?}", msg);
Ok(None)
}
// created with cmd_api! macro
// checks if the command matches the current verb in question
@ -33,7 +28,6 @@ macro_rules! cmd_api {
};
}
/////////////////////////// Command shell integration
#[derive(Debug)]
pub struct CommonEnv {
@ -61,17 +55,14 @@ mod echo; use echo::*;
mod test; use test::*;
mod sleep; use sleep::*;
mod sensors; use sensors::*;
mod callback; use callback::*;
#[derive(Debug)]
pub struct CmdEnv {
common_env: CommonEnv,
lastverb: String::<256>,
///// 2. declare storage for your command here.
test_cmd: Test,
sleep_cmd: Sleep,
sensors_cmd: Sensors,
callback_cmd: CallBack,
}
impl CmdEnv {
pub fn new(gam: xous::CID) -> CmdEnv {
@ -83,16 +74,14 @@ impl CmdEnv {
ticktimer: xous::connect(ticktimer_server_id).unwrap(),
gam,
},
lastverb: String::<256>::new(),
///// 3. initialize your storage, by calling new()
test_cmd: Test::new(),
sleep_cmd: Sleep::new(),
sensors_cmd: Sensors::new(),
callback_cmd: CallBack::new(),
}
}
pub fn dispatch(&mut self, maybe_cmdline: Option<&mut String::<1024>>, maybe_callback: Option<&MessageEnvelope>) -> Result<Option<String::<1024>>, xous::Error> {
pub fn dispatch(&mut self, cmdline: &mut String::<1024>) -> Result<Option<String::<1024>>, xous::Error> {
let mut ret = String::<1024>::new();
let mut echo_cmd = Echo {}; // this command has no persistent storage, so we can "create" it every time we call dispatch (but it's a zero-cost absraction so this doesn't actually create any instructions)
@ -102,59 +91,38 @@ impl CmdEnv {
&mut self.test_cmd,
&mut self.sleep_cmd,
&mut self.sensors_cmd,
&mut self.callback_cmd,
];
if let Some(cmdline) = maybe_cmdline {
let maybe_verb = tokenize(cmdline);
let maybe_verb = tokenize(cmdline);
let mut cmd_ret: Result<Option<String::<1024>>, xous::Error> = Ok(None);
if let Some(verb_string) = maybe_verb {
let verb = verb_string.to_str();
let mut cmd_ret: Result<Option<String::<1024>>, xous::Error> = Ok(None);
if let Some(verb_string) = maybe_verb {
let verb = verb_string.to_str();
// search through the list of commands linearly until one matches,
// then run it.
let mut match_found = false;
for cmd in commands.iter_mut() {
if cmd.matches(verb) {
match_found = true;
cmd_ret = cmd.process(*cmdline, &mut self.common_env);
self.lastverb.clear();
write!(self.lastverb, "{}", verb).expect("SHCH: couldn't record last verb");
};
}
// if none match, create a list of available commands
if !match_found {
let mut first = true;
write!(ret, "Commands: ").unwrap();
for cmd in commands.iter() {
if !first {
ret.append(", ")?;
}
ret.append(cmd.verb())?;
first = false;
}
Ok(Some(ret))
} else {
cmd_ret
}
} else {
Ok(None)
// search through the list of commands linearly until one matches,
// then run it.
let mut match_found = false;
for cmd in commands.iter_mut() {
if cmd.matches(verb) {
match_found = true;
cmd_ret = cmd.process(*cmdline, &mut self.common_env);
};
}
} else if let Some(callback) = maybe_callback {
let mut cmd_ret: Result<Option<String::<1024>>, xous::Error> = Ok(None);
if self.lastverb.len() > 0 {
let verb = self.lastverb.to_str();
for cmd in commands.iter_mut() {
if cmd.matches(verb) {
cmd_ret = cmd.callback(callback, &mut self.common_env);
break;
};
// if none match, create a list of available commands
if !match_found {
let mut first = true;
write!(ret, "Commands: ").unwrap();
for cmd in commands.iter() {
if !first {
ret.append(", ")?;
}
ret.append(cmd.verb())?;
first = false;
}
cmd_ret
Ok(Some(ret))
} else {
Ok(None)
cmd_ret
}
} else {
Ok(None)

View file

@ -1,7 +1,7 @@
#![cfg_attr(target_os = "none", no_std)]
#![cfg_attr(target_os = "none", no_main)]
use log::info;
use log::{error, info};
use core::fmt::Write;
use core::convert::TryFrom;
@ -16,8 +16,6 @@ use graphics_server::{Gid, Point, Rectangle, TextBounds, TextView, DrawStyle, Gl
use heapless::spsc::Queue;
use heapless::consts::U16;
use xous::MessageEnvelope;
mod cmds;
use cmds::*;
@ -34,8 +32,7 @@ struct Repl {
// optional structures that indicate new input to the Repl loop per iteration
// an input string
input: Option<xous::String<1024>>,
// messages from other servers
msg: Option<MessageEnvelope>,
// examples of future inputs: returned values via other services
// record our input history
history: Queue<History, U16>,
@ -60,7 +57,6 @@ impl Repl{
let screensize = gam::get_canvas_bounds(gam_conn, content).expect("SHCH: couldn't get dimensions of content canvas");
Repl {
input: None,
msg: None,
history: Queue::new(),
content,
gam: gam_conn,
@ -84,10 +80,6 @@ impl Repl{
Ok(())
}
fn msg(&mut self, message: MessageEnvelope) {
self.msg = Some(message);
}
fn circular_push(&mut self, item: History) {
if self.history.len() == self.history.capacity() {
self.history.dequeue().expect("SHCH: couldn't dequeue historye");
@ -116,17 +108,7 @@ impl Repl{
// take the input and pass it on to the various command parsers, and attach result
if let Some(mut local) = self.input {
if let Some(res) = self.env.dispatch(Some(&mut local), None).expect("SHCH: command dispatch failed") {
let mut response = xous::String::<1024>::new();
write!(response, "{}", res).expect("SHCH: can't copy result to history");
let output_history = History {
text: response,
is_input: false
};
self.circular_push(output_history);
}
} else if let Some(msg) = &self.msg {
if let Some(res) = self.env.dispatch(None, Some(msg)).expect("SCHC: callback failed") {
if let Some(res) = self.env.dispatch(&mut local).expect("SHCH: command dispatch failed") {
let mut response = xous::String::<1024>::new();
write!(response, "{}", res).expect("SHCH: can't copy result to history");
let output_history = History {
@ -139,7 +121,6 @@ impl Repl{
// clear all the inputs to the loop, so we don't process them twice
self.input = None;
self.msg = None;
// redraw UI now that we've responded
self.redraw().expect("SHCH: can't redraw");
@ -252,8 +233,7 @@ fn xmain() -> ! {
}
}
} else {
repl.msg(envelope);
update_repl = true;
error!("SHCH: couldn't convert message");
}
if update_repl {

View file

@ -59,3 +59,82 @@ impl Authenticate {
ID_AUTHENTICATE as u32
}
}
// We keep XousServerName around because want to be able to index off the server name, without
// burdening the Kernel String type with the Hash32 methods
// --------------------- Taken from rkyv docs https://docs.rs/rkyv/0.3.0/rkyv/trait.Archive.html //
#[derive(Debug, Copy, Clone)]
pub struct XousServerName {
value: [u8; 64],
length: u32,
}
impl Default for XousServerName {
fn default() -> Self {
XousServerName {
value: [0u8; 64],
length: 0,
}
}
}
impl XousServerName {
pub fn to_str(&self) -> &str {
core::str::from_utf8(unsafe {
core::slice::from_raw_parts(self.value.as_ptr(), self.length as usize)
})
.unwrap()
}
}
// Allow using the `write!()` macro to write into a `&XousServerName`
impl core::fmt::Write for XousServerName {
fn write_str(&mut self, s: &str) -> core::result::Result<(), core::fmt::Error> {
self.length = 0;
let b = s.bytes();
// Ensure the length is acceptable
if b.len() > self.value.len() {
Err(core::fmt::Error)?;
}
self.length = b.len() as u32;
// Copy the string into this variable
for (dest, src) in self.value.iter_mut().zip(s.bytes()) {
*dest = src;
}
// Attempt to convert the string to UTF-8 to validate it's correct UTF-8
core::str::from_utf8(unsafe {
core::slice::from_raw_parts(self.value.as_ptr(), self.length as usize)
})
.map_err(|_| core::fmt::Error)?;
Ok(())
}
}
impl hash32::Hash for XousServerName {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
Hash::hash(&self.value[..self.length as usize], state);
Hash::hash(&self.length, state)
}
}
impl PartialEq for XousServerName {
fn eq(&self, other: &Self) -> bool {
self.value[..self.length as usize] == other.value[..other.length as usize]
&& self.length == other.length
}
}
impl Eq for XousServerName {}
impl core::fmt::Display for XousServerName {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.to_str())
}
}

View file

@ -1328,10 +1328,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
``d77dca2f``, so this value is 3615345199, which should be enough to check out
``2a2abe5e``, so this value is 707444318, which should be enough to check out
the exact git version used to build this firmware.]]></description>
<addressOffset>0x0014</addressOffset>
<resetValue>0xd77dca2f</resetValue>
<resetValue>0x2a2abe5e</resetValue>
<size>32</size>
<fields>
<field>
@ -1348,7 +1348,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>0x0d</resetValue>
<resetValue>0x0e</resetValue>
<size>32</size>
<fields>
<field>
@ -1957,8 +1957,7 @@ upon the completion of one byte of data transfer.]]></description>
</register>
<register>
<name>EV_STATUS</name>
<description><![CDATA[This register contains the current raw level of the i2c_int event trigger.
Writes to this register have no effect.]]></description>
<description><![CDATA[Triggered on the falling edge of TIP]]></description>
<addressOffset>0x0018</addressOffset>
<resetValue>0x00</resetValue>
<size>32</size>
@ -1970,12 +1969,18 @@ Writes to this register have no effect.]]></description>
<lsb>0</lsb>
<description><![CDATA[Level of the ``i2c_int`` event]]></description>
</field>
<field>
<name>txrx_done</name>
<msb>1</msb>
<bitRange>[1:1]</bitRange>
<lsb>1</lsb>
<description><![CDATA[Level of the ``txrx_done`` event]]></description>
</field>
</fields>
</register>
<register>
<name>EV_PENDING</name>
<description><![CDATA[When a i2c_int event occurs, the corresponding bit will be set in this
register. To clear the Event, set the corresponding bit in this register.]]></description>
<description><![CDATA[Triggered on the falling edge of TIP]]></description>
<addressOffset>0x001c</addressOffset>
<resetValue>0x00</resetValue>
<size>32</size>
@ -1985,14 +1990,20 @@ register. To clear the Event, set the corresponding bit in this register.]]></d
<msb>0</msb>
<bitRange>[0:0]</bitRange>
<lsb>0</lsb>
<description><![CDATA[`1` if a `i2c_int` event occurred. This Event is triggered on a **rising** edge.]]></description>
<description><![CDATA[Triggered when arbitration is lost]]></description>
</field>
<field>
<name>txrx_done</name>
<msb>1</msb>
<bitRange>[1:1]</bitRange>
<lsb>1</lsb>
<description><![CDATA[Triggered on the falling edge of TIP]]></description>
</field>
</fields>
</register>
<register>
<name>EV_ENABLE</name>
<description><![CDATA[This register enables the corresponding i2c_int events. Write a ``0`` to this
register to disable individual events.]]></description>
<description><![CDATA[Triggered on the falling edge of TIP]]></description>
<addressOffset>0x0020</addressOffset>
<resetValue>0x00</resetValue>
<size>32</size>
@ -2004,6 +2015,13 @@ register to disable individual events.]]></description>
<lsb>0</lsb>
<description><![CDATA[Write a ``1`` to enable the ``i2c_int`` Event]]></description>
</field>
<field>
<name>txrx_done</name>
<msb>1</msb>
<bitRange>[1:1]</bitRange>
<lsb>1</lsb>
<description><![CDATA[Write a ``1`` to enable the ``txrx_done`` Event]]></description>
</field>
</fields>
</register>
</registers>
@ -3038,7 +3056,7 @@ again until the interrupt is serviced.]]></description>
<name>SEED1</name>
<description><![CDATA[Bits 32-63 of `SEED_SEED`. Seed used for the build]]></description>
<addressOffset>0x0000</addressOffset>
<resetValue>0x48f7dee1</resetValue>
<resetValue>0x1a7a2a66</resetValue>
<size>32</size>
<fields>
<field>
@ -3053,7 +3071,7 @@ again until the interrupt is serviced.]]></description>
<name>SEED0</name>
<description><![CDATA[Bits 0-31 of `SEED_SEED`.]]></description>
<addressOffset>0x0004</addressOffset>
<resetValue>0xc2db130</resetValue>
<resetValue>0x4b326b1e</resetValue>
<size>32</size>
<fields>
<field>

View file

@ -13,9 +13,6 @@ log = { version = "0.4", optional = true }
core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' }
compiler_builtins = { version = '0.1.0', optional = true }
# for allowing strings to be used as indexes in hash maps
hash32 = "0.1.0"
hash32-derive = "0.1.0"
# for sending strings
[dependencies.rkyv]
version = "0.4.1"

View file

@ -665,7 +665,12 @@ fn _xous_syscall_to(
// );
// Send the packet to the server
let mut pkt = vec![];
let mut capacity = 9 * core::mem::size_of::<usize>();
if let Some(mem) = call.memory() {
capacity += mem.len();
}
let mut pkt = Vec::with_capacity(capacity);
THREAD_ID.with(|tid| pkt.extend_from_slice(&tid.borrow().to_le_bytes()));
for word in &[nr, a1, a2, a3, a4, a5, a6, a7] {
pkt.extend_from_slice(&word.to_le_bytes());

View file

@ -5,7 +5,6 @@ use rkyv::ser::Serializer;
use rkyv::archived_value;
use rkyv::ArchiveUnsized;
use rkyv::SerializeUnsized;
use hash32::{Hash, Hasher};
#[derive(Copy, Clone)]
pub struct String<const N: usize> {
@ -276,17 +275,6 @@ impl<S: rkyv::ser::Serializer + ?Sized, const N: usize> rkyv::Serialize<S> for S
}
}
impl<const N: usize> hash32::Hash for String<N> {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
Hash::hash(&self.bytes[..self.len as usize], state);
Hash::hash(&self.len, state)
}
}
impl<const N: usize> PartialEq for String<N> {
fn eq(&self, other: &Self) -> bool {
self.bytes[..self.len as usize] == other.bytes[..other.len as usize]