> The reason I have such large apparent latency sometimes, is that several parts of our code disable interrupts, to ensure atomic operations.
But why is the "atomic operation" taking so darn long? The "atomic operation" should be "grab the lock flag" or "insert or delete one thing on a list", but it looks like your code might be searching a list or array. It shouldn't be too hard to make this better with smarter code and a better design.
> but I don't want to rewrite (our TCP/IP stack, for instance).
Why does a TCP stack lock out interrupts for so long? What is it doing (frankly, what is it doing wrong)?
There's another way. IPL7. The non-maskable interrupt. I'd normally steer well clear of ever doing this, but have just found one of our other products is using IPL7 all over the place without any obvious problems.
If you type "IPL7" into the "Search" you'll find previous posts on this. 12 of them by me I'm surprised to find!
So to support MODBUS I'd suggest writing the Serial port interrupt service routine to interrupt at IPL7. You can do this "all the time" or possibly just switch the level to IPL7 when you have a critical timing requirement.
If the rest of your code can handle interrupts being locked out for 3.5 characters, or the other times, then just spin in a hard loop in that ISR to obey the timing requirements. If the rest of your code can't handle that, then have the critical serial port interrupt drop the serial port interrupt priority back to normal and start a PIT timer to interrupt at IPL7 when you need to do the next thing in the protocol.
You should be able to design a simple state machine where the state transitions are made by normal, IPL7 and PIT interrupts.
The problem with IPL7 is it is possible to get the ISRs interrupting each other and you can't stop this unless you're REALLY careful with clearing the interrupt requests.
Scrub the above. There's a far better way that just needs a small fix in your existing code.
Let's assume the "ensure atomic operations" are to prevent the Ethernet interrupt from getting in the middle of some sensitive mainline code. You don't need to disable ALL interrupts, just the Ethernet one.
So the "atomic lock" should simply disable the Ethernet interrupt in the interrupt controller, and put it back afterwards. Ditto with any other interrupts.
A cleaner way to do the same thing is instead of totally disabling ALL interrupts, just set the CPU IPL to the same level as the interrupt level of the device the code needs to "lock" against. So if the Ethernet is interrupting at Level 4, then replace the TCP stack "disable interrupt" calls with code to set the CPU IPL to 4 and then restore afterwards. Run your UARTs at IPL5 or IPL6 and they won't be delayed.
This may require fixing all of your "disable interrupt" code, which I assume has been written by people familiar with simple and stupid CPUs that only have one level (like 8 bitters and all CPUs derived from them, like X86 and ARM and so on :-). You should find the function "uint32_t asm_set_ipl(uint32_t)" in mcf5xxx.h, and you should use it to change the IPL in all of your code, like this:
uint32_t nOldIpl = asm_set_ipl(7);
/* Interrupts now locked out completely, when you really have to */
... whatever ...
asm_set_ipl(nOldIpl);
#define IPL_LEVEL_ETHERNET (4)
/* Use the above #define when programming the Ethernet interrupt level in the ICR */
...
nOldIpl = asm_set_ipl(IPL_LEVEL_ETHERNET);
/* Ethernet interrupts locked out, but higher ones allowed */
... whatever ...
asm_set_ipl(nOldIpl);
Tom