This document contains information about how to build the hello, world app for the Chameleon board using the Zephyr RTOS.
The hello, world app was based heavily on experimental/scarlet
, in that I copied the tree over, renamed some files, and then edited all the files to change references to the STM32F0 series to the proper chip for Chameleon.
The Chameleon has an STM32F103ZG (1 MB flash, 96 KB RAM), but the ZG doesn't have a board support file in Zephyr yet. However, the ZE (STM32F103ZE, 512 KB flash, 64 KB RAM) does, so I just used that chip for this proof of concept.
See PR#28335 and PR#28336 for STM32F103ZG support added in Zephyr 2.4.
cd ~/zephyrproject west build -b chameleon -s ~/chromiumos/src/platform/chameleon/v3/ec
Output files are in ~/zephyrproject/build/zephyr
You can use a SEGGER JLink probe to program the firmware, or you can use the serial bootloader.
As of Proto 0, you can program via the JTAG interface only if the FPGA board is not installed.
Connect your JLink to the JTAG, using either an Altera FPGA adapter or a Xilinx FPGA adapter.
Connect 12V power to the Chameleon.
cd ~/zephyrproject west flash --erase --reset-after-load
To use the serial bootloader to update the STM32's firmware, you must install stm32flash
and build stm32reset
. For now all of these things have to do be done outside the chroot and some commands (eg. stm32reset) will be deprecated beyond proto0.
sudo apt-get install stm32flash gpiod libgpiod-dev cd ~/chromiumos/src/platform/chameleon/utils/stm32reset make
This section assumes that the chromiumos chroot environment is set up in ~/chromiumos, and that zephyr is set up in ~/zephyrproject.
Connect the USB-C cable to the USB-C port closest to the HDMI/DisplayPort connectors.
Connect 12V power to the Chameleon.
sudo ~/chromiumos/src/platform/chameleon/utils/stm32reset/stm32reset --bootloader stm32flash -o -f /dev/ttyUSB0 stm32flash -w ~/zephyrproject/build/zephyr/zephyr.bin -v -S 0x8000000 -f /dev/ttyUSB0 sudo ~/chromiumos/src/platform/chameleon/utils/stm32reset/stm32reset --user
Open miniterm
$ miniterm.py /dev/ttyUSB0 115200 --- Miniterm on /dev/ttyUSB0 115200,8,N,1 --- --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
Press the reset button, located between the STM32 and the edge of the PCB. Observe the following text on the terminal:
*** Booting Zephyr OS version 2.3.0 *** Hello World! arm
The red LED at the bottom edge of the board (near the PCIe connector) will blink at 0.5 Hz.
Instructions to build and run remain the same.
When the Zephyr kernel boots, the POWER_GOOD LED (red LED near the PCIe connector, previously the “blinky” LED in the hello, world! app) turns on and stays lit. The shell prompt (uart:~$
) appears after the startup messages.
io set POWER_GOOD off
and io set POWER_GOOD on
control the POWERGOOD LED.
The micro SD card can be connected to the PC through the same USB connection as the serial console, connected to the FPGA's SD card controller, or completely disconnected. The sd
commands takes a sub-command:
off
- Disconnect the SD card from the USB port and the FPGAusb
- Connect the SD card to the USB portfpga
- Connect the SD card to the FPGA's SD card controllerstatus
- Show the current setting for the SD mux and the status of the CD_DET line.Start with the uSD slot empty.
uart:~$ sd status The SD mux is set to disconnected CD_DET = 1
Push the card into the slot.
uart:~$ sd status The SD mux is set to disconnected CD_DET = 0
Physically eject the card.
Connect the mux to the USB controller.
uart:~$ sd usb
Push the card into the slot.
uart:~$ sd status The SD mux is set to USB CD_DET = 0
Your computer will show a new removable drive for the uSD card.
Use the operating system to “safely eject” the removable drive.
Physically eject the card.
Connect the mux to the FPGA.
uart:~$ sd fpga
Push the card into the slot.
uart:~$ sd status The SD mux is set to FPGA CD_DET = 0
Your computer will not show a new removable drive for the uSD card.
uart:~$ sd off
Physically eject the card.
The System Monitor reads several different voltages on the Chameleon and can provide a human-readable summary of the various voltages and the current monitors.
The command sysmon
prints a list of the various voltages being monitored:
uart:~$ sysmon 12V: 12.242871 CMON_SOM: 0.294067 CMON_BOARD: 0.235254 PP1200: 1.217358 PP1360: 1.372046 PP1260: 1.280200 PP1000: 1.005469 VMON_9V: 9.174902 VMON_5V: 4.908105 VMON_3V3: 3.232727 PP1800: 1.813550 SOM_VMON: 1.527539 SOM_VMON_1V2: 1.959375 SOM_VMON_MGT: 1.735400 SOM_VMON_1V8A: 1.886865 SOM_VMON_C8: 1.913452 SOM_VREF_CS: 0.001611
The SOM_VMON voltages will not be accurate unless the FPGA SOM is installed and its power supplies are enabled. In this example, the FPGA SOM is not installed.
Note that on proto0, the current monitors (CMON_SOM and CMON_BOARD) are not accurate due to a parts issue.
Because of the use of a timer to drive the sampling sequence, you can get zero values if you run the sysmon
command soon after start-up:
sysmon 12V: 12.091406 CMON_SOM: 0.275537 CMON_BOARD: 0.229614 PP1200: 1.207690 PP1360: 1.374463 PP1260: 1.304370 PP1000: 1.030444 VMON_9V: 9.139453 VMON_5V: 5.017676 VMON_3V3: 3.295569 PP1800: 1.774878 SOM_VMON: 0.000000 SOM_VMON_1V2: 0.000000 SOM_VMON_MGT: 0.000000 SOM_VMON_1V8A: 0.000000 SOM_VMON_C8: 0.000000 SOM_VREF_CS: 0.000000
Even a second later, all of the values are non-zero:
$ sysmon 12V: 12.117188 CMON_SOM: 0.281177 CMON_BOARD: 0.248145 PP1200: 1.200439 PP1360: 1.374463 PP1260: 1.304370 PP1000: 1.030444 VMON_9V: 9.139453 VMON_5V: 5.017676 VMON_3V3: 3.295569 PP1800: 1.774878 SOM_VMON: 1.290674 SOM_VMON_1V2: 1.688672 SOM_VMON_MGT: 1.513843 SOM_VMON_1V8A: 1.596020 SOM_VMON_C8: 1.546875 SOM_VREF_CS: 0.001611
The Si5338 clock generator chip provides a 100 MHz clock to the FPGA SOM. There is no API for this; the initialization is done at startup using a SYS_INIT
function.
The Si5338 is fairly complex, and configuration is best done with Silicon Labs' Clock Builder Pro software. This is an MS Windows-only tool, so I ran it on my personal laptop, saved the project (it uses a binary format) and exported a C header file with all of the register settings. Note that the header file is not modified, and checkpatch.pl
hates the way it's formatted.
The clock output can be verified with an oscilloscope and either a very steady hand or some short wires to attach probes. When the board is in reset, the clock output is idle, but as soon as the SYS_INIT
function runs, the clock output changes to a 100 MHz signal.
The STM32 controls the power enable and power-on-reset for the FPGA, and monitors the signals for power good and configuration done. With an FPGA module installed, the FPGA module power good LED (on proto0 this is D21, just to the right and above the PCIe connector) will briefly turn on and then off again as the STM32 asserts control over the signal. Chameleon FPGA Power/Boot Sequence State Machine provides details of the state machine.
The shell provides the fpga
command, which has three subcommands:
on
to start the power-on and boot sequence for the FPGA moduleoff
to turn off the FPGA module immediatelystatus
to report statusbootmode
to set the bootmode to emmc
, qspi
, or sdio
A new FPGA module will not have a bitstream on it, so a failure to reach the power-on state is expected behavior.
$ fpga bootmode qspi FPGA boot mode set to qspi $ fpga status FPGA steady status: true BOOT_MODE = 01 PWR_EN = 0 PWR_GOOD = 0 POR_L_LOAD_L = 0 FPGA_DONE = 0
$ fpga on FPGA power on ...
$ fpga bootmode emmc FPGA is busy. Try again in a few seconds. $ fpga status FPGA steady status: false BOOT_MODE = 01 PWR_EN = 1 PWR_GOOD = 1 POR_L_LOAD_L = 1 FPGA_DONE = 0
$ fpga status FPGA steady status: true BOOT_MODE = 01 PWR_EN = 0 PWR_GOOD = 0 POR_L_LOAD_L = 0 FPGA_DONE = 0 $
The Chameleon v3 has three 16-bit I/O expanders on I2C2. The signals connected to these I/O expanders are one-time configuration signals, operational mode and reset signals that are changed very infrequently, or fixed signals for board identification.
The misc_io
module exposes an API to read the three BOARD_VERSION signals as an integer. It also provides debugging commands in the shell to read the BOARD_VERSION signals, and to read and write two debug signals that are connected to TP125 and TP126.
After programming the latest firmware, experiment with these commands in the shell:
$ board ver = 0 $ io get TP125 TP125 = 0 $ io get TP126 TP126 = 0 $ io set TP125 on $ io get TP125 TP125 = 1 $ io set TP126 on $ io get TP126 TP126 = 1 $ io set TP125 off $ io get TP125 TP125 = 0 $ io set TP126 off $ io get TP126 TP126 = 0
If you hook up a logic analyzer to I2C2 SCL and SDA, TP125, and TP126, you can see the I2C bus transactions between the STM32 and U78 (which has device address 0x21) to read and write the individual bits, as well as watching TP125 and TP126 change when they are written.
The Chameleon v3 has a MAX7367 I2C bus switch on I2C1. On the other side of the switch, buses 0 and 1 have a PS8468 DP demux, PS8469 video output demux, and MCDP2900 DP to HDMI translator. These are on separate buses due to device address conflicts. Bus 2 has two EP91A6SQ HDMI repeaters with different device addresses set by pin strapping. Bus 3 has no connections. There is a single interrupt line from the MAX7367 that can be asserted by any of 4 interrupt lines on the other side of the switch. Only IRQ2 is actually connected to an interrupt source (the EP91A6SQ chips).
The i2c_switch
module exposes a simple API to control the reset line to the MAX7367 (to recover in case of a bus hang), to control the bus switching, and to read the interrupt line status. There is also a binary semaphore that the IRQ handler gives, although there is currently no task that waits on the semaphore.
The i2c_switch
shell command allows setting the switch value. Provide a value from 0 to 3 to select one of the buses, or a value outside that range to turn off all buses. The effect of the switch can be seen with i2c scan I2C_1
(as provided by Zephyr's i2c_shell.c
).
$ i2c_switch 2 $ i2c scan I2C_1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- 64 65 -- -- -- -- -- -- 6c 6d -- -- 70: -- -- -- 73 -- -- -- -- 5 devices found on I2C_1 $ i2c_switch 3 $ i2c scan I2C_1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- 73 -- -- -- -- 1 devices found on I2C_1