Briefly, I have a system where the flex timer running at approximately 700Hz is used to trigger the DMA from a 512x16bit buffer to the RGPIO pins which are connected to some LEDs. At the end of the DMA transfer, there's an interrupt which resets the count back to the start and optionally performs any updates on the buffer. The problem is that every now and then (anywhere from a few seconds to a couple of minutes apart), there's a brief strobe on the output that's very visible. The interrupt has been elevated to level 7 to give it the highest priority, and the blinking still happens. I haven't tried implementing a circular buffer as I'm not sure how to do that so that it runs indefinitely, and there are cases where I must do some processing in that interrupt (although I kick off the next DMA cycle in the first three instructions of the IRQ).
1. Are there any known cases where the DMA may have priority under another memory device that could postpone the transfer?
2. Has anyone experienced any jitter with the ColdFire's FlexTimer module?
3. Can I implement this with a true circular buffer and still have an end-of-DMA interrupt for other things?
4. Is there anything that can supersede a level 7 interrupt?
Any help would be appreciated. Thanks.
The answers to your questions depend on which CPU you're using, which you didn't say. I assume it is one of the MCF51 series as they're the ones with the "Flex Timer"..
It can't be the MCF51AC as that has the FlexTimer, but doesn't have DMA.
According to the Product Summary, the MCF51AG128 is the only one with DMA.
Is your "700Hz clock" starting a burst of DMA or is is triggering one 16-bit word at 700Hz? I'm guessing the latter, or it doesn't make much sense.
The DMA controller should only be transferring one word at a time. If it is getting away from you and starting a "burst" it might explain your problem. You may have a software bug with the DAM programming. Try disabling it before reprogramming for the next burst. Does the DMA controller have programmable priority in this chip? Is there a "crossbar switch" like there is in the higher chips?
Program a pin as the output of a FlexTimer channel and watch it on an oscilloscope. Maybe you can connect a FlexTimer output back to another timer in input capture mode and actually measure the FlexTimer period to the microsecond.
(3) Circular Buffer.
Yes. You should do that. Time to learn. This is stuff you need to know. Obvious starting point:
Follow the "Bip Buffer' link too. Other obvious places - just ask Google to find you code with "circular buffer" in it.
You should probably use a "ping pong buffer" rather than a circular one though. Again:
Assuming you've got an MCF51AG, then the DMA controller doesn't seem to support the "Buffer Chaining" that more sophisticated controllers can. You're just going to have to design your software and hardware to absolutely guarantee you can respond to the DMA Interrupt within 1/700th of a second.
(4) Beating Level 7?
Do you have a debug pod connected?
Don't use level 7. It can get you into all sorts or trouble. Use Level 6 and then find, eliminate or minimise any places where you disable interrupts in your code. If you do have any "disable interrupt" code, you should replace it with code that only elevates the priority "as high as necessary". For instance, if you have an interrupt driven serial port interrupting at IPL4 then the serial driver code should only raise the CPU to Level 4 when manipulating the buffer pointers in the mainline.
You should find out what the "strobe on the output" is. Are the LEDs being forced on for a time or off for a time? How long? Measure it (get an Oscilloscope). Is it "expected data" or is it running off the end of your buffer and sending data you don't expect to be sent? You should fill your buffer with a "known pattern", say the values 0-511. When sent the leds should blink in a binary counting sequence. You should also put a "known pattern" in the memory after the buffer in case you are getting an "overrun". When it goes wrong you might be able to see what's happening. If you can't get an oscilloscope, record the LEDs with a camera in movie-mode and then see if you can play the movie one frame at a time to see what is going wrong.
Thanks for the long reply. It was on a MCF51JU64 which does have DMA chaining (afaik). I also made a typo, it's 700kHz, not 700Hz, a few orders of magnitude different. I might look into page flipping since modifying the DMA buffer while "active" probably isn't a good idea and I have RAM to spare.
If you're wondering why such a high rate, it's because I was also generating a PWM output for 4-pin fans (which is supposed to be 22.5kHz). Now 22.5kHz * 256 is too high (5.76MHz) so I split the PWM into 8 cycles with each cycle having 32 levels of resolution. So within a long cycle I could achieve 256 levels of PWM but still maintain the requisite 22.5kHz output. The slight dithering of the PWM wasn't noticeable.
The FlexTimer was rock-solid, so then I suspected the problem was down to the Crossbar Switch. The USB is a higher-priority master than the DMA which might cause a conflict when accessing the same slave. This became evident when the flickering would subside when USB was disconnected (although not when idle due to I'm guessing the 1ms SOF packets).
I made a bunch of changes; first was going to a 16x16 envelope, thus cutting my primary DMA rate down to ~360kHz. This reduced the chance of the flicker happening, but didn't eliminate it. Attempting the see if the Crossbar was the culprit I moved the source of the PWM around to no avail -- the round robin scheduler might have delayed the access by only a few clock cycles which would have been imperceptible to the human eye.
Of course, the problem was weirder, because the original project was imported from a legacy S08 project, the JU64 was put into S08 compatibility mode, meaning one interrupt at a time -- no nesting. So if I was in a particularly long interrupt when the DMA interrupt was supposed to happen it would be delayed until the interrupt I was in would finish, regardless of priority. This was exacerbated by some bad design decisions where the majority of the program lived in interrupt-space, wrought full of floating point and complex logic.
So the USB would finish and kick of a flurry of activity, and there'd be this chance that the DMA interrupt would happen while I was in the middle of say computing the logarithmic temperature from a thermistor, and bam, white light. It's crazy how slow Freescale's math library is. Anyway, I rewrote the interrupts, into a simple priority-based cooperative scheduler with event flags set within the interrupts instead. This was a huge rework, but the flickering has gone away.
Non-maskable interrupts are the devils creation. I've learned my lesson -- never put too much work in the interrupts, and even the most basic priority based scheduler is better than a mostly interrupt loving super-loop. Nested interrupts (coming from ARM) are their own kettle of worms, so I'm happy that the scheduler did the trick.