Initial version of the Firmware Development Guide
Signed-off-by: Imre Kis <imre.kis@arm.com>
Change-Id: I9820a709e839d6a9df89f24405f4291ea4923d13
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..5ea4392
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,329 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "bitflags"
+version = "2.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
+
+[[package]]
+name = "bumpalo"
+version = "3.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "firmware-development-guide"
+version = "0.1.0"
+dependencies = [
+ "bitflags",
+ "num_enum",
+ "safe-mmio",
+ "spin",
+ "thiserror",
+ "uuid",
+ "zerocopy",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
+
+[[package]]
+name = "indexmap"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "num_enum"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
+dependencies = [
+ "num_enum_derive",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
+dependencies = [
+ "toml_edit",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
+
+[[package]]
+name = "safe-mmio"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db02a82ad13df46afeba34a4e54065fa912308b9101b060e4422898eac0e06f6"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "spin"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6397daf94fa90f058bd0fd88429dd9e5738999cca8d701813c80723add80462"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
+
+[[package]]
+name = "toml_edit"
+version = "0.22.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
+dependencies = [
+ "indexmap",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+
+[[package]]
+name = "uuid"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
+dependencies = [
+ "bumpalo",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "winnow"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..3371034
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,26 @@
+# SPDX-FileCopyrightText: Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
+# SPDX-License-Identifier: MIT OR Apache-2.0
+
+[package]
+name = "firmware-development-guide"
+version = "0.1.0"
+authors = [
+ "Balint Dobszay <balint.dobszay@arm.com>",
+ "Imre Kis <imre.kis@arm.com>",
+]
+description = "Firmware Development Guide"
+edition = "2024"
+license = "MIT OR Apache-2.0"
+repository = "https://git.trustedfirmware.org/rust-spmc/firmware-development-guide.git"
+keywords = ["arm", "aarch64", "firmware", "guide"]
+categories = ["embedded", "no-std", "data-structures", "api-bindings"]
+readme = "README.md"
+
+[dependencies]
+bitflags = "2.9.1"
+num_enum = "0.7.3"
+safe-mmio = "0.2.5"
+spin = "0.10.0"
+thiserror = "2.0.12"
+uuid = "1.17.0"
+zerocopy = { version = "0.8.25", features = ["derive"] }
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..752dea2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,404 @@
+# Firmware Development Guide
+
+The goal of this document is to provide a set of best practices, design guidelines, and tooling
+recommendations for developing firmware in Rust, particularly in `no_std` environments.
+
+## Set `no_std` attribute
+
+Rust's standard library relies on the presence of a rich OS. As a result, it isn’t available in
+firmware environments. Firmware projects or crates should therefore be marked with `no_std` to
+indicate that they do not use the std library.
+
+Insert the following line at the top of `main.rs`/`lib.rs`.
+
+```rust,ignore
+#![no_std]
+```
+
+Tests might rely on the standard library since they are typically executed in a host environment.
+Therefore, the `no_std` attribute should be conditionally applied when the build is not intended for
+testing.
+
+```rust,ignore
+#![cfg_attr(not(test), no_std)]
+```
+
+## Avoid using `alloc`
+
+Using a heap in firmware components can lead to non-deterministic behavior and potential
+fragmentation issues. As a result, many firmware projects choose to avoid using a heap entirely.
+
+Since a heap may not be available, relying on types from the alloc crate - such as `Vec` - can
+restrict the crate's applicability. Instead, prefer using slices for function inputs and outputs
+whenever possible. This approach allows the caller to manage memory allocation using the most
+appropriate storage mechanism for their context.
+
+```rust
+fn numbers_with_alloc(n: usize) -> Vec<usize> {
+ let mut result = Vec::new();
+ for i in 0..n {
+ result.push(i);
+ }
+ result
+}
+
+fn numbers_without_alloc(n: usize, result: &mut [usize]) {
+ for i in 0..n {
+ result[i] = i;
+ }
+}
+
+let result: Vec<usize> = numbers_with_alloc(10);
+
+let mut result: [usize; 10] = [0; 10];
+numbers_without_alloc(10, &mut result);
+
+// But now it still works with Vec too!
+let mut result: Vec<usize> = vec![0; 10];
+numbers_without_alloc(10, result.as_mut_slice());
+```
+
+## Derive or implement common traits for custom types
+
+Rust provides a wide range of traits that allow custom types to integrate seamlessly with the
+core and standard library. For instance, a slice can be sorted if its element type implements the
+`Ord` trait, and if the element type implements `PartialEq`, it’s possible to check whether a slice
+contains a specific item. Implementing these traits for your custom types greatly increases their
+usability. Avoid creating ad-hoc methods for common operations, prefer standard traits like `From`
+or `TryFrom` for type conversions.
+
+There are two main ways to implement a trait: by deriving it or by writing an explicit
+implementation. When a type's fields already implement a trait, using `#[derive]` is often enough
+to generate the trait implementation automatically. However, if the default behavior isn’t
+suitable, you can manually implement the trait to customize its logic.
+
+### Common traits
+
+* `Debug` - Enables printing the value using the `"{:?}"` formatter.
+* `Display` - Enables printing the value using the `"{}"` formatter. `Display` is similar to
+ `Debug`, but `Display` is for user-facing output, and so cannot be derived.
+* `Default` - Provides a `default()` function for creating default instance.
+* `Clone` - Enables explicit `clone()`-ing of the type.
+* `Copy` - Enable implicit copying of the type. Do not use it for large types to avoid unnecessary
+ copying.
+* `PartialEq` - Provides `==` and `!=` operators.
+* `Eq` - The implementor promises that `a == a`, i.e. the type is reflexive. This is not true for
+ floating point numbers where `NaN != Nan`.
+* `PartialOrd` - Provides `<`, `<=`, `>`, and `>=` operators.
+* `Ord` - Trait for types that form a total order.
+* `From<T>` - Used to do value-to-value conversions while consuming the input value. It is the
+ reciprocal of `Into`. Implementing `From` over `Into` is preferred, because `From` automatically
+ provides an `Into` implementation as well.
+* `TryFrom<T>`- Simple and safe type conversions that may fail in a controlled way under some
+ circumstances. It is the reciprocal of `TryInto`.
+
+### Example
+
+```rust
+#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
+struct Address {
+ city: String,
+ street: String,
+ house_number: u32
+}
+
+fn get_addresses() -> Vec<Address> {
+ vec![Address {
+ city: "Cambridge".to_string(),
+ street: "Fulbourn Rd".to_string(),
+ house_number: 110,
+ }]
+}
+
+let mut addresses: Vec<Address> = get_addresses();
+
+// Debug trait
+println!("{:?}", addresses[0]);
+
+// Default trait
+let empty_address = Address::default();
+
+// Clone trait
+let empty_clone = empty_address.clone();
+
+// PartialEq trait
+let has_arm_address = addresses.contains(&Address {
+ city: "Cambridge".to_string(),
+ street: "Fulbourn Rd".to_string(),
+ house_number: 110,
+});
+
+// Ord trait
+addresses.sort();
+```
+
+## Minimize and document unsafe code
+
+Maintaining and reviewing unsafe code demands additional effort, so it's best to keep its usage to
+a minimum. In most cases unsafe code can be avoided by using an existing crate that provides a safe
+wrapper around the unsafe logic. For example, `zerocopy` enables the developer to convert between
+types, along all the necessary size and alignment checks.
+
+During firmware development, writing some unsafe code is often unavoidable. Whenever you use unsafe
+features, clearly document how the necessary conditions for safety are satisfied to justify the use
+of unsafe blocks.
+
+When defining an unsafe function, always specify the safety requirements that callers must uphold to
+use the function correctly.
+
+```rust
+# struct BootInfo(u32);
+
+// SAFETY: The address is mapped statically and nothing else references it.
+let boot_info = unsafe { &*(0x8000_0000 as *const BootInfo) };
+
+/// # Safety
+///
+/// Before calling this function, ensure that all pending writes have been
+/// flushed and there are no references to the address.
+unsafe fn unmap_memory(address: u64) {
+ // [...]
+}
+```
+
+Add the following lines to `Cargo.toml` in order to enforce these rules by Clippy.
+
+```toml
+[lints.clippy]
+missing_safety_doc = "deny"
+undocumented_unsafe_blocks = "deny"
+```
+
+## Do not reinvent the ~~wheel~~ crate
+
+Use popular crates instead of reimplementing a functionality. `cargo` provides great ways of reusing
+code and `crates.io` provides high quality crates. On the other hand, try to minimize the number of
+dependencies for supply chain, code size and build time reasons.
+
+[npm left-pad incident](https://en.wikipedia.org/wiki/Npm_left-pad_incident)
+
+Recommended crates to use in firmware project:
+
+### bitflags
+
+🔗 [bitflags](https://crates.io/crates/bitflags)
+
+Provides a macro to define typesafe bitflags for managing sets of binary flags (single-bit). Not
+suitable for multi-bit fields.
+
+```rust
+bitflags::bitflags! {
+ struct Permissions: u32 {
+ const READ = 0b001;
+ const WRITE = 0b010;
+ const EXECUTE = 0b100;
+ }
+}
+
+let perms = Permissions::READ | Permissions::WRITE;
+let is_executable = perms.contains(Permissions::EXECUTE);
+```
+
+### embedded-hal
+
+🔗 [embedded-hal](https://crates.io/crates/embedded-hal)
+
+Provides hardware abstraction layers for embedded system as a family of crates
+(`embedded-hal`, `embedded-hal-async`, `embedded-hal-nb`, `embedded-io`, etc.). Peripheral drivers
+should implement the suitable traits of these crates in order to increase interoperability.
+
+### num_enum
+
+🔗 [num_enum](https://crates.io/crates/num_enum)
+
+Rust does not have a built-in way of converting from integers to enums. The `num_enum` crate
+provides utilities for defining enums with explicit integer representations and conversions between
+them safely.
+
+```rust
+use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
+use core::convert::TryFrom;
+
+#[derive(Debug, TryFromPrimitive, PartialEq, Eq)]
+#[repr(u8)]
+enum Command {
+ Start = 1,
+ Stop = 2,
+}
+
+assert_eq!(Ok(Command::Start), Command::try_from(1u8));
+assert_eq!(Ok(Command::Stop), Command::try_from(2u8));
+assert!(Command::try_from(3u8).is_err());
+```
+
+### safe-mmio
+
+🔗 [safe-mmio](https://crates.io/crates/safe-mmio)
+
+For MMIO peripheral access use the `safe-mmio` crate. This crates provides primitives for tracking
+the lifetime and ownership of an MMIO peripheral and for defining different types of peripherals
+registers.
+
+```rust,no_run
+use core::ptr::NonNull;
+use safe_mmio::{
+ field,
+ fields::{ReadOnly, ReadPure, ReadWrite, WriteOnly},
+ UniqueMmioPointer,
+};
+
+#[repr(C)]
+struct UartRegisters {
+ data: ReadWrite<u8>,
+ status: ReadPure<u8>,
+ pending_interrupt: ReadOnly<u8>,
+}
+
+// SAFETY:
+// The UART peripherals at 0x900_0000 is statically mapped for the lifetime of
+// the application and nothing else references it.
+let mut uart_registers: UniqueMmioPointer<UartRegisters> =
+ unsafe { UniqueMmioPointer::new(NonNull::new(0x900_0000 as _).unwrap()) };
+
+field!(uart_registers, data).write(b'x');
+```
+
+### spin
+
+🔗 [spin](https://crates.io/crates/spin)
+
+Implements simple spinlocks for `no_std` environments without relying on OS primitives. Use
+`spin::Once` for global/static variable lazy initialization.
+
+```rust
+use spin::Mutex;
+
+static DATA: Mutex<u32> = Mutex::new(0);
+
+fn increment() {
+ let mut data = DATA.lock();
+ *data += 1;
+}
+```
+
+### thiserror
+
+🔗 [thiserror](https://crates.io/crates/thiserror)
+
+This library provides a convenient derive macro for the standard library’s `core::error::Error`
+trait. It makes creating custom error types easier by
+
+```rust
+use thiserror::Error;
+
+#[derive(Debug, Error)]
+enum CustomError {
+ #[error("generic error")]
+ GenericError,
+ #[error("invalid capacity: {0}")]
+ InvalidCapacity(usize),
+ #[error("unknown command index: {0}")]
+ UnknownCommandIndex(u32),
+}
+```
+
+### uuid
+
+🔗 [uuid](https://crates.io/crates/uuid)
+
+Use the `uuid` crate for parsing/handling UUID/GUID values. It provides functions for converting
+between the `Uuid` type and strings, bytes and other representations.
+
+```rust
+use uuid::{uuid, Uuid};
+
+const ID: Uuid = uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8");
+let my_uuid =
+ Uuid::parse_str("67e55044-10b1-426f-9247-bb680e5fe0c8").expect("Failed to parse UUID");
+
+let bytes: &[u8; 16] = my_uuid.as_bytes();
+```
+
+### zerocopy
+
+🔗 [zerocopy](https://crates.io/crates/zerocopy)
+
+Enables zero-copy parsing and serialization of fixed-layout types using safe abstractions. Zerocopy
+ensures that the source and destination types are suitable for conversion and they have a matching
+size and alignment.
+
+```rust
+use zerocopy::{FromBytes, IntoBytes, KnownLayout, Immutable, transmute};
+
+#[derive(FromBytes, IntoBytes, KnownLayout, Immutable)]
+#[repr(C, packed)]
+struct Packet {
+ header: u16,
+ payload: u32,
+}
+
+let raw: [u8; 6] = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
+let packet = Packet::read_from(&raw[..]).unwrap();
+let raw_serialized = packet.as_bytes();
+assert_eq!(raw_serialized, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
+
+let one_dimensional: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
+let two_dimensional: [[u8; 4]; 2] = transmute!(one_dimensional);
+assert_eq!(two_dimensional, [[0, 1, 2, 3], [4, 5, 6, 7]]);
+```
+
+## Recommended tools
+
+### rustfmt
+
+🔗 [rustfmt](https://github.com/rust-lang/rustfmt)
+
+Rust has an official style guide that ensures consistent formatting across the Rust ecosystem.
+`cargo fmt` automatically formats the code according to the official style guide. Most editors can
+integrate rustfmt to format on save.
+
+### Clippy
+
+🔗 [Clippy](https://github.com/rust-lang/rust-clippy)
+
+Clippy is the official Rust linter tool. It can emit various style, performance, etc. issues.
+Always address the issues pointed out by Clippy to improve code quality.
+
+### Cargo Vet
+
+🔗 [Cargo Vet](https://github.com/mozilla/cargo-vet)
+
+Cargo provides an easy way for using third party crates, however this introduces the possibility of
+supply chain attacks. An attacker can take over a dependency or push a malicious change into it.
+Cargo Vet is a tool for supply chain auditing and it only allows the use of audited crate versions.
+
+## Pre-release checklist
+
+Before releasing the crate/project go thought the following action items.
+
+* Update version number in `Cargo.toml`
+* Update readme/changelog
+* Run the following commands
+
+```sh
+cargo update # Update dependencies
+cargo vet # Run audit check
+cargo build # Build project
+cargo test # Build and run tests
+cargo clippy # Run linter
+```
+
+## General advice
+
+* `use` only what's needed.
+* Create `const` variables for magic numbers.
+* Try to limit the visibility of types and functions using `pub`, `pub(crate)`, etc. modifiers.
+* The fields of enum variants inherit the visibility of the type, however this is not true for
+ struct field. Make sure that struct fields have the correct visibility if they are intended to be
+ accessed outside of the defining module.
+
+--------------
+
+*Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>*
+
+*Arm is a registered trademark of Arm Limited (or its subsidiaries or affiliates).*
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..0ac4f43
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,4 @@
+// SPDX-FileCopyrightText: Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+#![doc = include_str!("../README.md")]