Carrier board version and ID

The ConnectCore 6 is a surface mount device (SMD) module that must be assembled on user-designed carrier boards. Users often redesign carrier boards and may also offer multiple configuration options of those boards, thus reaching a wider market.

For example, your company designs a carrier board that goes to market as version 1. Later, you redesign the carrier board to add a new user button and some LEDs, and introduce a new power regulator. This becomes version 2. Your company decides to sell two different variants of version 2 in order to reach different markets. Carrier board ID=1 is fully populated. Carrier board ID=2 is the same board but with fewer components, as it does not assemble Ethernet, USB, or audio chip. Then you redesign the carrier board again to improve the layout and fix some issues with an external controller, moving some buttons and changing some GPIO lines in the process. This becomes version 3. Your company still creates two variants (ID=1, ID=2) of the board.

While certain changes to the design of the carrier board (different carrier board version) do not affect the software—for example, moving a button or a connector, some carrier board redesigns do require changes to the software—for example, enabling a GPIO that powers a new chip on the board.

Similarly, different variants of the same carrier board and version (different carrier board ID) may require changes to the software—for example, not enabling Ethernet PHY if you don't assemble one.

For these reasons, it is convenient to store both the carrier board version and carrier board ID in non-volatile media such as the OTP (One Time Programmable) bits. You can then add conditional code in the bootloader (or in the operating system) to make the same software run on different versions and variants of the carrier board.

Location in OTP bits

For the ConnectCore 6 SBC, Digi uses Bank 4 Word 6 (GP1)—general-purpose OTP word available to OEM/customers—to store the carrier board's version and ID as follows:

Word OCOTP_GP1
Bits 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Field                                         board ID board version
Field Description

11..4

 

 

board ID

Carrier board ID number starting at 1. A zero is considered an undefined ID. IDs 128 through 255 are reserved by Digi.

 

Users can use this field to program conditional software for different IDs of the same carrier board.

3..0

 

board version

Carrier board version. A zero is considered an undefined version.

 

Users can use this field to program conditional software for different versions of the carrier board.

Carrier board version and ID in U-Boot

Banner

Digi programs the board version and board ID on the OTP bits of ConnectCore 6 SOMs when assembled in Digi carrier boards:

Board ID Smart part number CPU family Wireless Bluetooth
129 CC-SB-WMX-J97C Quad/Dual
130 CC-SB-WMX-L87C Quad/Dual
131 CC-SB-WMX-L76C DualLite/Solo

The board version and the board ID appear in the U-Boot banner:

Board: ConnectCore 6 SBC, version 3, ID 129

On stand-alone SOMs, Digi does not program these fields on the OTP bits and the U-Boot banner displays warning messages:

Board: ConnectCore 6 SBC
   WARNING: Undefined board version!
   WARNING: Undefined board ID!

Environment variables

U-Boot reads the carrier board version and ID from the OTP bits during startup and generates the environment variables board_id and board_version accordingly. You can use these variables in scripts to perform different actions.

Boot script

The boot script for Digi Embedded Yocto and Android uses the board_id variable to determine the board it is running on and select the appropriate device tree file (among several in the kernel partition) to use when booting the system. The following boot script excerpt uses board_id:

(You can find the original at https://github.com/digi-embedded/meta-digi/blob/morty/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey-2015.04/ccimx6/boot.txt.)

# Set device tree filename depending on the board ID (if defined)
#
if test -n "${board_id}"; then
        setenv fdt_file uImage-${soc_family}-ccimx6sbc-id${board_id}.dtb
else
        #
        # Set device tree filename depending on the hardware variant
        #
        if test "${module_variant}" = "0x02"; then
                setenv fdt_file uImage-imx6q-ccimx6sbc-wb.dtb
        elif test "${module_variant}" = "0x03"; then
                setenv fdt_file uImage-imx6q-ccimx6sbc-wb.dtb
        elif test "${module_variant}" = "0x04"; then
                setenv fdt_file uImage-imx6q-ccimx6sbc-wb.dtb
        else
                echo "------ Using default fdt_file"
        fi
fi

The boot script does the following:

Programming in U-Boot

Programming the carrier board version and/or ID is optional. However, Digi's BSP in U-Boot and the Linux kernel may contain conditional code for the ConnectCore 6 SBC and its different versions.

CAUTION! Programming the OTP bits is an irreversible operation.

The command board_version enables you to read and program the carrier board version:

=> help board_version
board_version - Carrier board version on fuse sub-system
 
Usage:
board_version read - read carrier board version from shadow registers
board_version sense - sense carrier board version from fuses
board_version prog [-y] <version> - program carrier board version (PERMANENT)
board_version override <version> - override carrier board version
  
NOTE: <version> parameter is in DECIMAL

The command board_id enables you to read and program the carrier board ID:

=> help board_id
board_id - Carrier board ID on fuse sub-system

Usage:
board_id read - read carrier board ID from shadow registers
board_id sense - sense carrier board ID from fuses
board_id prog [-y] <id> - program carrier board ID (PERMANENT)
board_id override <id> - override carrier board ID

NOTE: <id> parameter is in DECIMAL

Note The carrier board version and/or ID number must be given in DECIMAL.

Conditional programming based on carrier board version and/or ID

U-Boot

Digi's U-Boot contains platform functions get_carrierboard_version()and get_carrierboard_id() which read the carrier board version and ID from the OTP bits.

You can use these values to program conditional code in U-Boot, like in this example:

static void my_function(void)
{
    unsigned int board_id = get_carrierboard_id();
    unsigned int board_version = get_carrierboard_version();
 
    if (board_id == 1) {
        if (board_version == 1) {
            /* do this */
        } else if (board_version > 1) {
            /* do that */
        }
    } else if (board_id == 2}
        /* more differences */
    }
}

Linux kernel

The carrier board version and ID are populated by U-Boot inside the device tree before the kernel boots. You can program conditional code by reading the board version and ID from the device tree properties, as in this example:

struct device_node *np = of_find_compatible_node(NULL, NULL,
                         "digi,ccimx6");
const char *board_version_str, *board_id_str;
int board_version;
int board_id;
 
if (!np)
    return -EPERM;
if (of_property_read_string(np, "digi,carrierboard,version",
                &board_version_str);
board_version = atoi(board_version_str);
if (of_property_read_string(np, "digi,carrierboard,id",
                &board_id_str);
board_id= atoi(board_id_str);
 
/* Conditional code based on carrier board version and ID */
switch(board_id) {
case 1:
case 2:
    /* code for carrier boards with ID 1 and 2 */
    switch(board_version) {
    case 0:
        /* code for undefined board version */
        break;
    case 1:
        /* code for carrier board version 1 */
        break;
    }
    break;
default:
    /* code for the rest of board IDs */
    break;
}

From user space, you can read the carrier board version and ID from the device tree, which is exposed via the procfs:

cat /proc/device-tree/digi,carrierboard,version
cat /proc/device-tree/digi,carrierboard,id

You can use the value of these properties to program conditional code in scripts or applications.