Skip to content
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

ws2812: Fix number of nops for AVR at 8 MHz #9559

Merged
merged 2 commits into from
Feb 8, 2021

Conversation

sigprof
Copy link
Contributor

@sigprof sigprof commented Jun 26, 2020

Description

When trying to calculate the number of nops for AVR running at 8 MHz, the value of w3 is expected to be negative; however, because F_CPU is defined in tmk_core/avr.mk with the UL suffix, the preprocessor performs its calculations using unsigned long, getting a very large positive number instead of the expected negative number; this then results in generating code with a huge number of nops. Fix the broken calculations by performing a comparison before subtraction, so that the unsigned number wraparound does not occur.

The keyboard which triggers the problem is handwired/promethium; the buggy code silently compiles, but the resulting timings would be wrong (but apparently not wrong enough to be noticed, because for most WS8212-like LEDs a low level pulse of 6 us is not enough to trigger a reset).


I cannot actually test the change on a real hardware (I tested that it does not break the generated code for XD87 HS, which has ATmega32u4 running at 16 MHz, but I don't have any hardware which uses 8 Mhz). Update: Got a 3.3V Pro Micro board for testing and verified that the code actually works (at least with those LEDs that I have).

I looked at the Travis build report for #9558 and found a single keyboard which actually has ATmega32u4 running at 8 MHz with some WS2812-style LEDs connected, then looked at the generated assembly code and noticed that it is obviously wrong (has lots of NOPs), and the fix makes it look right (no NOPs at the end of the loop).

It should be possible to simplify the code by removing the subsequent checks like w1 > 0, but I did not touch them for now to avoid conflicts with #9558. Update: Removed useless code.

Types of Changes

  • Core
  • Bugfix
  • New feature
  • Enhancement/optimization
  • Keyboard (addition or update)
  • Keymap/layout/userspace (addition or update)
  • Documentation

Checklist

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • I have tested the changes and verified that they work and don't break anything (as well as I can manage).

When trying to calculate the number of nops for AVR running at 8 MHz,
the value of `w3` is expected to be negative; however, because `F_CPU`
is defined in tmk_core/avr.mk with the `UL` suffix, the preprocessor
performs its calculations using `unsigned long`, getting a very large
positive number instead of the expected negative number; this then
results in generating code with a huge number of nops.  Fix the broken
calculations by performing a comparison before subtraction, so that the
unsigned number wraparound does not occur.

The keyboard which triggers the problem is `handwired/promethium`; the
buggy code silently compiles, but the resulting timings would be
completely wrong.
@mtei
Copy link
Contributor

mtei commented Jul 6, 2020

Looks good to me. But...

It should be possible to simplify the code by removing the subsequent checks like w1 > 0,

I recommend this. After merging this PR, you can fix #9558 .

@stale
Copy link

stale bot commented Aug 20, 2020

Thank you for your contribution!
This pull request has been automatically marked as stale because it has not had activity in the last 45 days. It will be closed in 30 days if no further activity occurs. Please feel free to give a status update now, or re-open when it's ready.
For maintainers: Please label with awaiting review, breaking_change, in progress, or on hold to prevent the issue from being re-flagged.

@tzarc tzarc requested a review from a team October 4, 2020 01:42
Remove old code which was unsuccessfully trying to clamp negative w1, w2
and w3 values to 0, and set w1_nops, w2_nops and w3_nops directly.
@sigprof
Copy link
Contributor Author

sigprof commented Feb 8, 2021

Now I actually tested the code on a 3.3V Pro Micro board, and even tried to make some actual measurements of the generated PWM signal (using Input Capture on STM32F411 @ 96 MHz).

Before the fix: T0H = 250 ns, T1H = 875 ns, total period = 5250 us (within a byte) or 6250 us (between bytes).
After the fix: T0H = 250 ns, T1H = 875 ns, total period = 1375 us (within a byte) or 2375 us (between bytes).

I also verified timings on a 5V Pro Micro:
Before the fix: T0H = 312.5 ns, T1H = 875 ns, total period = 1250 us (within a byte) or 1750 us (between bytes).
After the fix: T0H = 312.5 ns, T1H = 875 ns, total period = 1250 us (within a byte) or 1750 us (between bytes) — so the fix did not change anything for the 16 MHz configuration, as expected.

@tzarc tzarc merged commit 627ceeb into qmk:master Feb 8, 2021
flarestarwingz pushed a commit to flarestarwingz/qmk_firmware that referenced this pull request Feb 8, 2021
* ws2812: Fix number of nops for AVR at 8 MHz

When trying to calculate the number of nops for AVR running at 8 MHz,
the value of `w3` is expected to be negative; however, because `F_CPU`
is defined in tmk_core/avr.mk with the `UL` suffix, the preprocessor
performs its calculations using `unsigned long`, getting a very large
positive number instead of the expected negative number; this then
results in generating code with a huge number of nops.  Fix the broken
calculations by performing a comparison before subtraction, so that the
unsigned number wraparound does not occur.

The keyboard which triggers the problem is `handwired/promethium`; the
buggy code silently compiles, but the resulting timings would be
completely wrong.

* ws2812: Clean up the code after the 8 MHz fix

Remove old code which was unsuccessfully trying to clamp negative w1, w2
and w3 values to 0, and set w1_nops, w2_nops and w3_nops directly.
sowbug pushed a commit to sowbug/qmk_firmware that referenced this pull request Feb 10, 2021
* ws2812: Fix number of nops for AVR at 8 MHz

When trying to calculate the number of nops for AVR running at 8 MHz,
the value of `w3` is expected to be negative; however, because `F_CPU`
is defined in tmk_core/avr.mk with the `UL` suffix, the preprocessor
performs its calculations using `unsigned long`, getting a very large
positive number instead of the expected negative number; this then
results in generating code with a huge number of nops.  Fix the broken
calculations by performing a comparison before subtraction, so that the
unsigned number wraparound does not occur.

The keyboard which triggers the problem is `handwired/promethium`; the
buggy code silently compiles, but the resulting timings would be
completely wrong.

* ws2812: Clean up the code after the 8 MHz fix

Remove old code which was unsuccessfully trying to clamp negative w1, w2
and w3 values to 0, and set w1_nops, w2_nops and w3_nops directly.
@sigprof sigprof deleted the ws2812-fix-avr-8mhz branch February 10, 2021 08:24
BorisTestov pushed a commit to BorisTestov/qmk_firmware that referenced this pull request May 23, 2024
* ws2812: Fix number of nops for AVR at 8 MHz

When trying to calculate the number of nops for AVR running at 8 MHz,
the value of `w3` is expected to be negative; however, because `F_CPU`
is defined in tmk_core/avr.mk with the `UL` suffix, the preprocessor
performs its calculations using `unsigned long`, getting a very large
positive number instead of the expected negative number; this then
results in generating code with a huge number of nops.  Fix the broken
calculations by performing a comparison before subtraction, so that the
unsigned number wraparound does not occur.

The keyboard which triggers the problem is `handwired/promethium`; the
buggy code silently compiles, but the resulting timings would be
completely wrong.

* ws2812: Clean up the code after the 8 MHz fix

Remove old code which was unsuccessfully trying to clamp negative w1, w2
and w3 values to 0, and set w1_nops, w2_nops and w3_nops directly.
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.

None yet

5 participants