From 72763dc3c723a6b0980769d8bd6d5bf4b0dfdfea Mon Sep 17 00:00:00 2001 From: bunnie Date: Sat, 8 Feb 2025 05:20:25 +0800 Subject: [PATCH] modify fast dma test for clocking option --- libs/ux-api/src/widgets/action.rs | 37 +++++++ libs/ux-api/src/widgets/mod.rs | 6 ++ libs/ux-api/src/widgets/payload.rs | 116 ++++++++++++++++++++++ libs/ux-api/src/widgets/textentry.rs | 127 ++++++++++++++++++++++++ libs/xous-bio-bdma/src/bio_tests/dma.rs | 5 +- 5 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 libs/ux-api/src/widgets/action.rs create mode 100644 libs/ux-api/src/widgets/mod.rs create mode 100644 libs/ux-api/src/widgets/payload.rs create mode 100644 libs/ux-api/src/widgets/textentry.rs diff --git a/libs/ux-api/src/widgets/action.rs b/libs/ux-api/src/widgets/action.rs new file mode 100644 index 000000000..555b62df5 --- /dev/null +++ b/libs/ux-api/src/widgets/action.rs @@ -0,0 +1,37 @@ +use enum_dispatch::enum_dispatch; + +use super::*; + +#[enum_dispatch(ActionApi)] +pub enum ActionType { + #[cfg(feature = "textentry")] + TextEntry, + #[cfg(feature = "bip39entry")] + Bip39Entry, + #[cfg(feature = "radiobuttons")] + RadioButtons, + #[cfg(feature = "checkboxes")] + CheckBoxes, + #[cfg(feature = "slider")] + Slider, + #[cfg(feature = "notification")] + Notification, + #[cfg(feature = "ditherpunk")] + Image, + #[cfg(feature = "consoleinput")] + ConsoleInput, +} + +/* TODO: turn Modal into a trait */ +#[enum_dispatch] +pub trait ActionApi { + /// Returns the height of the widget for layout use + fn height(&self, glyph_height: i16, margin: i16, _modal: &Modal) -> i16 { glyph_height + margin * 2 } + /// Triggers a redraw of the action object + fn redraw(&self, _at_height: i16, _modal: &Modal) { unimplemented!() } + fn close(&mut self) {} + fn is_password(&self) -> bool { false } + /// navigation is one of '∴' | '←' | '→' | '↑' | '↓' + fn key_action(&mut self, _key: char) -> Option { None } + fn set_action_opcode(&mut self, _op: u32) {} +} diff --git a/libs/ux-api/src/widgets/mod.rs b/libs/ux-api/src/widgets/mod.rs new file mode 100644 index 000000000..7cc1e52e9 --- /dev/null +++ b/libs/ux-api/src/widgets/mod.rs @@ -0,0 +1,6 @@ +pub mod textentry; +pub use textentry::*; +pub mod payload; +pub use payload::*; +pub mod action; +pub use action::*; diff --git a/libs/ux-api/src/widgets/payload.rs b/libs/ux-api/src/widgets/payload.rs new file mode 100644 index 000000000..52fc54a5c --- /dev/null +++ b/libs/ux-api/src/widgets/payload.rs @@ -0,0 +1,116 @@ +use zeroize::Zeroize; + +use super::*; + +/// We use a new type for item names, so that it's easy to resize this as needed. +#[derive(Debug, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +pub struct ItemName(String); +impl ItemName { + pub fn new(name: &str) -> Self { ItemName(String::from(name)) } + + pub fn as_str(&self) -> &str { self.0.as_str() } +} + +#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone, Eq, PartialEq, Default)] +pub struct Bip39EntryPayload { + // up to 32 bytes (256 bits) could be entered + pub data: [u8; 32], + // the actual length entered is reported here + pub len: u32, +} + +#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Clone, Eq, PartialEq, Default)] +pub struct TextEntryPayload { + dirty: bool, + pub content: String, + pub placeholder: Option, + pub placeholder_persist: bool, + pub insertion_point: Option, +} + +impl TextEntryPayload { + pub fn new() -> Self { + TextEntryPayload { + dirty: Default::default(), + content: Default::default(), + placeholder: Default::default(), + placeholder_persist: false, + insertion_point: None, + } + } + + pub fn new_with_fields(content: String, placeholder: Option) -> Self { + TextEntryPayload { + dirty: false, + content, + placeholder, + placeholder_persist: false, + insertion_point: None, + } + } + + /// Ensures that 0's are written to the storage of this struct, and not optimized out; important for + /// password fields. + pub fn volatile_clear(&mut self) { self.content.zeroize(); } + + pub fn as_str(&self) -> &str { self.content.as_str() } +} + +#[derive(Debug, Copy, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +pub struct SliderPayload(pub u32); +impl SliderPayload { + pub fn new(value: u32) -> Self { SliderPayload(value) } +} + +#[derive(Debug, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +pub struct RadioButtonPayload(pub ItemName); // returns the name of the item corresponding to the radio button selection +impl RadioButtonPayload { + pub fn new(name: &str) -> Self { RadioButtonPayload(ItemName::new(name)) } + + pub fn as_str(&self) -> &str { self.0.as_str() } + + pub fn clear(&mut self) { self.0.0.clear(); } +} +#[derive(Debug, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +pub struct CheckBoxPayload(pub [Option; MAX_ITEMS]); // returns a list of potential items that could be selected +impl CheckBoxPayload { + pub fn new() -> Self { CheckBoxPayload([const { None }; MAX_ITEMS]) } + + pub fn payload(self) -> [Option; MAX_ITEMS] { self.0 } + + pub fn contains(&self, name: &str) -> bool { + for maybe_item in self.0.iter() { + if let Some(item) = maybe_item { + if item.as_str() == name { + return true; + } + } + } + false + } + + pub fn add(&mut self, name: &str) -> bool { + if self.contains(name) { + return true; + } + for maybe_item in self.0.iter_mut() { + if maybe_item.is_none() { + *maybe_item = Some(ItemName::new(name)); + return true; + } + } + false + } + + pub fn remove(&mut self, name: &str) -> bool { + for maybe_item in self.0.iter_mut() { + if let Some(item) = maybe_item { + if item.as_str() == name { + *maybe_item = None; + return true; + } + } + } + false + } +} diff --git a/libs/ux-api/src/widgets/textentry.rs b/libs/ux-api/src/widgets/textentry.rs new file mode 100644 index 000000000..6e37fc838 --- /dev/null +++ b/libs/ux-api/src/widgets/textentry.rs @@ -0,0 +1,127 @@ +use core::cell::Cell; +use core::fmt::Write; +use std::cell::RefCell; + +use super::*; + +const MAX_FIELDS: i16 = 10; +pub const MAX_ITEMS: usize = 8; + +pub type ValidatorErr = String; +pub type Payloads = [TextEntryPayload; MAX_FIELDS as usize]; + +#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Clone, Eq, PartialEq, Default)] +pub struct TextEntryPayloads(Payloads, usize); + +impl TextEntryPayloads { + pub fn first(&self) -> TextEntryPayload { self.0[0].clone() } + + pub fn content(&self) -> Vec { self.0[..self.1].to_vec() } +} + +#[derive(Debug, Copy, Clone, num_derive::FromPrimitive, num_derive::ToPrimitive)] +pub enum TextEntryVisibility { + /// text is fully visible + Visible = 0, + /// only last chars are shown of text entry, the rest obscured with * + LastChars = 1, + /// all chars hidden as * + Hidden = 2, +} + +#[derive(Clone)] +pub struct TextEntry { + pub is_password: bool, + pub visibility: TextEntryVisibility, + pub action_conn: xous::CID, + pub action_opcode: u32, + // validator borrows the text entry payload, and returns an error message if something didn't go well. + // validator takes as ragument the current action_payload, and the current action_opcode + pub validator: Option Option>, + pub action_payloads: Vec, + + max_field_amount: u32, + selected_field: i16, + field_height: Cell, + /// track if keys were hit since initialized: this allows us to clear the default text, + /// instead of having it re-appear every time the text area is cleared + keys_hit: [bool; MAX_FIELDS as usize], + // gam: crate::Gam, // no GAM field because this needs to be a clone-capable structure. We create a GAM + // handle when we need it. + /// Stores the allowed height of a given text line, based on the contents and the space available + /// in the box. The height of a given line may be limited to make sure there is enough space for + /// later lines to be rendered. + action_payloads_allowed_heights: RefCell>, +} + +impl Default for TextEntry { + fn default() -> Self { + Self { + is_password: Default::default(), + visibility: TextEntryVisibility::Visible, + action_conn: Default::default(), + action_opcode: Default::default(), + validator: Default::default(), + selected_field: Default::default(), + action_payloads: Default::default(), + max_field_amount: 0, + field_height: Cell::new(0), + keys_hit: [false; MAX_FIELDS as usize], + action_payloads_allowed_heights: RefCell::new(Vec::new()), + } + } +} + +impl TextEntry { + pub fn new( + is_password: bool, + visibility: TextEntryVisibility, + action_conn: xous::CID, + action_opcode: u32, + action_payloads: Vec, + validator: Option Option>, + ) -> Self { + if action_payloads.len() as i16 > MAX_FIELDS { + panic!("can't have more than {} fields, found {}", MAX_FIELDS, action_payloads.len()); + } + + Self { + is_password, + visibility, + action_conn, + action_opcode, + action_payloads, + validator, + ..Default::default() + } + } + + pub fn reset_action_payloads(&mut self, fields: u32, placeholders: Option<[Option<(String, bool)>; 10]>) { + let mut payload = vec![TextEntryPayload::default(); fields as usize]; + + if let Some(placeholders) = placeholders { + for (index, element) in payload.iter_mut().enumerate() { + if let Some((p, persist)) = &placeholders[index] { + element.placeholder = Some(p.to_string()); + element.placeholder_persist = *persist; + } else { + element.placeholder = None; + element.placeholder_persist = false; + } + element.insertion_point = None; + } + } + + self.action_payloads = payload; + self.max_field_amount = fields; + self.keys_hit = [false; MAX_FIELDS as usize]; + } + + fn get_bullet_margin(&self) -> i16 { + if self.action_payloads.len() > 1 { + 17 // this is the margin for drawing the selection bullet + } else { + 0 // no selection bullet + } + } +} diff --git a/libs/xous-bio-bdma/src/bio_tests/dma.rs b/libs/xous-bio-bdma/src/bio_tests/dma.rs index cd1894d3d..fdbefb390 100644 --- a/libs/xous-bio-bdma/src/bio_tests/dma.rs +++ b/libs/xous-bio-bdma/src/bio_tests/dma.rs @@ -543,7 +543,7 @@ bio_code!(dma_u16_code, DMA_U16_START, DMA_U16_END, /// Multi-core DMA copy. More performant, but uses two cores for address generation in /// parallel with the copy master. -pub fn dma_multicore() -> usize { +pub fn dma_multicore(clkmode: u8) -> usize { let mut passing = 0; print!("DMA fast\r"); // clear prior test config state @@ -555,6 +555,9 @@ pub fn dma_multicore() -> usize { // reset all the fifos bio_ss.bio.wo(utra::bio_bdma::SFR_FIFO_CLR, 0xF); + // setup clocking mode option + bio_ss.bio.rmwf(SFR_CONFIG_CLOCKING_MODE, clkmode as u32); + // stop all the machines, so that code can be loaded bio_ss.bio.wo(utra::bio_bdma::SFR_CTRL, 0x0); bio_ss.load_code(dma_mc_copy_code(), 0, BioCore::Core2);