Using CW 6.3
I have an input capture, capturing both edges of a square wave, measuring the periods based on the delta of the timer capture, in this ISR I toggle an output to drive some instrumentation. I do it this way as I use the pin to encode other data in other modes. What is happening is that at random times (every 10 to 20 seconds) there will be an extra write to the pin, changing its state an additional time. This extra change of state happens AFTER the ISR completes. It happens in either polarity, i.e. if the ISR sets the pin, then every once in a while the pin will clear after the ISR returns, and vice versa.
The input capture is TPM12, the pin in question PTA5, both of which are set up with Processor Expert beans/components.
If I toggle PTF5 in the same way, this does not happen
but it DOES happen if I write to PTB4 in the ISR, although the stray pulse on PTA5 and PTB4 (when I write to both) doesn't occurr at the same time.
changing PTA5 to RGPIO cleared it up, but PTB5 and PTF5 don't have RGPIO functionality.
It is truly bizarre. Has any one else seen something like this!
I'm going to try to boil the project down to the minimum that exhibits this, and get it to happen on the demo board. Then perhaps put in an SR. But I thought I'd toss it out to the audience first.
Bob
Are you toggling port B and port A pins from both, main thread and from interrupts? If so, then it is expected behaviour, unless your pin toggles in main thread are atomic operations and aren't interrupted. Check the generated code. In case there are separate read and write instructions, you need to disable interrupts while you are toggling pin in main thread.
Kef wrote:
> Are you toggling port B and port A pins from both, main thread and from interrupts?
Well spotted. I considered that, but have been reading TurboBob's posts for a long time and didn't even consider that he might have fallen into that newbie trap. Bob, tell us it ain't so!
I haven't used the "beans/components", and if they've contributed to this problem (by hiding the fact that the accesses aren't atomic), then detailing that omission here might help others.
In case that's not the problem, and if trying to get the debug module to perform a software controlled "data watchpoint" is too hard, you might try running the CPU with the "Trace" bit set. That would allow you to effectively "single step" your code under software control (with the single-step code looking for data changes). If that is too hard you could program a timer to interrupt at IPL6 at a very high rate (say taking up 50-90% of the CPU time) and have it monitor the ports for you.
I'd like to point out the following from the MCF51JM Reference Manual:
9.3 V1 ColdFire Rapid GPIO FunctionalityThe V1 ColdFire core is capable of performing higher speed I/Ovia its local bus, which does not have latency penaltiesassociated with the on-chip peripheral bus bridge. The RapidGPIO module contains separate set/clear/data registersbased at address 0x(00)C0_0000.
There are two interesting points in the above.
First, the "set/clear" registers allow for atomic pin changes so that mainline access to the data registers don't need interrupts turned off around the accesses.
The "latency penalties" mentioned (but NOT detailed) are always present in these sorts of chips with high-clock-speed CPUs and (you eventually find out) lower clock speed peripherals that jam the CPU with lots of wait-states when accessed. There is seldom anything in any of the manuals telling you that this happens, let alone how many wait states, so you "just have to know".
The MCF5329 that I'm using takes an 18 wait-state hit reading any IO port.The MCF52xx chips apparently take about 12 wait states. Here's someone with a 200MHz CPU taking 4us to flip a pin over and back, plus some good analysis:
https://community.freescale.com/message/42042#42042
That references a post that details the clock timing and mentions the "Rapid GPIO" that can be read here:
The worst chip I've worked on for this (PXA320) stalled the CPU for FOUR HUNDRED CPU CLOCK CYCLES to write to a GPIO register to turn a LED on. Actually the first write was immediate, but the second one would get stalled until the first one completed (after 400 clocks). All reads took this long. The User Manual didn't detail this but did recommend doing all IO (including port reads and writes) by DMA and having the DMA controller interrupt the CPU when the data transfer completed.
Tom
TomE wrote:Kef wrote:
> Are you toggling port B and port A pins from both, main thread and from interrupts?
Well spotted. I considered that, but have been reading TurboBob's posts for a long time and didn't even consider that he might have fallen into that newbie trap. Bob, tell us it ain't so!
it aint so
no, I was toggling from 2 different interrupts, the other one was a compare on a timer channel to generate a square wave, but was mutually exclusive from this one based on a mode flag.
and I commented it out, "just in case"
and I searched my own code for "leftovers" , none found.
So, hopefully no newbie mistakes here.
The pin write I was not 'toggling', but writing to the opposite state every-other interrupt, so the pin write was
like this (this one is a different pin, but you get the idea)
#define WindowSwOutput_SetVal() ( \ (void)setReg8Bits(PTFD, 0x20) /* PTFD5=0x01 */, \ Shadow_PTF |= (byte)0x20 /* Set appropriate bit in shadow variable */ \ )
so its a one-liner, except for the shadow register.
and, if it were related to another call in the main line of the code, I would expect it not to be synchronous with the end of the ISR every time.
I tried putting my pin write at the beginning, middle, end of the ISR, put it twice, lots of strange versions of it. I'm 99.x% sure its not me.
My thoughts are leaning towards a wierd pipeline issue related to the archeitecture..... a real wierd one.
So it may become an SR when I get the chance to boil it down to something the FSL guys can play with in the lab.
But, your guy's input is helpful as a sanity check, thanks for jumping in.
Either of you guys have the JM demo kit with the CF processor?
Bob
> synchronous with the end of the ISR every time.
How synchronous? How accurately can you measure this?
Does it happen "synchronous with the RTI" to the nanosecond, or does it happen "a few milliseconds after the interrupt returns"?
I'd suspect the interrupt isn't being cleared properly and the service routine is running TWICE. Can you check for that by logging the time of the service routine, or by flashing a "reliable pin" (preferably Rapid GPIO) on for the duration of the service routine?
I'm thinking of the usual tricky accesses to the interrupt mask bits in the interrupt controller. Which this CPU doesn't seem to have. The Freescale chips I use mostly use "write one to clear" interrupt handling.
This one doesn't:
22.6.2 Description of Interrupt Operation TPM interrupt flags are cleared by a two-step process including a read ofthe flag bit while it is set (1) followed by a write of zero (0) to the bit.If a new event is detected between these two steps, the sequence is resetand the interrupt flag remains set after the second step to avoid thepossibility of missing the new event.
Make sure that doesn't go wrong or get interrupted.
You could try surrounding everything that accesses any shared resources with code to set the IPL to 7 and back. If the problem goes away with that you start removing them until the problem comes back.
You could also try setting CPUCR[IME] and/or CPUCR[BWD] and see if anything changes.
You could also try compiling with a different optimisation level in case you've foud a compiler bug. Also check the generagte assembly code, just in case.
> Either of you guys have the JM demo kit with the CF processor?
MCF5329 and MCF5235 only in the ColdFire line.
Tom
ok,
it happens right after the ISR, I was toggling a 'reliable pin' at the end of the ISR, and the stray toggle of PTA 5 is 1us later.
I captured it on the logic analyzer, so I have it measured down in the ns range.
The ISR is definately not running twice, I spent considerable time proving that to myself, as this is a very critical ISR in this system. I checked the flag clearing, its ok, I even added a second flag clearing call (both steps).
This is (or was at the time) the only call to PTA5 in the whole software pile.
I tried compiler optimizations from none to max.
I checked the disassembled code, nothing fishy there.
I added code to check SWIACK, just to see if there were pending ISR that might be the issue. Nothing correlated.
So, since RGPIO has fixed the symptom, I'm moving on (the calendar is not my friend on this project). I'll circle back when I have more time.
Thanks for the input!!
Bob
Bob,
Then what about interrupt priority levels? Doesn't one interrupt, which is accessing portX bits, have different level than another interrupt, which is accessing the same portX?
On S08 it would be related to main thread vs interrupt. In case main is modifying port bits not in single instruction, interrupt could interrupt the sequence and make odd resuts. On CF, unless you disable interrupt priority leveling (IME=1), you also should consider interrupting lower level interrupt by higher level interrupt.
Please make sure that CPUCR IME bit is set, or tell us priority levels of interrupts in question. It seems you are using PE. So go to CPU settings, ADVANCED, or EXPERT modes. See CPU configuration-> Interrupt mask. It should be enabled. Also you can verify that in IME bit is set in generated cpu.c __initialize_hardware(). I think that this PE property name and description is odd and misleading.
the 2 ISRs were set up with a flag so that only one would access the port. The flag did not change very often, and didn't change during a test of this.
Also, I disabled the unaffected ISR, with no effect.
In case there was another interrupt messing with me, I raised my interrupt in question to the highest level, and also the lowest level, just trying to change the symptom (which it did not), so its not a nested interrupt issue. I also set it up to watch SWIACK to see if there were other interrupt flags getting set during my ISR, which there were, but it never coincided with my issue.
I also stretched my ISR with a delay loop and the stray toggle moved with the end of my ISR, i.e. it happens right after my ISR returns, regardless of the other ISRs waiting or whatever.
I'm not sure of the internal mechanism in the GPIO, it may be a read-modify-write type operation (most likely is).
So far, I have not identified the cause of this issue, just created a work-around.
Certainly odd.
Bob
> the stray toggle moved with the end of my ISR, i.e. it happens right after my ISR returns,
Here's a very quick test that should only take a minute to code up and might show the culprit. It would at least eliminate a few possibilities.
It still looks like the code that is being interrupted is changing the bit when the ISR returns. The hard part is finding what that code is.
I'm assuming from my memory of your previous descriptions that the ISR flips the bit by writing a "flipped value" to the port.
So could you add a line to the ISR so it reads the port to see if it is in the expected state prior to flipping. At that point it has detected that the error happened just after the PREVIOUS instance of that interrupt.
That isn't much use on its own, but it is very easy to add code to an ISR to look back on the stack and copy the PC of the code that was interrupted. So the ISR records the interrupted PC as well as checking the port, and when it find a flip it prints the PC that was captured on the PREVIOUS interrupt. If this is the same one every time (or one of 2 or 3) then looking at the code at that PC could show the problem.
Getting the interrupted PC from the stack is dead easy. The following ismore complicated than you'll probably need. In my codethe following function is called from a timer interrupt service routine, so we're "two deep" from the interrupt, andhave to walk back through a stack frame:
/* Stack Frame: Return Addr Previous FP Function Stack PC of Interrupted Function aka (i[1]])[2] Exception Word Previous FP aka *i[1] Function Stack (of ISR) PC inside ISR Frame Pointer aka i[1] i[0]*/static void DebugPageUProfileIsr(void){ int32_t i[1]; int32_t pc = i[1]; /* i[1] is exception frame link */ pc = ((uint32_t *)pc)[2]; /* Skip exception frame, get PC */
I'm using GCC so that's the stack frame the GCC ABI uses. Your compiler might be different. In your case I'd expect the PC to be at i[1] or i[2]. You could print all three and see which one matches a code address and matches your debugger's calling stack display. You'll notice the "trick" of walking off the end of the array on the stack. This trick assumes the compiler allocates the local variables on the stack in the order given. If it changes the order then you need to reverse-engineer this and change the magic offsets accordingly.
Tom
I did this, to verify that the condition was trappable (at least after-the-fact) and it was.
But of course the condition happened in the previous ISR, so I didn't spend too much time going down that path.
Thats a good idea to capture the previous stackframe. when I get back into this I'll check it.
Thanks for the good ideas,
Bob
I have an idea. I haven't done this on an MCF chip, but I've done something very similar on an MPC one.
The MCF51JM has a "Version 1 ColdFire Debug (CF1_DEBUG)". It should be able to support a "Data Watchpoint" trap.
The registers in the Debug Module can be written from the CPU with the WDEBUG instruction.
Set up ABLR and ABHR to cover the range of peripheral addresses that you think are getting clobbered.
Set up the other registers to trigger a Data Breakpoint Interrupt (CSR[BKD], TDR[TDC] and others).
Disable the Watchpoint around the code where you know you're writing the GPIO registers and enable it immediately afterwards.
If some other code is writing to the register when it shouldn't be, the Watchpoint should trigger and drop you into an interrupt. From there you should dump the stack to find out which line of code did it.
Tom
sounds like a reasonable plan.
Bob