Why to use a ringbuffer in usart peripheral (receiving) ?

Go To Last Post
37 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What is the purpose and advantages over other data structures using a ring buffer ? mainly, why to use it working with usart peripheral ?

 

thanks!

This topic has a solution.

Last Edited: Sun. Apr 17, 2016 - 04:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I am german so I can not say excatly what the vocabulary ring buffer means. But in my understanding the main benefit from such a design is that you can add more then one peripheral more efficiently. I.e. creating a data ring with UART interfaces.

Ring Buffer

Last Edited: Sun. Apr 17, 2016 - 07:44 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

The benefit is that you never move the data in the buffer, but only change pointers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

The ring buffer is a form of FIFO. Advantages? As sparrow points out, you don't move data, only the pointers so that makes it fast. It also overwrites the oldest data if you let it. It simply becomes the most obvious choice to solve the problem where you have variable rate data coming in and going out.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And the whole point of a buffer, whether or not it's a ring, is to deal with data coming in a burst faster than you can deal with it or being generated faster than it can be sent out. If the characters are coming slow enough your program can deal with them as they come, then there is no reason for a buffer.

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was just about to point 'kobidon' to this, but then I realized it's the same person. Is this thread a joke?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why use a ring BUFFER with a UART? Would that be to provide a buffer perhaps? 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No, it's not. I just wondered what are the advantages of it rather than just use it as a granted.

 

thanks! 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I will add to Torby:

 

that it's not only for speed, but often you can't do anything to a packet before all of it is received and you know it's correct.(crc check)

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

In the other thread by kobidon, he was directed to - and said he was reading - the Wikipedia article on the subject.

 

If we skip past the introduction/ingress of that article, the very first paragraph says

The useful property of a circular buffer is that it does not need to have its elements shuffled around when one is consumed. (If a non-circular buffer were used then it would be necessary to shift all elements when one is consumed.) In other words, the circular buffer is well-suited as a FIFO buffer while a standard, non-circular buffer is well suited as a LIFO buffer.

I have a hard time imagining a more to-the-point statement. Since the OP has read that I'd like to ask what this thread is really all about?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have a hard time imagining a more to-the-point statement.

+100.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The typical implementation of a UART ring buffer is also much more memory-efficient than the typical implementation many other data structures.  The "memory overhead" of the ring buffer is constant (and "small") regardless of the size of the buffer.  Whereas a typical linked list (for example) would require some overhead for each element that was added.  For "convenient" sizes of ring buffers, you can even use highly optimized math fro updating the structure, and for accessing data 'out of order' (if you have a reason to do that.)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:
often you can't do anything to a packet before all of it is received and you know it's correct.(crc check)

You can process bytes (and compute their CRC) as they come, just don't act on it until incoming CRC is received (and matches). Why would you need to hold the full thing all along?

 

Another great advantage that has not been mentioned yet is that (in your conditions) a ring buffer is thread-safe: no need to disable interrupts to push/pop bytes in/out of it. Each thread (producer and consumer) only writes to its own pointer, thus they don't step on each other's feet. This makes it even smaller and faster (you save cli and sei), and also more friendly to tiers code.

ɴᴇᴛɪᴢᴇᴎ

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Pointers can be used in linear (non-ring) buffers. Data still does not have to be moved. 

 

The "magic" of a ring seems to appear when there are intervals when data is arriving faster than it can be processed or that new data is coming in before an old message can be fully handled (typically, "parsed"). There may be a few other situations where rings also help. But, in a well-behaved, slow-speed system, they may be overkill.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

netizen wrote:
Another great advantage that has not been mentioned yet is that (in your conditions) a ring buffer is thread-safe: no need to disable interrupts to push/pop bytes in/out of it. Each thread (producer and consumer) only writes to its own pointer, thus they don't step on each other's feet. This makes it even smaller and faster (you save cli and sei), and also more friendly to tiers code.

I don't know what "tiers" is.  "Others"?

 

Anyway, you made me think as my standard USART routines do indeed disable interrupts in both putc and getc.  Upon examination that is essentially due to the maintenance of an auxiliary counter for both rx and tx.  In practice I like having the aux counter as it is a simple operation to peek at it, usually for zero/non. 

 

