Conditional blocks within the config.txt file

Greetings!

Here’s another tidbit from my research into multi-booting the Pi.  Though I could put it in those sections, it’s sufficiently interesting that I decided to break it out into its own posting.

Issue:
There may be times when - while booting - you may want to make configuration changes based on the boot environment.

For example:

  • Booting a Pi-4 is different than booting earlier versions because the GPU handles memory allocation differently - so you may want to set a different value.

  • If a particular monitor is attached, you may want to configure the HDMI video out differently to accommodate a quirk in the monitor.

  • You have set internal settable configuration bits on one particular board to change some fundamental aspect of how the board works, essentially making it unique.

  • Or, as in my case, you may want to allow GPIO pins to change something about the way it boots when you apply power.

The Raspberry Pi has a way to accommodate things like this by the use of simple “conditional” statements.  You’ve already seen one if you’ve looked at your Pi’s config.txt file - the part that starts with [pi4], followed by the one that says [all]

The “official” documentation on the config.txt file, (and all the fun things you can do with it!), can be found here: config.txt - Raspberry Pi Documentation

To summarize, (and point out an important “gotcha!”):

Conditional parts of the config.txt file are special conditional statements enclosed in square brackets, like this:

  • [gpio19=0] which works if Broadcomm GPIO pin 19 is pulled low.

  • [0x12345678] which ONLY works with the one, unique, board with serial number 0x12345678.

  • [EDID=VSC-TD2220] which works if a specific monitor is plugged in - maybe it needs audio forced, or a specific resolution that is not auto-detected?
     

There’s one big “gotcha!”
The documentation on config,.txt conditional statements implies strongly that groups of conditional statements, when grouped together, act as the logical AND of ALL the statements taken together.

Viz.:
[gpio19=0]
[pi4]
[EDID=VSC-TD2220]
do this specific thing. . . .

According to the documentation, this should work like this:
IF
gpio pin 19 is pulled low
AND
this is a Pi-4
AND
a specific monitor is plugged in
THEN
do this specific thing.

This is NOT true.

What actually happens is that:

  • each conditional statement is its own logical group of statements, independent of all others

  • The conditional’s logical scope ends when

    • Another conditional statement occurs
      or
    • The end of the config.txt file is reached.
      (Note that you can combine/merge config.txt type files using the special include statement, so this may not be entirely true.)
       

Because of this, the group of conditional statements given before:
[gpio19=0]
[pi4]
[EDID=VSC-TD2220]
do this. . . .

