Skip to content

Conversation

@mikesmitty
Copy link
Contributor

@mikesmitty mikesmitty commented Aug 16, 2025

At the networking SIG meeting the other day we got a little off topic, but one of the things that came up was reducing initial user friction and the relative difficulty of adding support for a new board. One idea that was mentioned was the fact that the -target flag can be used to specify an external json file and at least in my mind is the obvious choice for board-definitions. Its purpose is already to store board-specific information and passing in external json target files would make custom board definitions very easily accessible (a very popular request, of course: #3288, #3152, #2607, #2239)

I took a look over the various board definitions and the vast majority of the code boils down to just a few things:

  • Constants associating board labels, on-board LED(s), etc. to MCU pins
  • Constants or variables specifying the default UART/I2C/SPI pins to be configured
  • Variables specifying USB vendor/product id

All that can be reduced to a template generated from a JSON target file very easily so I put this together and converted a handful of targets as a demo.

The way this works is to take in the board section added to a target definition that contains the pin aliases, usb variables, etc. and templates out a file that is added to the cached TINYGOROOT for that target. I chose to put it into the cache because I wanted to avoid requiring that the TINYGOROOT be writable, but that can be changed pretty easily.

As a handy side-effect, this also makes auto-generating board definitions for already-supported MCUs fairly simple and would further reduce new-user friction if they find that their favorite board is already supported.

@mikesmitty
Copy link
Contributor Author

mikesmitty commented Aug 16, 2025

As an example, here is the file generated for a metro-rp2350 target:
$TINYGOROOT/goroot-$HASHKEY/src/machine/boardgen_metro-rp2350.go

// Code generated by board-generator. DO NOT EDIT. //go:build cortexm && baremetal && linux && arm && rp2350 && rp && rp2350b && metro_rp2350 package machine // Pin aliases for the Adafruit Metro RP2350. const (	GP0 = GPIO0	GP1 = GPIO1	GP2 = GPIO2	GP3 = GPIO3	GP4 = GPIO4	GP5 = GPIO5	GP6 = GPIO6	GP7 = GPIO7	GP8 = GPIO8	GP9 = GPIO9	GP10 = GPIO10	GP11 = GPIO11	GP12 = GPIO12	GP13 = GPIO13	GP14 = GPIO14	GP15 = GPIO15	GP16 = GPIO16	GP17 = GPIO17	GP18 = GPIO18	GP19 = GPIO19	GP20 = GPIO20	GP21 = GPIO21	GP22 = GPIO22	GP23 = GPIO23	GP24 = GPIO24	GP25 = GPIO25	GP26 = GPIO26	GP27 = GPIO27	GP28 = GPIO28	GP29 = GPIO29	GP30 = GPIO30	GP31 = GPIO31	GP32 = GPIO32	GP33 = GPIO33	GP34 = GPIO34	GP35 = GPIO35	GP36 = GPIO36	GP37 = GPIO37	GP38 = GPIO38	GP39 = GPIO39	GP40 = GPIO40	GP41 = GPIO41	GP42 = GPIO42	GP43 = GPIO43	GP44 = GPIO44	GP45 = GPIO45	GP46 = GPIO46	BUTTON = GPIO24	LED = GPIO23	NEOPIXEL = GPIO25	WS2812 = GPIO25	RX = GPIO1	TX = GPIO0	D2 = GPIO2	D3 = GPIO3	D4 = GPIO4	D5 = GPIO5	D6 = GPIO6	D7 = GPIO7	D8 = GPIO8	D9 = GPIO9	D10 = GPIO10	D11 = GPIO11	D22 = GPIO22	D23 = GPIO23	A0 = GPIO41	A1 = GPIO42	A2 = GPIO43	A3 = GPIO44	A4 = GPIO45	A5 = GPIO46	I2C0_SDA_PIN = GP20	I2C0_SCL_PIN = GP21	I2C1_SDA_PIN = GP2	I2C1_SCL_PIN = GP3	SPI0_SCK_PIN = GPIO18	SPI0_SDO_PIN = GPIO19	SPI0_SDI_PIN = GPIO16	SPI1_SCK_PIN = GPIO30	SPI1_SDO_PIN = GPIO31	SPI1_SDI_PIN = GPIO28	MOSI = SPI1_SDO_PIN	MISO = SPI1_SDI_PIN	SCK = SPI1_SCK_PIN	SD_SCK = GPIO34	SD_MOSI = GPIO35	SD_MISO = GPIO36	SDIO_DATA1 = GPIO37	SDIO_DATA2 = GPIO38	SD_CS = GPIO39	SD_CARD_DETECT = GPIO40	CKN = GPIO15	CKP = GPIO14	D0N = GPIO19	D0P = GPIO18	D1N = GPIO17	D1P = GPIO16	D2N = GPIO13	D2P = GPIO12	D26 = GPIO26	D27 = GPIO27	SCL = GPIO21	SDA = GPIO20	USB_HOST_DATA_PLUS = GPIO32	USB_HOST_DATA_MINUS = GPIO33	USB_HOST_5V_POWER = GPIO29	UART0_TX_PIN = GPIO0	UART0_RX_PIN = GPIO1	UART1_TX_PIN = GPIO8	UART1_RX_PIN = GPIO9	UART_TX_PIN = UART0_TX_PIN	UART_RX_PIN = UART0_RX_PIN ) const (	xoscFreq = 12	usb_STRING_MANUFACTURER = "Adafruit"	usb_STRING_PRODUCT = "Metro RP2350" ) var (	DefaultUART = UART0	usb_VID uint16 = 0x239A	usb_PID uint16 = 0x814E ) 
@aykevl
Copy link
Member

aykevl commented Aug 17, 2025

Oh this is an interesting approach!
Does this mean the cached GOROOT is board specific now?

@aykevl
Copy link
Member

aykevl commented Aug 17, 2025

I have been thinking about another approach, that is not necessarily competing with this PR but related. It's to provide some configurations (such as clock frequency / crystal selection / USB VID+PID / other configs) through flags set in the JSON file. One issue I have is that I make custom hardware and I run it at a low frequency to reduce current consumption. Right now I basically just reconfigure the clock, but it would be more convenient if I can just set a flag on the command line.

Something like:

{ // ... "configs": { "main-clock": ["LSI-131kHz", "LSI-262kHz", "LSI-524kHz", "HSI-16MHz", "HSI-32MHz", ...], } }

And then the clock can be set either in a JSON file or via a command line flag (-config-main-clock=LSI-131kHz etc).

This could then be used in the runtime like so (switch should get optimized at compile time):

switch tinygo.getConfig("main-clock") { case "LSI-131kHz": // configure with main clock set to 131kHz // etc }

Something similar could be used for USB VID/PID pairs.

Anyway, this is somewhat off-topic but perhaps another way some configuration could be done. It wouldn't work for pin constants, obviously.

@mikesmitty
Copy link
Contributor Author

Oh this is an interesting approach! Does this mean the cached GOROOT is board specific now?

Yep, I made sure a file with the target name is added to the hash key to make sure it would split the caches out and avoid inter-mixing.

I really like those ideas you mentioned. I'll do some thinking about how they could be integrated

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants