8000 stm32: Refactor pyb.CAN implementation, add tests. by projectgus · Pull Request #15989 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content

stm32: Refactor pyb.CAN implementation, add tests. #15989

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 2 commits into from
Mar 14, 2025

Conversation

projectgus
Copy link
Contributor
@projectgus projectgus commented Oct 10, 2024

Summary

I've been gradually working on stm32 as the first implementation of the new CAN API (#12337), and first step was to refactor the low-level part of the pyb.CAN driver to be reusable from machine.CAN as well. In the course of doing this I also wrote some simple automated tests for pyb.CAN.

After doing the refactors and adding the tests I discovered some small bugs in various STM32 boards. This PR contains bugfix commits also submitted in #16543, #16545, #16546, #16547. Only the top two commits in this branch are unique to this PR (but it's difficult to run the tests without the other fixes).

can.h API changes (only relevant for anyone writing C code against this API):

  • Peripheral argument is now CAN_HandleTypeDef * instead of pyb_can_obj_t *, to decouple the two layers.
  • Adds a new can_rx_fifo_t enum for the RX FIFO selection. The numeric value of this enum is the same as the previous values used by bxCAN (0, 1), but they are different to the enums previously used by FDCAN. The FDCAN ST HAL enums are buffer offset values but these are now moved into the implementation and are no longer exposed in the API.
  • Responsibility for re-enabling RX interrupts has been moved up to the caller of can_receive(). See pyb_can.c for the necessary code.

