### Install Project Dependencies Source: https://github.com/diondokter/device-driver/blob/master/website/README.md Run this command to install the necessary Node.js packages for the project. This should be done before building or serving. ```bash npm install ``` -------------------------------- ### Install device-driver-cli Source: https://github.com/diondokter/device-driver/blob/master/compiler/dd-cli/README.md Install the device-driver-cli tool using cargo. ```sh > cargo install device-driver-cli ``` -------------------------------- ### Install device-driver-cli Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/using-the-cli.md Install the CLI tool using cargo. This command ensures you have the latest version of the CLI available for use. ```bash cargo install device-driver-cli ``` -------------------------------- ### Define a full ref using manifest with cfg and description Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/refs.md This JSON example illustrates a full ref definition in the manifest, including a description, conditional compilation via 'cfg', and overrides. ```json "Foo": { "type": "register", "address": 3, "size_bits": 16, "fields": { "value": { "base": "uint", "start": 0, "end": 16 } } }, "Bar": { "type": "ref", "target": "Foo", "description": "This is a copy of Foo, but now with address 5!", "cfg": "feature = \"bar-enabled\"", "override": { "type": "register", "address": 3, } } ``` -------------------------------- ### Defining Commands using DSL Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/commands.md Examples of defining commands using the project's DSL, ranging from minimal definitions to full configurations with metadata and field definitions. ```rust command Foo = 5, ``` ```rust command Foo { const ADDRESS = 5; const SIZE_BITS_IN = 8; const SIZE_BITS_OUT = 16; in { value: uint = 0..8, }, out { value: uint = 0..16, } }, ``` ```rust /// Foo docs #[cfg(feature = "blah")] command Foo { type ByteOrder = LE; type BitOrder = LSB0; const ADDRESS = 5; const SIZE_BITS_IN = 8; const SIZE_BITS_OUT = 16; const REPEAT = { count: 4, stride: 2 }; const ALLOW_BIT_OVERLAP = false; const ALLOW_ADDRESS_OVERLAP = false; in { value: uint = 0..8, }, out { value: uint = 0..16, } }, ``` -------------------------------- ### Defining Registers with Manifest Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/registers.md Minimal and full examples for defining registers using JSON manifest format. ```json "Foo": { "type": "register", "address": 3, "size_bits": 16, "fields": { "value": { "base": "uint", "start": 0, "end": 16 } } } ``` ```json "Foo": { "type": "register", "cfg": "feature = \"foo\"", "description": "Register docs", "access": "WO", "byte_order": "LE", "bit_order": "LSB0", "address": 3, "size_bits": 16, "reset_value": 4066, // Or [52, 18] (no hex in json...) "repeat": { "count": 4, "stride": 2 }, "allow_bit_overlap": false, "allow_address_overlap": false, "fields": { "value": { "base": "uint", "start": 0, "end": 16 } } } ``` -------------------------------- ### Define a full ref using DSL with cfg Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/refs.md This example demonstrates a full ref definition in the DSL, including a doc comment and conditional compilation using #[cfg]. ```rust register Foo { const ADDRESS = 3; const SIZE_BITS = 16; value: uint = 0..16, }, /// This is a copy of Foo, but now with address 5! #[cfg(feature = "bar-enabled")] ref Bar = register Foo { const ADDRESS = 5; }, ``` -------------------------------- ### Defining Registers with DSL Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/registers.md Minimal and full examples for defining registers using the DSL syntax. ```rust register Foo { const ADDRESS = 3; const SIZE_BITS = 16; value: uint = 0..16, } ``` ```rust /// Register docs #[cfg(feature = "bar")] register Foo { type Access = WO; type ByteOrder = LE; type BitOrder = LSB0; const ADDRESS = 3; const SIZE_BITS = 16; const RESET_VALUE = 0x1234; // Or [0x34, 0x12] const REPEAT = { count: 4, stride: 2 }; const ALLOW_BIT_OVERLAP = false; const ALLOW_ADDRESS_OVERLAP = false; value: uint = 0..16, } ``` -------------------------------- ### Defining Commands using Manifest Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/commands.md Examples of defining commands using JSON manifests, which require an explicit type field and support similar configuration options as the DSL. ```json "Foo": { "type": "command", "address": 5 } ``` ```json "Foo": { "type": "command", "address": 5, "size_bits_in": 8, "fields_in": { "value": { "base": "uint", "start": 0, "end": 8 } }, "size_bits_out": 16, "fields_out": { "value": { "base": "uint", "start": 0, "end": 16 } }, } ``` ```json "Foo": { "type": "command", "cfg": "feature = \"blah\"", "description": "Foo docs", "byte_order": "LE", "bit_order": "LSB0", "address": 5, "repeat": { "count": 4, "stride": 2 }, "allow_bit_overlap": false, "allow_address_overlap": false, "size_bits_in": 8, "fields_in": { "value": { "base": "uint", "start": 0, "end": 8 } }, "size_bits_out": 16, "fields_out": { "value": { "base": "uint", "start": 0, "end": 16 } }, } ``` -------------------------------- ### Rust API: Initialize and Write Register Source: https://github.com/diondokter/device-driver/blob/master/website/pages/home/index.html Initialize a device instance and write to a register using the generated Rust API. This example demonstrates setting a register value with an enum. ```rust // Create device instance let mut device = MyDevice::new(DeviceInterface::new()); // Write a register device.foo().write(|reg| reg.set_value_1(MyEnum::B))?; ``` -------------------------------- ### Define a minimal ref using DSL Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/refs.md This example shows the minimal syntax for defining a ref in the DSL, overriding the ADDRESS of a register. ```rust register Foo { const ADDRESS = 3; const SIZE_BITS = 16; value: uint = 0..16, }, ref Bar = register Foo { const ADDRESS = 5; }, ``` -------------------------------- ### YAML Register Definition Example Source: https://github.com/diondokter/device-driver/blob/master/book/src/preface.md This snippet shows an example of defining a register in YAML format, including its type, address, size, reset value, and fields with their respective properties and descriptions. Useful for configuring hardware registers. ```yaml SYNT: type: register address: 0x05 size_bits: 32 reset_value: 0x42162762 fields: PLL_CP_ISEL: base: uint start: 29 end: 32 description: Set the charge pump current according to the XTAL frequency (see Table 37. Table 34). BS: base: bool start: 28 description: | Synthesizer band select. This parameter selects the out-of loop divide factor of the synthesizer: - false: 4, band select factor for high band - true: 8, band select factor for middle band (see Section 5.3.1 RF channel frequency settings). SYNT: base: uint start: 0 end: 28 description: The PLL programmable divider (see Section 5.3.1 RF channel frequency settings). ``` -------------------------------- ### Install wasm-pack Globally Source: https://github.com/diondokter/device-driver/blob/master/website/README.md Install the wasm-pack tool globally using npm. This is a required dependency for building WebAssembly projects. ```bash npm install -g wasm-pack ``` -------------------------------- ### Global Config Manifest (JSON) Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/global-config.md Specifies global configuration settings in a manifest format (JSON example shown). This mirrors the DSL settings for access types, address types, byte/bit order, and name transformations. ```json "config": { "default_register_access": "RW", "default_field_access": "RW", "default_buffer_access": "RW", "default_byte_order": "_", "default_bit_order": "LSB0", "register_address_type": "_", "command_address_type": "_", "buffer_address_type": "_", "name_word_boundaries": [ "Underscore", "Hyphen", "Space", "LowerUpper", "UpperDigit", "DigitUpper", "DigitLower", "LowerDigit", "Acronym" ], "defmt_feature": "my-feature" } ``` -------------------------------- ### Manifest Address Range Fields Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/field-sets.md In the manifest, use 'start' and 'end' fields to define the bit range. 'start' is the beginning bit, and 'end' is the exclusive end bit. ```json { "start": 0, "end": 16 } ``` -------------------------------- ### Define Device Driver in DDSL Source: https://github.com/diondokter/device-driver/blob/master/website/pages/home/index.html Define device registers, commands, and buffers using the DDSL language. This example shows how to define a register with fields and an enum. ```ddsl device Foo { register-address-type: u8, /// Doc comments get reflected in the output code! register Bar { address: 0, fields: fieldset BarFields { size-bytes: 1, field xena 1:0 -> u8 as enum Xena { A: _, B: _, C: default 3, }, field quux 7:2 -> u8, } } } ``` -------------------------------- ### DDSL Compiler Error Example Source: https://github.com/diondokter/device-driver/blob/master/website/pages/home/index.html Example of a DDSL compiler error message indicating an invalid fieldset reference. ```text error: invalid fieldset reference ╭▸ input.ddsl:21:17 │ 5 │ register Bar { │ ─── reference points to non-fieldset object ‡ 21 │ fields: Bar ╰╴ ━━━ no fieldset found with this name ``` -------------------------------- ### Display CLI Help Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/using-the-cli.md View all available options and commands for the device-driver CLI by running the help command. ```bash device-driver-cli --help ``` -------------------------------- ### Build Project Source: https://github.com/diondokter/device-driver/blob/master/website/README.md Execute this command to build the project. This command compiles the source code into a production-ready format. ```bash npm run build ``` -------------------------------- ### Create a device with the device_driver macro Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/writing-an-interface.md Initializes a device structure using the device_driver::create_device! macro. ```rust device_driver::create_device!( device_name: MyDevice, dsl: { // ... } ); ``` -------------------------------- ### Dispatching Commands in Rust Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/commands.md Demonstrates how to invoke commands on a device instance, including handling input data via closures and processing output. ```rust let mut device = MyDevice::new(DeviceInterface::new()); device.foo().dispatch().unwrap(); // Commands can carry data too let result = device.bar().dispatch(|data| data.set_val(1234)).unwrap(); assert_eq!(result.xeno(), true); ``` -------------------------------- ### Main Entry Point Source: https://github.com/diondokter/device-driver/blob/master/tests/ui/src/output_header.txt Standard Rust main function with strict warning denials and configuration allowances. ```rust #!/usr/bin/env cargo --- --- #![deny(warnings)] #![allow(unexpected_cfgs)] fn main() {} ``` -------------------------------- ### Serve Development Build Source: https://github.com/diondokter/device-driver/blob/master/website/README.md Use this command to serve a development build of the project. This is useful for testing and debugging during development. ```bash npm run serve ``` -------------------------------- ### Check device-driver-cli options Source: https://github.com/diondokter/device-driver/blob/master/compiler/dd-cli/README.md View the available options and commands for the device-driver-cli tool. ```sh ddc --help ``` -------------------------------- ### Accessing Device Blocks Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/blocks.md Demonstrates how to instantiate a root block and access child blocks via generated functions. ```rust // MyDevice is the root block let mut device = MyDevice::new(DeviceInterface::new()); let mut child_block = device.foo(); child_block.bar().dispatch().unwrap(); // Or in one go device.foo().bar().dispatch().unwrap(); ``` -------------------------------- ### Defining Blocks with Manifest Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/blocks.md Shows minimal and full block definitions using JSON manifests. ```json "Foo": { "type": "block", "objects": { "Bar": { "type": "buffer", "address": 0 } } } ``` ```json "Foo": { "type": "block", "cfg": "not(blah)", "description": "Block description", "address_offset": 10, "repeat": { "count": 2, "stride": 20, }, "objects": { "Bar": { "type": "buffer", "address": 0 } } } ``` -------------------------------- ### Create Device with Manifest File Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/using-the-macro.md Use this form to define device registers using an external manifest file. The path can be absolute or relative to the `CARGO_MANIFEST_DIR`. ```rust device_driver::create_device!( device_name: MyTestDevice, manifest: "driver-manifest.yaml" ) ``` -------------------------------- ### Create Device with Inline DSL Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/using-the-macro.md Use this form to define device registers using the DSL directly within your project's source code. Ensure the `device_name` is in PascalCase. ```rust device_driver::create_device!( device_name: MyTestDevice, dsl: { // DSL code goes here } ) ``` -------------------------------- ### Manifest Syntax Overview Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md This section provides an overview of the manifest syntax, including pre-defined types and the top-level structure. ```APIDOC ## Manifest Syntax Overview This document describes the manifest syntax for the device driver project. The syntax is manually written and may differ from the implementation. Any discrepancies should be reported as bugs. ### Pre-defined Types The following are the pre-defined types available for use in the manifest: - `bool`: Boolean type. - `uint`: Unsigned integer type. - `int`: Signed integer type. - `float`: Floating-point number type. - `array`: Represents an array. Uses `[]` brackets. Can specify inner types, e.g., `[float]`. - `map`: Represents a map or dictionary. Uses `{}` brackets. Keys are always strings. Restrictions can be denoted using `?` for optional fields and type restrictions, e.g., `{ foo?, bar?: float, xen: bool, *: bool }`. Further restrictions can be applied using `oneof()`, for example: `int oneof(1, 2, 3, 4)` or `oneof(bool, int)`. ``` -------------------------------- ### Initialize and Access Field Set Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/field-sets.md Demonstrates creating a new field set, setting a field's value, and retrieving it. Requires importing the relevant field set struct. ```rust use field_sets::MyFieldSet; let mut reg = MyFieldSet::new(); reg.set_foo(1234); let foo = reg.foo(); ``` -------------------------------- ### Command Object (_Command_) Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Defines a hardware command, including input and output fields. ```APIDOC ## Command Object (_Command_) This type defines a hardware command, which can have input and output data. ### _Command_ ```json { type: string oneof("command"), cfg?: string, description?: string, byte_order?: _ByteOrder_, bit_order?: _BitOrder_, address: int, repeat?: _Repeat_, allow_bit_overlap?: bool, allow_address_overlap?: bool, size_bits_in?: int, fields_in?: { *: _Field_ }, size_bits_out?: int, fields_out?: { *: _Field_ }, } ``` - `type` (string): Must be `"command"`. - `cfg` (string): Optional configuration string. - `description` (string): Optional description. - `byte_order` (_ByteOrder_): Byte order for command data. - `bit_order` (_BitOrder_): Bit order for command data. - `address` (int): The address associated with the command. - `repeat` (_Repeat_): Optional definition for repeating the command. - `allow_bit_overlap` (bool): Whether bit overlap is allowed. - `allow_address_overlap` (bool): Whether address overlap is allowed. - `size_bits_in` (int): Size in bits for input data. - `fields_in` (map): Fields for the command input. - `size_bits_out` (int): Size in bits for output data. - `fields_out` (map): Fields for the command output. ``` -------------------------------- ### Define Buffers using Manifest Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/buffers.md Define buffers using a JSON manifest structure. ```json "Foo": { "type": "buffer", "address": 5 }, ``` ```json "Foo": { "type": "buffer", "cfg": "bar", "description": "A foo buffer", "access": "WO", "address": 5 }, ``` -------------------------------- ### Define Device Interface with Macro DSL Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/intro.md Use the `create_device!` macro to define device structures, registers, and their properties. This macro handles the generation of safe and documented APIs for interacting with hardware registers. Ensure the `device_interface` is provided when creating a new device instance. ```rust device_driver::create_device!( device_name: MyDevice, dsl: { config { type RegisterAddressType = u8; } /// This is the Foo register register Foo { const ADDRESS = 0; const SIZE_BITS = 8; /// This is a bool at bit 0! value0: bool = 0, /// Integrated enum generation value1: int as enum GeneratedEnum { A, /// Variant B B, C = default, } = 1..4, /// This is a 4-bit integer value2: uint = 4..8, }, } ); let mut device = MyDevice::new(device_interface); device.foo().write(|reg| reg.set_value_1(GeneratedEnum::B)).unwrap(); ``` -------------------------------- ### Global Configuration (_GlobalConfig_) Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Defines global configuration settings applicable to the device. ```APIDOC ## Global Configuration (_GlobalConfig_) This type defines global configuration settings for a device. ### _GlobalConfig_ ```json { default_register_access?: _Access_, default_field_access?: _Access_, default_buffer_access?: _Access_, default_byte_order?: _ByteOrder_, default_bit_order?: _BitOrder_, register_address_type?: _IntegerType_, command_address_type?: _IntegerType_, buffer_address_type?: _IntegerType_, name_word_boundaries?: _NameWordBoundaries_ defmt_feature?: string } ``` - `default_register_access` (_Access_): Default access mode for registers. - `default_field_access` (_Access_): Default access mode for fields. - `default_buffer_access` (_Access_): Default access mode for buffers. - `default_byte_order` (_ByteOrder_): Default byte order (Little Endian or Big Endian). - `default_bit_order` (_BitOrder_): Default bit order (LSB0 or MSB0). - `register_address_type` (_IntegerType_): Default integer type for register addresses. - `command_address_type` (_IntegerType_): Default integer type for command addresses. - `buffer_address_type` (_IntegerType_): Default integer type for buffer addresses. - `name_word_boundaries` (_NameWordBoundaries_): Specifies word boundaries for naming. - `defmt_feature` (string): Optional feature flag for defmt. ``` -------------------------------- ### Defining Blocks with DSL Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/blocks.md Shows minimal and full block definitions using the domain-specific language. ```rust block Foo { buffer Bar = 0, } ``` ```rust /// Block description #[cfg(not(blah))] block Foo { const ADDRESS_OFFSET = 10; const REPEAT = { count: 2, stride: 20, }; buffer Bar = 0, } ``` -------------------------------- ### Define GlobalConfig structure Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Global configuration settings for the device driver. ```text { default_register_access?: _Access_, default_field_access?: _Access_, default_buffer_access?: _Access_, default_byte_order?: _ByteOrder_, default_bit_order?: _BitOrder_, register_address_type?: _IntegerType_, command_address_type?: _IntegerType_, buffer_address_type?: _IntegerType_, name_word_boundaries?: _NameWordBoundaries_ defmt_feature?: string } ``` -------------------------------- ### Run Tests with Cargo Source: https://github.com/diondokter/device-driver/blob/master/tests/ui/README.md Execute the test suite using Cargo. This command discovers and runs all defined tests. ```bash cargo test ``` -------------------------------- ### Define ByteOrder types Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Allowed byte order configurations. ```text string oneof("LE", "BE") ``` -------------------------------- ### Accessing a Register Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/registers.md Demonstrates reading and writing to a register using the generated device interface. ```rust let mut device = MyDevice::new(DeviceInterface::new()); device.foo().write(|reg| reg.set_bar(12345)).unwrap(); assert_eq!(device.foo().read().unwrap().bar(), 12345); ``` -------------------------------- ### Accept Test Changes Source: https://github.com/diondokter/device-driver/blob/master/tests/ui/README.md Update the known output files with the current generation output. Use this command when test changes are intentional and should be committed. ```bash cargo run -- accept ``` -------------------------------- ### Define Command structure Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Structure for defining a device command. ```text { type: string oneof("command"), cfg?: string, description?: string, byte_order?: _ByteOrder_, bit_order?: _BitOrder_, address: int, repeat?: _Repeat_, allow_bit_overlap?: bool, allow_address_overlap?: bool, size_bits_in?: int, fields_in?: { *: _Field_ }, size_bits_out?: int, fields_out?: { *: _Field_ }, } ``` -------------------------------- ### Define a minimal ref using manifest Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/refs.md This JSON snippet shows the minimal structure for defining a ref in the manifest, targeting 'Foo' and overriding its type and address. ```json "Foo": { "type": "register", "address": 3, "size_bits": 16, "fields": { "value": { "base": "uint", "start": 0, "end": 16 } } }, "Bar": { "type": "ref", "target": "Foo", "override": { "type": "register", "address": 3, } } ``` -------------------------------- ### Generate Device Driver Code Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/using-the-cli.md Generate Rust code for a device driver using the CLI. Specify the manifest file, output path, and device name. The device name must be in PascalCase. ```bash device-driver-cli -m -o -d ``` -------------------------------- ### Define Buffers using DSL Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/buffers.md Define buffers using the project's DSL syntax, supporting attributes like cfg and doc comments. ```rust buffer Foo = 5, ``` ```rust /// A foo buffer #[cfg(bar)] buffer Foo: WO = 5, ``` -------------------------------- ### Cargo Manifest Configuration Source: https://github.com/diondokter/device-driver/blob/master/tests/ui/src/output_header.txt Defines the project edition and local dependency path for the device-driver crate. ```toml [package] edition = "2024" [dependencies] device-driver = { path="../../../../device-driver", default-features=false } ``` -------------------------------- ### Bit Order (_BitOrder_) Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Defines the possible bit orderings. ```APIDOC ## Bit Order (_BitOrder_) This type defines the possible bit orderings. ### _BitOrder_ ```json string oneof("LSB0", "MSB0") ``` Possible values are: `LSB0` (Least Significant Bit first), `MSB0` (Most Significant Bit first). ``` -------------------------------- ### Byte Order (_ByteOrder_) Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Defines the possible byte orderings. ```APIDOC ## Byte Order (_ByteOrder_) This type defines the possible byte orderings. ### _ByteOrder_ ```json string oneof("LE", "BE") ``` Possible values are: `LE` (Little Endian), `BE` (Big Endian). ``` -------------------------------- ### Define BitOrder types Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Allowed bit order configurations. ```text string oneof("LSB0", "MSB0") ``` -------------------------------- ### Run Tests with Nextest Source: https://github.com/diondokter/device-driver/blob/master/tests/ui/README.md Execute the test suite using Nextest, an alternative test runner. This command discovers and runs all defined tests. ```bash cargo nextest run ``` -------------------------------- ### Command Configuration Attributes Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/commands.md Configuration attributes used to define command behavior, documentation, and memory layout. ```APIDOC ## Command Configuration Attributes ### Description Attributes used to control command generation, documentation, and data layout. ### Parameters - **cfg** (string) - Optional - Rust cfg-gating attribute for the command. - **description** (string) - Optional - Doc comments for generated code, supports markdown. - **byte_order** (string) - Optional - Overrides default byte order. Options: LE, BE. - **bit_order** (string) - Optional - Overrides default bit order. Options: LSB0, MSB0. - **repeat** (object) - Optional - Repeats command at different addresses. - **Count** (unsigned integer) - Number of repetitions. - **Stride** (signed integer) - Address increment per repeat. - **allow_bit_overlap** (boolean) - Optional - Allows field addresses to overlap. Default: false. - **allow_address_overlap** (boolean) - Optional - Allows command address to match other command addresses. Default: false. - **in / fields_in** (map/list) - Optional - Input fields of the command. - **out / fields_out** (map/list) - Optional - Output fields of the command. ``` -------------------------------- ### Optimize Compile Times for JSON Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/using-the-macro.md When using JSON for manifest files, disable default features and explicitly enable the 'json' feature to optimize compile times. ```toml [dependencies.device-driver] default-features = false features = ["json"] ``` -------------------------------- ### Optimize Compile Times for DSL Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/using-the-macro.md When using the DSL (inline or manifest), disable default features and explicitly enable the 'dsl' feature to optimize compile times. ```toml [dependencies.device-driver] default-features = false features = ["dsl"] ``` -------------------------------- ### Rust API: Dispatch Command Source: https://github.com/diondokter/device-driver/blob/master/website/pages/home/index.html Dispatch a simple command with optional input and output data using the generated Rust API. ```rust // Dispatch commands device.simple_command().dispatch()?; ``` -------------------------------- ### Optimize Compile Times for YAML Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/using-the-macro.md When using YAML for manifest files, disable default features and explicitly enable the 'yaml' feature to optimize compile times. ```toml [dependencies.device-driver] default-features = false features = ["yaml"] ``` -------------------------------- ### Register Object (_Register_) Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Defines a hardware register, including its access, address, size, and fields. ```APIDOC ## Register Object (_Register_) This type defines a hardware register. ### _Register_ ```json { type: string oneof("register"), cfg?: string, description?: string, access?: _Access_, byte_order?: _ByteOrder_, bit_order?: _BitOrder_, address: int, size_bits: int, reset_value?: oneof(int, [uint]), repeat?: _Repeat_, allow_bit_overlap?: bool, allow_address_overlap?: bool, fields?: { *: _Field_ } } ``` - `type` (string): Must be `"register"`. - `cfg` (string): Optional configuration string. - `description` (string): Optional description. - `access` (_Access_): Access mode for the register. - `byte_order` (_ByteOrder_): Byte order for the register. - `bit_order` (_BitOrder_): Bit order for the register. - `address` (int): The memory address of the register. - `size_bits` (int): The size of the register in bits. - `reset_value` (oneof(int, [uint])): Optional reset value for the register. - `repeat` (_Repeat_): Optional definition for repeating the register. - `allow_bit_overlap` (bool): Whether bit overlap is allowed within the register. - `allow_address_overlap` (bool): Whether address overlap is allowed for repeated registers. - `fields` (map): A map where keys are field names and values are `_Field_` definitions. ``` -------------------------------- ### Manifest Description Field Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/field-sets.md Provide documentation for generated code in the manifest using the 'description' string field. This supports markdown and is used for generated getters and setters. ```json { "description": "This is a documentation comment." } ``` -------------------------------- ### Buffer Object (_Buffer_) Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Defines a hardware buffer, specifying its access and address. ```APIDOC ## Buffer Object (_Buffer_) This type defines a hardware buffer. ### _Buffer_ ```json { type: string oneof("buffer"), cfg?: string, description?: string, access?: _Access_, address: int, } ``` - `type` (string): Must be `"buffer"`. - `cfg` (string): Optional configuration string. - `description` (string): Optional description. - `access` (_Access_): Access mode for the buffer. - `address` (int): The starting memory address of the buffer. ``` -------------------------------- ### Global Config DSL Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/global-config.md Defines global configuration settings using the DSL format. This includes default access types, address types, name word boundaries, and defmt features. ```rust config { type DefaultRegisterAccess = RW; type DefaultFieldAccess = RW; type DefaultBufferAccess = RW; type DefaultByteOrder = _; type DefaultBitOrder = LSB0; type RegisterAddressType = _; type CommandAddressType = _; type BufferAddressType = _; type NameWordBoundaries = [ Underscore, Hyphen, Space, LowerUpper, UpperDigit, DigitUpper, DigitLower, LowerDigit, Acronym, ]; type DefmtFeature = "my-feature"; } ``` -------------------------------- ### Optimize Compile Times for TOML Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/using-the-macro.md When using TOML for manifest files, disable default features and explicitly enable the 'toml' feature to optimize compile times. ```toml [dependencies.device-driver] default-features = false features = ["toml"] ``` -------------------------------- ### Define Field Set using JSON Manifest with Attributes Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/field-sets.md Defines fields using a JSON manifest, including attributes like configuration, description, and access specifiers. ```json { "foo": { "cfg": "blah", "description": "Field comment!", "access": "WO", "base": "uint", "start": 0, "end": 5 } } ``` -------------------------------- ### Bit placement for bit 0 in a 2-byte array Source: https://github.com/diondokter/device-driver/blob/master/book/src/memory.md Visual representation of how bit 0 is mapped across different byte and bit order configurations. ```text LE, LSB0: [0b0000_0001, 0b0000_0000] or [0x01, 0x00] ^ ^ <- Bits 0 ^^^^^^^^^^^ <- Byte 0 LE, MSB0: [0b1000_0000, 0b0000_0000] or [0x80, 0x00] ^ ^ <- Bits 0 ^^^^^^^^^^^ <- Byte 0 BE, LSB0: [0b0000_0000, 0b0000_0001] or [0x00, 0x01] ^ ^ <- Bits 0 ^^^^^^^^^^^ <- Byte 0 BE, MSB0: [0b0000_0000, 0b1000_0000] or [0x00, 0x80] ^ ^ <- Bits 0 ^^^^^^^^^^^ <- Byte 0 ``` -------------------------------- ### Define Field Set using DSL (Base Type) Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/field-sets.md Defines fields using a simple DSL, specifying the base type and bit range for each field. ```rust foo: uint = 0..5, bar: bool = 5, zoof: int = 6..=20, ``` -------------------------------- ### Bit Order Configuration Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/registers.md Overrides the default bit order for registers. Defaults to LSB0 if not globally configured. ```APIDOC ## `bit_order` Overrides the default bit order. If the global config does not define it, it's `LSB0`. Options are: `LSB0`, `MSB0`. They are written 'as is' in the DSL and as a string in the manifest. ``` -------------------------------- ### Define a custom interface struct Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/writing-an-interface.md Defines a struct to hold the bus and demonstrates the initialization of a device driver that fails due to missing trait implementations. ```rust /// Our interface struct that owns the bus. pub struct MyDeviceInterface { pub bus: BUS, } fn try_out() { // Initialize the bus somehow. Your HAL should help you there let bus = init_bus(); // Create our custom interface struct let interface = MyDeviceInterface { bus }; // Create the device driver based on the interface let mut my_device = MyDevice::new(interface); // Try to read the foo register. This results in an error let _ = my_device.foo().read(); // ERROR: ^^^^ method cannot be called due to unsatisfied trait bounds // // note: the following trait bounds were not satisfied: // `DeviceInterface: RegisterInterface` } ``` -------------------------------- ### Configure Conditional Compilation Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/commands.md Defines how to apply cfg-gating to command definitions in the DSL and manifest. ```rust #[cfg(foo)] ``` ```json "cfg": "foo", ``` -------------------------------- ### Define Device structure Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md The top-level structure for a device manifest. ```text { config?: _GlobalConfig_, *: _Object_ } ``` -------------------------------- ### Define Buffer structure Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Structure for defining a memory buffer. ```text { type: string oneof("buffer"), cfg?: string, description?: string, access?: _Access_, address: int, } ``` -------------------------------- ### Device Definition Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Defines the structure of a top-level device object in the manifest. ```APIDOC ## Device Definition The top-level item in the manifest is `_Device_`. The key of the object becomes the name of the device. ### _Device_ ```json { config?: _GlobalConfig_, *: _Object_ } ``` - `config` (_GlobalConfig_): Optional global configuration for the device. - `*` (_Object_): Represents any valid object type (Block, Register, Command, Buffer, RefObject). ``` -------------------------------- ### Byte Order Configuration Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/registers.md Overrides the default byte order for registers larger than one byte. ```APIDOC ## `byte_order` Overrides the default byte order. Options are: `LE`, `BE`. They are written 'as is' in the DSL and as a string in the manifest. When the size of a register is > 8 bits (more than one byte), then either the byte order has to be defined globally as a default or the register needs to define it. ``` -------------------------------- ### Implement RegisterInterface and AsyncRegisterInterface Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/writing-an-interface.md Implements synchronous and asynchronous register interfaces for a custom I2C bus structure. ```rust pub struct MyDeviceI2cInterface { pub bus: BUS, } // See the docs of the traits to get more up-to-date information about how and what to impl impl device_driver::RegisterInterface for MyDeviceI2cInterface { // ... } // For the async I2C we can implement the async register interface impl device_driver::AsyncRegisterInterface for MyDeviceI2cInterface { // ... } fn try_out_sync() { let bus = init_sync_bus(); // Implements the I2c trait let interface = MyDeviceI2cInterface { bus }; let mut my_device = MyDevice::new(interface); let _ = my_device.foo().read(); } async fn try_out_async() { let bus = init_async_bus(); // Implements the async I2c trait let interface = MyDeviceI2cInterface { bus }; let mut my_device = MyDevice::new(interface); let _ = my_device.foo().read_async().await; } ``` -------------------------------- ### Rust API: Async Register Read Source: https://github.com/diondokter/device-driver/blob/master/website/pages/home/index.html Perform an asynchronous read operation on a device register using the generated Rust API. ```rust // Anything can be used async device.foo().read_async().await?; ``` -------------------------------- ### Rust API: Buffer Read and Write Source: https://github.com/diondokter/device-driver/blob/master/website/pages/home/index.html Write data to a buffer and read data from a buffer using the generated Rust API. This handles byte slices for buffer operations. ```rust // Write and read buffers device.wo_buf().write(&[0, 1, 2, 3])?; let len = device.ro_buf().read(&mut buffer)?; ``` -------------------------------- ### Bit placement for bit 10 in a 2-byte array Source: https://github.com/diondokter/device-driver/blob/master/book/src/memory.md Visual representation of how bit 10 is mapped across different byte and bit order configurations. ```text LE, LSB0: [0b0000_0000, 0b0000_0100] or [0x00, 0x04] ^ ^ <- Bits 2 ^^^^^^^^^^^ <- Byte 1 LE, MSB0: [0b0000_0000, 0b0010_0000] or [0x00, 0x20] ^ ^ <- Bits 2 ^^^^^^^^^^^ <- Byte 1 BE, LSB0: [0b0000_0100, 0b0000_0000] or [0x04, 0x00] ^ ^ <- Bits 2 ^^^^^^^^^^^ <- Byte 1 BE, MSB0: [0b0010_0000, 0b0000_0000] or [0x20, 0x00] ^ ^ <- Bits 2 ^^^^^^^^^^^ <- Byte 1 ``` -------------------------------- ### Define Field Set using JSON Manifest (Base Type) Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/field-sets.md Defines fields using a JSON manifest, specifying the base type and address range for each field. ```json { "foo": { "base": "uint", "start": 0, "end": 5 }, "bar": { "base": "bool", "start": 5, }, "zoof": { "base": "int", "start": 6, "end": 21 } } ``` -------------------------------- ### Define RefObject structure Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Structure for referencing existing objects. ```text { type: string oneof("ref"), cfg?: string, description?: string, target: string, override: _Object_, } ``` -------------------------------- ### Rust API: Bulk Register Operations Source: https://github.com/diondokter/device-driver/blob/master/website/pages/home/index.html Read multiple registers in bulk using the generated Rust API. This allows planning and executing read operations for several registers efficiently. ```rust // Operate on registers in bulk let (foo, bar) = device .multi_read() .with(|d| d.foo().plan()) .with(|d| d.bar().plan()) .execute()?; ``` -------------------------------- ### Define Enum in Manifest Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/field-sets.md JSON structure for specifying an enum configuration in the manifest file. ```json "conversion": { "name": "Foo", "description": "Enum docs", // In manifest, enum can be separately documented "A": null, "B": 5, "C": { "description": "Comment", "value": null } } ``` -------------------------------- ### Define BaseType types Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Allowed base types for fields. ```text string oneof("bool", "int", "uint") ``` -------------------------------- ### Define Access types Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Allowed access modes for registers and buffers. ```text string oneof("ReadWrite", "RW", "ReadOnly", "RO", "WriteOnly", "WO") ``` -------------------------------- ### Register Fields Definition (Manifest Only) Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/registers.md Defines the fields within a register, used exclusively in the manifest. ```APIDOC ## `fields` (manifest only) The fields of the register. A map where the keys are the names of the fields. All values must be fields. ``` -------------------------------- ### Access Modes (_Access_) Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Defines the possible access modes for registers, fields, and buffers. ```APIDOC ## Access Modes (_Access_) This type defines the allowed access modes. ### _Access_ ```json string oneof("ReadWrite", "RW", "ReadOnly", "RO", "WriteOnly", "WO") ``` Possible values are: `ReadWrite`, `RW`, `ReadOnly`, `RO`, `WriteOnly`, `WO`. ``` -------------------------------- ### Define Field Set using DSL with Custom Type Conversion Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/field-sets.md Defines fields using DSL, specifying conversion to custom types. Supports fallible and infallible conversions. ```rust foo: uint as crate::MyCustomType = 0..16, bar: int as try crate::MyCustomType2 = 16..32, ``` -------------------------------- ### Define Field Set using DSL with Attributes Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/field-sets.md Defines fields using DSL, including attributes like documentation comments, conditional compilation, and access specifiers. ```rust /// Field comment! #[cfg(blah)] foo: WO uint = 0..5, ``` -------------------------------- ### Object Types (_Object_) Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Defines the different types of objects that can be defined within a device or block. ```APIDOC ## Object Types (_Object_) This type represents the different kinds of objects that can be defined. ### _Object_ ```json oneof( _Block_, _Register_, _Command_, _Buffer_, _RefObject_ ) ``` Possible object types are: `_Block_`, `_Register_`, `_Command_`, `_Buffer_`, `_RefObject_`. ``` -------------------------------- ### Define NameWordBoundaries types Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Configuration for name word boundaries. ```text oneof([_Boundary_], string) ``` -------------------------------- ### Repeat Definition (_Repeat_) Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Defines how an object or block should be repeated. ```APIDOC ## Repeat Definition (_Repeat_) This type specifies how an object or block should be repeated. ### _Repeat_ ```json { count: uint, stride: int } ``` - `count` (uint): The number of times to repeat. - `stride` (int): The address stride between repetitions. ``` -------------------------------- ### Manipulate Field Set with Bitwise Operators Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/field-sets.md Shows how to use bitwise operators for field set manipulation, including inverting all bits and initializing from a byte array. Supports conversion to and from byte arrays. ```rust use field_sets::MyFieldSet; let all_ones = !MyFieldSet::new_zero(); let lowest_byte_set = MyFieldSet::from([0xFF, 0x00]); let lowest_byte_inverted = all_ones ^ lowest_byte_set; ``` -------------------------------- ### Perform Buffer Read and Write Operations Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/buffers.md Access a buffer as a function on a device block to perform read and write operations. ```rust let mut device = MyDevice::new(DeviceInterface::new()); device.foo().write_all(&[0, 1, 2, 3]).unwrap(); let mut buffer = [0; 8]; let len = device.bar().read(&mut buffer).unwrap(); ``` -------------------------------- ### Define Register structure Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/manifest-syntax.md Structure for defining a hardware register. ```text { type: string oneof("register"), cfg?: string, description?: string, access?: _Access_, byte_order?: _ByteOrder_, bit_order?: _BitOrder_, address: int, size_bits: int, reset_value?: oneof(int, [uint]), repeat?: _Repeat_, allow_bit_overlap?: bool, allow_address_overlap?: bool, fields?: { *: _Field_ } } ``` -------------------------------- ### Register Repetition Configuration Source: https://github.com/diondokter/device-driver/blob/master/book/src/v1/registers.md Configures repeating a register at different addresses with a specified count and stride. ```APIDOC ## `repeat` Repeat the register a number of times at different addresses. It is specified with two fields: - Count: unsigned integer, the amount of times the register is repeated - Stride: signed integer, the amount the address changes per repeat The calculation is `address = base_address + index * stride`. When the repeat field is present, the function to do a register operation will have an extra parameter for the index. ```