Tang NanoのFPGAとPC間でUART通信をする

# 概要

Tang Nanoに搭載されているFPGA書き込み用のIC `CH552T` とFPGAとの間は、書き込みのためのJTAGの信号以外に、UARTの信号が接続されています。

よって、UARTの回路をFPGAに実装すればPCと通信出来ると思われたので、試しにFPGA内でUARTのTXとRXを直結したところ、PCから送信した文字がそのまま返ってくることを確認出来ました。

ただし、PCとFPGAでUART通信を行うにはいくつか作業が必要なので、手順を書いておきます。

# プロジェクトの設定

[Tang Nanoの回路図](http://dl.sipeed.com/TANG/Nano/HDK/Tang-NANO-2704(Schematic).pdf)より、CH552TのUART信号は、FPGAの`RECONFIG_N`および`DONE`ピンに接続されています。

<img alt="Tang Nanoの回路図 (1)" width="33%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58388/f7d1ae1e-c308-62a9-d0d5-6a0a02bfd861.png"></img><img alt="Tang Nanoの回路図 (2)" width="25%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58388/33e66124-5493-e905-0dc9-3bd132507c93.png"></img><img alt="Tang Nanoの回路図 (3)" width="33%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58388/8d6576aa-6eb9-503e-d48b-0ad9ec3e7a68.png"></img>

この`RECONFIG_N`および`DONE`ピンは、FPGAのコンフィグレーションに用いるピンなので、デフォルトのプロジェクト設定ではユーザー･ロジックから使用することができません。
よって、これらのピンを使用するために、以下のプロジェクトの設定を変更する必要があります。

1. `Project`->`Configuration` メニューを選択して、`Configurations`ダイアログを表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58388/8e4872b6-b57a-1ce2-945b-6a9890e7f01d.png)

2. `Configurations`ダイアログで`Dual-Purpose Pin`を選択し、`Use DONE as regular IO`と`Use RECONFIG_N as regular IO`にチェックを入れて`OK`ボタンを押します。
<img alt="" width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58388/54bfe142-14d4-ab18-ef7f-764319b371d1.png"></img>

これでUARTの信号が接続されているピンをユーザー・ロジックから使用できるようになります。

GUIを使わずに合成をしている場合は、gw_shで`add_file -cfg`で指定している`device.cfg`ファイルに`set DONE regular_io = true`と`set RECONFIG_N regular_io = true` を設定すると、`DONE`ピンと `RECONFIG_N`をユーザー・ロジックから使用できるようになります。

```device.cfg
set DONE regular_io = true
set RECONFIG_N regular_io = true
```

# CH552Tのファームウェアの更新

上記設定をして、FPGA内部でRECONFIG_NとDONEを接続したデザインを書き込み、PCから送信したデータがそのまま返ってくるかどうか確認しましたが、返ってきませんでした。

という内容をツイートしていたところ、

<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr">you may have older firmware. The Source code for the firmware is here.<a href="https://t.co/KlbCITVgoD">https://t.co/KlbCITVgoD</a></p>&mdash; Kali Prasad (@kprasadvnsi) <a href="https://twitter.com/kprasadvnsi/status/1247728603724308480?ref_src=twsrc%5Etfw">April 8, 2020</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

ということで、ファームウェアが古いということを教えてもらいました。
確かに、Tang Nanoではなく自作GOWIN FPGAボードには上記ファームウェアをビルドして書き込んで使っていましたが、そちらではUARTが使用できていました。

試しにTang NanoのCH552Tのファームウェアを更新してみたところ、PCから送信したデータが返ってくることを確認できました。

## ファームウェアのビルド

