Automate all the things: u-boot automated testing
by Anne Macedo
I’ve spent the whole week trying to get ethernet to work on my Orange Pi One Plus (spoiler: it still doesn’t work). My current pipeline is:
-
Make changes to u-boot, commit, generate a patch, copy to meta-sunxi
-
Clean bitbake state for the bootloader and run bitbake
-
dd the deploy images to the sd-card
-
Put on the sdcard and turn on the board
This definitely involves a lot of mechanical steps which are getting quite annoying. For each defconfig changes, I need to do all of those things.
My idea was to:
-
Get the u-boot image using tftp and keep it on RAM
-
Take the u-boot image from RAM and use the mmc command to reflash the sd card
-
Put it on a script that runs every time u-boot starts
-
If tftp fails, do nothing (it’s important to add some kind of print if a new image is loaded, to signal in case tftp fails)
Of course, the issue I’m having involves ethernet, so that is not going to work, buuuut I can try with my STM32 later.
I also wanted to run tests on the board. Thankfully, there’s a tool for that: tbot! [1]
That looks so cool, I wonder if I can later make my own CI for u-boot using the boards I have :).
tbot and testing ethernet
I decided to use tbot while I was developing the fix for ethernet on the Orange Pi One Plus. The tool is nice, and can integrate with Pytest.
First thing I did, following the documentation, was to create a config.py file. [2]
import tbot
import time
from tbot.machine import board, connector
class OrangePi(
connector.ConsoleConnector,
board.Board
):
baudrate = 115200
serial_port = "/dev/ttyUSB0"
def connect(self, mach):
return mach.open_channel("picocom", "-b", str(self.baudrate), self.serial_port)
class OrangePiUBoot(
board.Connector,
board.PowerControl,
board.UBootAutobootIntercept,
board.UBootShell
):
prompt = "=> "
def poweron(self):
time.sleep(1)
self.ch.sendline("reset")
def poweroff(self):
pass
def register_machines(ctx):
ctx.register(OrangePi, tbot.role.Board)
ctx.register(OrangePiUBoot, tbot.role.BoardUBoot)
I need to refactor this a little bit, but OrangePi is the generic board that controls the connection to picocom and serial port. This is required to read logs from serial or send stuff to the u-boot shell.
OrangePiUBoot is a little more specific to u-boot. It has board.UBootAutobootIntercept that sends a keyboard input when a message shows up on the logs (usually the default autoboot log message from u-boot). prompt
will stop sending this keyboard input one it detects this prompt.
board.PowerControl has two functions that can be implemented for poweron and poweroff procedures. For now, I just implement poweron with a reset command from u-boot, but I really want to use uhubctl
[3] to control turning usb power on and off (but I need to figure out if there’s any usb hub sold in Brazil that has vbus control).
I also have a conftest.py on my repo for allowing pytest to work.
This is where I implement the tests [4] - I commented out regulator tests because I’m not focusing on it anymore:
import tbot
import time
@tbot.testcase
def test_uboot_mdio_contains_phy() -> None:
with tbot.ctx.request(tbot.role.BoardUBoot) as ub:
mdio = ub.exec0("mdio", "list")
assert "RealTek RTL8211E" in mdio
@tbot.testcase
def test_uboot_dhcp() -> None:
with tbot.ctx.request(tbot.role.BoardUBoot) as ub:
ub.exec0("setenv", "autoload", "no")
ub.exec0("dhcp")
@tbot.testcase
def test_uboot_pinmux_pd6() -> None:
with tbot.ctx.request(tbot.role.BoardUBoot) as ub:
pinmux = ub.exec0("pinmux", "status", "PD6")
assert "gpio output" in pinmux
The tests are quite simple: the first one checks whether Realtek PHY is in use. The second one runs dhcp and checks if the command returns a zero exit code. The last one checks if the PD6 GPIO pin (that controls ethernet power) is in use.
_NOTE_ this post is being updated as I figure stuff out.
References
[1] tbot
[2] config.py
[3] uhubctl
[4] interactive.py
tags: hardware - embedded - yocto - test-automation