How to make PicoSOC speak USB on TinyFPGA-BX


#1

I noticed that the PicoSOC includes some examples for iCE40-HX8K Breakout and ICEBreaker, but unfortunately not TinyFPGA. While Luke has kindly provided an example in his repository, it can only blink an LED, which is a little trivial to me. I would like to play with a full-blown example like those for HX8K and ICEBreaker, whose firmware is much more sophisticated (you can do things like benchmarking the SPI, which is kinda cool).

I have made some progress toward this goal. See: https://github.com/nalzok/picorv32. You can try it out by

cd picosoc
make tinysim    # simulation
make tinyprog   # flashing

It was not hard to port the vanilla PicoSOC to TinyFPGA-BX, with the serial port placed on two GPIOs: A1 and A2, but I want to talk to it via the micro USB port, so I am trying to adapt the famous tinyfpga_bx_usbserial repository to my purpose. There were some timing/place-and-route problems, which are resolved by switching from arachne-pnr to nextpnr. Now, I have added the serial echo-back demo to PicoSOC on TinyFPGA-BX. However, I have no idea how to utilize the usb_uart module to translate the serial signals ser_tx and ser_rx to the USB signals pin_usbp and pin_usbn. I was expecting an “adapter” module sitting between these two signals and magically translate USB to serial and vice versa, just like an FTDI chip, but we don’t seem to have such modules.

Any idea?


#2

You would need to remove the “simpleuart” module from picosoc, and replace it with the USB UART connecting parallel data, valid and ready.


#3

Ah, I didn’t realize I could do that! These two modules seem to have different signatures, though, so I don’t think it would be a trivial replacement?

module usb_uart (
  input  clk_48mhz,
  input reset,

  // USB lines.  Split into input vs. output and oe control signal to maintain
  // highest level of compatibility with synthesis tools.
  output usb_p_tx,
  output usb_n_tx,
  input  usb_p_rx,
  input  usb_n_rx,
  output usb_tx_en,

  // uart pipeline in (into the module, out of the device, into the host)
  input [7:0] uart_in_data,
  input       uart_in_valid,
  output      uart_in_ready,

  // uart pipeline out (out of the host, into the device, out of the module)
  output [7:0] uart_out_data,
  output       uart_out_valid,
  input        uart_out_ready,

  output [11:0] debug
);

versus

module simpleuart (
    input clk,
    input resetn,

    output ser_tx,
    input  ser_rx,

    input   [3:0] reg_div_we,
    input  [31:0] reg_div_di,
    output [31:0] reg_div_do,

    input         reg_dat_we,
    input         reg_dat_re,
    input  [31:0] reg_dat_di,
    output [31:0] reg_dat_do,
    output        reg_dat_wait
);

As you can see, in usb_uart the data are somehow “pipelined” according to the comments, whereas simpleuart doesn’t come with a lot of comments so I have no idea how it works.

PS: I don’t even fully understand the meaning of all of these input and output pins. Maybe I need to learn the basics first? If that is the case, which topic should I familiarize myself with first? UART? USB? Pipelining?


#4

Have a general look into ready valid interfaces.

picosoc is also ready valid based internally, so you could remove a lot of the simpleuart “glue” (ie ignore the simpleuart specific interface and look more at the general interconnect in picosoc) and connect the USB UART to picosoc fairly easily.


#5

Hi there, I have done so research myself about the “ready valid interfaces” by reading this document, but I am still clueless regarding this problem. As you said, some modules in PicoSoc are ready/valid-based, e.g. spimemio.

However, simpleuart doesn’t seem to have a clear ready/valid interface to me. Instead, it talks with two registers at 0x02000004 (UART Clock Divider Register) and 0x02000008 (UART Send/Recv Data Register). Despite the documentation, I am still having problems understanding their relationship to the “ready” and “valid” signals. This is partially due to my incompetence, and partially due to the absence of comments and extensive use of abbreviations. For example, what on earth is mem_wstrb? Does reg_dat_we and reg_dat_re mean “read enable” and “write enable” respectively? That way, we essentially have two distinct valid signals. Also, is reg_dat_wait the opposite of a “ready” signal?

While you have advised that I should simply ignore the simpleuart glue, I can hardly decouple it from the rest of the design without understanding its interface. In addition, I am a little interested in how it works. I mean, this guy is the developer of Yosys, so he must be good at writing Verilog, right?

I would appreciate your further advice!