Rocksolid Light

Welcome to novaBBS (click a section below)

mail  files  register  newsreader  groups  login

Message-ID:  

"When anyone says `theoretically,' they really mean `not really.'" -- David Parnas


devel / comp.arch.embedded / Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

SubjectAuthor
* How to write a simple driver in bare metal systems: volatile, memorypozz
+- Re: How to write a simple driver in bare metal systems: volatile, memory barrierClifford Heath
+* Re: How to write a simple driver in bare metal systems: volatile,Don Y
|`* Re: How to write a simple driver in bare metal systems: volatile,pozz
| `- Re: How to write a simple driver in bare metal systems: volatile,Don Y
+* Re: How to write a simple driver in bare metal systems: volatile,David Brown
|+* Re: How to write a simple driver in bare metal systems: volatile,pozz
||`* Re: How to write a simple driver in bare metal systems: volatile,David Brown
|| `* Re: How to write a simple driver in bare metal systems: volatile,pozz
||  `- Re: How to write a simple driver in bare metal systems: volatile,David Brown
|`* Re: How to write a simple driver in bare metal systems: volatile,pozz
| +- Re: How to write a simple driver in bare metal systems: volatile,David Brown
| `- Re: How to write a simple driver in bare metal systems: volatile,Richard Damon
`* Re: How to write a simple driver in bare metal systems: volatile,Johann Klammer
 `* Re: How to write a simple driver in bare metal systems: volatile,Dimiter_Popoff
  +* Re: How to write a simple driver in bare metal systems: volatile,Don Y
  |+* Re: How to write a simple driver in bare metal systems: volatile,Dimiter_Popoff
  ||+* Re: How to write a simple driver in bare metal systems: volatile,Don Y
  |||+* Re: How to write a simple driver in bare metal systems: volatile,Dimiter_Popoff
  ||||`* Re: How to write a simple driver in bare metal systems: volatile,Don Y
  |||| `* Re: How to write a simple driver in bare metal systems: volatile,Dimiter_Popoff
  ||||  `- Re: How to write a simple driver in bare metal systems: volatile,Don Y
  |||+- Re: How to write a simple driver in bare metal systems: volatile,David Brown
  |||`* Re: How to write a simple driver in bare metal systems: volatile,Niklas Holsti
  ||| `* Re: How to write a simple driver in bare metal systems: volatile,Don Y
  |||  `* Re: How to write a simple driver in bare metal systems: volatile,Niklas Holsti
  |||   +- Re: How to write a simple driver in bare metal systems: volatile,Don Y
  |||   `* Re: How to write a simple driver in bare metal systems: volatile,David Brown
  |||    `- Re: How to write a simple driver in bare metal systems: volatile,Niklas Holsti
  ||`* Re: How to write a simple driver in bare metal systems: volatile,Niklas Holsti
  || +* Re: How to write a simple driver in bare metal systems: volatile,Don Y
  || |`* Re: How to write a simple driver in bare metal systems: volatile,Niklas Holsti
  || | `- Re: How to write a simple driver in bare metal systems: volatile,Don Y
  || +* Re: How to write a simple driver in bare metal systems: volatile,Dimiter_Popoff
  || |`* Re: How to write a simple driver in bare metal systems: volatile,Niklas Holsti
  || | +* Re: How to write a simple driver in bare metal systems: volatile,Don Y
  || | |`* Re: How to write a simple driver in bare metal systems: volatile,Dimiter_Popoff
  || | | `- Re: How to write a simple driver in bare metal systems: volatile,Don Y
  || | +* Re: How to write a simple driver in bare metal systems: volatile,pozz
  || | |+- Re: How to write a simple driver in bare metal systems: volatile,Don Y
  || | |`* Re: How to write a simple driver in bare metal systems: volatile,Niklas Holsti
  || | | +* Re: How to write a simple driver in bare metal systems: volatile,Dimiter_Popoff
  || | | |`* Re: How to write a simple driver in bare metal systems: volatile,Niklas Holsti
  || | | | `* Re: How to write a simple driver in bare metal systems: volatile,Dimiter_Popoff
  || | | |  `- Re: How to write a simple driver in bare metal systems: volatile,David Brown
  || | | `- Re: How to write a simple driver in bare metal systems: volatile,pozz
  || | `- Re: How to write a simple driver in bare metal systems: volatile,David Brown
  || `- Re: How to write a simple driver in bare metal systems: volatile, memory barrierClifford Heath
  |`* Re: How to write a simple driver in bare metal systems: volatile, memory barrierantispam
  | `* Re: How to write a simple driver in bare metal systems: volatile,Don Y
  |  `* Re: How to write a simple driver in bare metal systems: volatile, memory barrierantispam
  |   `* Re: How to write a simple driver in bare metal systems: volatile,Don Y
  |    `* Re: How to write a simple driver in bare metal systems: volatile, memory barrierantispam
  |     `* Re: How to write a simple driver in bare metal systems: volatile,Don Y
  |      `* Re: How to write a simple driver in bare metal systems: volatile, memory barrierantispam
  |       `* Re: How to write a simple driver in bare metal systems: volatile,Don Y
  |        `* Re: How to write a simple driver in bare metal systems: volatile, memory barrierantispam
  |         `- Re: How to write a simple driver in bare metal systems: volatile,Don Y
  `- Re: How to write a simple driver in bare metal systems: volatile,David Brown

Pages:123
How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<skvcnd$5dv$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=643&group=comp.arch.embedded#643

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: pozzu...@gmail.com (pozz)
Newsgroups: comp.arch.embedded
Subject: How to write a simple driver in bare metal systems: volatile, memory
barrier, critical sections and so on
Date: Sat, 23 Oct 2021 00:07:40 +0200
Organization: A noiseless patient Spider
Lines: 125
Message-ID: <skvcnd$5dv$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-15; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Fri, 22 Oct 2021 22:07:41 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="8e088add632fbe7c60bcf24707e822f6";
logging-data="5567"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18wpApB41JI8IY5x2L4Ie+ICBdodEjBjtk="
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101
Thunderbird/78.14.0
Cancel-Lock: sha1:GCxAPFY+MdqE6OecVpMz7pUH4Ws=
Content-Language: it
X-Mozilla-News-Host: snews://news.eternal-september.org:563
 by: pozz - Fri, 22 Oct 2021 22:07 UTC

Even I write software for embedded systems for more than 10 years,
there's an argument that from time to time let me think for hours and
leave me with many doubts.

Consider a simple embedded system based on a MCU (AVR8 or Cortex-Mx).
The software is bare metal, without any OS. The main pattern is the well
known mainloop (background code) that is interrupted by ISR.

Interrupts are used mainly for timings and for low-level driver. For
example, the UART reception ISR move the last received char in a FIFO
buffer, while the mainloop code pops new data from the FIFO.

static struct {
unsigned char buf[RXBUF_SIZE];
uint8_t in;
uint8_t out;
} rxfifo;

/* ISR */
void uart_rx_isr(void) {
unsigned char c = UART->DATA;
rxfifo.buf[in % RXBUF_SIZE] = c;
rxfifo.in++;
// Reset interrupt flag
}

/* Called regularly from mainloop code */
int uart_task(void) {
int c = -1;
if (out != in) {
c = rxfifo.buf[out % RXBUF_SIZE];
out++;
}
return -1;
}

From a 20-years old article[1] by Nigle Jones, this seems a situation
where volatile must be used for rxfifo.in, because is modified by an ISR
and used in the mainloop code.

I don't think so, rxfifo.in is read from memory only one time in
uart_task(), so there isn't the risk that compiler can optimize badly.
Even if ISR is fired immediately after the if statement, this doesn't
bring to a dangerous state: the just received data will be processed at
the next call to uart_task().

So IMHO volatile isn't necessary here. And critical sections (i.e.
disabling interrupts) aren't useful too.

However I'm thinking about memory barrier. Suppose the compiler reorder
the instructions in uart_task() as follows:

c = rxfifo.buf[out % RXBUF_SIZE]
if (out != in) {
out++;
return c;
} else {
return -1;
}

Here there's a big problem, because compiler decided to firstly read
rxfifo.buf[] and then test in and out equality. If the ISR is fired
immediately after moving data to c (most probably an internal register),
the condition in the if statement will be true and the register value is
returned. However the register value isn't correct.

I don't think any modern C compiler reorder uart_task() in this way, but
we can't be sure. The result shouldn't change for the compiler, so it
can do this kind of things.

How to fix this issue if I want to be extremely sure the compiler will
not reorder this way? Applying volatile to rxfifo.in shouldn't help for
this, because compiler is allowed to reorder access of non volatile
variables yet[2].

One solution is adding a memory barrier in this way:

int uart_task(void) {
int c = -1;
if (out != in) {
memory_barrier();
c = rxfifo.buf[out % RXBUF_SIZE];
out++;
}
return -1;
}

However this approach appears to me dangerous. You have to check and
double check if, when and where memory barriers are necessary and it's
simple to skip a barrier where it's nedded and add a barrier where it
isn't needed.

So I'm thinking that a sub-optimal (regarding efficiency) but reliable
(regarding the risk to skip a barrier where it is needed) could be to
enter a critical section (disabling interrupts) anyway, if it isn't
strictly needed.

int uart_task(void) {
ENTER_CRITICAL_SECTION();
int c = -1;
if (out != in) {
c = rxfifo.buf[out % RXBUF_SIZE];
out++;
}
EXIT_CRITICAL_SECTION();
return -1;
}

Another solution could be to apply volatile keyword to rxfifo.in *AND*
rxfifo.buf too, so compiler can't change the order of accesses them.

Do you have other suggestions?

[1] https://barrgroup.com/embedded-systems/how-to/c-volatile-keyword
[2] https://blog.regehr.org/archives/28

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<16b088959e4f85a7$1$2279452$2cdd266e@news.thecubenet.com>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=644&group=comp.arch.embedded#644

  copy link   Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on
Newsgroups: comp.arch.embedded
References: <skvcnd$5dv$1@dont-email.me>
From: no.s...@please.net (Clifford Heath)
Date: Sat, 23 Oct 2021 13:40:40 +1100
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.11.0
MIME-Version: 1.0
In-Reply-To: <skvcnd$5dv$1@dont-email.me>
Content-Type: text/plain; charset=iso-8859-15; format=flowed
Content-Language: en-US
Content-Transfer-Encoding: 8bit
Message-ID: <16b088959e4f85a7$1$2279452$2cdd266e@news.thecubenet.com>
Lines: 134
Path: i2pn2.org!i2pn.org!paganini.bofh.team!eternal-september.org!reader02.eternal-september.org!feeder5.feed.usenet.farm!feeder1.feed.usenet.farm!feed.usenet.farm!tr3.eu1.usenetexpress.com!feeder.usenetexpress.com!tr1.iad1.usenetexpress.com!2a07:8080:119:fe::52.MISMATCH!news.thecubenet.com!not-for-mail
NNTP-Posting-Date: Sat, 23 Oct 2021 02:40:40 +0000
X-Received-Bytes: 4873
Organization: theCubeNet - www.thecubenet.com
X-Complaints-To: abuse@thecubenet.com
 by: Clifford Heath - Sat, 23 Oct 2021 02:40 UTC

On 23/10/21 9:07 am, pozz wrote:
> Even I write software for embedded systems for more than 10 years,
> there's an argument that from time to time let me think for hours and
> leave me with many doubts.
>
> Consider a simple embedded system based on a MCU (AVR8 or Cortex-Mx).
> The software is bare metal, without any OS. The main pattern is the well
> known mainloop (background code) that is interrupted by ISR.
>
> Interrupts are used mainly for timings and for low-level driver. For
> example, the UART reception ISR move the last received char in a FIFO
> buffer, while the mainloop code pops new data from the FIFO.
>
>
> static struct {
>   unsigned char buf[RXBUF_SIZE];
>   uint8_t in;
>   uint8_t out;
> } rxfifo;
>
> /* ISR */
> void uart_rx_isr(void) {
>   unsigned char c = UART->DATA;
>   rxfifo.buf[in % RXBUF_SIZE] = c;
>   rxfifo.in++;
>   // Reset interrupt flag
> }
>
> /* Called regularly from mainloop code */
> int uart_task(void) {
>   int c = -1;
>   if (out != in) {
>     c = rxfifo.buf[out % RXBUF_SIZE];
>     out++;
>   }
>   return -1;
> }
>
>
> From a 20-years old article[1] by Nigle Jones, this seems a situation
> where volatile must be used for rxfifo.in, because is modified by an ISR
> and used in the mainloop code.
>
> I don't think so, rxfifo.in is read from memory only one time in
> uart_task(), so there isn't the risk that compiler can optimize badly.
> Even if ISR is fired immediately after the if statement, this doesn't
> bring to a dangerous state: the just received data will be processed at
> the next call to uart_task().
>
> So IMHO volatile isn't necessary here. And critical sections (i.e.
> disabling interrupts) aren't useful too.
>
> However I'm thinking about memory barrier. Suppose the compiler reorder
> the instructions in uart_task() as follows:
>
>
>   c = rxfifo.buf[out % RXBUF_SIZE]
>   if (out != in) {
>     out++;
>     return c;
>   } else {
>     return -1;
>   }
>
>
> Here there's a big problem, because compiler decided to firstly read
> rxfifo.buf[] and then test in and out equality. If the ISR is fired
> immediately after moving data to c (most probably an internal register),
> the condition in the if statement will be true and the register value is
> returned. However the register value isn't correct.
>
> I don't think any modern C compiler reorder uart_task() in this way, but
> we can't be sure. The result shouldn't change for the compiler, so it
> can do this kind of things.
>
> How to fix this issue if I want to be extremely sure the compiler will
> not reorder this way? Applying volatile to rxfifo.in shouldn't help for
> this, because compiler is allowed to reorder access of non volatile
> variables yet[2].
>
> One solution is adding a memory barrier in this way:
>
>
> int uart_task(void) {
>   int c = -1;
>   if (out != in) {
>     memory_barrier();
>     c = rxfifo.buf[out % RXBUF_SIZE];
>     out++;
>   }
>   return -1;
> }
>
>
> However this approach appears to me dangerous. You have to check and
> double check if, when and where memory barriers are necessary and it's
> simple to skip a barrier where it's nedded and add a barrier where it
> isn't needed.
>
> So I'm thinking that a sub-optimal (regarding efficiency) but reliable
> (regarding the risk to skip a barrier where it is needed) could be to
> enter a critical section (disabling interrupts) anyway, if it isn't
> strictly needed.
>
>
> int uart_task(void) {
>   ENTER_CRITICAL_SECTION();
>   int c = -1;
>   if (out != in) {
>     c = rxfifo.buf[out % RXBUF_SIZE];
>     out++;
>   }
>   EXIT_CRITICAL_SECTION();
>   return -1;
> }
>
>
> Another solution could be to apply volatile keyword to rxfifo.in *AND*
> rxfifo.buf too, so compiler can't change the order of accesses them.
>
> Do you have other suggestions?
>
>
>
> [1] https://barrgroup.com/embedded-systems/how-to/c-volatile-keyword
> [2] https://blog.regehr.org/archives/28

This is a good introduction to how Linux makes this possible for its
horde of device-driver authors:

<https://www.kernel.org/doc/Documentation/memory-barriers.txt>

Clifford Heath

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl05ed$ode$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=645&group=comp.arch.embedded#645

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: blockedo...@foo.invalid (Don Y)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Fri, 22 Oct 2021 22:09:17 -0700
Organization: A noiseless patient Spider
Lines: 55
Message-ID: <sl05ed$ode$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-15; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Sat, 23 Oct 2021 05:09:34 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="5ff78246b6676517666968e67cd464e1";
logging-data="25006"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX195uzxRjIm6GCciVklBWLWJ"
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101
Thunderbird/52.1.1
Cancel-Lock: sha1:fBeRBwb07sVU/ZpQ2rJN+VEJn9c=
In-Reply-To: <skvcnd$5dv$1@dont-email.me>
Content-Language: en-US
 by: Don Y - Sat, 23 Oct 2021 05:09 UTC

On 10/22/2021 3:07 PM, pozz wrote:
> static struct {
> unsigned char buf[RXBUF_SIZE];
> uint8_t in;
> uint8_t out;
> } rxfifo;
>
> /* ISR */
> void uart_rx_isr(void) {
> unsigned char c = UART->DATA;
> rxfifo.buf[in % RXBUF_SIZE] = c;
> rxfifo.in++;
> // Reset interrupt flag
> }
>
> /* Called regularly from mainloop code */
> int uart_task(void) {
> int c = -1;

Why? And why a retval from uart_task -- if it is always "-1"?

> if (out != in) {
> c = rxfifo.buf[out % RXBUF_SIZE];
> out++;
> }
> return -1;
> }

This is a bug(s) waiting to happen.

How is RXBUF_SIZE defined? How does it reflect the data rate (and,
thus, interrupt rate) as well as the maximum latency between "main
loop" accesses? I.e., what happens when the buffer is *full* -- and,
thus, appears EMPTY? What stops the "in" member from growing to the
maximum size of a uint8 -- and then wrapping? How do you convey this
to the upper level code ("Hey, we just lost a whole RXBUF_SIZE of
characters so if the character stream doesn't make sense, that might
be a cause...")? What if RXBUF_SIZE is relatively prime wrt uint8max?

When writing UART handlers, I fetch the received datum along with
the uart's flags and stuff *both* of those things in the FIFO.
If the FIFO would be full, I, instead, modify the flags of the
preceeding datum to reflect this fact ("Some number of characters
have been lost AFTER this one...") and discard the current character.

I then signal an event and let a task waiting for that specific event
wake up and retrieve the contents of the FIFO (which may include more
than one character, at that time as characters can arrive after the
initial event has been signaled).

This lets me move the line discipline out of the ISR and still keep
the system "responsive".

Figure out everything that you need to do before you start sorting out
how the compiler can "shaft" you...

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl1c3a$dfr$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=646&group=comp.arch.embedded#646

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: david.br...@hesbynett.no (David Brown)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Sat, 23 Oct 2021 18:09:13 +0200
Organization: A noiseless patient Spider
Lines: 247
Message-ID: <sl1c3a$dfr$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-15
Content-Transfer-Encoding: 8bit
Injection-Date: Sat, 23 Oct 2021 16:09:14 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="c15e4a1fc7736cdb6f8490df24172930";
logging-data="13819"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/EF2tzZd1J7RLgw4lTvPUuJ2YVfqJqv7Q="
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101
Thunderbird/78.11.0
Cancel-Lock: sha1:NwEaoccV29CUKtmgwT6L8/cKXhw=
In-Reply-To: <skvcnd$5dv$1@dont-email.me>
Content-Language: en-GB
 by: David Brown - Sat, 23 Oct 2021 16:09 UTC

On 23/10/2021 00:07, pozz wrote:
> Even I write software for embedded systems for more than 10 years,
> there's an argument that from time to time let me think for hours and
> leave me with many doubts.

It's nice to see a thread like this here - the group needs such discussions!

>
> Consider a simple embedded system based on a MCU (AVR8 or Cortex-Mx).
> The software is bare metal, without any OS. The main pattern is the well
> known mainloop (background code) that is interrupted by ISR.
>
> Interrupts are used mainly for timings and for low-level driver. For
> example, the UART reception ISR move the last received char in a FIFO
> buffer, while the mainloop code pops new data from the FIFO.
>
>
> static struct {
>   unsigned char buf[RXBUF_SIZE];
>   uint8_t in;
>   uint8_t out;
> } rxfifo;
>
> /* ISR */
> void uart_rx_isr(void) {
>   unsigned char c = UART->DATA;
>   rxfifo.buf[in % RXBUF_SIZE] = c;
>   rxfifo.in++;

Unless you are sure that RXBUF_SIZE is a power of two, this is going to
be quite slow on an AVR. Modulo means division, and while division by a
constant is usually optimised to a multiplication by the compiler, you
still have a multiply, a shift, and some compensation for it all being
done as signed integer arithmetic.

It's also wrong, for non-power of two sizes, since the wrapping of your
increment and your modulo RXBUF_SIZE get out of sync.

The usual choice is to track "head" and "tail", and use something like:

void uart_rx_isr(void) {
unsigned char c = UART->DATA;
// Reset interrupt flag
uint8_t next = rxfifo.tail;
rxfifo.buf[next] = c;
next++;
if (next >= RXBUF_SIZE) next -= RXBUF_SIZE;
rxfifo.tail = next;
}

>   // Reset interrupt flag
> }
>
> /* Called regularly from mainloop code */
> int uart_task(void) {
>   int c = -1;
>   if (out != in) {
>     c = rxfifo.buf[out % RXBUF_SIZE];
>     out++;
>   }
>   return -1;
> }

int uart_task(void) {
int c = -1;
uint8_t next = rxfifo.head;
if (next != rxfifo.tail) {
c = rxfifo.buf[next];
next++;
if (next >= RXBUF_SIZE) next -= RXBUF_SIZE;
rxfifo.head = next;
}
return c;
}

These don't track buffer overflow at all - you need to call uart_task()
often enough to avoid that.

(I'm skipping volatiles so we don't get ahead of your point.)

>
>
> From a 20-years old article[1] by Nigle Jones, this seems a situation
> where volatile must be used for rxfifo.in, because is modified by an ISR
> and used in the mainloop code.
>

Certainly whenever data is shared between ISR's and mainloop code, or
different threads, then you need to think about how to make sure data is
synchronised and exchanged. "volatile" is one method, atomics are
another, and memory barriers can be used.

> I don't think so, rxfifo.in is read from memory only one time in
> uart_task(), so there isn't the risk that compiler can optimize badly.

That is incorrect in two ways. One - baring compiler bugs (which do
occur, but they are very rare compared to user bugs), there is no such
thing as "optimising badly". If optimising changes the behaviour of the
code, other than its size and speed, the code is wrong. Two - it is a
very bad idea to imagine that having code inside a function somehow
"protects" it from re-ordering or other optimisation.

Functions can be inlined, outlined, cloned, and shuffled about.
Link-time optimisation, code in headers, C++ modules, and other
cross-unit optimisations are becoming more and more common. So while it
might be true /today/ that the compiler has no alternative but to read
rxfifo.in once per call to uart_task(), you cannot assume that will be
the case with later compilers or with more advanced optimisation
techniques enabled. It is safer, more portable, and more future-proof
to avoid such assumptions.

> Even if ISR is fired immediately after the if statement, this doesn't
> bring to a dangerous state: the just received data will be processed at
> the next call to uart_task().
>
> So IMHO volatile isn't necessary here. And critical sections (i.e.
> disabling interrupts) aren't useful too.
>
> However I'm thinking about memory barrier. Suppose the compiler reorder
> the instructions in uart_task() as follows:
>
>
>   c = rxfifo.buf[out % RXBUF_SIZE]
>   if (out != in) {
>     out++;
>     return c;
>   } else {
>     return -1;
>   }
>
>
> Here there's a big problem, because compiler decided to firstly read
> rxfifo.buf[] and then test in and out equality. If the ISR is fired
> immediately after moving data to c (most probably an internal register),
> the condition in the if statement will be true and the register value is
> returned. However the register value isn't correct.

You are absolutely correct.

>
> I don't think any modern C compiler reorder uart_task() in this way, but
> we can't be sure. The result shouldn't change for the compiler, so it
> can do this kind of things.

It is not an unreasonable re-arrangement. On processors with
out-of-order execution (which does not apply to the AVR or Cortex-M),
compilers will often push loads as early as they can in the instruction
stream so that they start the cache loading process as quickly as
possible. (But note that on such "big" processors, much of this
discussion on volatile and memory barriers is not sufficient, especially
if there is more than one core. You need atomics and fences, but that's
a story for another day.)

>
> How to fix this issue if I want to be extremely sure the compiler will
> not reorder this way? Applying volatile to rxfifo.in shouldn't help for
> this, because compiler is allowed to reorder access of non volatile
> variables yet[2].
>

The important thing about "volatile" is that it is /accesses/ that are
volatile, not objects. A volatile object is nothing more than an object
for which all accesses are volatile by default. But you can use
volatile accesses on non-volatile objects. This macro is your friend:

#define volatileAccess(v) *((volatile typeof((v)) *) &(v))

(Linux has the same macro, called ACCESS_ONCE. It uses a gcc extension
- if you are using other compilers then you can make an uglier
equivalent using _Generic. However, if you are using a C compiler that
supports C11, it is probably gcc or clang, and you can use the "typeof"
extension.)

That macro will let you make a volatile read or write to an object
without requiring that /all/ accesses to it are volatile.

> One solution is adding a memory barrier in this way:
>
>
> int uart_task(void) {
>   int c = -1;
>   if (out != in) {
>     memory_barrier();
>     c = rxfifo.buf[out % RXBUF_SIZE];
>     out++;
>   }
>   return -1;
> }
>

Note that you are forcing the compiler to read "out" twice here, as it
can't keep the value of "out" in a register across the memory barrier.
(And as I mentioned before, the compiler might be able to do larger
scale optimisation across compilation units or functions, and in that
way keep values across multiple calls to uart_task.)

>
> However this approach appears to me dangerous. You have to check and
> double check if, when and where memory barriers are necessary and it's
> simple to skip a barrier where it's nedded and add a barrier where it
> isn't needed.

Memory barriers are certainly useful, but they are a shotgun approach -
they affect /everything/ involving reads and writes to memory. (But
remember they don't affect ordering of calculations.)

>
> So I'm thinking that a sub-optimal (regarding efficiency) but reliable
> (regarding the risk to skip a barrier where it is needed) could be to
> enter a critical section (disabling interrupts) anyway, if it isn't
> strictly needed.
>
>
> int uart_task(void) {
>   ENTER_CRITICAL_SECTION();
>   int c = -1;
>   if (out != in) {
>     c = rxfifo.buf[out % RXBUF_SIZE];
>     out++;
>   }
>   EXIT_CRITICAL_SECTION();
>   return -1;
> }

Critical sections for something like this are /way/ overkill. And a
critical section with a division in the middle? Not a good idea.

>
>
> Another solution could be to apply volatile keyword to rxfifo.in *AND*
> rxfifo.buf too, so compiler can't change the order of accesses them.
>
> Do you have other suggestions?
>

Marking "in" and "buf" as volatile is /far/ better than using a critical
section, and likely to be more efficient than a memory barrier. You can
also use volatileAccess rather than making buf volatile, and it is often
slightly more efficient to cache volatile variables in a local variable
while working with them.


Click here to read the complete article
Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl1qc0$hlh$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=647&group=comp.arch.embedded#647

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: pozzu...@gmail.com (pozz)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Sat, 23 Oct 2021 22:12:47 +0200
Organization: A noiseless patient Spider
Lines: 122
Message-ID: <sl1qc0$hlh$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl05ed$ode$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-15; format=flowed
Content-Transfer-Encoding: 8bit
Injection-Date: Sat, 23 Oct 2021 20:12:48 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="8e088add632fbe7c60bcf24707e822f6";
logging-data="18097"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+7L7a2uO/9jCAvetqL5ob07qKrmGJdi7c="
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101
Thunderbird/78.14.0
Cancel-Lock: sha1:YWx+a9eWQkn8c75izX2x6y47IN8=
In-Reply-To: <sl05ed$ode$1@dont-email.me>
Content-Language: it
 by: pozz - Sat, 23 Oct 2021 20:12 UTC

Il 23/10/2021 07:09, Don Y ha scritto:
> On 10/22/2021 3:07 PM, pozz wrote:
>> static struct {
>>    unsigned char buf[RXBUF_SIZE];
>>    uint8_t in;
>>    uint8_t out;
>> } rxfifo;
>>
>> /* ISR */
>> void uart_rx_isr(void) {
>>    unsigned char c = UART->DATA;
>>    rxfifo.buf[in % RXBUF_SIZE] = c;
>>    rxfifo.in++;
>>    // Reset interrupt flag
>> }
>>
>> /* Called regularly from mainloop code */
>> int uart_task(void) {
>>    int c = -1;
>
> Why?  And why a retval from uart_task -- if it is always "-1"?

It was my mistake. The last instruction of uart_task() should be

return c;

And maybe the name of uart_task() is not so good, it should be uart_rx().

>
>>    if (out != in) {
>>      c = rxfifo.buf[out % RXBUF_SIZE];
>>      out++;
>>    }
>>    return -1;
>> }
>
> This is a bug(s) waiting to happen.
>
> How is RXBUF_SIZE defined?

Power of two.

> How does it reflect the data rate (and,
> thus, interrupt rate) as well as the maximum latency between "main
> loop" accesses?

Rx FIFO filled by interrupt is needed to face a burst (a packet?) of
incoming characters.

If the baudrate is 9600bps 8n1, interrupt would be fired every
10/9600=1ms. If maximum interval between two successive uart_task()
calls is 10ms, it is sufficient a buffer of 10 bytes, so RXBUF_SIZE
could be 16 or 32.

> I.e., what happens when the buffer is *full* -- and,
> thus, appears EMPTY?

These are good questions, but I didn't want to discuss about them. Of
course ISR is not complete, because before pushing a new byte, we must
check if FIFO is full. For example:

/* The difference in-out gives a correct result even after a
* wrap-around of in only, thanks to unsigned arithmetic. */
#define RXFIFO_IS_FULL() (rxfifo.in - rxfifo.out < RXBUF_SIZE)

void uart_rx_isr(void) {
unsigned char c = UART->DATA;
if (!RXFIFO_IS_FULL()) {
rxfifo.buf[in % RXBUF_SIZE] = c;
rxfifo.in++;
} else {
// FIFO is full, ignore the char
}
// Reset interrupt flag
}

> What stops the "in" member from growing to the
> maximum size of a uint8 -- and then wrapping?

As I wrote, this should work even after a wrap-around.

> How do you convey this
> to the upper level code ("Hey, we just lost a whole RXBUF_SIZE of
> characters so if the character stream doesn't make sense, that might
> be a cause...")?

FIFO full is event is extremely rare if I'm able to size rx FIFO
correctly, i.e. on the worst case.
Anyway I usually ignore incoming chars when the FIFO is full. The high
level protocols are usually defined in such a way the absence of chars
are detected, mostly thanks to CRC.

> What if RXBUF_SIZE is relatively prime wrt uint8max?
>
> When writing UART handlers, I fetch the received datum along with
> the uart's flags and stuff *both* of those things in the FIFO.
> If the FIFO would be full, I, instead, modify the flags of the
> preceeding datum to reflect this fact ("Some number of characters
> have been lost AFTER this one...") and discard the current character.
>
> I then signal an event and let a task waiting for that specific event
> wake up and retrieve the contents of the FIFO (which may include more
> than one character, at that time as characters can arrive after the
> initial event has been signaled).

Signal an event? Task waiting for a specific event? Maybe you are
thinking of a full RTOS. I was thinking of bare metal systems.

> This lets me move the line discipline out of the ISR and still keep
> the system "responsive".
>
> Figure out everything that you need to do before you start sorting out
> how the compiler can "shaft" you...

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl1sh2$vq6$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=648&group=comp.arch.embedded#648

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: pozzu...@gmail.com (pozz)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Sat, 23 Oct 2021 22:49:37 +0200
Organization: A noiseless patient Spider
Lines: 317
Message-ID: <sl1sh2$vq6$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl1c3a$dfr$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-15; format=flowed
Content-Transfer-Encoding: 8bit
Injection-Date: Sat, 23 Oct 2021 20:49:38 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="8e088add632fbe7c60bcf24707e822f6";
logging-data="32582"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX19we40eSmVuROKsQ1Qt7qXUXs3fSrZbq44="
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101
Thunderbird/78.14.0
Cancel-Lock: sha1:dStMDh3dSnry6Olw+cYaQu0x//o=
In-Reply-To: <sl1c3a$dfr$1@dont-email.me>
Content-Language: it
 by: pozz - Sat, 23 Oct 2021 20:49 UTC

Il 23/10/2021 18:09, David Brown ha scritto:
> On 23/10/2021 00:07, pozz wrote:
>> Even I write software for embedded systems for more than 10 years,
>> there's an argument that from time to time let me think for hours and
>> leave me with many doubts.
>
> It's nice to see a thread like this here - the group needs such discussions!
>
>>
>> Consider a simple embedded system based on a MCU (AVR8 or Cortex-Mx).
>> The software is bare metal, without any OS. The main pattern is the well
>> known mainloop (background code) that is interrupted by ISR.
>>
>> Interrupts are used mainly for timings and for low-level driver. For
>> example, the UART reception ISR move the last received char in a FIFO
>> buffer, while the mainloop code pops new data from the FIFO.
>>
>>
>> static struct {
>>   unsigned char buf[RXBUF_SIZE];
>>   uint8_t in;
>>   uint8_t out;
>> } rxfifo;
>>
>> /* ISR */
>> void uart_rx_isr(void) {
>>   unsigned char c = UART->DATA;
>>   rxfifo.buf[in % RXBUF_SIZE] = c;
>>   rxfifo.in++;
>
> Unless you are sure that RXBUF_SIZE is a power of two, this is going to
> be quite slow on an AVR. Modulo means division, and while division by a
> constant is usually optimised to a multiplication by the compiler, you
> still have a multiply, a shift, and some compensation for it all being
> done as signed integer arithmetic.
>
> It's also wrong, for non-power of two sizes, since the wrapping of your
> increment and your modulo RXBUF_SIZE get out of sync.

Yes, RXBUF_SIZE is a power of two.

> The usual choice is to track "head" and "tail", and use something like:
>
> void uart_rx_isr(void) {
> unsigned char c = UART->DATA;
> // Reset interrupt flag
> uint8_t next = rxfifo.tail;
> rxfifo.buf[next] = c;
> next++;
> if (next >= RXBUF_SIZE) next -= RXBUF_SIZE;
> rxfifo.tail = next;
> }

This isn't the point of this thread, anyway...
You insist that tail is always in the range [0...RXBUF_SIZE - 1]. My
approach is different.

RXBUF_SIZE is a power of two, usualy <=256. head and tail are uint8_t
and *can* reach the maximum value of 255, even RXBUF_SIZE is 128. All
works well.

Suppose rxfifo.in=rxfifo.out=127, FIFO is empty. When a new char is
received, it is saved into rxfifo.buf[127 % 128=127] and rxfifo.in will
be increased to 128.
Now mainloop detect the new char (in != out), reads the new char at
rxfifo.buf[127 % 128=127] and increase out that will be 128.

The next byte will be saved into rxfifo.rxbuf[rxfifo.in % 128=128 % 128
= 0] and rxfifo.in will be 129. Again, the next byte will be saved to
rxbuf[rxfifo.in % 128=129 % 128=1] and rxfifo.in will be 130.

When the mainloop tries to pop data from fifo, it tests

rxfifo.in(130) !=rxfifo.out(128)

The test is true, so the code extracts chars from rxbuf[out % 128] that
is rxbuf[0]... and so on.

I hope that explanation is good.

>
>>   // Reset interrupt flag
>> }
>>
>> /* Called regularly from mainloop code */
>> int uart_task(void) {
>>   int c = -1;
>>   if (out != in) {
>>     c = rxfifo.buf[out % RXBUF_SIZE];
>>     out++;
>>   }
>>   return -1;
>> }
>
> int uart_task(void) {
> int c = -1;
> uint8_t next = rxfifo.head;
> if (next != rxfifo.tail) {
> c = rxfifo.buf[next];
> next++;
> if (next >= RXBUF_SIZE) next -= RXBUF_SIZE;
> rxfifo.head = next;
> }
> return c;
> }
>
> These don't track buffer overflow at all - you need to call uart_task()
> often enough to avoid that.

Sure, with a good number for RXBUF_SIZE, buffer overflow shouldn't
happen ever. Anyway, if it happens, the higher level layers (protocol)
should detect a corrupted packet.

> (I'm skipping volatiles so we don't get ahead of your point.)
>
>>
>>
>> From a 20-years old article[1] by Nigle Jones, this seems a situation
>> where volatile must be used for rxfifo.in, because is modified by an ISR
>> and used in the mainloop code.
>>
>
> Certainly whenever data is shared between ISR's and mainloop code, or
> different threads, then you need to think about how to make sure data is
> synchronised and exchanged. "volatile" is one method, atomics are
> another, and memory barriers can be used.
>
>> I don't think so, rxfifo.in is read from memory only one time in
>> uart_task(), so there isn't the risk that compiler can optimize badly.
>
> That is incorrect in two ways. One - baring compiler bugs (which do
> occur, but they are very rare compared to user bugs), there is no such
> thing as "optimising badly". If optimising changes the behaviour of the
> code, other than its size and speed, the code is wrong.

Yes of course, but I don't think the absence of volatile for rxfifo.in,
even if it can change in ISR, could be a *real* problem with *modern and
current* compilers.

voltile attribute needs to avoid compiler optimization (that would be a
bad thing, because of volatile nature of the variabile), but on that
code it's difficult to think of an optimization, caused by the absence
of volatile, that changes the behaviour erroneously... except reorering.

> Two - it is a
> very bad idea to imagine that having code inside a function somehow
> "protects" it from re-ordering or other optimisation.

I didn't say this, at the contrary I was thinking exactly to reordering
issues.

> Functions can be inlined, outlined, cloned, and shuffled about.
> Link-time optimisation, code in headers, C++ modules, and other
> cross-unit optimisations are becoming more and more common. So while it
> might be true /today/ that the compiler has no alternative but to read
> rxfifo.in once per call to uart_task(), you cannot assume that will be
> the case with later compilers or with more advanced optimisation
> techniques enabled. It is safer, more portable, and more future-proof
> to avoid such assumptions.

Ok, you are talking of future scenarios. I don't think actually this
could be a real problem. Anyway your observation makes sense.

>
>> Even if ISR is fired immediately after the if statement, this doesn't
>> bring to a dangerous state: the just received data will be processed at
>> the next call to uart_task().
>>
>> So IMHO volatile isn't necessary here. And critical sections (i.e.
>> disabling interrupts) aren't useful too.
>>
>> However I'm thinking about memory barrier. Suppose the compiler reorder
>> the instructions in uart_task() as follows:
>>
>>
>>   c = rxfifo.buf[out % RXBUF_SIZE]
>>   if (out != in) {
>>     out++;
>>     return c;
>>   } else {
>>     return -1;
>>   }
>>
>>
>> Here there's a big problem, because compiler decided to firstly read
>> rxfifo.buf[] and then test in and out equality. If the ISR is fired
>> immediately after moving data to c (most probably an internal register),
>> the condition in the if statement will be true and the register value is
>> returned. However the register value isn't correct.
>
> You are absolutely correct.
>
>>
>> I don't think any modern C compiler reorder uart_task() in this way, but
>> we can't be sure. The result shouldn't change for the compiler, so it
>> can do this kind of things.
>
> It is not an unreasonable re-arrangement. On processors with
> out-of-order execution (which does not apply to the AVR or Cortex-M),
> compilers will often push loads as early as they can in the instruction
> stream so that they start the cache loading process as quickly as
> possible. (But note that on such "big" processors, much of this
> discussion on volatile and memory barriers is not sufficient, especially
> if there is more than one core. You need atomics and fences, but that's
> a story for another day.)
>
>>
>> How to fix this issue if I want to be extremely sure the compiler will
>> not reorder this way? Applying volatile to rxfifo.in shouldn't help for
>> this, because compiler is allowed to reorder access of non volatile
>> variables yet[2].
>>
>
> The important thing about "volatile" is that it is /accesses/ that are
> volatile, not objects. A volatile object is nothing more than an object
> for which all accesses are volatile by default. But you can use
> volatile accesses on non-volatile objects. This macro is your friend:
>
> #define volatileAccess(v) *((volatile typeof((v)) *) &(v))
>
> (Linux has the same macro, called ACCESS_ONCE. It uses a gcc extension
> - if you are using other compilers then you can make an uglier
> equivalent using _Generic. However, if you are using a C compiler that
> supports C11, it is probably gcc or clang, and you can use the "typeof"
> extension.)
>
> That macro will let you make a volatile read or write to an object
> without requiring that /all/ accesses to it are volatile.


Click here to read the complete article
Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl2461$s9k$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=649&group=comp.arch.embedded#649

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: blockedo...@foo.invalid (Don Y)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Sat, 23 Oct 2021 15:59:57 -0700
Organization: A noiseless patient Spider
Lines: 98
Message-ID: <sl2461$s9k$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl05ed$ode$1@dont-email.me>
<sl1qc0$hlh$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-15; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Sat, 23 Oct 2021 23:00:17 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="71824e2ef3f93c5e9d433ef1315bf619";
logging-data="28980"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+jK8us54DmiikFZ/xU8v+o"
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101
Thunderbird/52.1.1
Cancel-Lock: sha1:FGsGnjvqdYENaqrekt/zKsSQ3Ig=
In-Reply-To: <sl1qc0$hlh$1@dont-email.me>
Content-Language: en-US
 by: Don Y - Sat, 23 Oct 2021 22:59 UTC

On 10/23/2021 1:12 PM, pozz wrote:
>> This is a bug(s) waiting to happen.
>>
>> How is RXBUF_SIZE defined?
>
> Power of two.

The point was its relationship to the actual code.

>> How does it reflect the data rate (and,
>> thus, interrupt rate) as well as the maximum latency between "main
>> loop" accesses?
>
> Rx FIFO filled by interrupt is needed to face a burst (a packet?) of incoming
> characters.
>
> If the baudrate is 9600bps 8n1, interrupt would be fired every 10/9600=1ms. If
> maximum interval between two successive uart_task() calls is 10ms, it is
> sufficient a buffer of 10 bytes, so RXBUF_SIZE could be 16 or 32.

What GUARANTEES this in your system? Folks often see things that "can't
happen" -- yet DID (THEY SAW IT!). Your code/design should ensure that
"can't happen" REALLY /can't happen/. It costs very little to explain
(commentary) WHY you don't have to check for X, Y or Z in your code.

[If the user's actions (or any outside agency) can affect operation,
then how can you guarantee that THEY "behave"?]

And, give that a very high degree of visibility so that when someone
decides they can increase the baudrate or add some sluggish task
to your "main loop" that this ASSUMPTION isn't silently violated.

>> I.e., what happens when the buffer is *full* -- and,
>> thus, appears EMPTY?
>
> These are good questions, but I didn't want to discuss about them. Of course
> ISR is not complete, because before pushing a new byte, we must check if FIFO
> is full. For example:

My point is that you should fleshout your code before you start
thinking about what can go wrong.

E.g., if the ISR is the *only* entity to modify ".in" and always does so
in with interrupts off, then it can do so without worrying about conflict
with something else -- if those other things always ensure they read it
atomically (if they read it just before or just after it has been modified
by the ISR, the value will still "work" -- they just may not realize, yet,
that there is an extra character in the buffer that they haven't yet seen).

Likewise, if the "task" is the only entity modifying ".out", then ensuring
that those modifications are atomic means the ISR can safely use any *single*
reference to it.

>> How do you convey this
>> to the upper level code ("Hey, we just lost a whole RXBUF_SIZE of
>> characters so if the character stream doesn't make sense, that might
>> be a cause...")?
>
> FIFO full is event is extremely rare if I'm able to size rx FIFO correctly,
> i.e. on the worst case.

"Rare" and "impossible" are too entirely different scenarios.
It is extremely rare for a specific individual to win the lottery.
But, any individual *can* win it!

> Anyway I usually ignore incoming chars when the FIFO is full. The high level
> protocols are usually defined in such a way the absence of chars are detected,
> mostly thanks to CRC.

What if the CRC characters disappear? Are you sure the front of one message
can't appear to match the ass end of another?

"Pozz is here."
"Don is not here."

"Pozz is not here."

And that there is no value in knowing that one or more messages may have been
dropped?

>> What if RXBUF_SIZE is relatively prime wrt uint8max?
>>
>> When writing UART handlers, I fetch the received datum along with
>> the uart's flags and stuff *both* of those things in the FIFO.
>> If the FIFO would be full, I, instead, modify the flags of the
>> preceeding datum to reflect this fact ("Some number of characters
>> have been lost AFTER this one...") and discard the current character.
>>
>> I then signal an event and let a task waiting for that specific event
>> wake up and retrieve the contents of the FIFO (which may include more
>> than one character, at that time as characters can arrive after the
>> initial event has been signaled).
>
> Signal an event? Task waiting for a specific event? Maybe you are thinking of a
> full RTOS. I was thinking of bare metal systems.

You can implement as much or as little of an OS as you choose;
you're not stuck with "all or nothing".

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl3d4h$17d3$1@gioia.aioe.org>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=650&group=comp.arch.embedded#650

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!aioe.org!8RWoc1Ep6oX/vyHMguyukA.user.46.165.242.91.POSTED!not-for-mail
From: klamm...@NOSPAM.a1.net (Johann Klammer)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Sun, 24 Oct 2021 12:39:02 +0200
Organization: Aioe.org NNTP Server
Message-ID: <sl3d4h$17d3$1@gioia.aioe.org>
References: <skvcnd$5dv$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-15
Content-Transfer-Encoding: 7bit
Injection-Info: gioia.aioe.org; logging-data="40355"; posting-host="8RWoc1Ep6oX/vyHMguyukA.user.gioia.aioe.org"; mail-complaints-to="abuse@aioe.org";
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:24.0) Gecko/20100101 Icedove/24.5.0
X-Notice: Filtered by postfilter v. 0.9.2
 by: Johann Klammer - Sun, 24 Oct 2021 10:39 UTC

On 10/23/2021 12:07 AM, pozz wrote:
> Even I write software for embedded systems for more than 10 years, there's an argument that from time to time let me think for hours and leave me with many doubts.
>
> Consider a simple embedded system based on a MCU (AVR8 or Cortex-Mx). The software is bare metal, without any OS. The main pattern is the well known mainloop (background code) that is interrupted by ISR.
>
> Interrupts are used mainly for timings and for low-level driver. For example, the UART reception ISR move the last received char in a FIFO buffer, while the mainloop code pops new data from the FIFO.
>
>
> static struct {
> unsigned char buf[RXBUF_SIZE];
> uint8_t in;
> uint8_t out;
> } rxfifo;
>
> /* ISR */
> void uart_rx_isr(void) {
> unsigned char c = UART->DATA;
> rxfifo.buf[in % RXBUF_SIZE] = c;
> rxfifo.in++;
> // Reset interrupt flag
> }
>
> /* Called regularly from mainloop code */
> int uart_task(void) {
> int c = -1;
> if (out != in) {
> c = rxfifo.buf[out % RXBUF_SIZE];
> out++;
> }
> return -1;
> }
>
>
> From a 20-years old article[1] by Nigle Jones, this seems a situation where volatile must be used for rxfifo.in, because is modified by an ISR and used in the mainloop code.
>
> I don't think so, rxfifo.in is read from memory only one time in uart_task(), so there isn't the risk that compiler can optimize badly. Even if ISR is fired immediately after the if statement, this doesn't bring to a dangerous state: the just received data will be processed at the next call to uart_task().
>
> So IMHO volatile isn't necessary here. And critical sections (i.e. disabling interrupts) aren't useful too.
>
> However I'm thinking about memory barrier. Suppose the compiler reorder the instructions in uart_task() as follows:
>
>
> c = rxfifo.buf[out % RXBUF_SIZE]
> if (out != in) {
> out++;
> return c;
> } else {
> return -1;
> }
>
>
> Here there's a big problem, because compiler decided to firstly read rxfifo.buf[] and then test in and out equality. If the ISR is fired immediately after moving data to c (most probably an internal register), the condition in the if statement will be true and the register value is returned. However the register value isn't correct.
>
> I don't think any modern C compiler reorder uart_task() in this way, but we can't be sure. The result shouldn't change for the compiler, so it can do this kind of things.
>
> How to fix this issue if I want to be extremely sure the compiler will not reorder this way? Applying volatile to rxfifo.in shouldn't help for this, because compiler is allowed to reorder access of non volatile variables yet[2].
>
> One solution is adding a memory barrier in this way:
>
>
> int uart_task(void) {
> int c = -1;
> if (out != in) {
> memory_barrier();
> c = rxfifo.buf[out % RXBUF_SIZE];
> out++;
> }
> return -1;
> }
>
>
> However this approach appears to me dangerous. You have to check and double check if, when and where memory barriers are necessary and it's simple to skip a barrier where it's nedded and add a barrier where it isn't needed.
>
> So I'm thinking that a sub-optimal (regarding efficiency) but reliable (regarding the risk to skip a barrier where it is needed) could be to enter a critical section (disabling interrupts) anyway, if it isn't strictly needed.
>
>
> int uart_task(void) {
> ENTER_CRITICAL_SECTION();
> int c = -1;
> if (out != in) {
> c = rxfifo.buf[out % RXBUF_SIZE];
> out++;
> }
> EXIT_CRITICAL_SECTION();
> return -1;
> }
>
>
> Another solution could be to apply volatile keyword to rxfifo.in *AND* rxfifo.buf too, so compiler can't change the order of accesses them.
>
> Do you have other suggestions?
>
>
>
> [1] https://barrgroup.com/embedded-systems/how-to/c-volatile-keyword
> [2] https://blog.regehr.org/archives/28
Disable interrupts while accessing the fifo. you really have to.
alternatively you'll often get away not using a fifo at all,
unless you're blocking for a long while in some part of the code.

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl3eg8$674$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=651&group=comp.arch.embedded#651

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: david.br...@hesbynett.no (David Brown)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Sun, 24 Oct 2021 13:02:32 +0200
Organization: A noiseless patient Spider
Lines: 384
Message-ID: <sl3eg8$674$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl1c3a$dfr$1@dont-email.me>
<sl1sh2$vq6$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-15
Content-Transfer-Encoding: 8bit
Injection-Date: Sun, 24 Oct 2021 11:02:32 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="666b7f184d140ccf1515d4914a0e51ef";
logging-data="6372"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+2evSGIcrby3QaMhrAeCsOwzIWtXZfBEM="
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101
Thunderbird/78.11.0
Cancel-Lock: sha1:K1Jym+bEBNgZqSe1OliKKj4LWh0=
In-Reply-To: <sl1sh2$vq6$1@dont-email.me>
Content-Language: en-GB
 by: David Brown - Sun, 24 Oct 2021 11:02 UTC

On 23/10/2021 22:49, pozz wrote:
> Il 23/10/2021 18:09, David Brown ha scritto:
>> On 23/10/2021 00:07, pozz wrote:
>>> Even I write software for embedded systems for more than 10 years,
>>> there's an argument that from time to time let me think for hours and
>>> leave me with many doubts.
>>
>> It's nice to see a thread like this here - the group needs such
>> discussions!
>>
>>>
>>> Consider a simple embedded system based on a MCU (AVR8 or Cortex-Mx).
>>> The software is bare metal, without any OS. The main pattern is the well
>>> known mainloop (background code) that is interrupted by ISR.
>>>
>>> Interrupts are used mainly for timings and for low-level driver. For
>>> example, the UART reception ISR move the last received char in a FIFO
>>> buffer, while the mainloop code pops new data from the FIFO.
>>>
>>>
>>> static struct {
>>>    unsigned char buf[RXBUF_SIZE];
>>>    uint8_t in;
>>>    uint8_t out;
>>> } rxfifo;
>>>
>>> /* ISR */
>>> void uart_rx_isr(void) {
>>>    unsigned char c = UART->DATA;
>>>    rxfifo.buf[in % RXBUF_SIZE] = c;
>>>    rxfifo.in++;
>>
>> Unless you are sure that RXBUF_SIZE is a power of two, this is going to
>> be quite slow on an AVR.  Modulo means division, and while division by a
>> constant is usually optimised to a multiplication by the compiler, you
>> still have a multiply, a shift, and some compensation for it all being
>> done as signed integer arithmetic.
>>
>> It's also wrong, for non-power of two sizes, since the wrapping of your
>> increment and your modulo RXBUF_SIZE get out of sync.
>
> Yes, RXBUF_SIZE is a power of two.
>

If your code relies on that, make sure the code will fail to compile if
it is not the case. Documentation is good, compile-time check is better:

static_assert((RXBUF_SIZE & (RXBUF_SIZE - 1)) == 0, "Needs power of 2");

>
>
>> The usual choice is to track "head" and "tail", and use something like:
>>
>> void uart_rx_isr(void) {
>>    unsigned char c = UART->DATA;
>>    // Reset interrupt flag
>>    uint8_t next = rxfifo.tail;
>>    rxfifo.buf[next] = c;
>>    next++;
>>    if (next >= RXBUF_SIZE) next -= RXBUF_SIZE;
>>    rxfifo.tail = next;
>> }
>
> This isn't the point of this thread, anyway...
> You insist that tail is always in the range [0...RXBUF_SIZE - 1]. My
> approach is different.
>
> RXBUF_SIZE is a power of two, usualy <=256. head and tail are uint8_t
> and *can* reach the maximum value of 255, even RXBUF_SIZE is 128. All
> works well.
>

Yes, your approach will work - /if/ you have a power-of-two buffer size.
It has no noticeable efficiency advantages, merely an extra
inconvenient restriction and the possible confusion caused by doing
things in a different way from common idioms.

However, this is not the point of the thread - so I am happy to leave
that for now.

> Suppose rxfifo.in=rxfifo.out=127, FIFO is empty. When a new char is
> received, it is saved into rxfifo.buf[127 % 128=127] and rxfifo.in will
> be increased to 128.
> Now mainloop detect the new char (in != out), reads the new char at
> rxfifo.buf[127 % 128=127] and increase out that will be 128.
>
> The next byte will be saved into rxfifo.rxbuf[rxfifo.in % 128=128 % 128
> = 0] and rxfifo.in will be 129. Again, the next byte will be saved to
> rxbuf[rxfifo.in % 128=129 % 128=1] and rxfifo.in will be 130.
>
> When the mainloop tries to pop data from fifo, it tests
>
>    rxfifo.in(130) !=rxfifo.out(128)
>
> The test is true, so the code extracts chars from rxbuf[out % 128] that
> is rxbuf[0]... and so on.
>
> I hope that explanation is good.
>
>
>>
>>>    // Reset interrupt flag
>>> }
>>>
>>> /* Called regularly from mainloop code */
>>> int uart_task(void) {
>>>    int c = -1;
>>>    if (out != in) {
>>>      c = rxfifo.buf[out % RXBUF_SIZE];
>>>      out++;
>>>    }
>>>    return -1;
>>> }
>>
>> int uart_task(void) {
>>    int c = -1;
>>    uint8_t next = rxfifo.head;
>>    if (next != rxfifo.tail) {
>>        c = rxfifo.buf[next];
>>        next++;
>>        if (next >= RXBUF_SIZE) next -= RXBUF_SIZE;
>>        rxfifo.head = next;
>>    }
>>    return c;
>> }
>>
>> These don't track buffer overflow at all - you need to call uart_task()
>> often enough to avoid that.
>
> Sure, with a good number for RXBUF_SIZE, buffer overflow shouldn't
> happen ever. Anyway, if it happens, the higher level layers (protocol)
> should detect a corrupted packet.
>

You risk getting seriously out of sync if there is an overflow.
Normally, on an overflow there will be a dropped character or two (which
as you say, must be caught at a higher level). Here you could end up
going round your buffer an extra time and /gaining/ RXBUF_SIZE extra
characters.

Still, if you are sure that your functions are called fast enough so
that overflow is not a concern, then that's fine. Extra code to check
for a situation that can't occur is not helpful.

>
>> (I'm skipping volatiles so we don't get ahead of your point.)
>>
>>>
>>>
>>>  From a 20-years old article[1] by Nigle Jones, this seems a situation
>>> where volatile must be used for rxfifo.in, because is modified by an ISR
>>> and used in the mainloop code.
>>>
>>
>> Certainly whenever data is shared between ISR's and mainloop code, or
>> different threads, then you need to think about how to make sure data is
>> synchronised and exchanged.  "volatile" is one method, atomics are
>> another, and memory barriers can be used.
>>
>>> I don't think so, rxfifo.in is read from memory only one time in
>>> uart_task(), so there isn't the risk that compiler can optimize badly.
>>
>> That is incorrect in two ways.  One - baring compiler bugs (which do
>> occur, but they are very rare compared to user bugs), there is no such
>> thing as "optimising badly".  If optimising changes the behaviour of the
>> code, other than its size and speed, the code is wrong. 
>
> Yes of course, but I don't think the absence of volatile for rxfifo.in,
> even if it can change in ISR, could be a *real* problem with *modern and
> current* compilers.
>

Personally, I am not satisfied with "it's unlikely to be a problem in
practice" - I prefer "The language guarantees it is not a problem".
Remember, when you know the data needs to be read at this point, then
using a volatile read is free. Volatile does not make code less
efficient unless you use it incorrectly and force more accesses than are
necessary. So using volatile accesses for "rxfifo.in" here turns
"probably safe" into "certainly safe" without cost. What's not to like?

> voltile attribute needs to avoid compiler optimization (that would be a
> bad thing, because of volatile nature of the variabile), but on that
> code it's difficult to think of an optimization, caused by the absence
> of volatile, that changes the behaviour erroneously... except reorering.
>
>
>> Two - it is a
>> very bad idea to imagine that having code inside a function somehow
>> "protects" it from re-ordering or other optimisation.
>
> I didn't say this, at the contrary I was thinking exactly to reordering
> issues.
>
>
>> Functions can be inlined, outlined, cloned, and shuffled about.
>> Link-time optimisation, code in headers, C++ modules, and other
>> cross-unit optimisations are becoming more and more common.  So while it
>> might be true /today/ that the compiler has no alternative but to read
>> rxfifo.in once per call to uart_task(), you cannot assume that will be
>> the case with later compilers or with more advanced optimisation
>> techniques enabled.  It is safer, more portable, and more future-proof
>> to avoid such assumptions.
>
> Ok, you are talking of future scenarios. I don't think actually this
> could be a real problem. Anyway your observation makes sense.
>
>
>>
>>> Even if ISR is fired immediately after the if statement, this doesn't
>>> bring to a dangerous state: the just received data will be processed at
>>> the next call to uart_task().
>>>
>>> So IMHO volatile isn't necessary here. And critical sections (i.e.
>>> disabling interrupts) aren't useful too.
>>>
>>> However I'm thinking about memory barrier. Suppose the compiler reorder
>>> the instructions in uart_task() as follows:
>>>
>>>
>>>    c = rxfifo.buf[out % RXBUF_SIZE]
>>>    if (out != in) {
>>>      out++;
>>>      return c;
>>>    } else {
>>>      return -1;
>>>    }
>>>
>>>
>>> Here there's a big problem, because compiler decided to firstly read
>>> rxfifo.buf[] and then test in and out equality. If the ISR is fired
>>> immediately after moving data to c (most probably an internal register),
>>> the condition in the if statement will be true and the register value is
>>> returned. However the register value isn't correct.
>>
>> You are absolutely correct.
>>
>>>
>>> I don't think any modern C compiler reorder uart_task() in this way, but
>>> we can't be sure. The result shouldn't change for the compiler, so it
>>> can do this kind of things.
>>
>> It is not an unreasonable re-arrangement.  On processors with
>> out-of-order execution (which does not apply to the AVR or Cortex-M),
>> compilers will often push loads as early as they can in the instruction
>> stream so that they start the cache loading process as quickly as
>> possible.  (But note that on such "big" processors, much of this
>> discussion on volatile and memory barriers is not sufficient, especially
>> if there is more than one core.  You need atomics and fences, but that's
>> a story for another day.)
>>
>>>
>>> How to fix this issue if I want to be extremely sure the compiler will
>>> not reorder this way? Applying volatile to rxfifo.in shouldn't help for
>>> this, because compiler is allowed to reorder access of non volatile
>>> variables yet[2].
>>>
>>
>> The important thing about "volatile" is that it is /accesses/ that are
>> volatile, not objects.  A volatile object is nothing more than an object
>> for which all accesses are volatile by default.  But you can use
>> volatile accesses on non-volatile objects.  This macro is your friend:
>>
>> #define volatileAccess(v) *((volatile typeof((v)) *) &(v))
>>
>> (Linux has the same macro, called ACCESS_ONCE.  It uses a gcc extension
>> - if you are using other compilers then you can make an uglier
>> equivalent using _Generic.  However, if you are using a C compiler that
>> supports C11, it is probably gcc or clang, and you can use the "typeof"
>> extension.)
>>
>> That macro will let you make a volatile read or write to an object
>> without requiring that /all/ accesses to it are volatile.
>
> This is a good point. The code in ISR can't be interrupted, so there's
> no need to have volatile access in ISR.
>


Click here to read the complete article
Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl3f65$jdl$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=652&group=comp.arch.embedded#652

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: dp...@tgi-sci.com (Dimiter_Popoff)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Sun, 24 Oct 2021 14:14:11 +0300
Organization: TGI
Lines: 116
Message-ID: <sl3f65$jdl$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl3d4h$17d3$1@gioia.aioe.org>
Reply-To: dp@tgi-sci.com
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-15; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Sun, 24 Oct 2021 11:14:13 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="ea59a6d7e87f8b5148ca3d280306721a";
logging-data="19893"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX19arGRA/PCkgrCVOuxkjb2GJZV6Jr0yJYA="
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101
Thunderbird/78.14.0
Cancel-Lock: sha1:XF4hpG/BeIQb7KaQ2UxtSLo6bek=
In-Reply-To: <sl3d4h$17d3$1@gioia.aioe.org>
Content-Language: en-US
 by: Dimiter_Popoff - Sun, 24 Oct 2021 11:14 UTC

On 10/24/2021 13:39, Johann Klammer wrote:
> On 10/23/2021 12:07 AM, pozz wrote:
>> Even I write software for embedded systems for more than 10 years, there's an argument that from time to time let me think for hours and leave me with many doubts.
>>
>> Consider a simple embedded system based on a MCU (AVR8 or Cortex-Mx). The software is bare metal, without any OS. The main pattern is the well known mainloop (background code) that is interrupted by ISR.
>>
>> Interrupts are used mainly for timings and for low-level driver. For example, the UART reception ISR move the last received char in a FIFO buffer, while the mainloop code pops new data from the FIFO.
>>
>>
>> static struct {
>> unsigned char buf[RXBUF_SIZE];
>> uint8_t in;
>> uint8_t out;
>> } rxfifo;
>>
>> /* ISR */
>> void uart_rx_isr(void) {
>> unsigned char c = UART->DATA;
>> rxfifo.buf[in % RXBUF_SIZE] = c;
>> rxfifo.in++;
>> // Reset interrupt flag
>> }
>>
>> /* Called regularly from mainloop code */
>> int uart_task(void) {
>> int c = -1;
>> if (out != in) {
>> c = rxfifo.buf[out % RXBUF_SIZE];
>> out++;
>> }
>> return -1;
>> }
>>
>>
>> From a 20-years old article[1] by Nigle Jones, this seems a situation where volatile must be used for rxfifo.in, because is modified by an ISR and used in the mainloop code.
>>
>> I don't think so, rxfifo.in is read from memory only one time in uart_task(), so there isn't the risk that compiler can optimize badly. Even if ISR is fired immediately after the if statement, this doesn't bring to a dangerous state: the just received data will be processed at the next call to uart_task().
>>
>> So IMHO volatile isn't necessary here. And critical sections (i.e. disabling interrupts) aren't useful too.
>>
>> However I'm thinking about memory barrier. Suppose the compiler reorder the instructions in uart_task() as follows:
>>
>>
>> c = rxfifo.buf[out % RXBUF_SIZE]
>> if (out != in) {
>> out++;
>> return c;
>> } else {
>> return -1;
>> }
>>
>>
>> Here there's a big problem, because compiler decided to firstly read rxfifo.buf[] and then test in and out equality. If the ISR is fired immediately after moving data to c (most probably an internal register), the condition in the if statement will be true and the register value is returned. However the register value isn't correct.
>>
>> I don't think any modern C compiler reorder uart_task() in this way, but we can't be sure. The result shouldn't change for the compiler, so it can do this kind of things.
>>
>> How to fix this issue if I want to be extremely sure the compiler will not reorder this way? Applying volatile to rxfifo.in shouldn't help for this, because compiler is allowed to reorder access of non volatile variables yet[2].
>>
>> One solution is adding a memory barrier in this way:
>>
>>
>> int uart_task(void) {
>> int c = -1;
>> if (out != in) {
>> memory_barrier();
>> c = rxfifo.buf[out % RXBUF_SIZE];
>> out++;
>> }
>> return -1;
>> }
>>
>>
>> However this approach appears to me dangerous. You have to check and double check if, when and where memory barriers are necessary and it's simple to skip a barrier where it's nedded and add a barrier where it isn't needed.
>>
>> So I'm thinking that a sub-optimal (regarding efficiency) but reliable (regarding the risk to skip a barrier where it is needed) could be to enter a critical section (disabling interrupts) anyway, if it isn't strictly needed.
>>
>>
>> int uart_task(void) {
>> ENTER_CRITICAL_SECTION();
>> int c = -1;
>> if (out != in) {
>> c = rxfifo.buf[out % RXBUF_SIZE];
>> out++;
>> }
>> EXIT_CRITICAL_SECTION();
>> return -1;
>> }
>>
>>
>> Another solution could be to apply volatile keyword to rxfifo.in *AND* rxfifo.buf too, so compiler can't change the order of accesses them.
>>
>> Do you have other suggestions?
>>
>>
>>
>> [1] https://barrgroup.com/embedded-systems/how-to/c-volatile-keyword
>> [2] https://blog.regehr.org/archives/28
> Disable interrupts while accessing the fifo. you really have to.
> alternatively you'll often get away not using a fifo at all,
> unless you're blocking for a long while in some part of the code.
>

Why would you do that. The fifo write pointer is only modified by
the interrupt handler, the read pointer is only modified by the
interrupted code. Has been done so for times immemorial.

Although this thread is on how to wrestle a poor
language to do what you want, sort of how to use a hammer on a screw
instead of taking the screwdriver, there would be no need to
mask interrupts with C either.

======================================================
Dimiter Popoff, TGI http://www.tgi-sci.com
======================================================
http://www.flickr.com/photos/didi_tgi/

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl3ums$r4$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=653&group=comp.arch.embedded#653

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: pozzu...@gmail.com (pozz)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Sun, 24 Oct 2021 17:39:07 +0200
Organization: A noiseless patient Spider
Lines: 408
Message-ID: <sl3ums$r4$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl1c3a$dfr$1@dont-email.me>
<sl1sh2$vq6$1@dont-email.me> <sl3eg8$674$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-15; format=flowed
Content-Transfer-Encoding: 8bit
Injection-Date: Sun, 24 Oct 2021 15:39:08 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="9c279c591311bf3602da82796cf8db0c";
logging-data="868"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1//C9TIDcEOj8x2WoW5lLIk11TZofQ5nrw="
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101
Thunderbird/78.14.0
Cancel-Lock: sha1:W7C0xPW21hoVeyJxpaqyXIu3QHA=
In-Reply-To: <sl3eg8$674$1@dont-email.me>
Content-Language: it
 by: pozz - Sun, 24 Oct 2021 15:39 UTC

Il 24/10/2021 13:02, David Brown ha scritto:
> On 23/10/2021 22:49, pozz wrote:
>> Il 23/10/2021 18:09, David Brown ha scritto:
>>> On 23/10/2021 00:07, pozz wrote:
>>>> Even I write software for embedded systems for more than 10 years,
>>>> there's an argument that from time to time let me think for hours and
>>>> leave me with many doubts.
>>>
>>> It's nice to see a thread like this here - the group needs such
>>> discussions!
>>>
>>>>
>>>> Consider a simple embedded system based on a MCU (AVR8 or Cortex-Mx).
>>>> The software is bare metal, without any OS. The main pattern is the well
>>>> known mainloop (background code) that is interrupted by ISR.
>>>>
>>>> Interrupts are used mainly for timings and for low-level driver. For
>>>> example, the UART reception ISR move the last received char in a FIFO
>>>> buffer, while the mainloop code pops new data from the FIFO.
>>>>
>>>>
>>>> static struct {
>>>>    unsigned char buf[RXBUF_SIZE];
>>>>    uint8_t in;
>>>>    uint8_t out;
>>>> } rxfifo;
>>>>
>>>> /* ISR */
>>>> void uart_rx_isr(void) {
>>>>    unsigned char c = UART->DATA;
>>>>    rxfifo.buf[in % RXBUF_SIZE] = c;
>>>>    rxfifo.in++;
>>>
>>> Unless you are sure that RXBUF_SIZE is a power of two, this is going to
>>> be quite slow on an AVR.  Modulo means division, and while division by a
>>> constant is usually optimised to a multiplication by the compiler, you
>>> still have a multiply, a shift, and some compensation for it all being
>>> done as signed integer arithmetic.
>>>
>>> It's also wrong, for non-power of two sizes, since the wrapping of your
>>> increment and your modulo RXBUF_SIZE get out of sync.
>>
>> Yes, RXBUF_SIZE is a power of two.
>>
>
> If your code relies on that, make sure the code will fail to compile if
> it is not the case. Documentation is good, compile-time check is better:
>
> static_assert((RXBUF_SIZE & (RXBUF_SIZE - 1)) == 0, "Needs power of 2");

Good point.

>>> The usual choice is to track "head" and "tail", and use something like:
>>>
>>> void uart_rx_isr(void) {
>>>    unsigned char c = UART->DATA;
>>>    // Reset interrupt flag
>>>    uint8_t next = rxfifo.tail;
>>>    rxfifo.buf[next] = c;
>>>    next++;
>>>    if (next >= RXBUF_SIZE) next -= RXBUF_SIZE;
>>>    rxfifo.tail = next;
>>> }
>>
>> This isn't the point of this thread, anyway...
>> You insist that tail is always in the range [0...RXBUF_SIZE - 1]. My
>> approach is different.
>>
>> RXBUF_SIZE is a power of two, usualy <=256. head and tail are uint8_t
>> and *can* reach the maximum value of 255, even RXBUF_SIZE is 128. All
>> works well.
>>
>
> Yes, your approach will work - /if/ you have a power-of-two buffer size.
> It has no noticeable efficiency advantages, merely an extra
> inconvenient restriction and the possible confusion caused by doing
> things in a different way from common idioms.
>
> However, this is not the point of the thread - so I am happy to leave
> that for now.

If you want, we can start a small [OT].

<OT>
I know my ring-buffer implementation has the restriction of having a
buffer with a power-of-two size. However I like it, because I can avoid
introducing a new variable (actual number of elements in the buffer) or
waste an element to solve the ambiguity when the buffer is full or empty.
</OT>

>> Suppose rxfifo.in=rxfifo.out=127, FIFO is empty. When a new char is
>> received, it is saved into rxfifo.buf[127 % 128=127] and rxfifo.in will
>> be increased to 128.
>> Now mainloop detect the new char (in != out), reads the new char at
>> rxfifo.buf[127 % 128=127] and increase out that will be 128.
>>
>> The next byte will be saved into rxfifo.rxbuf[rxfifo.in % 128=128 % 128
>> = 0] and rxfifo.in will be 129. Again, the next byte will be saved to
>> rxbuf[rxfifo.in % 128=129 % 128=1] and rxfifo.in will be 130.
>>
>> When the mainloop tries to pop data from fifo, it tests
>>
>>    rxfifo.in(130) !=rxfifo.out(128)
>>
>> The test is true, so the code extracts chars from rxbuf[out % 128] that
>> is rxbuf[0]... and so on.
>>
>> I hope that explanation is good.
>>
>>
>>>
>>>>    // Reset interrupt flag
>>>> }
>>>>
>>>> /* Called regularly from mainloop code */
>>>> int uart_task(void) {
>>>>    int c = -1;
>>>>    if (out != in) {
>>>>      c = rxfifo.buf[out % RXBUF_SIZE];
>>>>      out++;
>>>>    }
>>>>    return -1;
>>>> }
>>>
>>> int uart_task(void) {
>>>    int c = -1;
>>>    uint8_t next = rxfifo.head;
>>>    if (next != rxfifo.tail) {
>>>        c = rxfifo.buf[next];
>>>        next++;
>>>        if (next >= RXBUF_SIZE) next -= RXBUF_SIZE;
>>>        rxfifo.head = next;
>>>    }
>>>    return c;
>>> }
>>>
>>> These don't track buffer overflow at all - you need to call uart_task()
>>> often enough to avoid that.
>>
>> Sure, with a good number for RXBUF_SIZE, buffer overflow shouldn't
>> happen ever. Anyway, if it happens, the higher level layers (protocol)
>> should detect a corrupted packet.
>>
>
> You risk getting seriously out of sync if there is an overflow.
> Normally, on an overflow there will be a dropped character or two (which
> as you say, must be caught at a higher level). Here you could end up
> going round your buffer an extra time and /gaining/ RXBUF_SIZE extra
> characters.
>
> Still, if you are sure that your functions are called fast enough so
> that overflow is not a concern, then that's fine. Extra code to check
> for a situation that can't occur is not helpful.
>
>>
>>> (I'm skipping volatiles so we don't get ahead of your point.)
>>>
>>>>
>>>>
>>>>  From a 20-years old article[1] by Nigle Jones, this seems a situation
>>>> where volatile must be used for rxfifo.in, because is modified by an ISR
>>>> and used in the mainloop code.
>>>>
>>>
>>> Certainly whenever data is shared between ISR's and mainloop code, or
>>> different threads, then you need to think about how to make sure data is
>>> synchronised and exchanged.  "volatile" is one method, atomics are
>>> another, and memory barriers can be used.
>>>
>>>> I don't think so, rxfifo.in is read from memory only one time in
>>>> uart_task(), so there isn't the risk that compiler can optimize badly.
>>>
>>> That is incorrect in two ways.  One - baring compiler bugs (which do
>>> occur, but they are very rare compared to user bugs), there is no such
>>> thing as "optimising badly".  If optimising changes the behaviour of the
>>> code, other than its size and speed, the code is wrong.
>>
>> Yes of course, but I don't think the absence of volatile for rxfifo.in,
>> even if it can change in ISR, could be a *real* problem with *modern and
>> current* compilers.
>>
>
> Personally, I am not satisfied with "it's unlikely to be a problem in
> practice" - I prefer "The language guarantees it is not a problem".
> Remember, when you know the data needs to be read at this point, then
> using a volatile read is free. Volatile does not make code less
> efficient unless you use it incorrectly and force more accesses than are
> necessary. So using volatile accesses for "rxfifo.in" here turns
> "probably safe" into "certainly safe" without cost. What's not to like?
>
>> voltile attribute needs to avoid compiler optimization (that would be a
>> bad thing, because of volatile nature of the variabile), but on that
>> code it's difficult to think of an optimization, caused by the absence
>> of volatile, that changes the behaviour erroneously... except reorering.
>>
>>
>>> Two - it is a
>>> very bad idea to imagine that having code inside a function somehow
>>> "protects" it from re-ordering or other optimisation.
>>
>> I didn't say this, at the contrary I was thinking exactly to reordering
>> issues.
>>
>>
>>> Functions can be inlined, outlined, cloned, and shuffled about.
>>> Link-time optimisation, code in headers, C++ modules, and other
>>> cross-unit optimisations are becoming more and more common.  So while it
>>> might be true /today/ that the compiler has no alternative but to read
>>> rxfifo.in once per call to uart_task(), you cannot assume that will be
>>> the case with later compilers or with more advanced optimisation
>>> techniques enabled.  It is safer, more portable, and more future-proof
>>> to avoid such assumptions.
>>
>> Ok, you are talking of future scenarios. I don't think actually this
>> could be a real problem. Anyway your observation makes sense.
>>
>>
>>>
>>>> Even if ISR is fired immediately after the if statement, this doesn't
>>>> bring to a dangerous state: the just received data will be processed at
>>>> the next call to uart_task().
>>>>
>>>> So IMHO volatile isn't necessary here. And critical sections (i.e.
>>>> disabling interrupts) aren't useful too.
>>>>
>>>> However I'm thinking about memory barrier. Suppose the compiler reorder
>>>> the instructions in uart_task() as follows:
>>>>
>>>>
>>>>    c = rxfifo.buf[out % RXBUF_SIZE]
>>>>    if (out != in) {
>>>>      out++;
>>>>      return c;
>>>>    } else {
>>>>      return -1;
>>>>    }
>>>>
>>>>
>>>> Here there's a big problem, because compiler decided to firstly read
>>>> rxfifo.buf[] and then test in and out equality. If the ISR is fired
>>>> immediately after moving data to c (most probably an internal register),
>>>> the condition in the if statement will be true and the register value is
>>>> returned. However the register value isn't correct.
>>>
>>> You are absolutely correct.
>>>
>>>>
>>>> I don't think any modern C compiler reorder uart_task() in this way, but
>>>> we can't be sure. The result shouldn't change for the compiler, so it
>>>> can do this kind of things.
>>>
>>> It is not an unreasonable re-arrangement.  On processors with
>>> out-of-order execution (which does not apply to the AVR or Cortex-M),
>>> compilers will often push loads as early as they can in the instruction
>>> stream so that they start the cache loading process as quickly as
>>> possible.  (But note that on such "big" processors, much of this
>>> discussion on volatile and memory barriers is not sufficient, especially
>>> if there is more than one core.  You need atomics and fences, but that's
>>> a story for another day.)
>>>
>>>>
>>>> How to fix this issue if I want to be extremely sure the compiler will
>>>> not reorder this way? Applying volatile to rxfifo.in shouldn't help for
>>>> this, because compiler is allowed to reorder access of non volatile
>>>> variables yet[2].
>>>>
>>>
>>> The important thing about "volatile" is that it is /accesses/ that are
>>> volatile, not objects.  A volatile object is nothing more than an object
>>> for which all accesses are volatile by default.  But you can use
>>> volatile accesses on non-volatile objects.  This macro is your friend:
>>>
>>> #define volatileAccess(v) *((volatile typeof((v)) *) &(v))
>>>
>>> (Linux has the same macro, called ACCESS_ONCE.  It uses a gcc extension
>>> - if you are using other compilers then you can make an uglier
>>> equivalent using _Generic.  However, if you are using a C compiler that
>>> supports C11, it is probably gcc or clang, and you can use the "typeof"
>>> extension.)
>>>
>>> That macro will let you make a volatile read or write to an object
>>> without requiring that /all/ accesses to it are volatile.
>>
>> This is a good point. The code in ISR can't be interrupted, so there's
>> no need to have volatile access in ISR.
>>
>
> Correct. (Well, /almost/ correct - bigger microcontrollers have
> multiple interrupt priorities. But it should be correct in this case,
> as no other interrupt would be messing with the same variables anyway.)


Click here to read the complete article
Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl423t$k6k$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=654&group=comp.arch.embedded#654

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!paganini.bofh.team!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: david.br...@hesbynett.no (David Brown)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Sun, 24 Oct 2021 18:37:16 +0200
Organization: A noiseless patient Spider
Lines: 60
Message-ID: <sl423t$k6k$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl1c3a$dfr$1@dont-email.me>
<sl1sh2$vq6$1@dont-email.me> <sl3eg8$674$1@dont-email.me>
<sl3ums$r4$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-15
Content-Transfer-Encoding: 8bit
Injection-Date: Sun, 24 Oct 2021 16:37:17 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="666b7f184d140ccf1515d4914a0e51ef";
logging-data="20692"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18ebk6uqefSze/joJOIAPTdep/QanHjzGY="
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101
Thunderbird/78.11.0
Cancel-Lock: sha1:PwMbWc3fkK5PXi3MtDZ/MjZmfvk=
In-Reply-To: <sl3ums$r4$1@dont-email.me>
Content-Language: en-GB
 by: David Brown - Sun, 24 Oct 2021 16:37 UTC

On 24/10/2021 17:39, pozz wrote:
> Il 24/10/2021 13:02, David Brown ha scritto:

<snipping to save on electrons>

>> People who say "volatile is a bad thing" are often wrong.  Remember, all
>> generalisations are false :-)
>
> Ok, I wrote "volatile is **often** a bad thing".
>

:-)

>
>> "volatile" is a tool.  It doesn't do everything that some people think
>> it does, but it is a very useful tool nonetheless.  It has little place
>> in big systems - Linus Torvalds wrote a rant against it as being both
>> too much and too little, and in the context of writing Linux code, he
>> was correct.  For Linux programming, you should be using OS-specific
>> features (which rely on "volatile" for their implementation) or atomics,
>> rather than using "volatile" directly.
>>
>> But for small-systems embedded programming, it is very handy.  Used
>> well, it is free - used excessively it has a cost, but an extra volatile
>> will not make an otherwise correct program fail.
>>
>> Memory barriers are great for utility functions such as interrupt
>> enable/disable inline functions, but are usually sub-optimal compared to
>> specific and targeted volatile accesses.
>
> Just to say what I read:
>
> https://blog.regehr.org/archives/28

Yes, you had that in your footnotes - and that is not a bad article.
(It's better than a lot of that guy's stuff - he also writes a lot of
crap about undefined behaviour and the evils of optimisation.)

>
> https://mcuoneclipse.com/2021/10/12/spilling-the-beans-volatile-qualifier/
>

That article is mostly wrong - or at best, inappropriate for what you
are doing. Critical sections are a massive over-kill for a ring buffer
in most cases. If you intend to call your uart_task() from multiple
places in a re-entrant manner (e.g., multiple RTOS threads), then you
have a lot more challenges to deal with than just the ordering of the
accesses to "in", "out" and "buf" - you can't just throw in a critical
section and hope it's all okay. And if you are /not/ doing such a daft
thing, then critical sections are completely unnecessary.

>
>>>>> [1] https://barrgroup.com/embedded-systems/how-to/c-volatile-keyword
>>>>> [2] https://blog.regehr.org/archives/28
>>>>
>>>
>>
>

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl4dm3$5ut$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=655&group=comp.arch.embedded#655

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!paganini.bofh.team!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: blockedo...@foo.invalid (Don Y)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Sun, 24 Oct 2021 12:54:39 -0700
Organization: A noiseless patient Spider
Lines: 56
Message-ID: <sl4dm3$5ut$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl3d4h$17d3$1@gioia.aioe.org>
<sl3f65$jdl$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-15; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Sun, 24 Oct 2021 19:54:44 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="71824e2ef3f93c5e9d433ef1315bf619";
logging-data="6109"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX19iJMk811582UvA4hOAO2jI"
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101
Thunderbird/52.1.1
Cancel-Lock: sha1:lnyTat9YbbvQA/dnjkUTZza81fA=
In-Reply-To: <sl3f65$jdl$1@dont-email.me>
Content-Language: en-US
 by: Don Y - Sun, 24 Oct 2021 19:54 UTC

On 10/24/2021 4:14 AM, Dimiter_Popoff wrote:
>> Disable interrupts while accessing the fifo. you really have to.
>> alternatively you'll often get away not using a fifo at all,
>> unless you're blocking for a long while in some part of the code.
>
> Why would you do that. The fifo write pointer is only modified by
> the interrupt handler, the read pointer is only modified by the
> interrupted code. Has been done so for times immemorial.

The OPs code doesn't differentiate between FIFO full and empty.
So, *he* may gain some advantage from disabling interrupts to
ensure the character he is about to retrieve is n ot overwritten
by an incoming character, placed at that location (cuz he lets
his FIFO wrap indiscriminately).

And, if the offsets ever got larger (wider) -- or became actual
pointers -- then the possibility of PART of a value being updated
on either "side" of an ISR is also possible.

And, there's nothing to say the OP has disclosed EVERYTHING
that might be happening in his ISR (maintaining handshaking signals,
flow control, etc.) which could compound the references
(e.g., if you need to know that you have space for N characters
remaining so you can signal the remote device to stop sending,
then you're doing "pointer/offset arithmetic" and *acting* on the
result)

> Although this thread is on how to wrestle a poor
> language to do what you want, sort of how to use a hammer on a screw
> instead of taking the screwdriver, there would be no need to
> mask interrupts with C either.

The "problem" with the language is that it gives the compiler the freedom
to make EQUIVALENT changes to your code that you might not have foreseen
or that might not have been consistent with your "style" -- yet do not
alter the results.

For example, you might want to write:
x = <expr1>
y = <expr2>
just because of some "residual OCD" that makes you think in terms of
"x before y". Yet, there may be no dependencies in those statements
that *require* that ordering. So, why should the compiler be crippled
to implementing them in that order if it has found a way to alter
their order (or their actual content)?

A correctly written compiler will follow a set of rules that it *knows*
to be safe "code translations"; but many developers don't have a similar
understanding of those; so the problem lies in the developer's skillset,
not the compiler or language.

After all, a programming language -- ANY programming language -- is just
a vehicle for conveying our desires to the machine in a semi-unambiguous
manner. I'd much rather *SAY*, "What are the roots of ax^2 + bx + c?"
than have to implement an algorithmic solution, worry about cancellation,
required precision, etc. (and, in some languages, you can do just that!)

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl4fk0$2si$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=656&group=comp.arch.embedded#656

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!paganini.bofh.team!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: dp...@tgi-sci.com (Dimiter_Popoff)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Sun, 24 Oct 2021 23:27:43 +0300
Organization: TGI
Lines: 77
Message-ID: <sl4fk0$2si$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl3d4h$17d3$1@gioia.aioe.org>
<sl3f65$jdl$1@dont-email.me> <sl4dm3$5ut$1@dont-email.me>
Reply-To: dp@tgi-sci.com
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-15; format=flowed
Content-Transfer-Encoding: 8bit
Injection-Date: Sun, 24 Oct 2021 20:27:44 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="ea59a6d7e87f8b5148ca3d280306721a";
logging-data="2962"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+ty7vPU05ZQ65iwmiX3YmBIPgmMNmUJXY="
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101
Thunderbird/78.14.0
Cancel-Lock: sha1:N2eaqfSsDWFhflvk9kcdropUDpM=
In-Reply-To: <sl4dm3$5ut$1@dont-email.me>
Content-Language: en-US
 by: Dimiter_Popoff - Sun, 24 Oct 2021 20:27 UTC

On 10/24/2021 22:54, Don Y wrote:
> On 10/24/2021 4:14 AM, Dimiter_Popoff wrote:
>>> Disable interrupts while accessing the fifo. you really have to.
>>> alternatively you'll often get away not using a fifo at all,
>>> unless you're blocking for a long while in some part of the code.
>>
>> Why would you do that. The fifo write pointer is only modified by
>> the interrupt handler, the read pointer is only modified by the
>> interrupted code. Has been done so for times immemorial.
>
> The OPs code doesn't differentiate between FIFO full and empty.

So he should fix that first, there is no sane reason why not.
Few things are simpler to do than that.

> So, *he* may gain some advantage from disabling interrupts to
> ensure the character he is about to retrieve is n ot overwritten
> by an incoming character, placed at that location (cuz he lets
> his FIFO wrap indiscriminately).
>
> And, if the offsets ever got larger (wider) -- or became actual
> pointers -- then the possibility of PART of a value being updated
> on either "side" of an ISR is also possible.
>
> And, there's nothing to say the OP has disclosed EVERYTHING
> that might be happening in his ISR (maintaining handshaking signals,
> flow control, etc.) which could compound the references
> (e.g., if you need to know that you have space for N characters
> remaining so you can signal the remote device to stop sending,
> then you're doing "pointer/offset arithmetic" and *acting* on the
> result)

Whatever handshakes he makes there is no problem knowing whether
the fifo is full - just check if the position the write pointer
will have after putting the next byte matches the read pointer
at the moment. Like I said before, few things are simpler than
that, can't imagine someone working as a programmer being
stuck at *that*.

>
>> Although this thread is on how to wrestle a poor
>> language to do what you want, sort of how to use a hammer on a screw
>> instead of taking the screwdriver, there would be no need to
>> mask interrupts with C either.
>
> The "problem" with the language is that it gives the compiler the freedom
> to make EQUIVALENT changes to your code that you might not have foreseen
> or that might not have been consistent with your "style" -- yet do not
> alter the results.

Don, let us not go into this. Just looking at the thread is enough to
see it is about wrestling the language so it can be made some use of.

>
> After all, a programming language -- ANY programming language -- is just
> a vehicle for conveying our desires to the machine in a semi-unambiguous
> manner.  I'd much rather *SAY*, "What are the roots of ax^2 + bx + c?"
> than have to implement an algorithmic solution, worry about cancellation,
> required precision, etc.  (and, in some languages, you can do just that!)

Indeed you don't want to write how the equation is solved every time.
This is why you can call it once you have it available. This is language
independent.
Then solving expressions etc. is well within 1% of the effort in
programming if the task at hand is going to take > 2 weeks; after that
the programmer's time is wasted on wrestling the language like
demonstrated by this thread. Sadly almost everybody has accepted
C as a standard - which makes it a very popular poor language.
Similar to say Chinese, very popular, spoken by billions, yet
where are its literary masterpieces. Being hieroglyph based there
are none; you will have to look at alphabet based languages to
find some.

======================================================
Dimiter Popoff, TGI http://www.tgi-sci.com
======================================================
http://www.flickr.com/photos/didi_tgi/

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl4i03$784$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=657&group=comp.arch.embedded#657

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!paganini.bofh.team!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: blockedo...@foo.invalid (Don Y)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Sun, 24 Oct 2021 14:08:13 -0700
Organization: A noiseless patient Spider
Lines: 135
Message-ID: <sl4i03$784$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl3d4h$17d3$1@gioia.aioe.org>
<sl3f65$jdl$1@dont-email.me> <sl4dm3$5ut$1@dont-email.me>
<sl4fk0$2si$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 8bit
Injection-Date: Sun, 24 Oct 2021 21:08:20 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="71824e2ef3f93c5e9d433ef1315bf619";
logging-data="7428"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+TYEjd5skg7xfCOn/Fb8BL"
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101
Thunderbird/52.1.1
Cancel-Lock: sha1:U6jb2HjddTc9cVC167wJ1AN3fhI=
In-Reply-To: <sl4fk0$2si$1@dont-email.me>
Content-Language: en-US
 by: Don Y - Sun, 24 Oct 2021 21:08 UTC

On 10/24/2021 1:27 PM, Dimiter_Popoff wrote:
> On 10/24/2021 22:54, Don Y wrote:
>> On 10/24/2021 4:14 AM, Dimiter_Popoff wrote:
>>>> Disable interrupts while accessing the fifo. you really have to.
>>>> alternatively you'll often get away not using a fifo at all,
>>>> unless you're blocking for a long while in some part of the code.
>>>
>>> Why would you do that. The fifo write pointer is only modified by
>>> the interrupt handler, the read pointer is only modified by the
>>> interrupted code. Has been done so for times immemorial.
>>
>> The OPs code doesn't differentiate between FIFO full and empty.
>
> So he should fix that first, there is no sane reason why not.
> Few things are simpler to do than that.

Yes, I pointed that out earlier, to him. Why worry about what the
compiler *might* do if you haven't sorted out what you really WANT to do?

>> So, *he* may gain some advantage from disabling interrupts to
>> ensure the character he is about to retrieve is n ot overwritten
>> by an incoming character, placed at that location (cuz he lets
>> his FIFO wrap indiscriminately).
>>
>> And, if the offsets ever got larger (wider) -- or became actual
>> pointers -- then the possibility of PART of a value being updated
>> on either "side" of an ISR is also possible.
>>
>> And, there's nothing to say the OP has disclosed EVERYTHING
>> that might be happening in his ISR (maintaining handshaking signals,
>> flow control, etc.) which could compound the references
>> (e.g., if you need to know that you have space for N characters
>> remaining so you can signal the remote device to stop sending,
>> then you're doing "pointer/offset arithmetic" and *acting* on the
>> result)
>
> Whatever handshakes he makes there is no problem knowing whether
> the fifo is full - just check if the position the write pointer
> will have after putting the next byte matches the read pointer
> at the moment. Like I said before, few things are simpler than
> that, can't imagine someone working as a programmer being
> stuck at *that*.

Yes, but if you want to implement flow control, you have to tell the
other end of the line BEFORE you've filled your buffer. There may be
a character being deserialized AS you are retrieving the "last"
character, another one (or more) preloaded into the transmitter on
the far device, etc. And, it will take some time for your
notification to reach the far end and be recognized as a desire
to suspend transmission. etc.

If you wait until you have no more space available, you are almost
certain to lose characters.

>>> Although this thread is on how to wrestle a poor
>>> language to do what you want, sort of how to use a hammer on a screw
>>> instead of taking the screwdriver, there would be no need to
>>> mask interrupts with C either.
>>
>> The "problem" with the language is that it gives the compiler the freedom
>> to make EQUIVALENT changes to your code that you might not have foreseen
>> or that might not have been consistent with your "style" -- yet do not
>> alter the results.
>
> Don, let us not go into this. Just looking at the thread is enough to
> see it is about wrestling the language so it can be made some use of.

The language isn't the problem. Witness the *millions* (?) of programs
written in it, over the past 5 decades.

The problem is that it never was an assembly language -- even though it
was treated as such "in days gone by" (because the compiler's were
just "language translators" and didn't add any OTHER value to the
"programming process").

It's only recently that compilers have become "independent agents",
of a sort... adding their own "spin" on the developer's code.

And, with more capable hardware (multiple cores/threads) being "dirt cheap",
it's a lot easier for a developer to find himself in a situation that
was previously pie-in-the-sky.

>> After all, a programming language -- ANY programming language -- is just
>> a vehicle for conveying our desires to the machine in a semi-unambiguous
>> manner. I'd much rather *SAY*, "What are the roots of ax^2 + bx + c?"
>> than have to implement an algorithmic solution, worry about cancellation,
>> required precision, etc. (and, in some languages, you can do just that!)
>
> Indeed you don't want to write how the equation is solved every time.
> This is why you can call it once you have it available. This is language
> independent.

For a simple quadratic, you can explore the coefficients to determine which
algorithm is best suited to giving you *accurate* results.

What if I present *any* expression? Can you have your solution available
to handle any case? Did you even bother to develop such a solution if you
were only encountering quadratics?

> Then solving expressions etc. is well within 1% of the effort in
> programming if the task at hand is going to take > 2 weeks; after that
> the programmer's time is wasted on wrestling the language like
> demonstrated by this thread. Sadly almost everybody has accepted
> C as a standard - which makes it a very popular poor language.

It makes it *popular* but concluding that it is "poor" is an overreach.

There are (and have been) many "safer" languages. Many that are more
descriptive (for certain classes of problem). But, C has survived to
handle all-of-the-above... perhaps in a suboptimal way but at least
a manner that can get to the desired solution.

Look at how few applications SNOBOL handles. Write an OS in COBOL? Ada?

A tool is only effective if it solves real problems. Under real cost
and time constraints. There are lots of externalities that come into
play in that analysis.

I've made some syntactic changes to my code that make it much easier
to read -- yet mean that I have to EXPLAIN how they work and why they
are present as any other developer would frown on encountering them.
(But, it's my opinion that, once explained, that developer will see them
as an efficient addition to the language in line with other *existing*
mechanisms that are already present, there).

> Similar to say Chinese, very popular, spoken by billions, yet
> where are its literary masterpieces. Being hieroglyph based there
> are none; you will have to look at alphabet based languages to
> find some.

One can say the same thing about Unangax̂ -- spoken by ~100!
Popularity and literary masterpieces are completely different
axis.

Hear much latin or ancient greek spoken, recently?

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl4kf7$e4r$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=658&group=comp.arch.embedded#658

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: dp...@tgi-sci.com (Dimiter_Popoff)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Mon, 25 Oct 2021 00:50:29 +0300
Organization: TGI
Lines: 102
Message-ID: <sl4kf7$e4r$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl3d4h$17d3$1@gioia.aioe.org>
<sl3f65$jdl$1@dont-email.me> <sl4dm3$5ut$1@dont-email.me>
<sl4fk0$2si$1@dont-email.me> <sl4i03$784$1@dont-email.me>
Reply-To: dp@tgi-sci.com
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 8bit
Injection-Date: Sun, 24 Oct 2021 21:50:31 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="ea59a6d7e87f8b5148ca3d280306721a";
logging-data="14491"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+MhIQKvIoqZ4dKD7hMOawC9yuJK2aOsFI="
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101
Thunderbird/78.14.0
Cancel-Lock: sha1:zaoO6BqsIn7FAigkDcaPSbc0PfI=
In-Reply-To: <sl4i03$784$1@dont-email.me>
Content-Language: en-US
 by: Dimiter_Popoff - Sun, 24 Oct 2021 21:50 UTC

On 10/25/2021 0:08, Don Y wrote:
> On 10/24/2021 1:27 PM, Dimiter_Popoff wrote:
> ....
>>
>> Whatever handshakes he makes there is no problem knowing whether
>> the fifo is full - just check if the position the write pointer
>> will have after putting the next byte matches the read pointer
>> at the moment.  Like I said before, few things are simpler than
>> that, can't imagine someone working as a programmer being
>> stuck at *that*.
>
> Yes, but if you want to implement flow control, you have to tell the
> other end of the line BEFORE you've filled your buffer.  There may be
> a character being deserialized AS you are retrieving the "last"
> character, another one (or more) preloaded into the transmitter on
> the far device, etc.  And, it will take some time for your
> notification to reach the far end and be recognized as a desire
> to suspend transmission.  etc.
>
> If you wait until you have no more space available, you are almost
> certain to lose characters.

Well of course so, we have all done that sort of thing since the 80-s,
other people have done it before I suppose. Implementing fifo thresholds
is not (and has never been) rocket science.
The point is there is no point in throwing huge efforts at a
self-inflicted problem instead of just doing it the easy way which
is well, common knowledge.

>
>>>> Although this thread is on how to wrestle a poor
>>>> language to do what you want, sort of how to use a hammer on a screw
>>>> instead of taking the screwdriver, there would be no need to
>>>> mask interrupts with C either.
>>>
>>> The "problem" with the language is that it gives the compiler the
>>> freedom
>>> to make EQUIVALENT changes to your code that you might not have foreseen
>>> or that might not have been consistent with your "style" -- yet do not
>>> alter the results.
>>
>> Don, let us not go into this. Just looking at the thread is enough to
>> see it is about wrestling the language so it can be made some use of.
>
> The language isn't the problem.  Witness the *millions* (?) of programs
> written in it, over the past 5 decades.

This does not prove much, it has been the only language allowing
"everybody" to do what they did. I am not denying this is the best
language currently available to almost everybody. I just happened to
have been daring enough to explore my own way/language and have seen
how much is there to be gained if not having to wrestle a language
which is just a more complete phrase book than the rest of the
phrase books (aka high level languages).

>> Indeed you don't want to write how the equation is solved every time.
>> This is why you can call it once you have it available. This is language
>> independent.
>
> For a simple quadratic, you can explore the coefficients to determine which
> algorithm is best suited to giving you *accurate* results. >
> What if I present *any* expression?  Can you have your solution available
> to handle any case?  Did you even bother to develop such a solution if you
> were only encountering quadratics?

Any expression solver has its limitations, why go into that? Mine (in
the dps environment) can do all arithmetic and logic for
integers, the fp can do all arithmetic, knows pi, e, haven't needed
to expand it for years. And again, solving expressions has never taken
me any significant part of the effort on a project.

> I've made some syntactic changes to my code that make it much easier
> to read -- yet mean that I have to EXPLAIN how they work and why they
> are present as any other developer would frown on encountering them.

Oh I am well aware of the value of standardization and popularity,
these are the strongest points of C.

> (But, it's my opinion that, once explained, that developer will see them
> as an efficient addition to the language in line with other *existing*
> mechanisms that are already present, there).

Of course, but you have to have them on board first...

>> Similar to say Chinese, very popular, spoken by billions, yet
>> where are its literary masterpieces. Being hieroglyph based there
>> are none; you will have to look at alphabet based languages to
>> find some.
>
> One can say the same thing about Unangax̂ -- spoken by ~100!
> Popularity and literary masterpieces are completely different
> axis.
>
> Hear much latin or ancient greek spoken, recently?

The Latin alphabet looks pretty popular nowadays :-). Everything
evolves, including languages. And there are dead ends within them
which just die out - e.g. roman numbers. Can't see much future in
any hieroglyph based language though, inventing a symbol for each
word has been demonstrated to be a bad idea by history.

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl4nqi$a2s$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=659&group=comp.arch.embedded#659

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: blockedo...@foo.invalid (Don Y)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Sun, 24 Oct 2021 15:47:39 -0700
Organization: A noiseless patient Spider
Lines: 123
Message-ID: <sl4nqi$a2s$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl3d4h$17d3$1@gioia.aioe.org>
<sl3f65$jdl$1@dont-email.me> <sl4dm3$5ut$1@dont-email.me>
<sl4fk0$2si$1@dont-email.me> <sl4i03$784$1@dont-email.me>
<sl4kf7$e4r$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 8bit
Injection-Date: Sun, 24 Oct 2021 22:47:46 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="0a41b6a329df076c2a27c48b9f62cde0";
logging-data="10332"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+jnlkquKNlxH6s1qWP28N9"
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101
Thunderbird/52.1.1
Cancel-Lock: sha1:u2QbLOqC2/FF/rexV08kKYPnYMo=
In-Reply-To: <sl4kf7$e4r$1@dont-email.me>
Content-Language: en-US
 by: Don Y - Sun, 24 Oct 2021 22:47 UTC

On 10/24/2021 2:50 PM, Dimiter_Popoff wrote:
> On 10/25/2021 0:08, Don Y wrote:
>> On 10/24/2021 1:27 PM, Dimiter_Popoff wrote:
>> ....
>>>
>>> Whatever handshakes he makes there is no problem knowing whether
>>> the fifo is full - just check if the position the write pointer
>>> will have after putting the next byte matches the read pointer
>>> at the moment. Like I said before, few things are simpler than
>>> that, can't imagine someone working as a programmer being
>>> stuck at *that*.
>>
>> Yes, but if you want to implement flow control, you have to tell the
>> other end of the line BEFORE you've filled your buffer. There may be
>> a character being deserialized AS you are retrieving the "last"
>> character, another one (or more) preloaded into the transmitter on
>> the far device, etc. And, it will take some time for your
>> notification to reach the far end and be recognized as a desire
>> to suspend transmission. etc.
>>
>> If you wait until you have no more space available, you are almost
>> certain to lose characters.
>
> Well of course so, we have all done that sort of thing since the 80-s,
> other people have done it before I suppose. Implementing fifo thresholds
> is not (and has never been) rocket science.
> The point is there is no point in throwing huge efforts at a
> self-inflicted problem instead of just doing it the easy way which
> is well, common knowledge.

*My* point (to the OP) was that you need to understand what you
will be doing before you can understand the "opportunities"
the compiler will have to catch you off guard.

>>>>> Although this thread is on how to wrestle a poor
>>>>> language to do what you want, sort of how to use a hammer on a screw
>>>>> instead of taking the screwdriver, there would be no need to
>>>>> mask interrupts with C either.
>>>>
>>>> The "problem" with the language is that it gives the compiler the freedom
>>>> to make EQUIVALENT changes to your code that you might not have foreseen
>>>> or that might not have been consistent with your "style" -- yet do not
>>>> alter the results.
>>>
>>> Don, let us not go into this. Just looking at the thread is enough to
>>> see it is about wrestling the language so it can be made some use of.
>>
>> The language isn't the problem. Witness the *millions* (?) of programs
>> written in it, over the past 5 decades.
>
> This does not prove much, it has been the only language allowing
> "everybody" to do what they did.

ASM has always been available. Folks just found it too inefficient
to solve "big" problems, in reasonable effort.

> I am not denying this is the best
> language currently available to almost everybody. I just happened to
> have been daring enough to explore my own way/language and have seen
> how much is there to be gained if not having to wrestle a language
> which is just a more complete phrase book than the rest of the
> phrase books (aka high level languages).

But you only have yourself as a client. Most of us have to write code
(or modify already written code) that others will see/maintain. It
does no good to have a "great tool" if no one else uses it!

I use (scant!) ASM, a modified ("proprietary") C dialect, SQL and a scripting
language in my current design. (not counting the tools that generate my
documentation).

This is a LOT to expect a developer to have a firm grasp of. But, inventing
a new language that will address all of these needs would be even moreso!
At least one can find books/documentation describing each of these individual
languages *and* likely find folks with proficiency in each of them. So, I
can spend my efforts describing "how things work" instead of the details
of how to TELL them to work.

>> I've made some syntactic changes to my code that make it much easier
>> to read -- yet mean that I have to EXPLAIN how they work and why they
>> are present as any other developer would frown on encountering them.
>
> Oh I am well aware of the value of standardization and popularity,
> these are the strongest points of C.
>
>> (But, it's my opinion that, once explained, that developer will see them
>> as an efficient addition to the language in line with other *existing*
>> mechanisms that are already present, there).
>
> Of course, but you have to have them on board first...

Yes. They have to have incentive to want to use the codebase.
They'd not be keen on making any special effort to learn how
to modify a "program" that picks resistor values to form
voltage dividers.

And, even "well motivated", what you are asking them to
embrace has to be acceptable to their sense of reason.
E.g., expecting folks to adopt postfix notation just
because you chose to use it is probably a nonstarter
(i.e., "show me some OTHER reason that justifies its use!").

Or, the wonky operator set that APL uses...

>>> Similar to say Chinese, very popular, spoken by billions, yet
>>> where are its literary masterpieces. Being hieroglyph based there
>>> are none; you will have to look at alphabet based languages to
>>> find some.
>>
>> One can say the same thing about Unangax̂ -- spoken by ~100!
>> Popularity and literary masterpieces are completely different
>> axis.
>>
>> Hear much latin or ancient greek spoken, recently?
>
> The Latin alphabet looks pretty popular nowadays :-). Everything
> evolves, including languages. And there are dead ends within them
> which just die out - e.g. roman numbers. Can't see much future in
> any hieroglyph based language though, inventing a symbol for each
> word has been demonstrated to be a bad idea by history.

Witness the rise of arabic numerals and their efficacy towards
advancing mathematics.

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl4qdj$n9h$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=660&group=comp.arch.embedded#660

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: dp...@tgi-sci.com (Dimiter_Popoff)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Mon, 25 Oct 2021 02:32:02 +0300
Organization: TGI
Lines: 64
Message-ID: <sl4qdj$n9h$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl3d4h$17d3$1@gioia.aioe.org>
<sl3f65$jdl$1@dont-email.me> <sl4dm3$5ut$1@dont-email.me>
<sl4fk0$2si$1@dont-email.me> <sl4i03$784$1@dont-email.me>
<sl4kf7$e4r$1@dont-email.me> <sl4nqi$a2s$1@dont-email.me>
Reply-To: dp@tgi-sci.com
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 8bit
Injection-Date: Sun, 24 Oct 2021 23:32:03 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="328f47122045252f51317f11fee19557";
logging-data="23857"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+B/zLGIeB9BIqKrpJNR2kFHcQs1TSR3WQ="
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101
Thunderbird/78.14.0
Cancel-Lock: sha1:UJIwULnB7ejePs/HqhdPVrQApE0=
In-Reply-To: <sl4nqi$a2s$1@dont-email.me>
Content-Language: en-US
 by: Dimiter_Popoff - Sun, 24 Oct 2021 23:32 UTC

On 10/25/2021 1:47, Don Y wrote:
> ...
>
> ASM has always been available.

There is no such language as ASM, there is a wide variety of machines.

> Folks just found it too inefficient
> to solve "big" problems, in reasonable effort.

Especially with the advent of load/store machines (although C must have
been helped a lot by the clunky x86 architecture for its popularity),
programming in the native assembler for any RISC machine would be
masochistic at best. Which is why I took the steps I took etc., no
need to go into that.

>
>> I am not denying this is the best
>> language currently available to almost everybody. I just happened to
>> have been daring enough to explore my own way/language and have seen
>> how much is there to be gained if not having to wrestle a language
>> which is just a more complete phrase book than the rest of the
>> phrase books (aka high level languages).
>
> But you only have yourself as a client.

Yes, but this does not mean much. Looking at pieces I wrote 20 or
30 years ago - even 10 years ago sometimes - is like reading it
for the first time for many parts (tens of megabytes of sources,
http://tgi-sci.com/misc/scnt21.gif ).

> Most of us have to write code
> (or modify already written code) that others will see/maintain.  It
> does no good to have a "great tool" if no one else uses it! >
> I use (scant!) ASM, a modified ("proprietary") C dialect, SQL and a
> scripting
> language in my current design.  (not counting the tools that generate my
> documentation).

Here comes the advantage of an "alphabet" rather than "hieroglyph" based
approach/language. A lot less of lookup tables to memorize, you learn
while going etc. I am quite sure someone like you would get used to it
quite fast, much much faster than to an unknown high level language.
In fact it may take you very short to see it is something you have more
or less been familiar with forever.
Grasping the big picture of the entire environment and becoming
really good at writing within it would take longer, obviously.

> ....
>>>
>>> Hear much latin or ancient greek spoken, recently?
>>
>> The Latin alphabet looks pretty popular nowadays :-). Everything
>> evolves, including languages. And there are dead ends within them
>> which just die out - e.g. roman numbers. Can't see much future in
>> any hieroglyph based language though, inventing a symbol for each
>> word has been demonstrated to be a bad idea by history.
>
> Witness the rise of arabic numerals and their efficacy towards
> advancing mathematics.

Yes, another good example of how it is the foundation you step on
that really matters. Step on the Roman numbers and good luck with
your math...

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl51il$81s$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=661&group=comp.arch.embedded#661

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: blockedo...@foo.invalid (Don Y)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Sun, 24 Oct 2021 18:34:07 -0700
Organization: A noiseless patient Spider
Lines: 65
Message-ID: <sl51il$81s$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl3d4h$17d3$1@gioia.aioe.org>
<sl3f65$jdl$1@dont-email.me> <sl4dm3$5ut$1@dont-email.me>
<sl4fk0$2si$1@dont-email.me> <sl4i03$784$1@dont-email.me>
<sl4kf7$e4r$1@dont-email.me> <sl4nqi$a2s$1@dont-email.me>
<sl4qdj$n9h$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Mon, 25 Oct 2021 01:34:13 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="0a41b6a329df076c2a27c48b9f62cde0";
logging-data="8252"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+w3bwzIa+4/8ZIgPdqcDLx"
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101
Thunderbird/52.1.1
Cancel-Lock: sha1:Qb5scJIGSj2ISuMwB/T5cX/aMdQ=
In-Reply-To: <sl4qdj$n9h$1@dont-email.me>
Content-Language: en-US
 by: Don Y - Mon, 25 Oct 2021 01:34 UTC

On 10/24/2021 4:32 PM, Dimiter_Popoff wrote:
> On 10/25/2021 1:47, Don Y wrote:
>> ...
>>
>> ASM has always been available.
>
> There is no such language as ASM, there is a wide variety of machines.

Of course there's a language called ASM! It's just target specific!
It is available for each different processor.

And highly NONportable!

>> Folks just found it too inefficient
>> to solve "big" problems, in reasonable effort.
>
> Especially with the advent of load/store machines (although C must have
> been helped a lot by the clunky x86 architecture for its popularity),
> programming in the native assembler for any RISC machine would be
> masochistic at best. Which is why I took the steps I took etc., no
> need to go into that.
>
>>> I am not denying this is the best
>>> language currently available to almost everybody. I just happened to
>>> have been daring enough to explore my own way/language and have seen
>>> how much is there to be gained if not having to wrestle a language
>>> which is just a more complete phrase book than the rest of the
>>> phrase books (aka high level languages).
>>
>> But you only have yourself as a client.
>
> Yes, but this does not mean much. Looking at pieces I wrote 20 or
> 30 years ago - even 10 years ago sometimes - is like reading it
> for the first time for many parts (tens of megabytes of sources,
> http://tgi-sci.com/misc/scnt21.gif ).

Of course it means something! If someone else has to step into
your role *tomorrow*, there'd be little/no progress on your codebase
until they learned your toolchain/language.

An employer has to expect that any employee can "become unavailable"
at any time. And, with that, the labors for which they'd previously
paid, should still retain their value. I've had clients outright ask
me, "What happens if you get hit by a bus?"

>> Most of us have to write code
>> (or modify already written code) that others will see/maintain. It
>> does no good to have a "great tool" if no one else uses it! >
>> I use (scant!) ASM, a modified ("proprietary") C dialect, SQL and a scripting
>> language in my current design. (not counting the tools that generate my
>> documentation).
>
> Here comes the advantage of an "alphabet" rather than "hieroglyph" based
> approach/language. A lot less of lookup tables to memorize, you learn
> while going etc. I am quite sure someone like you would get used to it
> quite fast, much much faster than to an unknown high level language.
> In fact it may take you very short to see it is something you have more
> or less been familiar with forever.
> Grasping the big picture of the entire environment and becoming
> really good at writing within it would take longer, obviously.

But that can be said of any HLL. That doesn't mean an employer
wants to pay you to *learn* (some *previous* employer was expected
to have done that!). They want to have to, at most, train you on
the needs of their applications/markets.

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl5kgb$f7g$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=662&group=comp.arch.embedded#662

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: david.br...@hesbynett.no (David Brown)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Mon, 25 Oct 2021 08:57:14 +0200
Organization: A noiseless patient Spider
Lines: 25
Message-ID: <sl5kgb$f7g$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl3d4h$17d3$1@gioia.aioe.org>
<sl3f65$jdl$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-15
Content-Transfer-Encoding: 7bit
Injection-Date: Mon, 25 Oct 2021 06:57:15 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="bc28c0b8ce32574cb05447f28b439cdd";
logging-data="15600"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18KBGXi3uzo6kKBJzVE9Dg+hrwY95TiKOw="
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101
Thunderbird/78.11.0
Cancel-Lock: sha1:maBGB0yVgwLpgEs25vXgKJr84ds=
In-Reply-To: <sl3f65$jdl$1@dont-email.me>
Content-Language: en-GB
 by: David Brown - Mon, 25 Oct 2021 06:57 UTC

On 24/10/2021 13:14, Dimiter_Popoff wrote:
> On 10/24/2021 13:39, Johann Klammer wrote:

>> Disable interrupts while accessing the fifo. you really have to.
>> alternatively you'll often get away not using a fifo at all,
>> unless you're blocking for a long while in some part of the code.
>>
>
> Why would you do that. The fifo write pointer is only modified by
> the interrupt handler, the read pointer is only modified by the
> interrupted code. Has been done so for times immemorial.
>
> Although this thread is on how to wrestle a poor
> language to do what you want, sort of how to use a hammer on a screw
> instead of taking the screwdriver, there would be no need to
> mask interrupts with C either.
>

There's nothing wrong with the language here - C is perfectly capable of
expressing what the OP needs. But getting the "volatile" usage optimal
here - enough to cover what you need, but not accidentally reducing the
efficiency of the code - requires a bit of thought. "volatile" is often
misunderstood in C, and it's good that the OP is asking to be sure. C
also has screwdrivers in its toolbox, they are just buried under all the
hammers!

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl5n34$v5r$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=663&group=comp.arch.embedded#663

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: david.br...@hesbynett.no (David Brown)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Mon, 25 Oct 2021 09:41:23 +0200
Organization: A noiseless patient Spider
Lines: 56
Message-ID: <sl5n34$v5r$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl3d4h$17d3$1@gioia.aioe.org>
<sl3f65$jdl$1@dont-email.me> <sl4dm3$5ut$1@dont-email.me>
<sl4fk0$2si$1@dont-email.me> <sl4i03$784$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Injection-Date: Mon, 25 Oct 2021 07:41:24 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="bc28c0b8ce32574cb05447f28b439cdd";
logging-data="31931"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18udFUmc9C9VrWmuD1f8fCHghkvgO1Zc2E="
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101
Thunderbird/78.11.0
Cancel-Lock: sha1:BwkHN/LKeqLST3jfqt+pPaPBsp4=
In-Reply-To: <sl4i03$784$1@dont-email.me>
Content-Language: en-GB
 by: David Brown - Mon, 25 Oct 2021 07:41 UTC

On 24/10/2021 23:08, Don Y wrote:

> The language isn't the problem.  Witness the *millions* (?) of programs
> written in it, over the past 5 decades.
>
> The problem is that it never was an assembly language -- even though it
> was treated as such "in days gone by" (because the compiler's were
> just "language translators" and didn't add any OTHER value to the
> "programming process").
>

No - the problem is that some people /thought/ it was supposed to be a
kind of assembly language. It's a people problem, not a language
problem. C has all you need to handle code such as the OP's - all it
takes is for people to understand that they need to use the right
features of the language.

> It's only recently that compilers have become "independent agents",
> of a sort... adding their own "spin" on the developer's code.
>

Baring bugs, compilers do what they are told - in the language
specified. If programmers don't properly understand the language they
are using, or think it means more than it does, that's the programmers
that are at fault - not the language or the compiler. If you go into a
French bakery and ask for horse dung instead of the end of a baguette,
that's /your/ fault - not the language's fault, and not the baker's fault.

Add to that, the idea that optimising compilers are new is equally silly.

The C language is defined in terms of an "abstract machine". The
generated code has the same effect "as if" it executed everything you
wrote - but the abstract machine and the real object code only
synchronise on the observable behaviour. In practice, that means
volatile accesses happen exactly as often, with exactly the values and
exactly the order that you gave in the code. Non-volatile accesses can
be re-ordered, re-arranged, combined, duplicated, or whatever.

This has been the situation since C was standardised and since more
advanced compilers arrived, perhaps 30 years ago.

C is what it is - a language designed long ago, but which turned out to
be surprisingly effective and long-lived. It's not perfect, but it is
pretty good and works well for many situations where you need low-level
coding or near-optimal efficiency. It's not as safe or advanced as many
new languages, and it is not a beginners' language - you have to know
what you are doing in order to write C code correctly. You have to
understand it and follow its rules, whether you like these rules or not.

Unfortunately, there are quite a few C programmers who /don't/ know
these rules. And there is a small but vocal fraction who /do/ know the
rules, but don't like them and feel the rules should therefore not apply
- and blame compilers, standards committees, and anyone else when things
inevitably go wrong. Some people are always a problem, regardless of
the language!

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<itn68pFpjgeU1@mid.individual.net>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=664&group=comp.arch.embedded#664

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!news.swapon.de!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail
From: niklas.h...@tidorum.invalid (Niklas Holsti)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Mon, 25 Oct 2021 10:56:08 +0300
Organization: Tidorum Ltd
Lines: 18
Message-ID: <itn68pFpjgeU1@mid.individual.net>
References: <skvcnd$5dv$1@dont-email.me> <sl3d4h$17d3$1@gioia.aioe.org>
<sl3f65$jdl$1@dont-email.me> <sl4dm3$5ut$1@dont-email.me>
<sl4fk0$2si$1@dont-email.me> <sl4i03$784$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 8bit
X-Trace: individual.net s610Qx0xY2eP8+IVkpw3Ygl3b8y1MEHV3baC++isRt12ag/D5X
Cancel-Lock: sha1:a/h4m3PgxhDWiM1h4kPfEsVAT6A=
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:78.0)
Gecko/20100101 Thunderbird/78.14.0
In-Reply-To: <sl4i03$784$1@dont-email.me>
Content-Language: en-US
 by: Niklas Holsti - Mon, 25 Oct 2021 07:56 UTC

On 2021-10-25 0:08, Don Y wrote:

[snip]

> There are (and have been) many "safer" languages.  Many that are more
> descriptive (for certain classes of problem).  But, C has survived to
> handle all-of-the-above... perhaps in a suboptimal way but at least
> a manner that can get to the desired solution.
>
> Look at how few applications SNOBOL handles.  Write an OS in COBOL?  Ada?

I don't know about COBOL, but typically the real-time kernels ("run-time
systems") associated with Ada compilers for bare-board embedded systems
are written in Ada, with a minor amount of assembly language for the
most HW-related bits like HW context saving and restoring. I'm pretty
sure that C-language OS kernels also use assembly for those things.

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<itn713FpntqU1@mid.individual.net>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=665&group=comp.arch.embedded#665

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!news.swapon.de!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail
From: niklas.h...@tidorum.invalid (Niklas Holsti)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Mon, 25 Oct 2021 11:09:07 +0300
Organization: Tidorum Ltd
Lines: 38
Message-ID: <itn713FpntqU1@mid.individual.net>
References: <skvcnd$5dv$1@dont-email.me> <sl3d4h$17d3$1@gioia.aioe.org>
<sl3f65$jdl$1@dont-email.me> <sl4dm3$5ut$1@dont-email.me>
<sl4fk0$2si$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-15; format=flowed
Content-Transfer-Encoding: 8bit
X-Trace: individual.net drEIdHYbBnIwt5o2ndWMhwffHZFBe4m1wCYfzzo3h3hu/YAbii
Cancel-Lock: sha1:jFLYdhqYFCH5SAWZ8QLhrDiArp0=
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:78.0)
Gecko/20100101 Thunderbird/78.14.0
In-Reply-To: <sl4fk0$2si$1@dont-email.me>
Content-Language: en-US
 by: Niklas Holsti - Mon, 25 Oct 2021 08:09 UTC

On 2021-10-24 23:27, Dimiter_Popoff wrote:
> On 10/24/2021 22:54, Don Y wrote:
>> On 10/24/2021 4:14 AM, Dimiter_Popoff wrote:
>>>> Disable interrupts while accessing the fifo. you really have to.
>>>> alternatively you'll often get away not using a fifo at all,
>>>> unless you're blocking for a long while in some part of the code.
>>>
>>> Why would you do that. The fifo write pointer is only modified by
>>> the interrupt handler, the read pointer is only modified by the
>>> interrupted code. Has been done so for times immemorial.
>>
>> The OPs code doesn't differentiate between FIFO full and empty.
>
> So he should fix that first, there is no sane reason why not.
> Few things are simpler to do than that.

[snip]

> Whatever handshakes he makes there is no problem knowing whether
> the fifo is full - just check if the position the write pointer
> will have after putting the next byte matches the read pointer
> at the moment.  Like I said before, few things are simpler than
> that, can't imagine someone working as a programmer being
> stuck at *that*.

That simple check would require keeping a maximum of only N-1 entries in
the N-position FIFO buffer, and the OP explicitly said they did not want
to allocate an unused place in the buffer (which I think is unreasonable
of the OP, but that is only IMO).

The simple explanation for the N-1 limit is that the difference between
two wrap-around pointers into an N-place buffer has at most N different
values, while there are N+1 possible filling states of the buffer, from
empty (zero items) to full (N items).

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl5pbe$dv8$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=666&group=comp.arch.embedded#666

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: blockedo...@foo.invalid (Don Y)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Mon, 25 Oct 2021 01:19:53 -0700
Organization: A noiseless patient Spider
Lines: 45
Message-ID: <sl5pbe$dv8$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl3d4h$17d3$1@gioia.aioe.org>
<sl3f65$jdl$1@dont-email.me> <sl4dm3$5ut$1@dont-email.me>
<sl4fk0$2si$1@dont-email.me> <sl4i03$784$1@dont-email.me>
<itn68pFpjgeU1@mid.individual.net>
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Mon, 25 Oct 2021 08:19:58 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="0a41b6a329df076c2a27c48b9f62cde0";
logging-data="14312"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+r0XxRI6Z23QO2vUdi3lDL"
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101
Thunderbird/52.1.1
Cancel-Lock: sha1:TiYwB6kVXkJJVdkwSIS1WcdDCvU=
In-Reply-To: <itn68pFpjgeU1@mid.individual.net>
Content-Language: en-US
 by: Don Y - Mon, 25 Oct 2021 08:19 UTC

On 10/25/2021 12:56 AM, Niklas Holsti wrote:
> On 2021-10-25 0:08, Don Y wrote:
>
>> There are (and have been) many "safer" languages. Many that are more
>> descriptive (for certain classes of problem). But, C has survived to
>> handle all-of-the-above... perhaps in a suboptimal way but at least
>> a manner that can get to the desired solution.
>>
>> Look at how few applications SNOBOL handles. Write an OS in COBOL? Ada?
>
> I don't know about COBOL, but typically the real-time kernels ("run-time
> systems") associated with Ada compilers for bare-board embedded systems are
> written in Ada, with a minor amount of assembly language for the most
> HW-related bits like HW context saving and restoring. I'm pretty sure that
> C-language OS kernels also use assembly for those things.

Of course you *can* do these things. The question is how often
they are ACTUALLY done with these other languages.

"Suitability for a particular task" isn't often the criteria that is
used to make a selection -- for better or worse. There are countless
other factors that affect an implementation, depending on the environment
in which it is undertaken (e.g., designs from academia are considerably
different than hobbyist designs which are different from commercial
designs which are...)

This is true of other disciplines, as well. How often do you think a hardware
design follows a course heavily influenced by the "prejudices"/"preferences"
of the folks responsible for the design vs. the "best" approach to it?

Step back yet another level of abstraction and see that even the tools
chosen to perform those tasks are often not "optimally chosen".

If you are the sole entity involved in a decision making process, then
you've (typically) got /carte blanche/. But, in most cases, there are
other voices -- seats at the table -- that shape the final decisions. It
pays to lift one's head and see which way the wind is blowing, *today*...

[By the same token, expecting the past to mirror the present is equally
naive. People forget that tools and processes have evolved (in the 40+
years that I've been designing embedded products). And, that the isssues
folks now face often weren't issues when tools were "stupider" (I've
probably got $60K of obsolete compilers to prove this -- anyone written
any C on an 1802 recently? Or, a 2A03? 65816? Z180? 6809?) Don't
even *think* about finding an Ada compiler for them -- in the past!]

Re: How to write a simple driver in bare metal systems: volatile, memory barrier, critical sections and so on

<sl5pqv$gu1$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=667&group=comp.arch.embedded#667

  copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: blockedo...@foo.invalid (Don Y)
Newsgroups: comp.arch.embedded
Subject: Re: How to write a simple driver in bare metal systems: volatile,
memory barrier, critical sections and so on
Date: Mon, 25 Oct 2021 01:28:08 -0700
Organization: A noiseless patient Spider
Lines: 59
Message-ID: <sl5pqv$gu1$1@dont-email.me>
References: <skvcnd$5dv$1@dont-email.me> <sl3d4h$17d3$1@gioia.aioe.org>
<sl3f65$jdl$1@dont-email.me> <sl4dm3$5ut$1@dont-email.me>
<sl4fk0$2si$1@dont-email.me> <itn713FpntqU1@mid.individual.net>
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-15; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Mon, 25 Oct 2021 08:28:15 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="0a41b6a329df076c2a27c48b9f62cde0";
logging-data="17345"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18N2ohrScuYCtBaAVDtjfMz"
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101
Thunderbird/52.1.1
Cancel-Lock: sha1:WuZiqX7hoy1bdJ4YAyrp8qkH3AQ=
In-Reply-To: <itn713FpntqU1@mid.individual.net>
Content-Language: en-US
 by: Don Y - Mon, 25 Oct 2021 08:28 UTC

On 10/25/2021 1:09 AM, Niklas Holsti wrote:
> On 2021-10-24 23:27, Dimiter_Popoff wrote:
>> On 10/24/2021 22:54, Don Y wrote:
>>> On 10/24/2021 4:14 AM, Dimiter_Popoff wrote:
>>>>> Disable interrupts while accessing the fifo. you really have to.
>>>>> alternatively you'll often get away not using a fifo at all,
>>>>> unless you're blocking for a long while in some part of the code.
>>>>
>>>> Why would you do that. The fifo write pointer is only modified by
>>>> the interrupt handler, the read pointer is only modified by the
>>>> interrupted code. Has been done so for times immemorial.
>>>
>>> The OPs code doesn't differentiate between FIFO full and empty.
>>
>> So he should fix that first, there is no sane reason why not.
>> Few things are simpler to do than that.
>
>
> [snip]
>
>
>> Whatever handshakes he makes there is no problem knowing whether
>> the fifo is full - just check if the position the write pointer
>> will have after putting the next byte matches the read pointer
>> at the moment. Like I said before, few things are simpler than
>> that, can't imagine someone working as a programmer being
>> stuck at *that*.
>
> That simple check would require keeping a maximum of only N-1 entries in the
> N-position FIFO buffer, and the OP explicitly said they did not want to
> allocate an unused place in the buffer (which I think is unreasonable of the
> OP, but that is only IMO).
>
> The simple explanation for the N-1 limit is that the difference between two
> wrap-around pointers into an N-place buffer has at most N different values,
> while there are N+1 possible filling states of the buffer, from empty (zero
> items) to full (N items).

But, again, that just deals with the "full check". The easiest way to do
this is just to check ".in" *after* advancement and inhibit the store if
it coincides with the ".out" value.

Checking for a "high water mark" to enable flow control requires more
computation (albeit simple) as you have to accommodate the delays in
that notification reaching the remote sender (lest he continue
sending and overrun your buffer).

And, later noting when you've consumed enough of the FIFO's contents
to reach a "low water mark" and reenable the remote's transmissions.

[And, if you ever have to deal with more "established" protocols
that require the sequencing of specific control signals DURING
a transfer, the ISR quickly becomes very complex!]

When you start "fleshing out" an ISR in this way, you see the code
quickly becomes more involved than just pushing bytes into a buffer.
(and, this should give you pause to rethink what you are doing *in*
the ISR and what can best be handled out of that "precious"
environment)

Pages:123
server_pubkey.txt

rocksolid light 0.9.8
clearnet tor