CH552Tのファームウェアのソースコードは[GitHubのリポジトリ](https://github.com/diodep/ch55x_jtag)で公開されていますので、以下の手順でビルドします。(筆者の環境はUbuntu 18.04なので、Ubuntu 18.04での手順を示します)
ビルドするのが面倒な人向けに[ビルド済みバイナリ](https://1drv.ms/u/s!AvgeuJrD0ORom-Nu1BJF-CHNS9MCLg?e=0IMU1m)も用意してあります。

1. ビルドに必要なパッケージ (gitとコンパイラとMakeとbinutils)をインストールします
    
    ```bash
    $ sudo apt install git sdcc make binutils
    ```
2. ソースコードをcloneします
    
    ```bash
    $ git clone --recursive https://github.com/diodep/ch55x_jtag
    ```
3. ファームウェアをビルドします

    ```bash
    $ cd ch55x_jtag/src
    $ make
    ```

これで、`ch55x_jtag/src/usb_jtag.bin` が生成されます。

## ファームウェアの書き込み

CH552TはUSB経由でファームウェアを更新することができます。Windowsであれば、CH552Tの製造元純正の書き込みツール`WCHISPTool`で更新できます。Linuxでは`ch552tool`というPythonスクリプトで更新できます。

### Windowsでの手順

WindowsではWCHISPToolを使用するので、あらかじめ[製造元のページからダウンロード](http://www.wch.cn/download/WCHISPTool_Setup_exe.html) してインストールしておきます。

CH552Tにファームウェアを書き込むには、CH552Tを書き込みモード(ブートローダーモード)にする必要があります。そのための方法は以下の2つがあります。

* CH552Tの14番ピンと20番ピンをショートした状態でUSBケーブルを接続する
* FT_Progを使う

前者の方法の手順は言葉通りの手順です。14番ピンと20番ピンをピンセットなどで挟んでショートしながら、Tang NanoのUSBケーブルを差し込みます。
ほかのピンに触れるなどするとPCのUSBポートやTang Nanoが壊れる可能性がありますので、慎重に行います。

後者の方法は電気的には安全ですが、FTDIが自社のチップ向けに公開しているFT_Progを他社製のチップのために使うことになりますので、ライセンス的にどうかは微妙なところです。 (そもそもWindowsドライバのライセンスがヤバいという話もある)。
このあたりを承知の上で作業する方向けに手順を記載します。

ちなみに、FT_Progを使う方法もtwitterで教えていただきました。
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr">You don&#39;t have to do any of this. Download the FTProg tool from FTDI. Click on scan device then program device to put CH552 in Bootloader mode then you can use wchisptool<a href="https://t.co/DM4on2sYCx">https://t.co/DM4on2sYCx</a></p>&mdash; Kali Prasad (@kprasadvnsi) <a href="https://twitter.com/kprasadvnsi/status/1247735214622666753?ref_src=twsrc%5Etfw">April 8, 2020</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

1. FT_Progを起動して`DEVICES->Scan and Parse`メニューを選択する

<img width="600px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58388/7bd5451c-21de-6723-1f56-0fe454ece016.png"></img>
2. `Device Tree`にデバイスが表示されたら、`DEVICES->Program`メニューを選択し、`Program Devices`ダイアログで`Program`ボタンを押す
<img width="600px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58388/6cdc6ae0-9958-90ca-ed00-ad50438243dc.png"></img>
3. `Programming Failed`とエラーメッセージが表示されるので、`OK`を押して閉じる

以上でCH552Tが書き込みモードになりました。

次に、WCHISPToolでファームウェアを書き込みます。インストールしたWCHISPToolを起動します。

<img width="600px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58388/99d95a93-d43e-e4a4-84f2-5e0ed6761075.png"></img>

1. `8 Bit CH55X series` タブを選択し、`Chip-option`グループの`Chip model`リストボックスから`CH552`を選びます。
2. `BeginDownload`グループの`DeviceList`にデバイス名が表示されていることを確認します。表示されない場合はCH552Tが書き込みモードになっていませんので、再度書き込みモードにする作業を行います。
3. `User File`の右側のボタンをおして、ビルドした`usb_jtag.bin`を選択します。
4. `Download(D)`ボタンを押して書き込み処理を実行します。
5. Tang NanoからUSBケーブルを抜いて再度差し込みます。

以上で、ファームウェアのアップデート処理は終わりです。

### Linuxでの手順

LinuxでCH552Tのファームウェアの更新を行うには、GitHubで公開されている[ch552tool](https://github.com/MarsTechHAN/ch552tool)を使います。

ch552toolはPythonで記述されており、USBデバイスの操作のために`PyUSB`を使っているため、あらかじめPython及びPyUSBを入れておきます。
参考までにUbuntu 18.04での手順を記載します。

```
$ sudo apt install python3-pip
$ pip3 install pyusb
```

ch552toolはWindowsでのWCHISPtool同様に書き込みの処理を行いますが、書き込みモードに切り替える機能はありません。
一方、FT_ProgはWindows用のプログラムですので、Linuxでは使用できません。
対策として、FT_Progが送っているコマンドを送るPythonスクリプトを教えてもらったので、このスクリプトとch552toolを使った方法を以下に記載します。

1. ch552toolをcloneします。

    ```bash
    $ git clone https://github.com/MarsTechHAN/ch552tool
    ```
2. 以下のスクリプトを`to_dfu.py`としてch552toolをcloneしたディレクトリに保存します。

    ```to_dfu.py
    import sys
    import time
    import usb.core
    import usb.util

    dev = usb.core.find(idVendor=0x0403, idProduct=0x6010)

    if dev is None:
        dev = usb.core.find(idVendor=0x4348, idProduct=0x55e0) # WCH bootloader
        if dev is None:
            print("No devices are found.")
            sys.exit()
    else:    
        try:
            dev.ctrl_transfer(0x40, 0x91, 0, 0, 0)
        except usb.core.USBError as e:
            print('Apply magic success.')

    retryCounter = 20
    while retryCounter:
        print(".", end="")
        retryCounter = retryCounter - 1
        dev = usb.core.find(idVendor=0x4348, idProduct=0x55e0)
        time.sleep(0.5)
        if dev is not None:
            break
    print("CH552 is now in DFU mode.")
    ```
3. `to_dfu.py`を実行します。

    ```
    $ cd ch552tool
    $ python3 ./to_dfu.py
    Apply magic success.
    ..CH552 is now in DFU mode.
    ```

4. `ch552tool.py`を使って`usb_jtag.bin`を書き込みます。(`~/ch55x_jtag/src/usb_jtag.bin`はusb_jtag.binのパスに置き換えてください)

    ```
    $ sudo python3 ch55xtool.py -f ~/ch55x_jtag/src/usb_jtag.bin -r
    Found CH552.
    BTVER: V2.31.
    Flash done.
    Restart and run.
    ```

以上でCH552Tのファームウェアの更新作業は完了です。

# 動作確認

前に作ったLチカプロジェクトに[UARTのループバックを追加して](https://github.com/ciniml/gowin_blinky/tree/uart_loopback)動作確認しました。

```blinky.sv
  
module blinky_top (
    input  wire resetn,
    output wire [2:0] gpio,
    input wire fpga_rx,
    output wire fpga_tx
);
    logic clk;
    OSCH #(
        .FREQ_DIV(125) // 250[MHz] / FREQ_DIV = output
    ) osc_inst (
        .OSCOUT(clk)
    );

    localparam int COUNTER_TOP = 32'd2_000_000 - 32'd1;
    
    assign fpga_tx = fpga_rx;
```

```blinky.cst
IO_LOC "resetn" 15;
IO_PORT "resetn" IO_TYPE=LVCMOS33;

IO_LOC "gpio[0]" 18;
IO_PORT "gpio[0]" IO_TYPE=LVCMOS33;
IO_LOC "gpio[1]" 16;
IO_PORT "gpio[1]" IO_TYPE=LVCMOS33;
IO_LOC "gpio[2]" 17;
IO_PORT "gpio[2]" IO_TYPE=LVCMOS33;

IO_LOC "fpga_rx" 9;
IO_PORT "fpga_rx" IO_TYPE=LVCMOS33;
IO_LOC "fpga_tx" 8;
IO_PORT "fpga_tx" IO_TYPE=LVCMOS33;
```

合成したデザインを描き込んだ後、screenコマンドなどで、CH552Tに対する2個目のデバイスを開きます。(筆者の環境では`/dev/ttyUSB1`です)
ちなみに、UARTのボーレートはあまり速すぎない限りいくらでもいいです。今回は115200を指定しています。

```
$ screen /dev/ttyUSB1 115200
```

screen起動後文字入力すると、入力した文字が画面に表示されます。screenはローカルエコーをしないので、画面に入力文字が表示されるということは、UARTで送信した文字が返ってきていることを表します。
試しにUSBケーブルを抜き差しして、FPGAのデザインを元に戻してからscreenを実行すると、入力文字が返ってこないことが確認できます。

# おわりに

Tang NanoでもPCとのUART通信が手軽に出来るようになりましたので、Tang Nanoの使い道が広がります。
合成後にパラメータをUARTで送ったり、デバッグ用データを送るなどが可能です。