20 January 2022 by Phillip Johnston • Last updated 19 September 2022
We consider hardware versioning an essential device-side capability and process for embedded systems development. Every PCB used in an embedded system is bound to have multiple revisions throughout the product’s development cycle, and it is important that we can easily identify the revision we are currently working with – especially from within the software system.
The version number for a PCB should be incremented every time a new version is released, no matter how trivial the changes may seem.
Version Silkscreen on PCB
The PCB version should be printed on the board via silkscreen so that engineers and developers can unambiguously determine the version. Silkscreen is preferred to a sticker because stickers often come off, and a board may very well be mislabeled.
Version Listed on Schematic
At a minimum, print the PCB version on the schematic’s front page. Ideally, the version will be on every page of the schematic. This makes it quite easy for anyone on the team to check whether the schematic they are looking at refers to the PCB they have in hand.
Software-Interpretable Versioning
While the PCB silkscreen and schematic versions aid developers, they do not give the software access to a method to determine what hardware revision it is running on.
Some PCB revisions will be agnostic to software and merely involve tweaking some board design parameters. However, many revisions end up being significant enough to necessitate corresponding software changes: pinouts are changed, peripheral devices are changed, features are added/removed, etc.
In the “ideal” case, all of the will hardware can be immediately scrapped and the software will only need to support newer hardware. However, these ideal case rarely plays out. Because development hardware is often in limited supply due to its high production cost, teams often need to support multiple hardware revisions concurrently. Likewise, we may already be selling our devices, and now we need to maintain support for existing devices as well as the new variant. If we have a way to read the hardware version from software, we have the ability to support multiple variants with a single revision. We can also use the revision to access the proper firmware variant in the event that we maintain different binaries for different variants.
Frustratingly, the majority of embedded systems projects we work on still do not provide a method for the software to determine the hardware revision. This feature isn’t considered until it is too late, and there are multiple variants that need to be supported with no good way to distinguish them in software. At this point, the problem becomes one of build management: we must manually ensure that existing devices are flashed with the proper software version. This is an error-prone process
Use GPIO Encode PCB Version
The simplest and most common technique for encoding PCB revision so that software can access is by encoding a version using GPIO. The software will read the three GPIO lines and interpret them as a 3-bit binary version number. The GPIO will be configured in software with internal pull-ups. The version number in hardware is then controlled by placing resistors that connect the pin to GND.
The same system works if the polarity is reversed as well. The directionality of the system and the value of the resistors are usually driven by power concerns (to minimize leakage current).
| Version |
GPIO 2 |
GPIO 1 |
GPIO 0 |
| 1 |
0 |
0 |
1 |
| 2 |
0 |
1 |
0 |
| 3 |
0 |
1 |
1 |
| 4 |
1 |
0 |
0 |
| 5 |
1 |
0 |
1 |
| 6 |
1 |
1 |
0 |
| 7 |
1 |
1 |
1 |
| 8 |
0 |
0 |
0 |
Some teams use 0b000 to represent version 1, and 0b111 will represent version 8, and some just make 0b000 version 0. In other cases, we have seen teams reverse the ordering such that they count down instead of up.
Three GPIO lines are often sufficient, as they provide a possible set of 8 versions before we need to reset. We have worked on teams that used only two GPIO lines due to GPIO constraints, but we have always found this insufficient: almost every system we have worked on has gone through four revisions before or after product launch. Four GPIO lines gives you much more wiggle room, but we have rarely been through that many revisions. In our opinion, it is better to re-start the version numbers once production hardware is ready, scrapping support for all development hardware at that time. Version increments after that point apply to production hardware changes.
Include High-Z as a Pin State
Most GPIO have a high-Z state, and you could leverage this to get 3 bits per pin instead of 2.
High-Z is detected in the following way:
- Drive the line low
- Convert to input (high-Z) and read
- Drive the line high
- Convert to input (high-Z) and read
If the two input readings hold different values, the pin is in high-Z state.
Use an ADC to Encode Version
Another method for supporting version numbers is to use an ADC to convert a voltage to a version. A resistor network is used to control the voltage seen at the ADC (the midpoint of the resistor network is connected to the ADC input). Versions correspond to “binned” ADC readings, representing equal divisions of the full ADC range (e.g., 4095 for a 12-bit ADC).
| Version |
ADC Range (12-bit) |
| 1 |
0-511 |
| 2 |
512-1023 |
| 3 |
1024-1535 |
| 4 |
1536-2047 |
| 5 |
2048-2559 |
| 6 |
2560-3071 |
| 7 |
3072-3583 |
| 8 |
3584-4095 |
Values for the resistor network should be chosen so that the target voltage/reading falls within the midpoint of the range specified. This scheme also necessitates using high-precision components (1%) that will not de-rate over the course of the product’s lifetime such that the version falls outside of the specified range.
Resistor values need to be appropriately selected to minimize leakage current. In addition, a second GPIO can be assigned to the versioning scheme. This pin will supply VCC to the resistor network, and it can be turned on only when the software needs to read the board version.
A larger range of versions can be supported by tightening the binning on each version. However, tightening bins too much may lead to de-rated resistors causing erroneous version reads later in the product’s lifetime.
You can also combine the ADC and GPIO approaches, using one GPIO pin + one ADC pin. This would double the number of values supported by the ADC approach.
Write Hardware Version in Persistent Memory
Some processors provide user-configurable non-volatile memory. These registers can be written during manufacturing, storing the associated hardware revision directly in processor memory. You can also use a dedicated configuration EEPROM or a reserved region of flash to store this information.
Ideally, these registers/regions could be locked so that the version (and any other configuration information) cannot be accidentally overwritten.
Prototyping New Revisions
With software-readable board versioning, it becomes possible for software to be updated and tested prior to having new hardware produced. Existing boards can be reworked to match the new version as closely as possible, and that board can have its resistor network changed to the next revision. Software developers can use this reworked hardware to add support for the new revision in software.
Not all changes are easily supported via rework, so some changes will need to land once the final hardware is ready. Nonetheless, we have used this strategy on multiple occasions to make significant progress ahead of receiving new hardware.
“Securing” Hardware Revision
Common practice involves stuffing resistors on traces present on the PCB, which enables easier rework. However, for teams that are concerned about CM’s “cheating” by marking old PCBs with a new revision ID, you can hard-code the board revision using the GPIO method by laying it out within the PCB (i.e., wiring the traces internally to encode the 0’s and 1’s of the PVB layout. We do not recommend this during development, as the rework method above is often quite useful, but it is suitable for production.
Further Reading