Skip to content

Conversation

lzrd
Copy link
Contributor

@lzrd lzrd commented Oct 14, 2025

$ humility gpio --input -with-config --pins B:0,B:14,E:1
humility: attached to 0483:3754:002F00174741500820383733 via ST-Link V3
B:0 = 0 Analog:OutPushPull:LowSpeed:NoPull:AF0:Unlocked:InZero:OutZero
B:14 = 1 Input:OutPushPull:HighSpeed:NoPull:AF5:Unlocked:InOne:OutZero
E:1 = 0 Alternate:OutPushPull:VeryHighSpeed:NoPull:AF12:Unlocked:InZero:OutZero

If no pins are specified, then the pin names, values and config settings
are printeed one per line for all GPIO pins.

@lzrd lzrd requested a review from mkeeter October 14, 2025 08:08
@mkeeter
Copy link
Contributor

mkeeter commented Oct 14, 2025

This PR does not build on my Mac (and CI suggests that it also fails on Windows and illumos):

➜  humility jj:(zyl (empty)) cargo run -- doc gpio
   Compiling stm32h7 v0.14.0
   Compiling humility-cmd-rendmp v0.1.0 (/Users/mjk/oxide/humility/cmd/rendmp)
   Compiling humility-cmd-mwocp v0.1.0 (/Users/mjk/oxide/humility/cmd/mwocp)
   Compiling humility-cmd-pmbus v0.1.0 (/Users/mjk/oxide/humility/cmd/pmbus)
   Compiling humility-cmd-ibc v0.1.0 (/Users/mjk/oxide/humility/cmd/ibc)
   Compiling humility-cmd-powershelf v0.1.0 (/Users/mjk/oxide/humility/cmd/powershelf)
   Compiling humility-cmd-gpio v0.1.0 (/Users/mjk/oxide/humility/cmd/gpio)
rustc-LLVM ERROR: Global variable '__INTERRUPTS' has an invalid section specifier '.vector_table.interrupts': mach-o section specifier requires a segment and section separated by a comma.
error: could not compile `stm32h7` (lib)

Caused by:
  process didn't exit successfully: `/Users/mjk/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rustc --crate-name stm32h7 --edition=2018 /Users/mjk/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/stm32h7-0.14.0/src/lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=93 --crate-type lib --emit=dep-info,metadata,link -C embed-bitcode=no -C debuginfo=2 -C split-debuginfo=unpacked --cfg 'feature="cortex-m-rt"' --cfg 'feature="rt"' --cfg 'feature="stm32h753"' --check-cfg 'cfg(docsrs,test)' --check-cfg 'cfg(feature, values("cortex-m-rt", "default", "rt", "stm32h735", "stm32h743", "stm32h743v", "stm32h747cm4", "stm32h747cm7", "stm32h753", "stm32h753v", "stm32h7b3"))' -C metadata=2a96e9273e0fc052 -C extra-filename=-3c0ef28dd79b8815 --out-dir /Users/mjk/oxide/humility/target/debug/deps -L dependency=/Users/mjk/oxide/humility/target/debug/deps --extern bare_metal=/Users/mjk/oxide/humility/target/debug/deps/libbare_metal-a6961b40f0c2a16d.rmeta --extern cortex_m=/Users/mjk/oxide/humility/target/debug/deps/libcortex_m-56dd93b623e45811.rmeta --extern cortex_m_rt=/Users/mjk/oxide/humility/target/debug/deps/libcortex_m_rt-ae17ea24e6f95e27.rmeta --extern vcell=/Users/mjk/oxide/humility/target/debug/deps/libvcell-4a9d6411460b449c.rmeta --cap-lints allow -L /Users/mjk/oxide/humility/target/debug/build/stm32h7-8c5346118f983ea4/out -L /Users/mjk/oxide/humility/target/debug/build/cortex-m-rt-e913a996d93012fe/out` (exit status: 101)
warning: build failed, waiting for other jobs to finish...

I believe that's because you specified the hf feature for stm32h7 crate, which is meant for running on an STM32H7 and brings in the cortex-m-rt crate.

@mkeeter
Copy link
Contributor

mkeeter commented Oct 14, 2025

One big thing that needs to be fixed is making this generic across chips (or printing an appropriate error message). Right now, it would happily try to read the STM32H7's memory locations for any chip that's attached.

You could fix this with a trait InputConfig, then implement that trait starting with the STM32H753. The trait would be something like

trait InputConfig {
    type PinConfig: Display;
    
    fn get_pin_config(ctx: &mut ExecutionContext, port: &str, pin: u32) -> Self::PinConfig;
}

Then, the ConfigCache could be made generic across a T: InputConfig, as could show_gpio_with_config, and we could dispatch based on chip in the manifest.

@lzrd
Copy link
Contributor Author

lzrd commented Oct 14, 2025

Non-STM32H7 support would be nice to have. I've only done some generalization for the STM32H7 series for now and output error messages for unknown chips.
If dumps don't contain the GPIO configuration register blocks then that would be nice to add.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

license-eye has totally checked 104 files.

