machine.Pin('P15') crash debugging
-
I noticed that
machine.Pin('P15')
causes a crash on LoPy firmware <=0.9.4.b1:>>> p=machine.Pin('P15') Guru Meditation Error of type IllegalInstruction occurred on core 0. Exception was unhandled. Register dump: PC : 400d68b8 PS : 00060930 A0 : 800d6e81 A1 : 3ffdd370 A2 : 3ffd5cd0 A3 : 00000000 A4 : 00000001 A5 : 00000000 A6 : ffffffff A7 : 3ffdd530 A8 : 800d6e3b A9 : 3ffdd340 A10 : 00000026 A11 : 00000028 A12 : 00000027 A13 : 00000040 A14 : 00000000 A15 : 00000020 SAR : 00000007 EXCCAUSE: 00000000 EXCVADDR: 00000000 LBEG : 4000c46c LEND : 4000c477 LCOUNT : ffffffff CPU halted.
Significantly,
machine.Pin.module.P15
exists and doesn't crash to access. So the bug is isolated to some pins when the Pin class is called (which normally would involve creating a new instance). I expect the pin instances are singletons, since id(machine.Pin.module.P19) == id(machine.Pin('P19')). P15 and P16 crash in this manner.The particular pin I'm debugging, P15, is also known as G0 and GPI38 (P16=GPI39=G3 is similar, but has the Vbat connection on the expansion board). That places it in two unusual groups: above 32 and input only. It's input only because it's one of the dedicated analog inputs, which also means it's an RTC pin.
Calling
init()
on the pin causes an identically reported crash, suggesting the issue is withinpin_obj_init_helper
, which is called by bothpin_make_new
andpin_obj_init
.A quick
objdump -d build/LOPY/release/application.elf
indicates that the crash address is inmachpin_disable_pull_up
. The odd thing about it is that it gives the precise start of that function, which should be a fully general instruction, hardly invalid. A0 would point at a return instruction inpin_config
, except for the part where it starts with 8 not 4. Not sure if that's at all relevant. d6e3b, found in A8, looks like a jump target inpin_obj_configure
. Those are reasonable places to have visited, though the disassembly seems to have failed to realign after a realigning set of jumps. The compiler generated assembly does not show such confusion.I'm tired for now. I will probably resume looking at this tomorrow.
-
@Ralph No worries. It turned out those two were the same subtle bug, and my 3 letter fix was applied not only where I found it but a handful more places. Commit fc4dd58 contains that fix, and reenables a bit of pin initialization which I'm guessing was commented out because it hit the same bug.
-
Hi @LoneTech. Thanks for helping out here, the embedded team merged your pull request yesterday.
I hear from @daniel that they are planning to completely re-write the interrupt code from scratch. You might want to consider that before you dive in too deep :)
-
The bug I'm trying to hunt down right now is a little more distinct. The
irq
method on pins numbered higher than 32 produces irq objects that link to their lower numbered counterparts; so for instance trying to set an interrupt handler for P19=GPIO32 instead affects P2=GPIO0. They produce separate irq objects but both affect the registers for GPIO 0.
-
@LoneTech No problem. I'll create the GitHub issue anyway and if it's fixed by the pull request we can close it. Thanks for your contributions
-
@sakis Sorry, I'm confusing myself. It's the non-issue of the bit shifts that I did a pull request for, I haven't tracked down the crash. Still learning to get around the code, I'm aiming at the oddity where interrupts seem to map wrong for GPIO>=32 at the moment.
-
@LoneTech Then no need to create a new issue right? Thanks
-
This post is deleted!
-
Hi,
Thanks for the detailed report. I confirm that this is a bug and I'll create a GitHub issue.
-
Minor hint for others who choose to inspect the assembly, since objdump doesn't realign properly (the reason it needs to is perhaps a linker weakness). If you run make with
V=1
, it prints the exact commands run. You can then pick a particular object file and replace-c -o filename.o
with-S -fverbose-asm -o filename.S
to generate somewhat more readable assembly.
-
Department of pseudobugs: In
pin_set_value
, the expression1 << pin_number
is used for numbers larger than 32. This is undefined behaviour in C, but happens to work because thessl
instruction only reads 5 bits. GCC seems unaware of this, since adding a bitmask to make the behaviour defined adds an unnecessaryextui
instruction. Edit: Apparently only because it didn't optimize fully. Doing it in the same expression instead of to a new variable worked better.