tinygo icon indicating copy to clipboard operation
tinygo copied to clipboard

Compiler option for selecting output implementation

Open bgould opened this issue 5 years ago • 6 comments
trafficstars

I would like feedback on this as a possible mechanism for allowing to disable the USB CDC implementation on boards where it is the default output for print()

This is useful for devices/applications that need more control over the USB interface. Such a capability is probably a necessity in order to be able to implement other USB interface classes such as HID, MSD, etc.

This draft implementation utilizes a build tag named no_default_usb which causes UART0 (used as the destination for putchar) to be a UART implementation instead of configuring/using USB CDC. Right now I've only implemented this for samd targets, but if it looks good it will be easy to do the same for nrf52840 (which I believe is only other chipset with USB CDC support at the moment)

bgould avatar Aug 29 '20 05:08 bgould

I have hit a more general problem too. Enabling the UART on Nordic chips means a lot of current is constantly consumed. I fixed this in a battery powered application by disabling the UART, but it's not ideal. So I see three options here:

  • Use USB-CDC (when available)
  • Use native UART
  • Don't provide an output

You could imagine other options such as ARM SemiHosting, which may be useful if the extra UART connection is inconvenient or unavailable.

aykevl avatar Aug 29 '20 11:08 aykevl

Strawman proposal: add a -output flag to the compiler which can be set as needed (-output=usb, -output=uart, -output=none). The builder will set a build tag accordingly, such as output.uart. This is more extensible than the binary option with a single build tag.

What do you think?

aykevl avatar Aug 29 '20 11:08 aykevl

Strawman proposal: add a -output flag to the compiler which can be set as needed (-output=usb, -output=uart, -output=none). The builder will set a build tag accordingly, such as output.uart. This is more extensible than the binary option with a single build tag.

Good idea.

deadprogram avatar Aug 29 '20 13:08 deadprogram

I agree, I think it is a good proposal, especially if we add that option as something that can be specified in the target JSON file (and overridden from the command line of course).

As long as we are considering general cases, one other configuration would be to allow for a custom putchar function. Currently this is more or less impossible on most platforms because it would require replacing machine.UART0, which is for the most part a concrete type. Maybe that is something that could be added later after the options you mentioned for the -output flag are implemented, just wanted to mention it now though in case it is worth considering as it is sort of the most general case I can think of.

bgould avatar Aug 29 '20 14:08 bgould

Yes, now you say it, that should definitely be possible. I've considered it before but didn't see a good way to do it. However, I think this would work, perhaps with -output=custom that allows setting a custom putchar function with no impact on normal execution:

// +build output.custom

package runtime

var customOutput func(c char)

func putchar(c char) {
    if customOutput != nil {
        customOutput(c)
    }
}

func SetOutput(putchar func(c char)) {
    customOutput = putchar
}

Regarding implementation, I would propose the following:

  • Provide a new output property that is set in the JSON file but can also be overridden from the command line. On many chips, it is set to uart. On some chips it is usb (or usb-cdc?), on very bare bones chips it may be set to none (e.g. the Digispark, which doesn't have hardware UART support).
  • The compiler automatically adds a new build tag based on this, for example output.uart (analogous to scheduler.tasks).
  • Each target doesn't implement the generic putchar and doesn't configure the output at startup. Instead, it provides a number of functions for each: uartPutchar, uartConfigure, usbPutchar, usbConfigure, etc.
  • A few new runtime files (for example src/runtime/output-uart.go, src/runtime/output-usb.go etc) provide generic outputConfigure and putchar functions that call the target or board specific functions such as uartPutchar.

This design also avoids odd things like the following, by using the none output instead:

https://github.com/tinygo-org/tinygo/blob/26a0819119287713efe95f2ec0c091070c0bd3a3/src/runtime/runtime_arm7tdmi.go#L12-L14

aykevl avatar Aug 29 '20 14:08 aykevl

Yes, in my opinion that is a good design that can be implemented cleanly. I can go ahead and get started implementing this and have it ready for review pretty soon

bgould avatar Aug 29 '20 15:08 bgould