actually works like this:
[gpio19=0]
(if true, then do nothing because there’s nothing here)
[pi4]
(there’s nothing here either, so nothing happens)
[EDID=VSC-TD2220]
do this. . . .
(because there’s something here, that gets done, but ONLY if that particular monitor is plugged in, without any input from the other two conditions.

Note what I said before - a conditional block ends ONLY when another conditional block is reached, (or, maybe, the end of the file.)

This has important consequences.

For the sake of argument, assume you have a config.txt file like this:

and (for orginazitional reasons), you want to add a special group of statements that ONLY happen when it’s run on a PI-4, so you do this:

Do you see the problem?  (Hint:  I got caught by this very thing yesterday and it drove me crazy for a day and a half!)

Answer:  The conditional statement NEVER ends, so ALL of the statements after the [pi4] ONLY happen if a Pi-4 is plugged in, which is not what we want.  

The solution is a special conditional statement - [all] - which translates to “if (true)” which ends the previous conditional statement and starts reading configurations for everyone again.

This is how you fix that particular problem:

In my case, I inadvertently disabled the robot’s camera unless a specific switch was thrown and I wasted a day and a half taking my poor 'bot apart because I thought either the camera or the cable had died.  What actually happened is that I forgot to end a conditional statement with an [all], and it included the rest of the entire config.txt as part of the condition! - :face_with_symbols_over_mouth:   :woozy_face:   :crazy_face:

Of course, you could have put the [pi] at the end, but it’s easier to understand if you group statements logically by putting configuration statements that are similar together in a compact group.  You can do that by creating conditional blocks and ending each block with an [all] condition.

The official documentation for conditional statements strongly advises ending conditional blocks with an [all], even if you think they’re not necessary, just to avoid this kind of confusion.

Enjoy!

1 Like

Seems like you’re making headway. Or at the least learning a lot.
/K

1 Like

I am also learning that, (at least some), of the development and/or project management people at Raspberry Pi are rude, arrogant, and dismissive.

One of my “colleagues” on the Raspberry Pi forums helped me research this and discovered that the true root cause is that GPIO pin definitions themselves are mutually exclusive.

The official documentation states that GPIO statements “of the same type” override each other because the options are mutually exclusive - the canonnical example in the documentation being that a particular Pi cannot be both a pi-2 and a pi-4 at the same time.

Unfortunately, they consider GPIO pin conditional statements as “mutually exclusive”, even though they aren’t. (i.e.  GPIO pins 16 and 17 are not mutually exclusive because they are different pins.)

The response by someone, (who I presume is in authority), was that was “as documented, designed, and it was expected behavior” and he then slammed the issue closed without allowing any opportunity for comment or objection.

Both I and the original poster have posted objections and counter-examples and - so far - it has fallen on deaf ears.

To say that I am incredibly disappointed is a masterpiece of understatement.

Viz.:

1 Like

Curious. I skimmed through the github discussion. I haven’t messed with the GPIO pins on the Raspberry Pi much, certainly not directly (although I have on the Arduino), so I’m not quite sure what they mean by “same type”. But as far as I was following the discussion on brief review, it really doesn’t make sense that you can’t read GPIO 16 and 17 independently.

/K

1 Like

Actually, you can read them independently. The problem is that you cannot read them in combination such as:
[gpip14=0]
[gpio15=1]
[gpio16=1]
[gpio17=0]
do something
[all]

which should cause “something” to happen only if ALL the GPIO conditions were met at the same time.

The current behavior of that stack of GPIO conditions is that the first three do nothing because there is nothing to do, and gpio17 does something only if it is zero, without regard for the other three GPIO pins.

That the behavior is “as expected” begs the question of whose expectations are being met - obviously not the end user because I have seen so many people who believe that GPIO conditions can be stacked and AND’ed together.

I believe that the ability to use the GPIO pins as tests during boot-up is one of the “hidden gems” of the Raspberry Pi. It opens so many potential applications for the Raspberry Pi - especially in headless configurations - that currently require Rube Goldberg, or, (who is that guy - McGivver?) -esque contraptions and software kludges to accomplish what a simple GPIO test can do.

Once I’m done with this, (and the dust settles), I’m a-gonna have to write a QA Tech-Tips article about this.

1 Like

Ah. I didn’t understand the syntax.
But surely there’s a way to do the individual comparisons and then do the logic??
/K

1 Like

Based on the information I have at hand, (which may be limited), I don’t believe so as I don’t know of any way to save the result of a conditional test and then compare it to the result of other conditional tests.

The problem is that the config.txt file was not originally intended to be an environment where relatively sophisticated logic could be done.  Instead it was intended to be a way of establishing a simple configuration file based on, (at the time), a relatively small number of relatively low powered boards, so that the same software could run on different board types.

For example:

  • If it’s a Pi-1:
    • Set the GPU memory split relatively low because there isn’t much memory there.
    • Set the HDMI video resolution to a low value to conserve video and processing resources.
    • Disable this other feature that, though nice to have, isn’t essential.
    • Bump up the clock speed a lot so the performance isn’t abysmal.
       
  • If it’s a Pi-2 (with more memory):
    • Set a more generous GPU memory split because there’s more memory to spend.
    • Set a higher video resolution to make the display easier to read.
    • We have enough resources now to allow us to use this other desirable feature too.
    • We still need to bump up the clock speed, but not by such a large amount as before.
       

The ability to test GPIO pin state was, (apparently), an afterthought at a later time and as a consequence, didn’t get much thought. Ergo, it’s a very, very simplistic arrangement.

So what you get is something like this:

(Assume four different operating systems with four unique and distinct config.txt files, each with a unique name.  Also note that the syntax is not necessarily correct, but you should get the idea.)

(configure the necessary GPIO pin properties)

# default configuration overridden by the optional configurations below
use default_config.txt
# no "os_prefix" because the default kernel and overlay files are here.

[gpio1=0]
use OS1_config.txt  # Use the specific config.txt for this O/S
os_prefix=OS1/
# this tells us that the OS1 config and kernel files are in a
# subdirectory of /boot called "OS1" as in /boot/OS1

[gpio2=0]
use OS2_config.txt
os_prefix=OS2/

[gpio3=0]
use OS3_config.txt
os_prefix=OS3/

[gpio4=0]
use OS4_config.txt
os_prefix=OS4/

(end of preliminary config.txt)

If there is more than one GPIO bit set low, the last one wins.

Additional OS specific configuration can be done within the individual OS config.txt files.

For example:

# there is a copy of these lines in the special config files for OS's 1-3
[gpio5=0]
arm_64=1 # use the experimental 64 bit kernel instead of the normal 32 bit one.

#  O/S 4 doesn't get this line because O/S 4 is already 64 bit.

So, by adding additional single-bit tests, using different bits than before, you can create additional restrictions.

Of course, you could do something like this:

[gpio1=0]
(use the configuration from before)

[gpio2=0]
(use the configuration from gpio1)
arm_64=1

[gpio3=0]
(use the configuration from gpio2 from before)

[gpio4=1)
(use the configuration from gpio2 as before)
arm_64=1

(etc. . .)

However, that will cost you 2x the number of bits.

And so forth.

The ability to “stack” GPIO tests would allow a smaller number of bits to control a larger number of possible configurations.

However since “almost nobody ever uses this”, it’s an abysmally low priority.

1 Like

Ah, now I see. Wow - that really does seem problematic.

I certainly don’t know enough about low level programming to understand what changes would be needed to be made to make this work. But if the issue is that changing this would require herculean effort and they don’t think it’s worth it, or that it would break other things, then they should just be upfront about it, rather than just saying it’s “working as documented”.
/K

1 Like

Apparently, (as far as I could understand the “feel” of the response), they do not have the absolute freedom of action with the Raspberry Pi’s firmware as they do with the operating systems, and that burns their biscuits.

They mentioned that the firmware itself is “closed source” - likely by Broadcomm - which translates to either:

  • They don’t own it at all and have to beg Broadcomm for new releases.
  • They have partial ownership, but do this at the grace of Broadcomm, whom they have to beg permission every time they breathe on it.
  • Or, they’re just being pissy about it because they don’t see the utility of that kind of combinational logic.
     

I even offered to try to include the fix myself and submit a pull request - that’s when they told me it’s closed source.

That kind-of surprises me that Debian would allow their name and operating system to be associated with something where a fundamental part of the O/S, (both the firmware and the video are closed-source), since they’re almost as anal about FOSS as Fedora.  They pitched Firefox many years ago over a controversy with the “Mozilla license”, so they spun their own - Ice Weasel.  (They’ve since kissed and made up.)

Maybe this is why they’ve rebranded their O/S from Raspbian to Raspberry Pi O/S - Debian got fed-up with Broadcomm’s BS about their hardware and API’s.

I don’t know.

Maybe the Raspberry Pi people themselves are so fed up with Broadcomm’s BS that they’re touchy on the subject.

Even if so, I strongly suspect that someone is going to reverse-engineer their hardware.  Supposedly, even in the age of the DCMA, it’s legal to reverse-engineer something for compatibility reasons.  That’s for the lawyers to fight over - I’m no expert there.

Anyway, I’m able to get something like 95% of the functionality I want and 100% of what is absolutely necessary, so I guess I’ll take my winnings and run.

1 Like

This is one of the reasons I always recommend at least some knowledge of assembly language and/or bare-metal programming as it gives you as a programmer a much better feel for what’s actually happening “under the hood”.

You said you have a Circuit Playground Express, right?  Or maybe a GemmaMO?  I believe that both of these can be “bare metal” programmed in their associated assemblers, though I’d have to research and make sure.  I’m pretty sure the Arduino can.

Of course, I don’t expect you, (or anyone else), to become expert enough to write video kernel software for NVIDIA, but knowing the difference between and ADS (add and store) and an ADI (add immediate), as well as the difference between a “near” jump and a “far” jump helps you understand why the languages you DO program in have the quirks they do.

There’s a book called Understanding the Linux Kernel that gets into the real nitty-gritty about what the kernel does and how it does it - some of which is pretty gnarly stuff.  The first couple of chapters that describe exactly what happens within an x86/AMD64 processor, memory, etc. - between the time power is first applied and the actual Linux kernel starts to load - is enough to cross Einstein’s eyes!

But it’s interesting!  The way memory indirection/abstraction is done that allows programs and pages of memory to be swapped out and back in, (without having to re-write the code), is fascinating!

But that’s me.  I’ve been a bit of a masochist about technology since I was a kid. . .

1 Like

Agree this would be interesting, but just not enough hours in the week to even get to the projects that I really want to do.
/K

1 Like

Ain’t that the truth!

I have some high-density neopixel strips, a shiny new Circuit Playground Express, some other “feather”-type controller boards and goodies from Adafruit that I’m just itching to mess with - and they’re collecting dust because Charlie is a jealous god and demands all my time.  (:wink:)

1 Like