USB_CDC is a Verilog implementation of the Full Speed (12Mbit/s) USB communications device class (or USB CDC class). It implements the Abstract Control Model (ACM) subclass.
USB_CDC can be configured through CHANNELS parameter to implement from 1 to a maximum of 7 CDC channels.
Windows 10 provides a built-in driver (Usbser.sys) for USB CDC devices. A USB_CDC device is automatically recognized by Windows 10 as a virtual COM port, and a serial port terminal application such as CoolTerm can be used to communicate with it.
macOS and Linux provide built-in drivers for USB CDC ACM devices too.
On macOS, the virtual COM gets a name like /dev/cu.usbmodem14601
, whereas, on Linux, it gets a name like /dev/ttyACM0
. Linux requires that the user account belongs to the dialout group to grant permissions for virtual COM access.
The USB_CDC idea was born from the awesome Luke Valenty's TinyFPGA board. TinyFPGA uses a "bit-banged" USB port implemented in the FPGA fabric for communication with the host PC. David Williams, with his TinyFPGA-BX USB serial module, changed Luke's code to allow USB communication for FPGA designs. David's code uses the same clock for both USB internal stuff and data interface with FPGA application designs. Instead, USB_CDC aims to use a different asynchronous clock to allow a lower clock frequency for FPGA application designs.
Furthermore, USB_CDC was designed from scratch. This allowed to:
- keep FPGA resource utilization at the minimum and without the use of EBR memories.
- manage properly both IN and OUT data flows with USB ACK/NAK handshake without data loss.
- USB communication interface for microcontrollers and soft cores. USB_CDC takes little gate resources and no memories, leaving them for the CPU. See
soc
example. - Bootloaders. See the
bootloader
example for completely replacing the original TinyFPGA bootloader with one fully compatible with thetinyprog
programmer. - Etc, it is up to your imagination.
clk_i
: clock with a frequency of 12MHz*BIT_SAMPLESapp_clk_i
: asynchronous clock used if parameterUSE_APP_CLK = 1
rstn_i
: asynchronous reset, active low
out_data_o
: data (1 byte if CHANNELS=1, n bytes if CHANNELS=n)out_valid_o
: valid control signal (1 bit if CHANNEL=1, n bits if CHANNELS=n)out_ready_i
: ready control signal (1 bit if CHANNEL=1, n bits if CHANNELS=n)
in_data_i
: data (1 byte if CHANNELS=1, n bytes if CHANNELS=n)in_valid_i
: valid control signal (1 bit if CHANNEL=1, n bits if CHANNELS=n)in_ready_o
: ready control signal (1 bit if CHANNEL=1, n bits if CHANNELS=n)
dp_rx_i
: D input bit streamdn_rx_i
: D- input bit streamdp_tx_o
: D output bit streamdn_tx_o
: D- output bit streamtx_en_o
: D /D- output enabledp_up_o
: 1.5kΩ D pullup enable
frame_o
: last received USB frame numberconfigured_o
: 1 if USB device is in configured state, 0 otherwise
USB_CDC provides a FIFO interface to transfer data to/from FPGA application. Both in_*
and out_*
channels use the same transmission protocol.
Data is consumed on rising app_clk
when both valid
and ready
signals are high (red up arrows on the picture). Tsetup and Thold depend on FPGA/ASIC technology.
The valid
signal is high only when new data is available. After data is consumed and there is no new data available, the valid
signal is asserted low.
USB_CDC has few Verilog parameters that allow customizing some module features.
VENDORID and PRODUCTID define USB vendor ID (VID) and product ID (PID).
For TinyFPGA: VID=0x1D50 and PID=0x6130.
For Fomu: VID=0x1209 and PID=0x5BF0.
By default, they are not defined (VENDORID=0x0000 and PRODUCTID=0x0000).
IN_BULK_MAXPACKETSIZE and OUT_BULK_MAXPACKETSIZE define maximum bulk data payload sizes for IN and OUT bulk transactions. The allowable full-speed values are only 8, 16, 32, and 64 bytes. The default value for both is 8.
CHANNELS defines how many CDC channels to implement. It is possible to implement from a minimum of 1 (default) to a maximum of 7 channels.
BIT_SAMPLES defines the number of samples taken on USB dp/dn lines for each bit. Full Speed USB has a bit rate of 12MHz, so the clk
clock has to be BIT_SAMPLES times faster. For example, the default value of 4 needs a clk
frequency of 48MHz (see the picture below). BIT_SAMPLES has to be ≥ 4.
USE_APP_CLK parameter configures if the FPGA application uses the same USB_CDC internal stuff clock (USE_APP_CLK = 0) or a different asynchronous one (USE_APP_CLK = 1). If USE_APP_CLK = 0 then app_clk
input is not used and can be connected to a constant value such as 1'b0
.
When USE_APP_CLK = 1, APP_CLK_FREQ parameter defines the app_clk
frequency in MHz.
To improve data throughput for lower app_clk
frequencies, APP_CLK_FREQ parameter selects one of two different approaches to synchronize data that cross the two clock domains:
-
APP_CLK_FREQ ≤ 12. FPGA application can exchange data at every 1 or 2
app_clk
cycles. -
APP_CLK_FREQ > 12. FPGA application can exchange data at an average of 2*2.5
app_clk
cycles 2*2.5clk
cycles.
Overall, the USB Full-speed protocol caps data throughput to 1.5MB/s.
So, with freq(clk
) ≥ 48MHz, data throughput is 1.5MB/s if freq(app_clk
) > 1.5MHz, otherwise it is freq(app_clk
) bytes.
A few examples with complete implementation on both Fomu and TinyFPGA-BX are present in the examples
directory. In addition, simulation testbenches are provided for each one.
The USB_CDC code alone (with IN/OUT data in simple loopback configuration and all verilog parameters to default) shows the following logic resource utilization from iCEcube2:
Logic Resource Utilization:
---------------------------
Total Logic Cells: 1158/7680
Combinational Logic Cells: 734 out of 7680 9.55729%
Sequential Logic Cells: 424 out of 7680 5.52083%
Logic Tiles: 212 out of 960 22.0833%
Registers:
Logic Registers: 424 out of 7680 5.52083%
IO Registers: 0 out of 1280 0
Block RAMs: 0 out of 32 0%
Warm Boots: 0 out of 1 0%
Pins:
Input Pins: 1 out of 63 1.5873%
Output Pins: 2 out of 63 3.1746%
InOut Pins: 2 out of 63 3.1746%
Global Buffers: 4 out of 8 50%
PLLs: 1 out of 1 100%
The clock timing summary is:
1::Clock Frequency Summary
=====================================================================
Number of clocks: 1
Clock: clk_usb | Frequency: 76.60 MHz | Target: 48.01 MHz |
.
├── README.md --> This file
├── usb_cdc --> USB_CDC verilog files
│ ├── bulk_endp.v
│ ├── ctrl_endp.v
│ ├── in_fifo.v
│ ├── out_fifo.v
│ ├── phy_rx.v
│ ├── phy_tx.v
│ ├── sie.v
│ └── usb_cdc.v
└── examples --> Example designs
├── Fomu
│ :
│
└── TinyFPGA-BX
├── hdl
│ ├── demo
│ │ ├── demo_fpga.vhd --> Top level (VHDL)
│ │ ├── demo.v --> Top level (verilog)
│ │ :
│ │
│ └── loopback
│ ├── loopback.v --> Top level (verilog)
│ :
│
├── iCEcube2 --> iCEcube2 projects
│ ├── demo
│ │ ├── usb_cdc_sbt.project --> iCEcube2 project file
│ │ :
│ │
│ └── loopback
│ ├── usb_cdc_sbt.project --> iCEcube2 project file
│ :
│
├── OSS_CAD_Suite --> OSS CAD Suite projects
│ ├── Makefile
│ ├── input
│ │ ├── demo
│ │ │ :
│ │ └── loopback
│ │ :
│ │
│ └── output
│ :
│
└── python --> test files
└── demo
├── run.py
: