Insidious Bug
-
This little snippet of ARM Cortex-M3 assembly code has an insidious bug. It cost me multiple hours of debug time including unnecessarily playing with interrupt priorities.
; equates used below I2CMaster equ 0x40020000 I2CMCS equ 0x004 ;R/W I2C Master Control/Status I2CMICR equ 0x01C ;WO I2C Master Interrupt Clear ; my ISR began like this... I2CHandler ldr r0,=I2CMaster ;\ movs r1,#1 ; > reset that int now str r1,[r0,#I2CMICR];/ ldr r1,[r0,#I2CMCS] ands r1,r1,#0x01 it ne bxne lr ;return if busy ; ... ;code continues...
For those who wonder about I2CMICR and I2CMCS, they are defined correctly. FWIW, I am using The Luminary Micro Stellaris™ LM3S811 Evaluation Kit with its enclosed ILR “Jump Start” IDE. (I call the that IDE the “get to know us for free before we surprise you with how much the full version costs” version.) ARM Cortex-M3 processors are setup to use raw C code directly in an ISR. Specifically, an interrupt saves r0-r3 along with the (subroutine return) link register and the PSW. The link register is loaded with a funky value such that the normal C subroutine return restores what was saved. Bottom line on this: an ISR using only r0-r3 does not have to concern itself with register saves and restores. ARM Cortex-M3 has an if/then instruction. After the opcode “it” up to 4 instructions are conditional on the operand condition. In my code snippet that condition is (PSW not equal) ne, using the result of the previous ands instruction. Those not familiar with this processor may safely assume that my ands instruction performs r1=and(r1,0x01) with PSW setting. Many years ago I had the same bug in another assembler for a totally different processor. After a few days I will share some detail debug info. For now I will only say that my system appeared to stop working. And nothing I remembered doing should have had any effect. -Peter -
This little snippet of ARM Cortex-M3 assembly code has an insidious bug. It cost me multiple hours of debug time including unnecessarily playing with interrupt priorities.
; equates used below I2CMaster equ 0x40020000 I2CMCS equ 0x004 ;R/W I2C Master Control/Status I2CMICR equ 0x01C ;WO I2C Master Interrupt Clear ; my ISR began like this... I2CHandler ldr r0,=I2CMaster ;\ movs r1,#1 ; > reset that int now str r1,[r0,#I2CMICR];/ ldr r1,[r0,#I2CMCS] ands r1,r1,#0x01 it ne bxne lr ;return if busy ; ... ;code continues...
For those who wonder about I2CMICR and I2CMCS, they are defined correctly. FWIW, I am using The Luminary Micro Stellaris™ LM3S811 Evaluation Kit with its enclosed ILR “Jump Start” IDE. (I call the that IDE the “get to know us for free before we surprise you with how much the full version costs” version.) ARM Cortex-M3 processors are setup to use raw C code directly in an ISR. Specifically, an interrupt saves r0-r3 along with the (subroutine return) link register and the PSW. The link register is loaded with a funky value such that the normal C subroutine return restores what was saved. Bottom line on this: an ISR using only r0-r3 does not have to concern itself with register saves and restores. ARM Cortex-M3 has an if/then instruction. After the opcode “it” up to 4 instructions are conditional on the operand condition. In my code snippet that condition is (PSW not equal) ne, using the result of the previous ands instruction. Those not familiar with this processor may safely assume that my ands instruction performs r1=and(r1,0x01) with PSW setting. Many years ago I had the same bug in another assembler for a totally different processor. After a few days I will share some detail debug info. For now I will only say that my system appeared to stop working. And nothing I remembered doing should have had any effect. -PeterBack to back interrupts perhaps? 1) first interrupt occurs, and you process the first three handler ops, clearing the interrupt. 2) second pending interrupt occurs immediately, changes the I2CMCS register. 3) second interrupt completes normally. 4) resume handling of first interrupt, I2CMCS now no longer correct. Something like that? If so, I'm guessing the fix involves reading I2CMCS before clearing the first interrupt (and thus permitting another interrupt to occur)... -- Ian
-
Back to back interrupts perhaps? 1) first interrupt occurs, and you process the first three handler ops, clearing the interrupt. 2) second pending interrupt occurs immediately, changes the I2CMCS register. 3) second interrupt completes normally. 4) resume handling of first interrupt, I2CMCS now no longer correct. Something like that? If so, I'm guessing the fix involves reading I2CMCS before clearing the first interrupt (and thus permitting another interrupt to occur)... -- Ian
Depending on interrupt priority your #1 could happen. Likewise #2a. But ISR code is supposed to play nice. And because I wrote all the code involve (save for an unused strlen and other simple library stuff I don’t know how to get rid of) my other ISR only reads the ADC and works a few GPIO line for the analog mux. #3 and #4a could happen subject to interrupt priority but that ADC ISR knows nothing of my I2C use. ARM Cortex-M3 will leave pending interrupts at the same or lower priority until the current ISR ends with its special link register value. This ARM variant will interrupt lower priority interrupt service to run a higher priority handler. Well behaved ISR’s do not step on stuff that ain’t theirs. So that is not the answer. Everything needed to solve the problem is in my post. To leave out an external misbehaving ISR would not be fair. Strange off the wall hint posed as a chemistry question . . . Q what is HIJHLMNO? Scroll down for answer . . . . . . . . . Is that far enough? . . A: its H2O – HtoO Sorry for the silly chemistry thing but the fewer details you know about interrupt handlers and the diverse conflicts they can lead one into the easier it might be to find this little nasty. Just as someone who knows little chemistry might say H . . . to . . . Oh . . .
-
Depending on interrupt priority your #1 could happen. Likewise #2a. But ISR code is supposed to play nice. And because I wrote all the code involve (save for an unused strlen and other simple library stuff I don’t know how to get rid of) my other ISR only reads the ADC and works a few GPIO line for the analog mux. #3 and #4a could happen subject to interrupt priority but that ADC ISR knows nothing of my I2C use. ARM Cortex-M3 will leave pending interrupts at the same or lower priority until the current ISR ends with its special link register value. This ARM variant will interrupt lower priority interrupt service to run a higher priority handler. Well behaved ISR’s do not step on stuff that ain’t theirs. So that is not the answer. Everything needed to solve the problem is in my post. To leave out an external misbehaving ISR would not be fair. Strange off the wall hint posed as a chemistry question . . . Q what is HIJHLMNO? Scroll down for answer . . . . . . . . . Is that far enough? . . A: its H2O – HtoO Sorry for the silly chemistry thing but the fewer details you know about interrupt handlers and the diverse conflicts they can lead one into the easier it might be to find this little nasty. Just as someone who knows little chemistry might say H . . . to . . . Oh . . .
PICguy wrote:
Q what is HIJHLMNO?
You forgot K
I have no blog...
-
Depending on interrupt priority your #1 could happen. Likewise #2a. But ISR code is supposed to play nice. And because I wrote all the code involve (save for an unused strlen and other simple library stuff I don’t know how to get rid of) my other ISR only reads the ADC and works a few GPIO line for the analog mux. #3 and #4a could happen subject to interrupt priority but that ADC ISR knows nothing of my I2C use. ARM Cortex-M3 will leave pending interrupts at the same or lower priority until the current ISR ends with its special link register value. This ARM variant will interrupt lower priority interrupt service to run a higher priority handler. Well behaved ISR’s do not step on stuff that ain’t theirs. So that is not the answer. Everything needed to solve the problem is in my post. To leave out an external misbehaving ISR would not be fair. Strange off the wall hint posed as a chemistry question . . . Q what is HIJHLMNO? Scroll down for answer . . . . . . . . . Is that far enough? . . A: its H2O – HtoO Sorry for the silly chemistry thing but the fewer details you know about interrupt handlers and the diverse conflicts they can lead one into the easier it might be to find this little nasty. Just as someone who knows little chemistry might say H . . . to . . . Oh . . .
-
This little snippet of ARM Cortex-M3 assembly code has an insidious bug. It cost me multiple hours of debug time including unnecessarily playing with interrupt priorities.
; equates used below I2CMaster equ 0x40020000 I2CMCS equ 0x004 ;R/W I2C Master Control/Status I2CMICR equ 0x01C ;WO I2C Master Interrupt Clear ; my ISR began like this... I2CHandler ldr r0,=I2CMaster ;\ movs r1,#1 ; > reset that int now str r1,[r0,#I2CMICR];/ ldr r1,[r0,#I2CMCS] ands r1,r1,#0x01 it ne bxne lr ;return if busy ; ... ;code continues...
For those who wonder about I2CMICR and I2CMCS, they are defined correctly. FWIW, I am using The Luminary Micro Stellaris™ LM3S811 Evaluation Kit with its enclosed ILR “Jump Start” IDE. (I call the that IDE the “get to know us for free before we surprise you with how much the full version costs” version.) ARM Cortex-M3 processors are setup to use raw C code directly in an ISR. Specifically, an interrupt saves r0-r3 along with the (subroutine return) link register and the PSW. The link register is loaded with a funky value such that the normal C subroutine return restores what was saved. Bottom line on this: an ISR using only r0-r3 does not have to concern itself with register saves and restores. ARM Cortex-M3 has an if/then instruction. After the opcode “it” up to 4 instructions are conditional on the operand condition. In my code snippet that condition is (PSW not equal) ne, using the result of the previous ands instruction. Those not familiar with this processor may safely assume that my ands instruction performs r1=and(r1,0x01) with PSW setting. Many years ago I had the same bug in another assembler for a totally different processor. After a few days I will share some detail debug info. For now I will only say that my system appeared to stop working. And nothing I remembered doing should have had any effect. -PeterGood god, am I happy that I don't have to dwell into these depths of bit juggling!
Regards, mav -- Black holes are the places where God divided by 0...
-
Good god, am I happy that I don't have to dwell into these depths of bit juggling!
Regards, mav -- Black holes are the places where God divided by 0...
-
This little snippet of ARM Cortex-M3 assembly code has an insidious bug. It cost me multiple hours of debug time including unnecessarily playing with interrupt priorities.
; equates used below I2CMaster equ 0x40020000 I2CMCS equ 0x004 ;R/W I2C Master Control/Status I2CMICR equ 0x01C ;WO I2C Master Interrupt Clear ; my ISR began like this... I2CHandler ldr r0,=I2CMaster ;\ movs r1,#1 ; > reset that int now str r1,[r0,#I2CMICR];/ ldr r1,[r0,#I2CMCS] ands r1,r1,#0x01 it ne bxne lr ;return if busy ; ... ;code continues...
For those who wonder about I2CMICR and I2CMCS, they are defined correctly. FWIW, I am using The Luminary Micro Stellaris™ LM3S811 Evaluation Kit with its enclosed ILR “Jump Start” IDE. (I call the that IDE the “get to know us for free before we surprise you with how much the full version costs” version.) ARM Cortex-M3 processors are setup to use raw C code directly in an ISR. Specifically, an interrupt saves r0-r3 along with the (subroutine return) link register and the PSW. The link register is loaded with a funky value such that the normal C subroutine return restores what was saved. Bottom line on this: an ISR using only r0-r3 does not have to concern itself with register saves and restores. ARM Cortex-M3 has an if/then instruction. After the opcode “it” up to 4 instructions are conditional on the operand condition. In my code snippet that condition is (PSW not equal) ne, using the result of the previous ands instruction. Those not familiar with this processor may safely assume that my ands instruction performs r1=and(r1,0x01) with PSW setting. Many years ago I had the same bug in another assembler for a totally different processor. After a few days I will share some detail debug info. For now I will only say that my system appeared to stop working. And nothing I remembered doing should have had any effect. -PeterYou know, THIS is why we need a new smiley. It should start off with either the :omg: or :wtf: expressions, then just explode into a pile of bloody sushi!
A guide to posting questions on CodeProject[^]
Dave Kreskowiak Microsoft MVP Visual Developer - Visual Basic
2006, 2007 -
You know, THIS is why we need a new smiley. It should start off with either the :omg: or :wtf: expressions, then just explode into a pile of bloody sushi!
A guide to posting questions on CodeProject[^]
Dave Kreskowiak Microsoft MVP Visual Developer - Visual Basic
2006, 2007Mmmmm... sushi...
-
This little snippet of ARM Cortex-M3 assembly code has an insidious bug. It cost me multiple hours of debug time including unnecessarily playing with interrupt priorities.
; equates used below I2CMaster equ 0x40020000 I2CMCS equ 0x004 ;R/W I2C Master Control/Status I2CMICR equ 0x01C ;WO I2C Master Interrupt Clear ; my ISR began like this... I2CHandler ldr r0,=I2CMaster ;\ movs r1,#1 ; > reset that int now str r1,[r0,#I2CMICR];/ ldr r1,[r0,#I2CMCS] ands r1,r1,#0x01 it ne bxne lr ;return if busy ; ... ;code continues...
For those who wonder about I2CMICR and I2CMCS, they are defined correctly. FWIW, I am using The Luminary Micro Stellaris™ LM3S811 Evaluation Kit with its enclosed ILR “Jump Start” IDE. (I call the that IDE the “get to know us for free before we surprise you with how much the full version costs” version.) ARM Cortex-M3 processors are setup to use raw C code directly in an ISR. Specifically, an interrupt saves r0-r3 along with the (subroutine return) link register and the PSW. The link register is loaded with a funky value such that the normal C subroutine return restores what was saved. Bottom line on this: an ISR using only r0-r3 does not have to concern itself with register saves and restores. ARM Cortex-M3 has an if/then instruction. After the opcode “it” up to 4 instructions are conditional on the operand condition. In my code snippet that condition is (PSW not equal) ne, using the result of the previous ands instruction. Those not familiar with this processor may safely assume that my ands instruction performs r1=and(r1,0x01) with PSW setting. Many years ago I had the same bug in another assembler for a totally different processor. After a few days I will share some detail debug info. For now I will only say that my system appeared to stop working. And nothing I remembered doing should have had any effect. -Peteried assumed incorrectly. The bug is among the mundane things that no one suspects. Consider the following C/C++ do nothing program that does NOT generate a compile time error. But to its credit my MSVC++ ver 6 does issue a warning which if read, points the reader in the right direction.
void main(void) { int i; i=0; // \ what? // > no error on this line?? return; // / }
Had the IAR assembler issued a similar warning I would have muttered a thank you toward its install directory. Hey, there is no excuse not to be polite – even to software. But for software that does not catch my mundane errors there is always The Code Project Subtle Bugs forum. Does everybody see it now? -Peter -
This little snippet of ARM Cortex-M3 assembly code has an insidious bug. It cost me multiple hours of debug time including unnecessarily playing with interrupt priorities.
; equates used below I2CMaster equ 0x40020000 I2CMCS equ 0x004 ;R/W I2C Master Control/Status I2CMICR equ 0x01C ;WO I2C Master Interrupt Clear ; my ISR began like this... I2CHandler ldr r0,=I2CMaster ;\ movs r1,#1 ; > reset that int now str r1,[r0,#I2CMICR];/ ldr r1,[r0,#I2CMCS] ands r1,r1,#0x01 it ne bxne lr ;return if busy ; ... ;code continues...
For those who wonder about I2CMICR and I2CMCS, they are defined correctly. FWIW, I am using The Luminary Micro Stellaris™ LM3S811 Evaluation Kit with its enclosed ILR “Jump Start” IDE. (I call the that IDE the “get to know us for free before we surprise you with how much the full version costs” version.) ARM Cortex-M3 processors are setup to use raw C code directly in an ISR. Specifically, an interrupt saves r0-r3 along with the (subroutine return) link register and the PSW. The link register is loaded with a funky value such that the normal C subroutine return restores what was saved. Bottom line on this: an ISR using only r0-r3 does not have to concern itself with register saves and restores. ARM Cortex-M3 has an if/then instruction. After the opcode “it” up to 4 instructions are conditional on the operand condition. In my code snippet that condition is (PSW not equal) ne, using the result of the previous ands instruction. Those not familiar with this processor may safely assume that my ands instruction performs r1=and(r1,0x01) with PSW setting. Many years ago I had the same bug in another assembler for a totally different processor. After a few days I will share some detail debug info. For now I will only say that my system appeared to stop working. And nothing I remembered doing should have had any effect. -PeterPICguy wrote:
I2CMaster equ 0x40020000
It's several years since I did ARM assembler (which I enjoyed :)) but is an EQU long enough to hold a 32-bit value? I remember EQUB and EQUD and EQUS but don't recognise EQU.
-
This little snippet of ARM Cortex-M3 assembly code has an insidious bug. It cost me multiple hours of debug time including unnecessarily playing with interrupt priorities.
; equates used below I2CMaster equ 0x40020000 I2CMCS equ 0x004 ;R/W I2C Master Control/Status I2CMICR equ 0x01C ;WO I2C Master Interrupt Clear ; my ISR began like this... I2CHandler ldr r0,=I2CMaster ;\ movs r1,#1 ; > reset that int now str r1,[r0,#I2CMICR];/ ldr r1,[r0,#I2CMCS] ands r1,r1,#0x01 it ne bxne lr ;return if busy ; ... ;code continues...
For those who wonder about I2CMICR and I2CMCS, they are defined correctly. FWIW, I am using The Luminary Micro Stellaris™ LM3S811 Evaluation Kit with its enclosed ILR “Jump Start” IDE. (I call the that IDE the “get to know us for free before we surprise you with how much the full version costs” version.) ARM Cortex-M3 processors are setup to use raw C code directly in an ISR. Specifically, an interrupt saves r0-r3 along with the (subroutine return) link register and the PSW. The link register is loaded with a funky value such that the normal C subroutine return restores what was saved. Bottom line on this: an ISR using only r0-r3 does not have to concern itself with register saves and restores. ARM Cortex-M3 has an if/then instruction. After the opcode “it” up to 4 instructions are conditional on the operand condition. In my code snippet that condition is (PSW not equal) ne, using the result of the previous ands instruction. Those not familiar with this processor may safely assume that my ands instruction performs r1=and(r1,0x01) with PSW setting. Many years ago I had the same bug in another assembler for a totally different processor. After a few days I will share some detail debug info. For now I will only say that my system appeared to stop working. And nothing I remembered doing should have had any effect. -PeterFrom your hint below I surmise that you omitted the controlled statements, rather as if you'd done
if ( ( i & 1 ) == 0 );
return;I wasn't previously familiar with Thumb-2 instruction set, which is the only instruction set on this processor. I see a note in the Quick Reference Card[^] (PDF) that says: "All Thumb-2 instructions (except those with Note U) can have any one of these condition codes after the instruction mnemonic. This condition is encoded in a preceding IT instruction (except in the case of conditional Branch instructions). Condition codes in instructions must match those in the preceding IT instruction." Therefore I think the IT is unnecessary and perhaps even incorrect in front of the branch instruction. Having said that I'm not sure BX is necessary on this processor if it doesn't implement the classic ARM instruction set (that's my reading of the ARMv7-M profile). The classic ARM function return is
mov pc,lr
or if balancing the stack as well,ldmia sp!,{regs,pc}
assuming that the function prolog usedstmdb sp!,{regs,lr}
(which is SUCH a cool instruction!) I'm not totally sure whether it's possible tomov
to the pc register on Thumb-2, though! So if it really is illegal, you probably got an illegal instruction fault. My experience with ARM is only on Pocket PCs and other Windows CE-based devices, so most of the code is generated for ARMv4 at the latest (some of it for ARMv3 if using the eVC 3.0 compilers). I've found knowing ARM assembly useful even if I'm only reading the output of the compiler - sometimes a program will raise an exception (e.g. access violation, data alignment fault) in the middle of a system routine and because it doesn't have debugging symbols :mad: and it can't read the stack unwind info :mad::mad: you have to unwind the stack by hand to find out where you are in your program. My reference book is very old - The ARM RISC Chip: A Programmer's Guide[^] but mos -
This little snippet of ARM Cortex-M3 assembly code has an insidious bug. It cost me multiple hours of debug time including unnecessarily playing with interrupt priorities.
; equates used below I2CMaster equ 0x40020000 I2CMCS equ 0x004 ;R/W I2C Master Control/Status I2CMICR equ 0x01C ;WO I2C Master Interrupt Clear ; my ISR began like this... I2CHandler ldr r0,=I2CMaster ;\ movs r1,#1 ; > reset that int now str r1,[r0,#I2CMICR];/ ldr r1,[r0,#I2CMCS] ands r1,r1,#0x01 it ne bxne lr ;return if busy ; ... ;code continues...
For those who wonder about I2CMICR and I2CMCS, they are defined correctly. FWIW, I am using The Luminary Micro Stellaris™ LM3S811 Evaluation Kit with its enclosed ILR “Jump Start” IDE. (I call the that IDE the “get to know us for free before we surprise you with how much the full version costs” version.) ARM Cortex-M3 processors are setup to use raw C code directly in an ISR. Specifically, an interrupt saves r0-r3 along with the (subroutine return) link register and the PSW. The link register is loaded with a funky value such that the normal C subroutine return restores what was saved. Bottom line on this: an ISR using only r0-r3 does not have to concern itself with register saves and restores. ARM Cortex-M3 has an if/then instruction. After the opcode “it” up to 4 instructions are conditional on the operand condition. In my code snippet that condition is (PSW not equal) ne, using the result of the previous ands instruction. Those not familiar with this processor may safely assume that my ands instruction performs r1=and(r1,0x01) with PSW setting. Many years ago I had the same bug in another assembler for a totally different processor. After a few days I will share some detail debug info. For now I will only say that my system appeared to stop working. And nothing I remembered doing should have had any effect. -PeterSteve_Harris, Equ is a simple equate which on this assembler had better (and does) work as expected on this 32-bit processor. Mike Dimmick, Quite a while ago ARM used to be pure 32-bit instructions. Then came the 16-but Thumb instructions. Subroutines called ARM code using address ending in 00 (two zero bits) and called Thumb code with addresses ending in x1. You had to tell the assembler which type of code you were using. #pragma’s allowed tweaking compiler output for speed (ARM) or code size (Thumb.) The Cortex-M3 is supposed to be the best of both worlds. In a 32-bit instruction one can get r9=r2+r3 with optional PSW setting. In a 16 bit instruction one can do r2=r2+r3 and set the PSW. With only 6 bits to hold register numbers the destination and one source must be r0-r7 and the other register must be r0-r7. Thus by avoiding the higher numbered registers a compiler (and even a person) can generate shorter code while still having access to the upper registers as needed. BTW, the I2CMICR register has only one bit that means anything. In my code fragment at the top of this thread storing 0x01 clears the interrupt condition. BUT MY INSIDIOUS BUG had nothing to do with that at all. . LAST CHANCE spoiler coming up . . . . . . The problem is the backslash at the end of the line just before where I interned to load r1 with 0x01. In this assembler and MSVC++ ver 6 and many other languages and makefiles a backslash at the end of a line makes the next line a continuation of the line ending with a backslash. Thus my movs r1,#1 was looked at as a part of the comment beginning on the prior line. The low bit of whatever was in r1 at the time of the interrupt was what cleared or did not clear the interrupt. Thus when I exited the interrupt with “bx lr” (which more than anything else sets pc=lr) I came right back. But somehow it continued as if the interrupt had been cleared when I was single stepping. (Side note: The funky “address” put into lr when entering the interrupt when moved to pc does the reload r0-r3, PSW and the rest of returning from an interrupt.) When debugging this I clicked on the processor pause icon and run alternately. I always paused in the early instructions of my handler. Finally I set a breakpoint on the str (store register) instruction and looked at r1. It was far from 0x01. A quick glance at the disassembled code, followed by !@#$% and I had the last clue needed. Clearly the movs was not getting into my object file. Then I saw the backsla
-
mav.northwind wrote:
Good god, am I happy that I don't have to dwell into these depths of bit juggling!
Really? I miss it.
Faith is a fine invention For gentlemen who see; But microscopes are prudent In an emergency! -Emily Dickinson