As part of the refactoring some pyb.CAN behaviour changed on FDCAN, to make it the same as bxCAN:

  • FDCAN peripherals no longer send rx callbacks with ids 3,4,5 when the bus error state changes (this behaviour wasn't documented).
  • FDCAN peripherals now increment the error counters the same as bxCAN-based peripherals.

Testing

Ran the new multi-tests via command line:

./run-multitests.py -i pyb:/dev/ttyACM0 -i pyb:/dev/ttyACM1 -p2 multi_pyb_can/*.py

Tests pass on various pairings of the following boards:

  • PyBoard V1.1 (bxCAN).
  • NUCLEO_H723ZG board (FDCAN).
  • ARDUINO_NICLA_VISION (STM32H747, FDCAN)
  • NUCLEO_G474RE board (FDCAN, but G series has different peripheral design to H5+H7).

Trade-offs and Alternatives

  • I think the only real alternative would have been to ignore the existing pyb.CAN driver and make the STM32 machine.CAN driver from scratch. Given how much refactoring was needed here then that does look like a somewhat appealing alternative in hindsight, especially as I'm not done yet! However, this way the code size will be smaller for as long as both pyb.CAN and the future machine.CAN drivers are included in MicroPython.

Follow-up work (not in this PR)

  • Add multi-tests around CAN-FD mode.
  • Finish the stm32 machine.CAN implementation based on this lower layer.

Copy link
codecov bot commented Oct 10, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.54%. Comparing base (96ce08e) to head (dbda43b).
Report is 2 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #15989   +/-   ##
=======================================
  Coverage   98.54%   98.54%           
=======================================
  Files         169      169           
  Lines       21877    21877           
=======================================
  Hits        21558    21558           
  Misses        319      319           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@projectgus
8000 Copy link
Contributor Author

@iabdalkader @H-Grobben It's been a while, but this PR touches a bunch of driver code you've both worked on. Would appreciate any comments if you have the time.

Copy link
github-actions bot commented Oct 10, 2024

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:   +48 +0.012% PYBV10
     mimxrt:    +0 +0.000% TEENSY40
        rp2:    +0 +0.000% RPI_PICO_W
       samd:    +0 +0.000% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@projectgus
Copy link
Contributor Author

stm32: +0 +0.000% PYBV10

🤨 It seems very coincidental that this PR refactors so much code and ends up with exactly the same binary size...

@iabdalkader
Copy link
Contributor

@iabdalkader @H-Grobben It's been a while, but this PR touches a bunch of driver code you've both worked on. Would appreciate any comments if you have the time.

It's been years since I worked on FDCAN and CAN, so I don't remember much, but I'll try to take a look as soon as I have some free time. @kwagyeman is very interested in CAN support he might also want to take a look.

@kwagyeman
Copy link
Contributor

@projectgus - I'll update my PR for CAN on the IMX after an API is approved and merged.

@projectgus
Copy link
Contributor Author

@projectgus - I'll update my PR for CAN on the IMX after an API is approved and merged.

Sounds great. Appreciate your patience with the CAN support, @kwagyeman.

@dpgeorge
Copy link
Member

I didn't get time for a full review of this yet, but one thing I suggest is renaming the multi_pyb_can directory to just multi_pyb. It can potentially include tests for other thing in the pyb module in the future.

@projectgus
Copy link
Contributor Author
projectgus commented Jan 7, 2025

I didn't get time for a full review of this yet, but one thing I suggest is renaming the multi_pyb_can directory to just multi_pyb. It can potentially include tests for other thing in the pyb module in the future.

I can change this, but I wanted to check you're sure about it? From a usability perspective, it seems fiddly: Once other tests are added, it'd be necessary to run ./run-multitests.py multi_pyb/can_*.py most of the time as the PyBoard CAN tests require CAN transceivers connected to the boards in the test. Other pyb module tests may have very different environment requirements.

@projectgus

This comment was marked as outdated.

@projectgus projectgus changed the title stm32: Refactor pyb.CAN implementation, add tests, fix some related bugs. stm32: Refactor pyb.CAN implementation, add tests. Jan 17, 2025
@H-Grobben
Copy link
Contributor
H-Grobben commented Feb 5, 2025

User modules can not access the 'pyb_can_obj_t' struct if it is defined in the pyb_can.c file. Is the struct also defined as a machine.CAN struct, in a .h file, which then be used by user modules?

If it's put in the pyb_can.h file, it is still usable.

I will try to review the code and run some tests on the can changes if I can find the time.

-- update: need more time, but initial test locks up the micropython on the G4, with our custom c module, with this PR

@projectgus
Copy link
Contributor Author
projectgus commented Feb 10, 2025

Thanks for taking a look, @H-Grobben.

User modules can not access the 'pyb_can_obj_t' struct if it is defined in the pyb_can.c file.

Ah, sorry - I actually hadn't thought of the use case that a user C module might be using pyb_can_obj_t. I'll move it to pyb_can.h to minimise headaches on upgrade.

Is the struct also defined as a machine.CAN struct, in a .h file, which then be used by user modules?

Everything in pyb_can.* is to-be-eventually-deprecated code for pyb module compatibility. The new machine.CAN driver is still pending (unfortunately), but it will have a different implementation that uses can.h but not pyb_can.h.

I don't think any of the current machine module hardware drivers expose a C interface, so probably the new CAN driver won't do this either (apart from the new can.h). Can explain more about your user C module's use case, please? Is the code published anywhere?

-- update: need more time, but initial test locks up the micropython on the G4, with our custom c module, with this PR

It wouldn't surprise me if there are other subtle behaviour changes that impact a custom C module calling into pyb module functions. If you figure out what's happening then I'm happy to try and tweak things to keep compatibility intact.

@kwagyeman
Copy link
Contributor
kwagyeman commented Feb 10, 2025

@projectgus - Is CAN for the machine module ever going to be completed?

Just providing a gentle push :)

@projectgus
Copy link
Contributor Author

@projectgus - Is CAN for the machine module ever going to be completed?

I know it's frustrating, I'm sorry it's dragging so long. I have not forgotten about it, but there's been a lot of other things to take care of as well. There has been some progress behind the scenes, hopefully will be visible in a PR draft soon.

@projectgus
Copy link
Contributor Author

@H-Grobben Have rebased this PR and moved the pyb_can_obj_t struct into pyb_can.h

@projectgus
Copy link
Contributor Author
projectgus commented Feb 18, 2025

(Rebased, no other changes.)

@dpgeorge
Copy link
Member

I have run the tests from this PR on PYBV10+PYBV10 and PYBV10+PYBD_SF6 (so just standard CAN, not FDCAN). They pass before the refactoring in this PR and with this PR. Very good!

I do like how the can.h API is now operate on CAN_HandleTypeDef* rather than pyb_can_obj_t*. That's a good refactoring.

Copy link
Member
@dpgeorge dpgeorge left a comment

Choose a reason for hiding this comment

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

I've now done a full review of this PR. Apart from the above comments, it looks good.

@projectgus projectgus force-pushed the refactor/stm32_can branch 3 times, most recently from 6b72c1c to ea09d2f Compare February 26, 2025 03:16
@projectgus
Copy link
Contributor Author

@H-Grobben I've now noted two C API changes in the PR description above, it may help you debug your user C module.

@projectgus projectgus marked this pull request as draft February 26, 2025 05:29
@projectgus
Copy link
Contributor Author

Marked as draft again as I need to add a bit more functionality to the lower layer for machine.CAN to work.

@projectgus projectgus marked this pull request as ready for review March 4, 2025 03:21
@projectgus
Copy link
Contributor Author

Marked as draft again as I need to add a bit more functionality to the lower layer for machine.CAN to work.

Actually this is probably OK, can always do a second refactor PR if needed.

Comment on lines +44 to +46
uint16_t num_error_warning;
uint16_t num_error_passive;
uint16_t num_bus_off;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it necessary to count hardware errors?
It may be enough to perform bus recovering inside the driver.

See RM0008 Reference manual 24.7.6 Error management
See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/twai.html#error-states-and-counters

Copy link
Member

Choose a reason for hiding this comment

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

This is useful for the user to get an idea of if/when the bus is failing. They can access these "performance counters" via can.info().

(This is part of the Python API of pyb.CAN, so can't really be changed.)

This is necessary for the machine.CAN implementation to use the same
low-level functions.

Includes some refactoring around FIFO selection as there was a footgun
where CAN_FIFO0/1 are 0/1 but FDCAN_RX_FIFO0/1 are not. Added an explicit
type for non-hardware-specific FIFO numbering.

Also moved responsibility for re-enabling CAN receive interrupts into the
higher layer (pyb_can.c layer) after calling can_receive().

Also includes this behaviour change for FDCAN boards:

- Fix for boards with FDCAN not updating error status
  counters (num_error_warning, num_error_passive, num_bus_off). These are
  now updated the same as on boards with CAN Classic controllers, as
  documented.

- Previously FDCAN boards would trigger the RX callback function on error
  events instead (passing undocumented irq numbers 3, 4, 5).
  This behaviour has been removed in favour of the documented behaviour of
  updating the status counters.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
Currently only classic CAN, but tests run on both the stm32 classic CAN
controller and the FD-CAN controller with the same results.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
@dpgeorge dpgeorge force-pushed the refactor/stm32_can branch from ea09d2f to dbda43b Compare March 14, 2025 03:53
Copy link
Member
@dpgeorge dpgeorge left a comment

Choose a reason for hiding this comment

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

I retested on PYBV10+PYBV10. The new multitests still pass.

@dpgeorge dpgeorge merged commit dbda43b into micropython:master Mar 14, 2025
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants
0