RISC-V Lichee RV Dock Setup with UART

The Sipeed Lichee RV Dock is a RISC-V single board computer with a 40-pin GPIO header that makes it a package similar to a Raspberry Pi. This particular board supports both a custom Debian and Ubuntu build, which I found to be compelling. A list of Ubuntu supported RISC-V boards can be found here, and this is the distribution I decided to install. This setup will be a short guide of how to setup the board and transfer files strictly through a USB connection via UART, without needing to setup the wireless module.

Flashing the SD Card
This part was a little confusing because the official Sipeed website provides a link to their Ubuntu image on mega.nz. Instead I opted to download the image from the Ubuntu site linked above, which gave me a little more sense of security. Although both versions are Ubuntu 22.10, the download from mega.nz and the Ubuntu site have different hashes. With that out of the way I first tried 23.04 from the main RISC-V page on the Ubuntu website, which ended up just not working at all. Instead click on "How to install Ubuntu on the LicheeRV Dock". Follow the instructions under Step 1, or for longetivity purposes:

wget https://cdimage.ubuntu.com/releases/22.10/release/ubuntu-22.10-preinstalled-server-riscv64+licheerv.img.xz
xzcat ubuntu-22.10-preinstalled-server-riscv64+licheerv.img.xz | sudo dd of=/dev/sdX bs=1M conv=fsync

Replace /dev/sdX with the connected SD card, you can use gparted, parted -l, or fdisk -l to figure out this information.

Wiring UART
Connecting the board via UART solves a couple issues, first you won't need to hook the board up to another keyboard and monitor. Secondly, I'm not familiar with Sipeed so connecting this to my home WiFi was not something I was particularly interested in. Data will instead be transfered from my Ubuntu virtual machine to the board via UART, instead of the board reaching out to the internet directly. Both the Ubuntu setup page and the Sipeed webpage mentioned UART but didn't show setup explicitly. In order to connect I used a Ximimark CP2102 TTL UART USB Cable which works great; I also ordered a DTech USB to TTL PL2303 USB Cable which I did not test. I'm not an expert in this area, however the UART to USB conversion requires a bridge chip which includes CP2102 and PL2303. Although I'm using an Ubuntu virtual machine on Windows 10, it appears that the PL2303 drivers are not supported on Windows 11 so CP2102 may a preferable option. If you plug these cables into your computer, they will register even if the wires aren't hooked up to anything; this is because the bridge chips are providing the interface and not the board. Below is my wiring diagram, using the Sipeed datasheet as my reference:

The 5V from the UART goes to either 5V pin on the board and GND goes to GND. Both TX and RX swap between the board and UART. The RX on UART goes to TX on the board, and TX on UART goes to RX on the board. Once this is complete you can plug in the UART USB into your computer, and in my case I have it routed to my virtual machine.

Connecting via UART
First we need to figure out which /dev/ttyUSBX the bridge is connected to; if you are on a VM there is likely only one result. Otherwise there are a couple of approached, but the easiest solution I found is to use sudo dmesg | grep ttyUSB and read the logs. For my CP2102 bridge this shows up as "[245657.452796] usb 2-2.1: cp210x converter now attached to ttyUSB0"; so /dev/ttyUSB0 in this case. To make the actual connection via UART I found screen to work better for me than minicom. Although you can specify a screen session name, it shouldn't really matter as long as you only have one session open. To connect with screen with a baud rate of 115200:

sudo screen /dev/ttyUSB0 115200

Once connected eventually you will get a login prompt. The login prompt may not show if you connected to the screen session after the prompt was already supposed to be displayed. Another problem may be a startup service logged messages after the login prompt, so it's not clear that you're being prompted to login. The default username and password is ubuntu, and this is also noted on the Ubuntu LicheeRV page. You will be asked to change your password, and after that you should be logged in.

Transferring files via UART
Since I'm connecting via screen, I'll also be transferring files via screen. I've tried other approaches to file transfers over UART, and this appears to be the most straight forward without writing your own code. The first file I wanted to transfer was an optional debian package called flashrom. You can search for all compatible debian packages through launchpad, under Kinetic (Ubuntu 22.10) and RISCV64. Once the debian package is downloaded, it cannot be transferred via screen directly because it slurps characters into stdout. This will be problematic for binary data which contains non-ascii characters, so the file must be first converted to base64. Below contains the complete steps for transferring a file via a single screen session.

Host to Remote:
Step 1 (Remote): cat > file_b64
Step 2 (Host): cat flashrom_1.2-5build1_riscv64.deb | base64 > file_b64
Step 3 (Host): sudo screen -X readreg p file_b64
Step 4 (Host): sudo screen -X paste p
Step 5 (Remote): cat file_b64 | base64 --decode > flashrom_1.2-5build1_riscv64.deb

You can now verify the file was transferred correctly by comparing the md5sum of the file on host vs remote (the board).

Going from the board back to the host is a little more tricky, but generally involves the same steps. The program being transfered in this example is called test.

Remote to Host:
Step 1 (Remote): cat test | base64 > file_b64
Step 2 (Host): sudo screen -X clear
Step 3 (Host): sudo screen -X scrollback 0
Step 4 (Host): sudo screen -X scrollback 9999
Step 5 (Remote): cat file_b64 && echo %
Step 6 (Host): sudo screen -X hardcopy -h temp_b64
Step 7 (Host): cat temp_b64 | tr -d "[:space:]" | cut -d '%' -f 2 > file_b64
Step 8 (Host): cat file_b64 | base64 --decode > test

There are really only two approaches that I found using screen, and the first is to use copy. The problem with copy is that in order to slurp the base64 text into a buffer, you have to manually enter copy mode and select the text. What you can do after the copy is simply call writebuf (filename) and the file will automatically get saved on the host with the filename you specified. Alternatively I opted for a slightly more automatic approach by clearing the screen, and resetting the scrollback buffer. When hardcopy gets called with the -h flag, it will use everything in the scrollback buffer. By clearing the scrollback buffer, I can limit the amount of cleanup necessary to just a couple of simple linux commands.

Above is a screenshot of temp_b64 prior to file_b64

Above is a screenshot of the completed file transfer from board to host