I'd have to work through it but if you just use indices (are you really carrying around pointers?) then the "anything in buffer" routine would need to compare indices (or pointers).  And that would need to be made thread-safe, wouldn't it?

 

NB:  I'd have to work through it, but if you don't have the aux counter then the >>only<< comparison that could be made is empty or not, right?  No knowledge if whether there are e.g. four bytes to turn into a number.  (I also use it to see if a received sentence has the correct number of bytes.)

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can process bytes (and compute their CRC) as they come, just don't act on it until incoming CRC is received (and matches). Why would you need to hold the full thing all along?

 

? so you don't react on a block with a wrong CRC.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:

You can process bytes (and compute their CRC) as they come, just don't act on it until incoming CRC is received (and matches). Why would you need to hold the full thing all along?

 

? so you don't react on a block with a wrong CRC.

'net sez "process but don't act".  If I have a full Modbus message (which might be up to 256+ bytes) then indeed I could "process but not act".  But then I'd have to build quite a large aux structure with the info to act on tailored for each function code.  I'd rather wait for the end and then act as needed.

 

[lol Modbus is one time I don't use my circular buffers for USART work, partially because of the above waiting for CRC.  So a full Modbus RTU slave has 256+ bytes of fixed buffer.  I then build the response in the request buffer so I only have one.]

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
I don't know what "tiers" is.  "Others"?

Yes, other parts of the code, which might benefit from having interrupts on: it's always good behavior not to turn interrupts off unless you absolutely have to anyway.

 

theusch wrote:
Anyway, you made me think as my standard USART routines do indeed disable interrupts in both putc and getc.  Upon examination that is essentially due to the maintenance of an auxiliary counter for both rx and tx.  In practice I like having the aux counter as it is a simple operation to peek at it, usually for zero/non.

Instead of a counter, you can use writeIdx-readIdx. But usually you only need is_empty() (i.e. writeIdx==readIdx) ―and perhaps is_full().

 

theusch wrote:
I'd have to work through it but if you just use indices (are you really carrying around pointers?) then the "anything in buffer" routine would need to compare indices (or pointers).  And that would need to be made thread-safe, wouldn't it?

It is thread-safe as long as you can allow leaving one byte in the buffer until the next is_empty() check. That would happen in an empty RX buffer if the new byte arrived in the middle of your index comparison.

But let's face it: if you switched off interrupts, you'd leave that byte in the hardware buffer instead, just so your interrupt-protected test perfectly reflects the software buffer state. You're missing the new byte in both cases, and you'll have to take care of it at the next iteration. But if you've left interrupts on, at least the HW buffer can be freed early (which is usually more important).

 

theusch wrote:
NB:  I'd have to work through it, but if you don't have the aux counter then the >>only<< comparison that could be made is empty or not, right?

Why? Have a look at this implementation for example.

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Mon. Apr 18, 2016 - 08:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

netizen wrote:
Instead of a counter, you can use writeIdx-readIdx.

No, you can't, as the buffer is circular.

 

netizen wrote:
But usually you only need is_empty()

I gave my counter examples.

netizen wrote:
It is thread-safe as long as ...

??? Perhaps with single-byte indices.  But your posts suggest "pointers" so work would need to be atomic.

 

netizen wrote:
You're missing the new byte in both cases

 

I don't miss bytes.  I've been using essentially the same AVR8 USART drivers [based on CodeVision Wizard-generatedfor over 15 years, in over 100 production apps, many of which have more than one USART.  I've expressed my thoughts above, and mentioned how I use the aux counter and why I think it is convenient.  I can post production examples for each of my points, if you really care to see them.

 

netizen wrote:
Why? Have a look at this implementation for example.

As I'm not proficient in the linked language (C++?) you'll need to tell me where that code tells you how many characters are available to e.g. read or e.g. to be  written.

 

Perhaps show one of your production implementations of the above.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
netizen wrote: Instead of a counter, you can use writeIdx-readIdx.

No, you can't, as the buffer is circular.

So?

I'm taking shortcuts, assuming you can fill the holes. Have a look at the code or ask questions if you need.

In case you're wondering: this is not my code, but as far as this thread is concerned it should do.

 

theusch wrote:
netizen wrote: It is thread-safe as long as ...

??? Perhaps with single-byte indices. But your posts suggest "pointers" so work would need to be atomic.

No, I talked about pointers because everyone else did, and I did not want to confuse the OP (there isn't enough difference between a pointer and an indexed array in C to be worth loosing him). This is an implementation detail that is beside the general point: what I'm saying about ring buffers is valid all around. If your processor can atomically process as many bytes as are needed to address its memory, pointers are fine; if not, use indices.

Now I answered you with indices and linked to an implementation that uses indices to. That should have ringed a bell. :]

 

theusch wrote:
netizen wrote: You're missing the new byte in both cases

I don't miss bytes.

You've misunderstood my example. Yes, you do miss the byte that's stuck into the HW buffer because you've switched off interrupts. You'll get it at the next iteration.

Read my initial answer again and ask questions if you still don't get it.

 

theusch wrote:
netizen wrote: Why? Have a look at this implementation for example.

As I'm not proficient in the linked language (C++?)

You can ignore all the end which is a C++ template based implementation. The beginning is a C implementation using macros only.

 

theusch wrote:
you'll need to tell me where that code tells you how many characters are available to e.g. read or e.g. to be written.

You're looking for the CBUF_Len() macro, which is the second line of code. As a side note: if you want to show you've looked at some code, at least try to correctly identify the language and don't abandon before the second line of code. ;-)

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Mon. Apr 18, 2016 - 09:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Using a ring buffer on USART transmit allows you to avoid waiting for every byte to finish being sent before you send another of the string/message/transmission.

 

Most USART transmit routines look like this: 

  

  // Wait until last byte has been transmitted
  while((UCSRA &(1<<UDRE)) == 0);

  // Transmit data
  UDR = u8Data;

 

This means that you stick one byte in the USART, then stand around picking your nose until it is finished, and do the next one.

 

With a ring buffer you put all the bytes that you want to send into the buffer.  The ring buffer has an in pointer and an out pointer.  With every byte that you add to the buffer, you increment the in pointer.   

 

Then you turn on the Transmit interrupt.  This interrupt triggers when the USART is not in the process of transmitting a byte.  So the interrupt triggers immediately after it has been enabled.  The interrupt routine compares the in pointer to the out pointer (of the TX buffer).  If they are different, it takes the byte pointed to by the out pointer (which is the first that was written to the ring buff) and sends it to the USART.  Then it increments the out ptr, and exits.   Now the USART is sending this data, and the AVR can be doing other things. 

 

When the USART is through sending the byte, the TX interrupt happens again.  To transmit six bytes, the TX interrupt happens seven times.  The in ptr is compared to the out.  When all the bytes in the ring buffer have been sent, the ptrs will be equal.  So the IRQ turns off the TX interrupt.

 

The TX interrupt is a little different from the others.  If you enable it and goof up the IRQ routine, then it will trigger over and over again.  It will do the routine, then one instruction of the main code, then trigger a new TX interrupt.  That's why most beginners don't fool with it.  But it is relatively simple and it works.  You can add 20 other USARTs with different Baud rates, and as long as each has its own ring buffer, it won't crash.  MIDI monitor/multiplexers in recording studios have 20+ USARTs.

 

 

The ring buffer on the input is different from the one on the output.  With a ring input buffer, you can receive and store bytes from the USART faster than the AVR can process them (up to the size of the buffer).  This is useful for megabit Baud rates.

Last Edited: Mon. Apr 18, 2016 - 10:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

On second thought, this implementation uses several other tricks to get a more efficient ring buffer. Pedagogically speaking, it's probably not the best example to illustrate just the thread-safe characteristic…

ɴᴇᴛɪᴢᴇᴎ

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

netizen wrote:
Have a look at the code or ask questions if you need.

I DID.  I ASKED A QUESTION.  YOUR ANSWER IS TO LOOK AT THE CODE.

theusch wrote:
As I'm not proficient in the linked language (C++?) you'll need to tell me where that code tells you how many characters are available to e.g. read or e.g. to be written.

 

netizen wrote:
I'm taking shortcuts

Indeed.

 

netizen wrote:
No, I talked about pointers because everyone else did, and I did not want to confuse the OP (there isn't enough difference between a pointer and an indexed array in C to be worth loosing him).

But >>you<< were the one that went off on the pointer tangent, and about no need to turn off interrupts.  And there will be more atomicity issues with two-byte pointers vs one-byte indices.

netizen wrote:
Yes, you do miss the byte that's stuck into the HW buffer because you've switched off interrupts.

 

No, I don't miss any bytes, and lothing is stuck, and it is never "behind".  The getc() is only fired when a byte available.  It only gets one byte per invocation.  The fact that interrupts are turned off for a microseconds while the counter is adjusted has nothing to do with anything -- when this invocation of getc() has been completed, the next character, if it arrives, will be available.

 

netizen wrote:
You're looking for the CBUF_Len() macro, which is the second line of code.

No, I'm not.  That macro is the length of the buffer.

 

I'm out.

 

 

 

 

 

 

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
netizen wrote: You're looking for the CBUF_Len() macro, which is the second line of code.

No, I'm not. That macro is the length of the buffer.

It's not: it's how many bytes are currently stored in the buffer. Exactly what you asked for:

theusch wrote:
you'll need to tell me where that code tells you how many characters are available to e.g. read or e.g. to be written.

If you're not willing to parse a one-liner in C, arguing that's too much in some unidentified language, expect some tongue-in-cheek correction. :-)

 

theusch wrote:
But >>you<< were the one that went off on the pointer tangent, and about no need to turn off interrupts. And there will be more atomicity issues with two-byte pointers vs one-byte indices.

Ring buffers are inherently thread-safe between producer and consumer sides ―unless you implement them wrong. If you have several producers (resp. consumers), you'll have to protect against concurrent producer (resp. consumer) access. That's it.

This has nothing to do with the fact that, on an 8 bit processor with 16 bits RAM addresses (for example) pointer incrementing is not atomic, thus you need to use indices instead (and limit the buffer size accordingly). You're just getting distracted: this thread is a general thread about (RX) ring buffers, it's not about the platform you cherry-picked to make a pointer implementation fail.

 

theusch wrote:
No, I don't miss any bytes, and lothing is stuck, and it is never "behind". The getc() is only fired when a byte available.

If the new byte never arrives in the middle of the bytes-available-check, then there is no need for interrupts disabling in the first place. If it does, the byte is available… in the HW buffer, not to the consumer routine; With an interrupt-free implementation it is available in the SW buffer, but "hidden" because it arrived in the middle of the is_empty() check. Exactly the same result in both cases: available byte missed (or you're one byte behind if you prefer).

The only problem that could arise is if this interrupt triggers your checking the SW buffer: you won't get any "notification" until the next byte arrives. Note that this has nothing to do with ring buffers per se, but only with code using ring buffers.

 

theusch wrote:
netizen wrote: Have a look at the code or ask questions if you need.

I DID. I ASKED A QUESTION. YOUR ANSWER IS TO LOOK AT THE CODE.

You've asked many questions :

theusch wrote:
are you really carrying around pointers?

theusch wrote:
that would need to be made thread-safe, wouldn't it?

theusch wrote:
if you don't have the aux counter then the >>only<< comparison that could be made is empty or not, right?

theusch wrote:
??? Perhaps with single-byte indices. But your posts suggest "pointers" so work would need to be atomic.

theusch wrote:
As I'm not proficient in the linked language (C++?)

I've tried to answer most of them. I'm not sure which one you're referring to…

 

You were really close to the conceptual challenge when you wrote:

theusch wrote:
Anyway, you made me think as my standard USART routines do indeed disable interrupts in both putc and getc. Upon examination that is essentially due to the maintenance of an auxiliary counter

I believe I've answered this as well (no such counter is necessary). At this point it'd be a good idea to really look into some working implementation. But if it still does not sink and you have precise questions, I'll be happy to answer them.

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Tue. Apr 19, 2016 - 11:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't want to be a part of the war but my input is:

 

If the buffer is 256 byte or less

size of buffer is a power of 2

char is 8 bit  :)

 

Then there is no atomic problems, and if you AND to the correct length mask, char's in buffer is correct with a simple minus.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I had forgotten that this implementation uses many tricks, so the thread-safe part of it is less obvious. I did not realize it could be that confusing, even to people as experienced as theusch is. Sorry about that.

 

sparrow2 wrote:
If the buffer is 256 byte or less

Of course this is only true if your processor is 8 bits (otherwise the buffer can be longer) and its RAM addresses are longer (otherwise you can directly use pointers because they're atomic).

 

sparrow2 wrote:
size of buffer is a power of 2

This constraint comes from other tricks this implementation uses: it is separated from interrupt-friendly thread-safe ringbuffer design. By the way, if you stick to this implementation and use an 8 bits processor, max buffer size is 128 (not 256).

 

sparrow2 wrote:
Then there is no atomic problems, and if you AND to the correct length mask, char's in buffer is correct with a simple minus.

You make it sound like all these conditions are required to obtain a thread-safe ring buffer, which is not the case (sorry if I'm repeating myself).

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Wed. Apr 20, 2016 - 01:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Netizen - are things slow in marseilles today?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

netizen wrote:
sparrow2 wrote: If the buffer is 256 byte or less

Of course this is only true if your processor is 8 bits (otherwise the buffer can be longer) and its RAM addresses are longer (otherwise you can directly use pointers because they're atomic).

Also, in all honesty if you need a ringbuffer longer than 256 bytes on an 8 bit processor, you're probably doing something wrong (or too clever).

An RX ringbuffer (as that's the topic of this thread) is only there to complement the HW buffer, thus an ISR can free it early and your main code does not need to come test it too often. It has more time to do significant processing before it gets back to checking the ringbuffer (and when it does it empties it all). If a big buffer is needed (e.g. to hold a large packet), it should better be another standard linear buffer that can be worked with quickly and easily, without any concurrence issues.

I remember working on a Linux wifi driver (so we're talking about rather large packets), yet the ringbuffers were tiny IIRC. Because you're obviously not going to parse, say IPv4, straight from a ringbuffer. In small embedded systems it can be tempting to do everything in a big ringbuffer, but it's probably not as good an idea as it may look at first (of course I suppose there may be exceptions).

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Wed. Apr 20, 2016 - 01:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
Netizen - are things slow in marseilles today?

Sorry: I don't know what you mean.

ɴᴇᴛɪᴢᴇᴎ

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I guess I'm just not up to netizen's standards. 

/**
*   Returns the number of elements which are currently
*   contained in the circular buffer.
*/

#define CBUF_Len( cbuf )        ((typeof( cbuf.m_putIdx ))(( cbuf.m_putIdx ) - ( cbuf.m_getIdx )))

How many elements are contained in the buffer when this macro is invoked with m_putIdx of 2 and m_getIdx of 6?  Will you get the same answer if myQ_SIZE is 8 as when it is 16?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
How many elements are contained in the buffer when this macro is invoked with m_putIdx of 2 and m_getIdx of 6? Will you get the same answer if myQ_SIZE is 8 as when it is 16?

You cannot get such indices with such myQ_SIZEs. In fact, you can never get such indices at all with this implementation because max buffer size is 128 (and 2-6>128).

*   It requires that the circular buffer size be a power of two, and the
*   size of the buffer needs to smaller than the index. So an 8 bit index
*   supports a circular buffer upto ( 1 << 7 ) = 128 entries, and a 16 bit index
*   supports a circular buffer upto ( 1 << 15 ) = 32768 entries.

 

But I see what's confusing you. You'd have expected something like:

(cbuf.m_putIdx - cbuf.m_getIdx) & (myQ_SIZE-1)

This necessary ANDing is done in buffer addressing instead. For example:

#define CBUF_Pop( cbuf ) (cbuf.m_entry)[ cbuf.m_getIdx++ & (( cbuf##_SIZE ) - 1 )]

That's one of the tricks this implementation uses that makes it a rather poor choice for educational purposes.

 

Please note that this has nothing to do with interrupt-friendly thread-safe ring-buffer design (i.e. if you'd rather AND it your way, it's fine too).

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Wed. Apr 20, 2016 - 02:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

netizen wrote:
Sorry: I don't know what you mean.

he means - what is the point in simply arguing for argument's sake unless you are just bored and have nothing better to do? I tend to agree.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
he means - what is the point in simply arguing for argument's sake unless you are just bored and have nothing better to do? I tend to agree.

Sorry you feel that way. It sure wasn't my intention.

I'm not even sure which comment(s) made you feel that way… Is it my answer to sparrow2's comment?

ɴᴇᴛɪᴢᴇᴎ

Last Edited: Wed. Apr 20, 2016 - 03:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

netizen wrote:
theusch wrote: How many elements are contained in the buffer when this macro is invoked with m_putIdx of 2 and m_getIdx of 6? Will you get the same answer if myQ_SIZE is 8 as when it is 16? You cannot get such indices with such myQ_SIZEs. In fact, you can never get such indices at all with this implementation because max buffer size is 128 (and 2-6>128).

 

??? Why can't I get those numbers?

 

I picked small numbers, 8 and 16, for myQ_SIZE.

1)  8 and 16 are powers of 2.  Are those not valid?  Why?

 

2) Why is m_putIdx of 2 and m_getIdx of 6 not valid?  Using buffer size of 16 as an example, say I put 12 characters into the buffer with put.  Then I consume with get until m_getIdx is 6.  Then I put more characters into the buffer until it wraps, until m_putIdx is 2.  Why would that not be a valid scenario?

 

3) Then, repeating my question, what value does CBUF_Len give with the above scenario?  The answer should be like 12 (I'd have to figure out the fence-posting) with a buffer size of 16.  2-6 is -4, which would be 252 with uint8_t subtraction and cast, right?  Are there 252 characters in the buffer?  Won't the valid answer be different if the buffer size is different?

 

NB:  The reason I asked for interpretation of that macro and the language involved is that the only way I could think of to get the right answer out of that would be if the - (minus) operator was overloaded somehow to do the wrap arithmetic.  Is that the case?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Wed. Apr 20, 2016 - 03:44 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

netizen wrote:

theusch wrote: How many elements are contained in the buffer when this macro is invoked with m_putIdx of 2 and m_getIdx of 6? Will you get the same answer if myQ_SIZE is 8 as when it is 16? You cannot get such indices with such myQ_SIZEs. In fact, you can never get such indices at all with this implementation because max buffer size is 128 (and 2-6>128).

 

 

??? Why can't I get those numbers?

 

I picked small numbers, 8 and 16, for myQ_SIZE.

1)  8 and 16 are powers of 2.  Are those not valid?  Why?

 

2) Why is m_putIdx of 2 and m_getIdx of 6 not valid?  Using buffer size of 16 as an example, say I put 12 characters into the buffer with put.  Then I consume with get until m_getIdx is 6.  Then I put more characters into the buffer until it wraps, until m_putIdx is 2.  Why would that not be a valid scenario?

 

3) Then, repeating my question, what value does CBUF_Len give with the above scenario?  The answer should be like 12 (I'd have to figure out the fence-posting) with a buffer size of 16.  2-6 is -4, which would be 252 with uint8_t subtraction and cast, right?  Are there 252 characters in the buffer?  Won't the valid answer be different if the buffer size is different?

 

NB:  The reason I asked for interpretation of that macro and the language involved is that the only way I could think of to get the right answer out of that would be if the - (minus) operator was overloaded somehow to do the wrap arithmetic.  Is that the case?

If myQ_SIZE is 16, m_getIdx is 6, and you have 12 bytes into the queue, then m_putIdx would be 18 (6 + 12). In a full queue, m_putIdx would be 22 (6 + 16). Both m_getIdx and m_putIdx are modulo'd with the queue size only when getting or putting bytes.
 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

christop wrote:
If myQ_SIZE is 16, m_getIdx is 6, and you have 12 bytes into the queue, then m_putIdx would be 18 (6 + 12). In a full queue, m_putIdx would be 22 (6 + 16). Both m_getIdx and m_putIdx are modulo'd with the queue size only when getting or putting bytes.

 

Aaah, that is the part I missed...

 

So now fast-forward and a total of 258 bytes have been "put", and only 250 "get"s.  2 - 250 => -248 which is 0x08 and all is well.  Nice--now I understand. (and probably why max buffer size is 128 and not 256?)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Wed. Apr 20, 2016 - 04:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As a side note, this is just another trick (a couple actually): using bit masks instead of overflow tests because it's faster, thus buffer size must be a power of 2. But you can also implement a "naturally thread-safe" ringbuffer with if (idx>size) idx=0; if you prefer (and use any buffer size you like).

Again: I linked to this implementation in the OP's first thread, because at the time he was looking for an efficient ready-to-use library. So I quite naturally linked to it again in this thread, missing the fact that it has a completely different purpose (education), and this lib sure isn't the best in this regard. My mistake.

ɴᴇᴛɪᴢᴇᴎ