diff --git a/apps/system/components/MlCoordinator/Cargo.toml b/apps/system/components/MlCoordinator/Cargo.toml index 56ee0c3..bc1937e 100644 --- a/apps/system/components/MlCoordinator/Cargo.toml +++ b/apps/system/components/MlCoordinator/Cargo.toml @@ -1,6 +1,6 @@ [workspace] members = [ - "fake-executive", - "ml-common" + "ml-common", + "ml-coordinator", ] diff --git a/apps/system/components/MlCoordinator/fake-executive/src/lib.rs b/apps/system/components/MlCoordinator/fake-executive/src/lib.rs deleted file mode 100644 index 87d80ab..0000000 --- a/apps/system/components/MlCoordinator/fake-executive/src/lib.rs +++ /dev/null @@ -1,44 +0,0 @@ -use ml_common as ml; -use ml_common::ExecutiveInterface; - -pub struct FakeExecutive { - output_memory: [u8; 512], - fake_error: Option, -} - -impl ml::ExecutiveInterface for FakeExecutive { - fn run_model(&self, _model: &ml::Model) -> Result<&[u8], ml::ExecutionError> { - match self.fake_error { - Some(err) => Err(err), - None => Ok(&self.output_memory), - } - } -} - -#[test] -fn return_ok() { - let exec = FakeExecutive { - output_memory: [0xAD; 512], - fake_error: None, - }; - let model = ml::Model { - output_activations_len: 512, - }; - - let res = exec.run_model(&model); - assert!(res.is_ok()); - assert_eq!(res.unwrap()[0], 0xAD) -} - -#[test] -fn return_err() { - let exec = FakeExecutive { - output_memory: [0; 512], - fake_error: Some(ml::ExecutionError::CoreReset), - }; - let model = ml::Model { - output_activations_len: 512, - }; - - assert!(exec.run_model(&model).is_err()); -} diff --git a/apps/system/components/MlCoordinator/ml-common/src/lib.rs b/apps/system/components/MlCoordinator/ml-common/src/lib.rs index ddca64a..89cdd7d 100644 --- a/apps/system/components/MlCoordinator/ml-common/src/lib.rs +++ b/apps/system/components/MlCoordinator/ml-common/src/lib.rs @@ -1,9 +1,10 @@ // TODO(jesionowski): What are the actual errors we may encounter? #[derive(Copy, Clone, Debug)] pub enum ExecutionError { + ModelNotLoaded, InvalidInstruction, InvalidFetch, - CoreReset + CoreReset, } // The abstraction layer over the "hardware" of running an execution. @@ -12,6 +13,15 @@ pub trait ExecutiveInterface { fn run_model(&self, model: &Model) -> Result<&[u8], ExecutionError>; } +// Options for execution that may be set by the application. +pub struct ModelOptions { + pub rate: u32, +} + +// Immutable model attributes. +#[derive(PartialEq, Debug)] pub struct Model { pub output_activations_len: usize, -} \ No newline at end of file +} + +pub const MAX_MODELS: u32 = 10; diff --git a/apps/system/components/MlCoordinator/fake-executive/Cargo.toml b/apps/system/components/MlCoordinator/ml-coordinator/Cargo.toml similarity index 59% rename from apps/system/components/MlCoordinator/fake-executive/Cargo.toml rename to apps/system/components/MlCoordinator/ml-coordinator/Cargo.toml index 3955d68..188a734 100644 --- a/apps/system/components/MlCoordinator/fake-executive/Cargo.toml +++ b/apps/system/components/MlCoordinator/ml-coordinator/Cargo.toml @@ -1,8 +1,9 @@ [package] -name = "fake-executive" +name = "ml-coordinator" version = "0.1.0" authors = ["Adam Jesionowski "] edition = "2018" [dependencies] -ml-common = { path = "../ml-common" } \ No newline at end of file +ml-common = { path = "../ml-common" } +arrayvec = "0.5.2" \ No newline at end of file diff --git a/apps/system/components/MlCoordinator/ml-coordinator/src/lib.rs b/apps/system/components/MlCoordinator/ml-coordinator/src/lib.rs new file mode 100644 index 0000000..6b2548e --- /dev/null +++ b/apps/system/components/MlCoordinator/ml-coordinator/src/lib.rs @@ -0,0 +1,161 @@ +#![no_std] +use arrayvec::ArrayVec; +use ml_common as ml; +use ml_common::ExecutiveInterface; + +pub struct MlCoordinator<'a> { + executive: &'a dyn ExecutiveInterface, + // Application owns the model struct. + // ML Coordinator owns a reference to the model. + models: ArrayVec<[&'a ml::Model; 10]>, +} + +impl<'a> MlCoordinator<'a> { + // Create a new ML Coordinator instance. + fn new(executive: &'a dyn ExecutiveInterface) -> MlCoordinator<'a> { + let mut models = ArrayVec::<[&ml::Model; 10]>::new(); + + MlCoordinator { + executive: executive, + models: models, + } + } + + // Returns the index of the model if it has already been loaded. + fn has_model(&self, model: &'a ml::Model) -> Option { + self.models.iter().position(|&x| model == x) + } + + // Load the passed model. Returns true if the model was loaded + // successfully, and false if there is no room or the model was already loaded. + // This function will eventually perform validation of the model, create transfer buffers, etc. + #[must_use] + fn load(&mut self, model: &'a ml::Model) -> bool { + if self.models.is_full() || self.has_model(model).is_some() { + return false; + } + self.models.push(model); + true + } + + // Unload the passed model. Returns true if the model was unloaded successfully, + // false if the model was not loaded in the first place. + fn unload(&mut self, model: &'a ml::Model) -> bool { + let res = self.has_model(model); + if let Some(idx) = res { + self.models.remove(idx); + } + res.is_some() + } + + // Execute the passed model. + fn execute(&self, model: &'a ml::Model) -> Result<&'a [u8], ml::ExecutionError> { + self.has_model(model).ok_or(ml::ExecutionError::ModelNotLoaded)?; + self.executive.run_model(model) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + struct FakeExecutive { + output_memory: [u8; 512], + fake_error: Option, + } + + impl ExecutiveInterface for FakeExecutive { + fn run_model(&self, _model: &ml::Model) -> Result<&[u8], ml::ExecutionError> { + match self.fake_error { + Some(err) => Err(err), + None => Ok(&self.output_memory), + } + } + } + + #[test] + fn test_load_unload() { + let exec = tests::FakeExecutive { + output_memory: [0x00; 512], + fake_error: None, + }; + let model = ml::Model { + output_activations_len: 512, + }; + let mut ml_coord = MlCoordinator::new(&exec); + assert!(ml_coord.load(&model)); + assert!(ml_coord.unload(&model)); + } + + #[test] + fn test_unload_noload() { + let exec = tests::FakeExecutive { + output_memory: [0x00; 512], + fake_error: None, + }; + let model = ml::Model { + output_activations_len: 512, + }; + let mut ml_coord = MlCoordinator::new(&exec); + assert_eq!(ml_coord.unload(&model), false); + } + + + #[test] + fn test_unload_twice() { + let exec = tests::FakeExecutive { + output_memory: [0x00; 512], + fake_error: None, + }; + let model = ml::Model { + output_activations_len: 512, + }; + let mut ml_coord = MlCoordinator::new(&exec); + assert!(ml_coord.load(&model)); + assert!(ml_coord.unload(&model)); + assert_eq!(ml_coord.unload(&model), false); + } + + #[test] + fn test_load_twice() { + let exec = tests::FakeExecutive { + output_memory: [0x00; 512], + fake_error: None, + }; + let model = ml::Model { + output_activations_len: 512, + }; + let mut ml_coord = MlCoordinator::new(&exec); + assert!(ml_coord.load(&model)); + assert_eq!(ml_coord.load(&model), false); + } + + #[test] + fn test_execute_noload() { + let exec = tests::FakeExecutive { + output_memory: [0x00; 512], + fake_error: None, + }; + let model = ml::Model { + output_activations_len: 512, + }; + let mut ml_coord = MlCoordinator::new(&exec); + assert!(ml_coord.execute(&model).is_err()); + } + + #[test] + fn test_execute() { + let exec = tests::FakeExecutive { + output_memory: [0xAD; 512], + fake_error: None, + }; + let model = ml::Model { + output_activations_len: 512, + }; + let mut ml_coord = MlCoordinator::new(&exec); + assert!(ml_coord.load(&model)); + + let res = ml_coord.execute(&model); + assert_eq!(res.unwrap()[0], 0xAD) + } +}