ice_usb.h
The pico-ice-sdk makes use of the pico-sdk which uses the TinyUSB library for providing USB device mode and host mode.
ice_usb
comes as a separate pico_ice_usb
library to add in the CMakeLists.txt
.
The library user needs to provide tusb_config.h
and usb_descriptors.c
. Examples listed below. tud_task()
needs to be called frequently in the firmware.
USB CDC: UART forwarding
The pico-ice-sdk allows to configure forwarding from an USB CDC interface to an UART interface with the two defines below:
To enable:
- Define
ICE_USB_UART0_CDC
orICE_USB_UART1_CDC
to the CDC interface number to use. - Adjust these as needed:
ITF_NUM_CDCx
,ITF_NUM_DATAx
,CFG_TUD_CDC
,TUD_CDC_DESCRIPTOR
,STRID_CDC+x
See the pico_usb_uart
example.
USB SPI: FPGA/SRAM/flash forwarding
The pico-ice-sdk allows to configure forwarding data between an USB CDC interface and the main SPI interface around which everything is hooked-up together. This allows to exchange data with the iCE40, SRAM and flash directly from USB. To do so, a very small protocol was added on op of USB-CDC to control SPI:
When wanting to write \x31\x32\x33\x34\x35
over SPI, the I/O over USB-CDC would be:
TX: -<0x05 0x31 0x32 0x33 0x34 0x35 0x00>-
RX: --------------------------------------
WR*5 D1 D2 D3 D4 D5 END
When trying to read 4 bytes from SPI, the I/O over USB-CDC would be:
TX: -<0x84>-----------------------<0x00>-
RX: --------<0x00 0x00 0x00 0x00>--------
RD*4 D1 D2 D3 D4 END
To change to a different SPI chip select pin, the I/O over USB-CDC would be:
TX: -<0x80 0x00>- or -<0x80 0x01>- or -<0x80 0x02>-
RX: ------------- ------------- -------------
CMD FPGA CMD SRAM CMD FLASH
You can use this python library: pico_ice_spi.py
To enable:
- Define
ICE_USB_SPI_CDC
to the CDC interface number to use. - Adjust these as needed:
ITF_NUM_CDCx
,ITF_NUM_DATAx
,CFG_TUD_CDC
,TUD_CDC_DESCRIPTOR
,STRID_CDC+x
See the pico_usb_spi
example.
USB CDC: FPGA forwarding
The pico-ice-sdk allows to configure forwarding from the USB CDC interface using the wishbone-serial protocol to the FPGA SPI interface using the wishbone-spi protocol..
To enable:
- Define
ICE_USB_FPGA_CDC
to the CDC interface number to use. - Adjust these as needed:
ITF_NUM_CDCx
,ITF_NUM_DATAx
,CFG_TUD_CDC
,TUD_CDC_DESCRIPTOR
,STRID_CDC+x
See the pico_usb_fpga
example.
USB MSC: TinyUF2
The TinyUF2 library from Adafruit permits to expose an MSC USB storage device, with a fake FAT filesystem. This will allow to copy UF2-formatted files using a drag-and-drop scheme containing a bitstream programmed into the FPGA flash. This means no Zadig driver setup needed on Windows.
You would need something like the uf2-utils
to generate the UF2-formatted files.
To enable:
- Define
ICE_USB_USE_TINYUF2_MSC
- Adjust these as needed:
ITF_NUM_MSCx
,CFG_TUD_MSC
,TUD_MSC_DESCRIPTOR
,STRID_MSC+x
See the pico_usb_uf2
example.
#define ICE_USB_USE_DEFAULT_DFU
If set to non-zero, provides an implementation for TinyUSB DFU callbacks. Callbacks for DFU permit to program the FPGA through the CRAM (alt0) or flash (alt1) with i.e dfu-util.
#define ICE_USB_USE_TINYUF2_MSC
If set to non-zero, provides an implementation for TinyUSB MSC callbacks. These permits to enable an extra UF2 drive for loading the FPGA bitstream to them.
#define ICE_USB_USE_DEFAULT_CDC
If set to non-zero, provides an implementation for TinyUSB CDC callbacks. See tud_cdc_rx_cb_table
below.
#define ICE_USB_UART_CDC
The USB CDC interface number (0, 1, 2…) to use for USB-CDC <-> UART forwarding. All data sent to this USB interface will be sent to the UART below.
#define ICE_USB_UART_NUM
The UART interface number (0 or 1) to use for USB-CDC <-> UART forwarding. All data sent ot this UART interface will be sent to the USB CDC above.
#define ICE_USB_FPGA_CDC
The USB CDC interface number (0, 1, 2…) to use. wishbone-serial requests sent to that interface will be sent as wishbone-spi requests to the FPGA.
This sets <ice_wishbone.h>
callbacks.
See the pico_fpga_io
example.
void ice_usb_init(void)
Initiates the CDC forwarding as well as the TinyUSB library.
If CDC forwarding is not used, you can initiate TinyUSB yourself by calling tud_init()
directly.
void ice_usb_sleep_ms(uint32_t ms)
ms
to sleep in milliseconds, while callingtud_task()
every millisecond.
This is a replacement for sleep_ms()
, making USB stuck. Shorter delays can use sleep_us()
which does not get the appication stuck long enough to become a problem.
https://forums.raspberrypi.com/viewtopic.php?t=333027
void (*tud_cdc_rx_cb_table[CFG_TUD_CDC])(uint8_t byte)
byte
- this will be filled with a byte read from the selected CDC interface.
A table for controlling how USB CDC interfaces are dispatched: upon reception of a byte on a CDC interface N
, the function in the table at position N
is called with that byte as argument.
This permits to catch the content sent to an USB CDC stream by adding a new entry to that table.
It is only required to adjust it for custom CDC interfaces. The built-in ones are already configured in that table by default.
Examples
Troubleshooting
Error: undefined reference to `tud_msc_read10_cb’
If the linker complains that TinyUSB library callbacks are missing, it is possible that you still have USB devcie classes turned on in tusb_config.h
that you do not use in usb_descriptors.c
(inside tud_desc_configuration[]
).
You may check the configuration constants such as CFG_TUD_MSC
and set them to 0 if you do not use that feature.
If you do plan to use that feature, you might lack either the ice_sdk_usb
or tinyuf2
who provide the TinyUSB callbacks.