diff --git a/src/riscv/lib/src/cache_utils.rs b/src/riscv/lib/src/cache_utils.rs index 84b473da4b1014689220f82d3c91fd5cfce69944..4ad7331ffcc5f650e4cdcbc52005922ca2cc0b0b 100644 --- a/src/riscv/lib/src/cache_utils.rs +++ b/src/riscv/lib/src/cache_utils.rs @@ -5,7 +5,9 @@ use crate::state_backend::Elem; /// Integer to keep track of the fence counter -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive( + Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize, +)] pub struct FenceCounter(pub u32); impl FenceCounter { diff --git a/src/riscv/lib/src/machine_state/bus/main_memory.rs b/src/riscv/lib/src/machine_state/bus/main_memory.rs index 2035ddc3fccaeeb9b0e2f1e53a78bbc4a781488c..2a75b38472a0880cd7fc6d62abe256e88d1ff4a7 100644 --- a/src/riscv/lib/src/machine_state/bus/main_memory.rs +++ b/src/riscv/lib/src/machine_state/bus/main_memory.rs @@ -3,7 +3,8 @@ // SPDX-License-Identifier: MIT use super::{Address, AddressableRead, AddressableWrite}; -use crate::state_backend::{self as backend}; +use crate::state_backend::{self as backend, ManagerDeserialise, ManagerSerialise}; +use serde::{Deserialize, Serialize}; use std::mem; /// Configuration object for memory size @@ -74,6 +75,17 @@ pub trait MainMemoryLayout: backend::Layout { address: usize, values: &[E], ); + + /// Serialise main memory's dynamic region. + fn serialise_data( + data: &Self::Data, + serializer: S, + ) -> Result; + + /// Deserialise main memory's dynamic region. + fn deserialise_data<'de, M: ManagerDeserialise, D: serde::Deserializer<'de>>( + deserializer: D, + ) -> Result, D::Error>; } impl MainMemoryLayout for Sizes { @@ -123,6 +135,19 @@ impl MainMemoryLayout for Sizes { ) { data.write_all(address, values); } + + fn serialise_data( + data: &Self::Data, + serializer: S, + ) -> Result { + data.serialize(serializer) + } + + fn deserialise_data<'de, M: ManagerDeserialise, D: serde::Deserializer<'de>>( + deserializer: D, + ) -> Result, D::Error> { + serde::Deserialize::deserialize(deserializer) + } } impl backend::Layout for Sizes { @@ -225,6 +250,29 @@ impl Addressabl } } +impl Serialize for MainMemory { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + L::serialise_data(&self.data, serializer) + } +} + +impl<'de, L, M> Deserialize<'de> for MainMemory +where + L: MainMemoryLayout, + M: ManagerDeserialise, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let data = L::deserialise_data(deserializer)?; + Ok(Self { data }) + } +} + #[cfg(test)] pub mod tests { use crate::{ diff --git a/src/riscv/lib/src/machine_state/csregisters/values.rs b/src/riscv/lib/src/machine_state/csregisters/values.rs index 0493ab1ae019e296b7bbb9d73b697169b136b069..5b46c9e78d8611d7290e6f06551b45702d77fc26 100644 --- a/src/riscv/lib/src/machine_state/csregisters/values.rs +++ b/src/riscv/lib/src/machine_state/csregisters/values.rs @@ -161,7 +161,7 @@ impl Layout for CSRValuesLayout { } } -#[derive(Default, Debug, Clone, PartialEq, Eq)] +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct CSRValuesF { pub mstatus: MStatus, pub mnscratch: Raw, diff --git a/src/riscv/lib/src/machine_state/csregisters/values/mstatus.rs b/src/riscv/lib/src/machine_state/csregisters/values/mstatus.rs index 295a65626d3b30c5d20b6e243cb6928827b6f8ea..4459962ed9807fb353ef50b055534f71b8cdcde9 100644 --- a/src/riscv/lib/src/machine_state/csregisters/values/mstatus.rs +++ b/src/riscv/lib/src/machine_state/csregisters/values/mstatus.rs @@ -72,7 +72,7 @@ impl MStatusValue { /// Obtain a structure with references to the bound regions of this type. pub fn struct_ref(&self) -> AllocatedOf> { - MStatusLayoutAllocated { + MStatusLayoutF { sie: self.sie.struct_ref(), mie: self.mie.struct_ref(), spie: self.spie.struct_ref(), diff --git a/src/riscv/lib/src/machine_state/instruction_cache.rs b/src/riscv/lib/src/machine_state/instruction_cache.rs index b547888e7ee22bf9c2a3cf1ceff10088558d5c2d..8aea926797783e8d2cc235e5fd85395fbc9a3bbc 100644 --- a/src/riscv/lib/src/machine_state/instruction_cache.rs +++ b/src/riscv/lib/src/machine_state/instruction_cache.rs @@ -98,7 +98,7 @@ impl Cached { /// /// Compressed instructions are represented as the lower-16 bits of the u32, with upper-16 bits /// set to zero. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[repr(transparent)] pub struct Unparsed(u32); diff --git a/src/riscv/lib/src/machine_state/registers.rs b/src/riscv/lib/src/machine_state/registers.rs index e4eec482e6cf0855a4df35805b197c6d7ff074b4..dddc87fc9e7b821a2aaa3aefa109a07d8743ca42 100644 --- a/src/riscv/lib/src/machine_state/registers.rs +++ b/src/riscv/lib/src/machine_state/registers.rs @@ -335,7 +335,16 @@ impl fmt::Display for FRegister { /// Floating-point number register value #[repr(transparent)] #[derive( - Clone, Copy, PartialEq, PartialOrd, Default, Debug, derive_more::From, derive_more::Into, + Clone, + Copy, + PartialEq, + PartialOrd, + Default, + Debug, + derive_more::From, + derive_more::Into, + serde::Serialize, + serde::Deserialize, )] pub struct FValue(u64); diff --git a/src/riscv/lib/src/state_backend.rs b/src/riscv/lib/src/state_backend.rs index c23b7054acf6a50349f6dbe2c491717bde18a7a8..b4649115cddd7994b5207e8a3a5c48ca6b1a48e6 100644 --- a/src/riscv/lib/src/state_backend.rs +++ b/src/riscv/lib/src/state_backend.rs @@ -171,6 +171,39 @@ pub trait ManagerReadWrite: ManagerRead + ManagerWrite { ) -> E; } +/// Manager with the ability to serialise regions +pub trait ManagerSerialise: ManagerBase { + /// Serialise the contents of the region. + fn serialise_region( + region: &Self::Region, + serializer: S, + ) -> Result; + + /// Serialise the contents of the dynamic region. + fn serialise_dyn_region( + region: &Self::DynRegion, + serializer: S, + ) -> Result; +} + +/// Manager with the ability to deserialise regions +pub trait ManagerDeserialise: ManagerBase { + /// Deserialise a region. + fn deserialise_region< + 'de, + E: serde::Deserialize<'de> + Elem, + const LEN: usize, + D: serde::Deserializer<'de>, + >( + deserializer: D, + ) -> Result, D::Error>; + + /// Deserialise the dyanmic region. + fn deserialise_dyn_region<'de, const LEN: usize, D: serde::Deserializer<'de>>( + deserializer: D, + ) -> Result, D::Error>; +} + /// State backend with manager pub trait BackendManagement { /// Backend manager @@ -216,6 +249,22 @@ impl<'backend, M: ManagerBase> ManagerBase for Ref<'backend, M> { type DynRegion = &'backend M::DynRegion; } +impl ManagerSerialise for Ref<'_, M> { + fn serialise_region( + region: &Self::Region, + serializer: S, + ) -> Result { + M::serialise_region(region, serializer) + } + + fn serialise_dyn_region( + region: &Self::DynRegion, + serializer: S, + ) -> Result { + M::serialise_dyn_region(region, serializer) + } +} + pub mod test_helpers { use super::{BackendFull, Layout}; use std::fmt; diff --git a/src/riscv/lib/src/state_backend/layout.rs b/src/riscv/lib/src/state_backend/layout.rs index 14447488ad83217336af6204b030028a4774a078..6a570e9161ea69fbfab7d0cfabb1004176b45526 100644 --- a/src/riscv/lib/src/state_backend/layout.rs +++ b/src/riscv/lib/src/state_backend/layout.rs @@ -108,25 +108,38 @@ macro_rules! struct_layout { $( , )? }) => { paste::paste! { - $vis struct $layout_t { - $( [<_$field_name>]: $cell_repr ),+ + #[derive(serde::Deserialize, serde::Serialize)] + $vis struct [<$layout_t F>]< + $( + [<$field_name:upper>] + ),+ + > { + $( + $field_name: [<$field_name:upper>] + ),+ } - pub struct [<$layout_t Placed>] { - $( $field_name: $crate::state_backend::PlacedOf<$cell_repr> ),+ - } - - pub struct [<$layout_t Allocated>] { - $( $field_name: AllocatedOf<$cell_repr, M> ),+ - } + $vis type $layout_t = [<$layout_t F>]< + $( + $cell_repr + ),+ + >; impl $crate::state_backend::Layout for $layout_t { - type Placed = [<$layout_t Placed>]; - - type Allocated = [<$layout_t Allocated>]; + type Placed = [<$layout_t F>]< + $( + $crate::state_backend::PlacedOf<$cell_repr> + ),+ + >; + + type Allocated = [<$layout_t F>]< + $( + AllocatedOf<$cell_repr, M> + ),+ + >; fn place_with(alloc: &mut $crate::state_backend::Choreographer) -> Self::Placed { - [<$layout_t Placed>] { + Self::Placed { $($field_name: <$cell_repr>::place_with(alloc)),+ } } diff --git a/src/riscv/lib/src/state_backend/memory_backend.rs b/src/riscv/lib/src/state_backend/memory_backend.rs index 2e0569d1599e1e1523a39dfdd36f70e05228ae1a..a28d94ae9e4fffb1986672a731d717d96fe6fb44 100644 --- a/src/riscv/lib/src/state_backend/memory_backend.rs +++ b/src/riscv/lib/src/state_backend/memory_backend.rs @@ -3,6 +3,7 @@ // SPDX-License-Identifier: MIT use crate::state_backend::{self as backend, Layout, ManagerReadWrite}; +use serde::ser::SerializeTuple; use std::{alloc, marker::PhantomData, mem, ptr, slice}; /// In-memory state backend @@ -379,6 +380,32 @@ impl ManagerReadWrite for SliceManager<'_> { } } +impl backend::ManagerSerialise for SliceManager<'_> { + fn serialise_region< + E: serde::Serialize + backend::Elem, + const LEN: usize, + S: serde::Serializer, + >( + region: &Self::Region, + serializer: S, + ) -> Result { + let mut serializer = serializer.serialize_tuple(LEN)?; + + for elem in region.iter() { + serializer.serialize_element(elem)?; + } + + serializer.end() + } + + fn serialise_dyn_region( + region: &Self::DynRegion, + serializer: S, + ) -> Result { + serializer.serialize_bytes(region.as_slice()) + } +} + /// Read-only manager for in-memory backing storage pub struct SliceManagerRO<'backend> { backing_storage: usize, @@ -508,6 +535,32 @@ impl<'backend> backend::ManagerRead for SliceManagerRO<'backend> { } } +impl backend::ManagerSerialise for SliceManagerRO<'_> { + fn serialise_region< + E: serde::Serialize + backend::Elem, + const LEN: usize, + S: serde::Serializer, + >( + region: &Self::Region, + serializer: S, + ) -> Result { + let mut serializer = serializer.serialize_tuple(LEN)?; + + for elem in region.iter() { + serializer.serialize_element(elem)?; + } + + serializer.end() + } + + fn serialise_dyn_region( + region: &Self::DynRegion, + serializer: S, + ) -> Result { + serializer.serialize_bytes(region.as_slice()) + } +} + pub mod test_helpers { use super::InMemoryBackend; use crate::state_backend::{test_helpers::TestBackendFactory, Layout}; diff --git a/src/riscv/lib/src/state_backend/owned_backend.rs b/src/riscv/lib/src/state_backend/owned_backend.rs index 384f93aba8bf3850b56ff0500796946aba3b5158..499e3f687280139fedd354077776e735ac104cc6 100644 --- a/src/riscv/lib/src/state_backend/owned_backend.rs +++ b/src/riscv/lib/src/state_backend/owned_backend.rs @@ -3,10 +3,15 @@ // SPDX-License-Identifier: MIT use super::{ - AllocatedOf, Elem, Layout, Location, ManagerAlloc, ManagerBase, ManagerRead, ManagerReadWrite, - ManagerWrite, + AllocatedOf, Elem, Layout, Location, ManagerAlloc, ManagerBase, ManagerDeserialise, + ManagerRead, ManagerReadWrite, ManagerSerialise, ManagerWrite, +}; +use serde::ser::SerializeTuple; +use std::{ + fmt, + marker::PhantomData, + mem::{self, MaybeUninit}, }; -use std::mem; /// Manager that allows state binders to own the state storage pub struct Owned; @@ -164,3 +169,144 @@ impl ManagerReadWrite for Owned { mem::replace(&mut region[index], value) } } + +impl ManagerSerialise for Owned { + fn serialise_region( + region: &Self::Region, + serializer: S, + ) -> Result { + // We're serialising this as a fixed-sized tuple because otherwise `bincode` would prefix + // the length of this array, which is not needed. + let mut serializer = serializer.serialize_tuple(LEN)?; + + for item in region.iter() { + serializer.serialize_element(item)?; + } + + serializer.end() + } + + fn serialise_dyn_region( + region: &Self::DynRegion, + serializer: S, + ) -> Result { + serializer.serialize_bytes(region.as_slice()) + } +} + +impl ManagerDeserialise for Owned { + fn deserialise_region< + 'de, + E: serde::de::Deserialize<'de> + Elem, + const LEN: usize, + D: serde::de::Deserializer<'de>, + >( + deserializer: D, + ) -> Result, D::Error> { + struct Inner(PhantomData); + + impl<'de, E: serde::Deserialize<'de> + Sized, const LEN: usize> serde::de::Visitor<'de> + for Inner + { + type Value = [E; LEN]; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "{}", std::any::type_name::()) + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut values: [MaybeUninit; LEN] = + std::array::from_fn(|_| MaybeUninit::uninit()); + + for value in values.iter_mut() { + value.write(seq.next_element::()?.ok_or_else(|| { + serde::de::Error::custom(format!( + "Not enough elements to construct {}", + std::any::type_name::() + )) + })?); + } + + // We can't use `std::mem::transmute` here because `[_; LEN]` does not have a fixed + // size according to the compiler. I suspect this because `LEN` is a const generic + // parameter. + Ok(values.map(|value| { + // SAFETY: We've initialised all the elements in the array. + unsafe { value.assume_init() } + })) + } + } + + deserializer.deserialize_tuple(LEN, Inner(PhantomData)) + } + + fn deserialise_dyn_region<'de, const LEN: usize, D: serde::de::Deserializer<'de>>( + deserializer: D, + ) -> Result, D::Error> { + let vec: Vec = serde::Deserialize::deserialize(deserializer)?; + vec.try_into() + .map_err(|_err| serde::de::Error::custom("Dynamic region of mismatching length")) + } +} + +#[cfg(test)] +pub mod test_helpers { + use super::*; + use crate::state_backend::{Cell, Cells, DynCells}; + + /// Ensure [`Cell`] can be serialised and deserialised in a consistent way. + #[test] + fn cell_serialise() { + proptest::proptest!(|(value: u64)|{ + let cell: Cell = Cell::bind([value; 1]); + let bytes = bincode::serialize(&cell).unwrap(); + + let cell_after: Cell = bincode::deserialize(&bytes).unwrap(); + assert_eq!(cell.read(), cell_after.read()); + + let bytes_after = bincode::serialize(&cell_after).unwrap(); + assert_eq!(bytes, bytes_after); + }); + } + + /// Ensure [`Cells`] can be serialised and deserialised in a consistent way. + #[test] + fn cells_serialise() { + proptest::proptest!(|(a: u64, b: u64, c: u64)|{ + let cell: Cells = Cells::bind([a, b, c]); + let bytes = bincode::serialize(&cell).unwrap(); + + let cell_after: Cells = bincode::deserialize(&bytes).unwrap(); + + assert_eq!(cell.read_all(), cell_after.read_all()); + + for i in 0..3 { + assert_eq!(cell.read(i), cell_after.read(i)); + } + + let bytes_after = bincode::serialize(&cell_after).unwrap(); + assert_eq!(bytes, bytes_after); + }); + } + + /// Ensure [`DynCells`] can be serialised and deserialised in a consistent way. + #[test] + fn dyn_cells_serialise() { + proptest::proptest!(|(address in (0usize..120), value: u64)|{ + let mut cells: DynCells<128, Owned> = DynCells::bind(Box::new([0u8; 128])); + cells.write(address, value); + let bytes = bincode::serialize(&cells).unwrap(); + + let cells_after: DynCells<128, Owned> = bincode::deserialize(&bytes).unwrap(); + for i in 0..128 { + assert_eq!(cells.read::(i), cells_after.read::(i)); + } + + let bytes_after = bincode::serialize(&cells_after).unwrap(); + assert_eq!(bytes, bytes_after); + }); + } +} diff --git a/src/riscv/lib/src/state_backend/region.rs b/src/riscv/lib/src/state_backend/region.rs index 564af2a6e2f27fb5b83c1714e5aca7cfb6f23aad..20f046fd745bbcdfbb53ac92497cfe9d1909093a 100644 --- a/src/riscv/lib/src/state_backend/region.rs +++ b/src/riscv/lib/src/state_backend/region.rs @@ -3,7 +3,8 @@ // SPDX-License-Identifier: MIT use super::{ - AllocatedOf, Array, Atom, Elem, ManagerBase, ManagerRead, ManagerReadWrite, ManagerWrite, Ref, + AllocatedOf, Array, Atom, Elem, ManagerBase, ManagerDeserialise, ManagerRead, ManagerReadWrite, + ManagerSerialise, ManagerWrite, Ref, }; /// Single element of type `E` @@ -55,6 +56,27 @@ impl Cell { } } +impl serde::Serialize for Cell { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.region.serialize(serializer) + } +} + +impl<'de, E: serde::Deserialize<'de> + Elem, M: ManagerDeserialise> serde::Deserialize<'de> + for Cell +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let region = Cells::deserialize(deserializer)?; + Ok(Self { region }) + } +} + /// A [Cell] wrapper that holds an additional in-memory /// representation of the stored value. This value is lazily /// constructed on the first [read] or [replace]. @@ -305,6 +327,29 @@ impl Cells { } } +impl serde::Serialize + for Cells +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + M::serialise_region(&self.region, serializer) + } +} + +impl<'de, E: serde::Deserialize<'de> + Elem, const LEN: usize, M: ManagerDeserialise> + serde::Deserialize<'de> for Cells +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let region = M::deserialise_region(deserializer)?; + Ok(Self { region }) + } +} + /// Multiple elements of an unspecified type pub struct DynCells { region: M::DynRegion, @@ -358,6 +403,25 @@ impl DynCells { } } +impl serde::Serialize for DynCells { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + M::serialise_dyn_region(&self.region, serializer) + } +} + +impl<'de, const LEN: usize, M: ManagerDeserialise> serde::Deserialize<'de> for DynCells { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let region = M::deserialise_dyn_region(deserializer)?; + Ok(DynCells { region }) + } +} + #[cfg(test)] pub(crate) mod tests { use crate::{