Files
RTLPlayground/doc/adding-a-device.md
T

7.0 KiB
Raw Blame History

Adding a new device

A device is supported by adding one board definition under machine/. the build selects the definition by name, and CI compiles every file it finds.

make MACHINE=<NAME>

MACHINE=<NAME> selects machine/<NAME>.c and is passed on to the compiler as both -DMACHINE_<NAME> (for the #if defined(MACHINE_...) blocks in the rest of the firmware) and -DMACHINE_NAME=<NAME> (which machine.h uses to include machine/<NAME>.h). The two always take the same value, so they cannot drift.

What a new device consists of

File Purpose
machine/<NAME>.c the struct machine machine board definition
machine/<NAME>.h MACHINE_MODEL, the model string shown in the UI and the CLI
doc/devices/<name>.md + doc/supported_devices.md (only if the board is verified) user-facing documentation

<NAME> is the machine symbol without its MACHINE_ prefix, so MACHINE is passed exactly as spelled on the command line: make MACHINE=MY_BOARD_1_0 needs machine/MY_BOARD_1_0.c and machine/MY_BOARD_1_0.h.

Step 1: the model header

machine/<NAME>.h only carries the product string. Use the existing files as the template:

#ifndef _MACHINE_MY_BOARD_1_0_H_
#define _MACHINE_MY_BOARD_1_0_H_

#define MACHINE_MODEL "Brand Model V1.0"

#endif

The header is what the UI and the CLI print; machine.h includes it by name. Nothing else belongs here — the machine is selected by the build, not by a #define in the header.

Note

machine_name is a char[30] and MACHINE_MODEL is copied into it at compile time. A longer model string still builds, but SDCC emits warning 147: excess elements in array of chars initializer and the name is truncated in the UI. Keep it to 29 characters or less.

Step 2: the board definition

machine/<NAME>.c starts with the shared prelude and then defines the single machine object. Copy the closest existing board and adjust it — do not start from a blank file, the field meanings are easier to see in a real example.

#include "machine_defs.h"

__code const struct machine machine = {
	.machine_name = MACHINE_MODEL,
	.isRTL8373 = 1,
	.min_port = 0,
	.max_port = 8,
	.n_sfp = 1,
	.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
	.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
	.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},

	.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
	.sfp_port[0].pin_los = GPIO37,
	.sfp_port[0].pin_tx_disable = GPIO_NA,
	.sfp_port[0].sds = 1,
	.sfp_port[0].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ),
	.reset_pin = GPIO_NA,

	.high_leds = { .mux = LED_27 | LED_29, .enable = LED_28_SYS | LED_29 },
	.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1 },
	.led_sets = {
		{   /* RJ45 ports */
			LEDS_2G5 | LEDS_LINK,
			LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
			0,
			0
		}, { /* SFP port, single green LED */
			LEDS_10G | LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
			0,
			0,
			0
		}
	},
};

Fields

Field Meaning
machine_name always MACHINE_MODEL
isRTL8373 1 for an RTL8373 SoC, 0 for RTL8372
min_port / max_port range of logical port numbers the device has; min_port is 0 for RTL8373 boards and 3 for RTL8372 boards (logical ports 1–2 are the SoC's internal ports on the 8372)
n_sfp number of SFP+ cages
n_10g SerDes 10G capability used by the RTL837xN init: 0 none, 1 one, 2 two 10G-capable SerDes
log_to_phys_port[9] logical port (0-based index) → physical front-panel port number
phys_to_log_port[9] the inverse, physical port 1 is index 0, unused slots are 0
is_sfp[9] 0 for copper, 1/2 for the I2C bus number of the SFP on that port
sfp_port[2] per-cage wiring, sfp_port[0] is the leftmost cage
reset_pin GPIO of the reset button, GPIO_NA if unpopulated
high_leds mux/enable for the LED pins 27–29 (LED_27, LED_28_SYS, LED_29)
port_led_set[9] which LED set (0–3) each logical port uses
led_sets[4][4] the 4 LED combinations per set; which of the 4 is used depends on how the LED is wired
led_mux_custom / led_mux[28] 1 plus the 28 mux values when the stock LED muxing does not fit the board
mac_flash_offset flash offset of the factory MAC address, omit or 0 if the board does not store one

Each struct sfp_port entry holds pin_detect, pin_los, pin_tx_disable, sds (which SerDes drives the cage) and i2c (built with I2CBUS(sda, scl), or GPIO_NA-based values where a cage has no such pin).

I2CBUS(...) only accepts the pin pairs listed in machine_defs.h. A wrong pair is caught at compile time: SDCC reports error 176: sizeof applied to an incomplete type pointing at the offending .i2c line, because the macro deliberately turns an invalid bus number into an array of size 0.

Reusing an existing definition

If the new board is electrically identical to one that already exists — same port mapping, same SFP wiring, same LEDs — do not copy the file. Make the new .c a symlink and keep only a distinct .h, so the two boards cannot drift apart:

ln -s KP_9000_6XHML_X2.c machine/MY_BOARD_1_0.c

The new board still needs machine/MY_BOARD_1_0.h with its own MACHINE_MODEL, because MACHINE_MODEL is what distinguishes the products. Existing examples: machine/PCB_K0402WS_V3.c, machine/KP_9000_9XHML_X_V3_2.c and the six KP_9000_* revision variants. git ls-files -s machine shows these as mode 120000.

Two files that differ only in whitespace or comments are duplicates and should be collapsed this way.

Step 3: build and verify

make MACHINE=MY_BOARD_1_0

The image appears as output/rtlplayground-<version>-MY_BOARD_1_0.bin. An unknown or empty MACHINE fails immediately with a message naming the expected file, before anything is compiled.

Then run the checks CI runs:

make machine_check    # compiles machine/<NAME>.c and machine_init.c for every machine
make -C test          # host-side unit tests

make machine_check discovers the definitions with $(wildcard machine/*.c), so a new file is covered without registering it anywhere. CI (.github/workflows/build.yml) runs it on every push and pull request, alongside full builds for a 4+2 and an 8+1 board.

A successful compile only proves the definition is syntactically valid. The actual behaviour — port numbering, SFP detection, LEDs — has to be confirmed on hardware, and the console commands (port, stat, sfp, leds_dump) are the tools for that.

Step 4: documentation

Once the board has been tested on hardware:

  1. Add a row to the table in Supported devices.
  2. Add doc/devices/<name>.md describing the board, the build target (make MACHINE=MY_BOARD_1_0), what works and what is unverified, plus photos and the PCB silkscreen marking.
  3. If the board shares a PCB with other revisions, follow doc/devices/KP-9000-6XH-X2.md and document which MACHINE to pick for which silkscreen.