-
Notifications
You must be signed in to change notification settings - Fork 0
Support #100 #102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Support #100 #102
Changes from all commits
Commits
Show all changes
47 commits
Select commit
Hold shift + click to select a range
010258d
draft
p14c31355 166efa6
cargo add crc32fast
p14c31355 d4f0948
Define logger argument
p14c31355 818fee5
Compile errors fixed by cline
p14c31355 d100fb8
#102 PR review respond
p14c31355 10329e2
add USAGE_EXPLORE.md
p14c31355 c8ddde1
Merge branch 'feature/#101' into docs/README-EXPLORE
p14c31355 054a2ad
Update docs/USAGE_EXPLORE.md
p14c31355 322ddbf
Update docs/USAGE_EXPLORE.md
p14c31355 e394c51
Update docs/USAGE_EXPLORE.md
p14c31355 752e44d
Merge pull request #103 from p14c31355/docs/README-EXPLORE
p14c31355 0af52d1
#102 PR review respond
p14c31355 b159eba
cargo fmt
p14c31355 7e3ce03
For clippy friendly
p14c31355 d1c6a32
Update docs/USAGE_EXPLORE.md
p14c31355 80231d3
#102 PR review respond
p14c31355 1eb1ffd
#102 PR review respond
p14c31355 adff02c
#102 PR review respond
p14c31355 5a3116c
Update src/explorer.rs
p14c31355 32b9192
Update src/explorer.rs
p14c31355 8f5c31a
Update src/explorer.rs
p14c31355 d7c890a
Delete cfc32fast
p14c31355 43a4aa8
Error mapping & Logging level
p14c31355 f90977b
Serial log rewrote
p14c31355 a106359
#102 PR review respond
p14c31355 ed8d3a2
#102 PR review respond
p14c31355 d4df14e
Update src/scanner.rs
p14c31355 a7834a2
edit rustdoc
p14c31355 81c893a
Merge branch 'feature/#101' of https://github.com/p14c31355/dvcdbg in…
p14c31355 3100083
Update docs/USAGE_EXPLORE.md
p14c31355 911b846
Update docs/USAGE_EXPLORE.md
p14c31355 968bb7c
flush
p14c31355 11ae41d
cargo fmt
p14c31355 da662b3
send chunl
p14c31355 f8412bf
Add pruned exploration method
p14c31355 2f1ab03
compile errors fixed by cline
p14c31355 7bd2b8a
use heapless
p14c31355 69da2c0
Use writeln macro
p14c31355 3d2d02c
run_pruned_explorer include scan_i2c()
p14c31355 a63187e
#102 PR review respond
p14c31355 51ddcd5
#102 PR review respond
p14c31355 10ef551
Update src/explorer.rs
p14c31355 b4de3a9
#102 PR review respond
p14c31355 ed28743
Merge branch 'feature/#101' of https://github.com/p14c31355/dvcdbg in…
p14c31355 e96b3ec
#102 PR review respond
p14c31355 52d60ef
#102 PR review respond
p14c31355 a56a602
#102 PR review respond
p14c31355 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,240 @@ | ||
| # I2C Command Sequence Explorer | ||
|
|
||
| This module provides an algorithm and supporting utilities for discovering | ||
| valid sequences of I2C commands when device dependencies are unknown or | ||
| partially specified. It is designed for embedded bring-up scenarios, where | ||
| experimenting with permutations of initialization commands can reveal the | ||
| correct sequence for a new or undocumented device. | ||
|
|
||
| ## Overview | ||
| - [`Explorer`] manages a dependency graph of commands and produces only | ||
| valid permutations that satisfy the declared constraints. | ||
| - [`PermutationIter`] generates these permutations using an iterative, | ||
| stack-safe backtracking algorithm (no recursion). | ||
| - [`CmdExecutor`] abstracts the execution of a command on the I2C bus. | ||
| - [`Logger`] provides pluggable logging backends (serial console, null logger, etc.). | ||
|
|
||
| ## Key Features | ||
| 1. **Separation of Concerns**: The permutation engine (`PermutationIter`) is | ||
| isolated from bus execution logic (`explore`). | ||
| 2. **Iterator-based API**: The [`Explorer::permutations`] method yields an | ||
| iterator, making the algorithm testable and composable. | ||
| 3. **Generic Capacity**: The const generic `N` defines the maximum command | ||
| capacity at compile time, enabling efficient use on resource-constrained | ||
| microcontrollers. | ||
| 4. **Flexible Logging**: The [`Logger`] trait supports both formatted and | ||
| lightweight logging without allocating large buffers. | ||
| 5. **Robust Error Handling**: [`ExplorerError`] reports dependency cycles, | ||
| buffer exhaustion, and runtime execution issues explicitly. | ||
|
|
||
| ## Typical Use Case | ||
| - Bring-up of a new I2C peripheral when the required initialization sequence | ||
| is undocumented or incomplete. | ||
| - Automated discovery of valid command orderings under dependency constraints. | ||
| - Filtering of device addresses that respond consistently to a tested sequence. | ||
|
|
||
| ## Example | ||
| ```ignore | ||
| use dvcdbg::prelude::*; | ||
| use heapless::Vec; | ||
| // Example executor for a specific I2C implementation. | ||
| struct MyExecutor; | ||
| impl<I2C: crate::compat::I2cCompat> CmdExecutor<I2C> for MyExecutor { | ||
| fn exec( | ||
| &mut self, | ||
| i2c: &mut I2C, | ||
| addr: u8, | ||
| cmd: &[u8] | ||
| ) -> Result<(), crate::explorer::ExecutorError> { | ||
| // Simplified example: prepend 0x00 control byte. | ||
| if cmd.len() != 1 { | ||
| return Err(crate::explorer::ExecutorError::ExecFailed); | ||
| } | ||
| let buf = [0x00, cmd[0]]; | ||
| i2c.write(addr, &buf) | ||
| .map_err(|e| crate::explorer::ExecutorError::I2cError(e.to_compat(Some(addr)))) | ||
| } | ||
| } | ||
|
|
||
| // Dummy logger that ignores messages. | ||
| struct NullLogger; | ||
| impl Logger for NullLogger { | ||
| fn log_info(&mut self, _msg: &str) {} | ||
| fn log_warning(&mut self, _msg: &str) {} | ||
| fn log_error(&mut self, _msg: &str) {} | ||
| fn log_info_fmt<F>(&mut self, _fmt: F) | ||
| where F: FnOnce(&mut heapless::String<{ crate::explorer::LOG_BUFFER_CAPACITY }>) -> Result<(), core::fmt::Error> {} | ||
| fn log_error_fmt<F>(&mut self, _fmt: F) | ||
| where F: FnOnce(&mut heapless::String<{ crate::explorer::LOG_BUFFER_CAPACITY }>) -> Result<(), core::fmt::Error> {} | ||
| } | ||
|
|
||
| // Define candidate commands with dependencies. | ||
| const CAPACITY: usize = 32; | ||
| let cmds = &[ | ||
| CmdNode { bytes: &[0x01], deps: &[] }, | ||
| CmdNode { bytes: &[0x02], deps: &[0] }, // depends on first command | ||
| CmdNode { bytes: &[0x03], deps: &[0] }, | ||
| ]; | ||
|
|
||
| let explorer = Explorer::<CAPACITY> { sequence: cmds }; | ||
| let mut executor = MyExecutor; | ||
| let mut logger = NullLogger; | ||
| let mut i2c = /* platform-specific I2C impl */; | ||
| let result = explorer.explore(&mut i2c, &mut executor, &mut logger); | ||
| if let Err(e) = result { | ||
| logger.log_error(&format!("Exploration failed: {:?}", e)); | ||
| } | ||
| ``` | ||
|
|
||
| ### `CmdNode` | ||
| Represents a single I2C command in the dependency graph. | ||
|
|
||
| - `bytes` - The I2C command bytes to be sent. | ||
| - `deps` - The indices of the commands that must precede this command. | ||
|
|
||
| The dependency is now on the index of the dependent command in the sequence. | ||
|
|
||
| ```rust | ||
| #[derive(Copy, Clone)] | ||
| pub struct CmdNode { | ||
| pub bytes: &'static [u8], | ||
| pub deps: &'static [usize], | ||
| } | ||
| ``` | ||
|
|
||
|
|
||
| Returns a stack-safe iterator for all valid command permutations (topological sorts). | ||
|
|
||
| This function first performs cycle detection using a modified Kahn's algorithm. | ||
| If a cycle is detected, it returns an `ExplorerError::DependencyCycle`. | ||
| Otherwise, it initializes and returns a `PermutationIter` to generate all valid permutations. | ||
|
|
||
| Explores valid sequences, attempting to execute them on an I2C bus. | ||
|
|
||
| This function iterates through all valid command permutations generated by `PermutationIter`, | ||
| and for each permutation, attempts to execute it on all active I2C addresses. | ||
| It filters out addresses that fail to respond to any command in a sequence. | ||
|
|
||
| # Parameters | ||
| - `i2c`: An I2C implementation used to test candidate sequences against device addresses. | ||
| - `executor`: The object responsible for executing a single command on the bus. | ||
| - `logger`: The object responsible for logging progress and results. | ||
|
|
||
| # Returns | ||
| - `Ok(ExploreResult)` containing the list of found addresses and the number of permutations tested. | ||
| - `Err(ExplorerError)` if an error occurs during permutation generation or I2C execution. | ||
|
|
||
| Generates a single valid topological sort of the command sequence. | ||
| This is useful when only one valid ordering is needed, and avoids | ||
| the computational cost of generating all permutations. | ||
|
|
||
| Returns `Ok(Vec<&'a [u8], N>)` containing one valid command sequence, | ||
| or `Err(ExplorerError)` if a cycle is detected or buffer overflows. | ||
|
|
||
| Attempts to extend the current partial permutation by adding a new command. | ||
|
|
||
| It iterates through all available (not yet used) commands and checks if their | ||
| dependencies are satisfied (i.e., their in-degree is 0). If a valid command | ||
| is found, it's added to the current permutation, its `used` status is updated, | ||
| and the in-degrees of its dependent nodes are decremented. | ||
|
|
||
| Returns `true` if a command was successfully added, `false` otherwise. | ||
|
|
||
| Backtracks to the previous decision point in the permutation search. | ||
|
|
||
| This method undoes the last choice made: it removes the last added command | ||
| from the current permutation, unmarks it as used, and increments the | ||
| in-degrees of its dependent nodes (reversing the decrement). | ||
| It then updates the `loop_start_indices` to ensure the next search | ||
| at the parent level continues from the next sibling. | ||
|
|
||
| Returns `true` if backtracking can continue (i.e., there are more options | ||
| at a previous level), or `false` if the root was reached and no more | ||
| permutations can be generated. | ||
|
|
||
| Runs the I2C explorer with a given initial sequence and logs the results. | ||
|
|
||
| This function first performs an I2C scan with the provided `init_sequence` to identify | ||
| responsive commands. Then, it uses the `explorer` to find valid command sequences | ||
| for discovered devices, applying a `prefix` to each command. | ||
|
|
||
| # Type Parameters | ||
| - `I2C`: The I2C interface type that implements `crate::compat::I2cCompat`. | ||
| - `S`: The serial interface type used for logging, implementing `core::fmt::Write`. | ||
| - `N`: A const generic for the maximum number of commands. | ||
| - `BUF_CAP`: A const generic for the command buffer capacity. | ||
|
|
||
| # Parameters | ||
| - `explorer`: An instance of `Explorer` containing the command nodes and their dependencies. | ||
| - `i2c`: The I2C bus instance. | ||
| - `serial`: The serial writer for logging. | ||
| - `init_sequence`: The initial sequence of bytes to test for device responsiveness. | ||
| - `prefix`: A byte to prepend to every command sent during exploration. | ||
| - `log_level`: The desired logging level. | ||
|
|
||
| # Example | ||
| ```ignore | ||
| use dvcdbg::prelude::*; | ||
| use arduino_hal::I2c; | ||
| use arduino_hal::hal::port::Port; | ||
| use arduino_hal::pac::TWI; | ||
| use heapless::Vec; | ||
| use core::fmt::Write; | ||
|
|
||
| # struct MyI2c; // Dummy I2c implementation | ||
| # impl dvcdbg::compat::I2cCompat for MyI2c { | ||
| # type Error = dvcdbg::error::ErrorKind; | ||
| # fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { Ok(()) } | ||
| # fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { Ok(()) } | ||
| # fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { Ok(()) } | ||
| # } | ||
| # struct MySerial; // Dummy Serial implementation | ||
| # impl core::fmt::Write for MySerial { | ||
| # fn write_str(&mut self, s: &str) -> core::fmt::Result { Ok(()) } | ||
| # } | ||
|
|
||
| let mut i2c = /* your I2C instance */; | ||
| let mut serial = /* your serial instance */; | ||
| let init_sequence = [0u8; 16]; // Example initial sequence | ||
| const EXPLORER_CAP: usize = 32; | ||
| const BUF_CAP: usize = 128; | ||
| let explorer = Explorer::<EXPLORER_CAP> { sequence: &[] }; // Dummy explorer | ||
|
|
||
| run_explorer::<_, _, EXPLORER_CAP, BUF_CAP>( | ||
| &explorer, | ||
| &mut i2c, | ||
| &mut serial, | ||
| &init_sequence, | ||
| 0x00, // Example prefix | ||
| LogLevel::Verbose, | ||
| ).unwrap(); | ||
| # Ok::<(), dvcdbg::explorer::ExplorerError>(()) | ||
| # } | ||
| ``` | ||
|
|
||
| Runs the I2C explorer to find and execute a single valid command sequence. | ||
|
|
||
| This function first obtains one topological sort of the commands from the explorer. | ||
| Then, it attempts to execute this single sequence on a specified I2C address. | ||
| This is useful for device initialization where only one valid sequence is needed, | ||
| avoiding the high computational cost of exploring all permutations. | ||
|
|
||
| # Type Parameters | ||
| - `I2C`: The I2C interface type that implements `crate::compat::I2cCompat`. | ||
| - `S`: The serial interface type used for logging, implementing `core::fmt::Write`. | ||
| - `N`: A const generic for the maximum number of commands. | ||
| - `BUF_CAP`: A const generic for the command buffer capacity. | ||
|
|
||
| ### Parameters | ||
|
|
||
| - `explorer`: An instance of `Explorer` containing the command nodes and their dependencies. | ||
| - `i2c`: The I2C bus instance. | ||
| - `serial`: The serial writer for logging. | ||
| - `target_addr`: The specific I2C address to execute the sequence on. | ||
| - `prefix`: A byte to prepend to every command sent during execution. | ||
| - `log_level`: The desired logging level. | ||
|
|
||
| ### Returns | ||
|
|
||
| Returns `Ok(())` if the sequence was successfully executed, | ||
| or `Err(ExplorerError)` if an error occurred (e.g., cycle detected, execution failed). | ||
|
Comment on lines
+1
to
+240
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The documentation in this file is a great addition, but it seems to be out of sync with the latest refactoring. Several examples and descriptions refer to old APIs that have been changed or removed. For instance:
Please review and update the entire document to match the new API. |
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation in this file appears to be out of sync with the recent code changes, which could confuse users. For example:
CmdExecutorandLoggerexamples are missing the newconstgeneric parameters for buffer capacity.Explorer::permutations, but this method has been removed and its logic is now inPermutationIter::new.Please review the entire document and update it to reflect the current API.