5

I'm writing code for an embedded system (Cortex M0) and do not have all the luxuries of mutexes/spinlocks/etc. Is there a simple way to add data to a shared buffer (log-file) which will be flushed to disk from my Main() loop?

If there is only a single producer (1 interrupt) and single consumer (main-loop), I could use a simple buffer where the producer increases the 'head' and the consumer the 'tail'. And it will be perfectly safe. But now that I have multiple producers (interrupts) it seems like I'm stuck.

I could give each interrupt its own buffer, and combine them in Main(), but this will require a lot of extra RAM and complexity.

Maestro
  • 9,046
  • 15
  • 83
  • 116
  • Do you have any sort of run-time library you link with? Maybe it contains "atomic" functions? Or of there is no such functions/library, then the CPU might have instructions to atomically compare and increase/decrease values? These can be used for your own implementation of semaphores or mutexes. – Some programmer dude Jan 21 '13 at 11:40
  • @JoachimPileborg It's an NXP LPC11U35 CPU and it's linked against the RedLib library, which doesn't provide atomic functions as far as I'm aware. – Maestro Jan 21 '13 at 11:42
  • I'm not familiar with the interrupt structure of this system. But are your interrupts of the same priority, or is there a pecking order? Either way, it seems you could just use a stack for a buffer. Or can you forsee a stack overflow problem with this solution? – happy coder Jan 21 '13 at 11:49
  • @happycoder I'm free to give interrupts different (or equal) priorities. – Maestro Jan 21 '13 at 11:52
  • If you can give them the same priority and if that avoids mutual preemption of their ISRs (which is the case normally, but I'd check with the interrupt controller/CPU spec), your single producer-single consumer implementation will just work! – Alexey Frunze Jan 21 '13 at 12:16
  • You can implement this through a simple ring buffer (circular array), where you turn off the hardware interrupt sources during access. Just around 100 LOC in three functions: init, add, remove. – Lundin Jan 21 '13 at 12:23
  • @Lundin I was trying to avoid turning of the interrupts because I thought I could miss some of them while they were disabled. After reading into it, they are just queued untill you re-enable them. So if you add your comment as an answer, I'll accept it. – Maestro Jan 21 '13 at 13:09
  • @Joshua Not much of an answer I think, but ok. I added some thoughts about interrupts and timing. The annoying thing is that I have the very code you are looking for right here, but it is unfortunately part of proprietary software. Maybe I can post some pseudo code. – Lundin Jan 21 '13 at 14:16

1 Answers1

5

You can implement this through a simple ring buffer (circular array), where you turn off the hardware interrupt sources during access. It only needs the functions init, add and remove.

I'm not certain how your particular MCU handles interrupts, but most likely they will remain pending, as long as you only enable/disable the particular hardware peripheral's interrupt. Depending on the nature of your application, you could also disable the global interrupt mask, but that's rather crude.

Generally, you don't need to worry about missing out interrupts, because if the code that handles the incoming interrupts is slower than the interrupt frequency, no software in the world will fix it. You would either have to accept data losses or increase the CPU clock to dodge such scenarios. But of course you should always try to keep the code inside the ISR as compact as possible.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • For a single producer/single consumer, there is no need to turn off interrupts or lock access, as long as the producer only moves the head pointer, and consumer only moves the tail pointer. – Lou May 18 '17 at 18:07
  • @Lou That is not correct unless access is atomic, which is not the case here. – Lundin May 19 '17 at 06:30
  • @Lunding: access to what isn't atomic? One interrupt is writing to a variable, the other one is reading it. For a 32-bit processor like Cortex M0, I don't expect you can get partial 32-bit writes or reads. – Lou Jun 02 '17 at 17:47
  • @Lou Of course you can. It is quite common that you have a scenario such as "load value into register, modify register, store value back on stack". Which may result in 3 instructions, not one. You can never assume atomic access unless you know for certain what machine code your C code gets translated to. – Lundin Jun 05 '17 at 10:28
  • Of course, read-modify-store isn't atomic. I wrote that for a *single producer single consumer* scenario, there is no need to turn off interrupts because you cannot get partial writes or reads if your head/tail pointers match the word size of the architecture. I am stating that one interrupt cannot write a word sized value and result in a different interrupt reading only half of the word. So, producer is writing to the head, consumer is writing to the tail. But now I noticed that that OP already wrote s/he is aware of this special case. – Lou Jun 05 '17 at 10:38
  • @Lou If reading old data is acceptable, then maybe... Consider a scenario where you get important data from an interrupt every 10 seconds. Main program reads last received data into register -> interrupt writes new data -> main program modifies the old data in the register and uses that for another 10 seconds. The new data is lost. In many systems this is unacceptable. You have a bug even though it there is a single producer. – Lundin Jun 07 '17 at 06:01
  • I am not sure why the main program would use old data for 10 s, perhaps I didn't quite understand how the main loop is supposed to run compared to the interrupt? Especially the part where a register holds the same value for 10 seconds? What would be different with interrupts turned off if the main loop makes the read and then forgets about it for the next 10 s? – Lou Jun 12 '17 at 09:14