Valid Invalid Ignored Fixed
103 1 0 0
Click to see the invalid file list
  • cmd/gpio/src/stm32h7.rs

@lzrd lzrd force-pushed the gpio-config-dump branch 3 times, most recently from 932a51a to 2d95254 Compare October 15, 2025 00:17
@lzrd lzrd requested a review from mkeeter October 15, 2025 00:38
results,
)
} else {
Err(anyhow!(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: you could use bail!(..) instead of Err(anyhow!(..))

@lzrd lzrd force-pushed the gpio-config-dump branch from b78e3f4 to 3ae551f Compare October 16, 2025 05:16
lzrd added 6 commits October 15, 2025 22:24
 $ humility gpio --input -with-config --pins B:0,B:14,E:1
 humility: attached to 0483:3754:002F00174741500820383733 via ST-Link V3
 B:0  = 0 Analog:OutPushPull:LowSpeed:NoPull:AF0:Unlocked:InZero:OutZero
 B:14 = 1 Input:OutPushPull:HighSpeed:NoPull:AF5:Unlocked:InOne:OutZero
 E:1  = 0 Alternate:OutPushPull:VeryHighSpeed:NoPull:AF12:Unlocked:InZero:OutZero
 ```
 If no pins are specified, then the pin names, values and config settings
 are printeed one per line for all GPIO pins.
Applied several suggestions from Matt.

    Co-authored-by: Matt Keeter <[email protected]>
The stm32h7 crate and the stm32-metapac crates are not practical given
that they cause difficulties in compiling humility for multiple OS
environments and they require a good bit of code to coerce into making
generalized GPIO register decoding.

Just define the one needed structure.
  - Updated documentation to reflect GPIO configuration output specific to chip configuration.
  - Non-existant pins are not printed.
  - Added tests for the new behavior.
@lzrd lzrd force-pushed the gpio-config-dump branch from 3ae551f to c3533ce Compare October 16, 2025 05:25
@lzrd lzrd requested a review from mkeeter October 16, 2025 06:55
/// cache is chip-independent and works with any architecture that organizes
/// GPIO pins into groups/ports with contiguous register blocks.
pub struct ConfigCache {
cache: BTreeMap<char, Vec<u8>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We never store multiple register block types, so this should be made generic across a register block type T. This would let us avoid needing to parse from bytes elsewhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok with this separation right now. The code that knows about STM32H7 registers doesn't do IO, doesn't hold state, etc. The code that does the IO is just told to that if you want to talk about a pin group, you need to get me this data. There needs to be a from_bytes somewhere to translate the raw data from the device. I think this approach would work well for non STM32H7 type devices as well. But, I'd like to have a motivating case before making a change here.

);
}

(Some(part.max_group()), Some(part))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why you're redundantly storing both part.max_group() and part separately. Below, some code checks for the presence of part (then calls max_group() on it again), while other code uses the max_group variable. You could just store part and call max_group() as needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

// Check if pin exists (group exists and pin within range)
let pin_exists =
if let (Some(g), Some(part)) = (group, chip_part) {
g >= 'A'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This repeats the logic from group_exists above.

None => {
// Skip ports that don't exist on this chip variant
if !group_exists {
continue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we print something if the user requests a non-existent group?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"None" is printed for pins that don't exist or a '.' in the simple grid form.

context: &mut ExecutionContext,
part: &Stm32H7Part,
group: char,
) -> Result<Vec<u8>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you take my suggestion to make ConfigCache generic, this should just return a RegisterBlock (read into it with zerocopy)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't disagree with the suggestion on ConfigCache, but I'll wait for the next chip type to see how that plays out.


for (ndx, arg) in args.iter().enumerate() {
let group =
arg.2.chars().next().ok_or(anyhow!("invalid group '{}'", arg.2))?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, we return an error if the user specifies an invalid group; in the non-config routine, we allow it. Resolve this inconsistency one way or the other?

"Warning: Unknown STM32H7 chip '{}'. Using default GPIO layout (groups A-K).",
chip
);
eprintln!(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this second warning is needed, because show_gpio_with_config will bail with a similar error.


// Warn about unknown chips
if !part.is_known() {
eprintln!(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use humility::warn! instead?

}
}
);
} else if pin >= part.pins_in_group(group) {
Copy link
Contributor

@mkeeter mkeeter Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is identical to the branch above, merge them?

|ctx, g| read_gpio_register_block(ctx, part, g),
)?;

let max_pin = part.pins_in_group(group);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just iterate from 0..max_pin here?

max_group: 'I',
},
Stm32H7Part {
com_part_no: "STM32H743AGI6",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spot-checking this one example, it doesn't look correct. Here is the UFGBA pinout:

Image

Notice that it has PJ0/1 and Port I is partial (e.g. PI4 does not exist). The assumption that pins are tightly packed in groups of 16 is an oversimplification.

More generally, I think that the c3533ce commit is trying to do too much (+1600 lines), and is not worth its weight (e.g. because it's incorrect here).

I'd be fine with the old assumptions of Ports A-K / 176 pins, as long as we check against a small list of blessed chips IDs (instead of trying to support many STM32 part).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants