Python で FPGA/Linux + UART 使う(Zybo 編)

# ARM Linux の UART と通信する
外の UART とは通信できました。次の要求は ARM との UART 通信です。Linux で出来れば応用範囲が広がります。

## Zynq の UART
Zynq の Linux などのコンソール用の UART はなぜか UART1 がアサインされています。理由は不明ですが、伝統的にそうなっています。そこで UART0 を増やすと、tty の番号がずれます。UART0 がコンソールになってしまいます。USB UART で PC に繋がっている方は物理的に UART1 なので、混乱してしまいます。

UART を変えて（増やして）”表示しなくなったら"その辺を疑うのがいいかと思います。私は混乱しました。

## デザイン
echo を階層化してみました。Linux 側の UART の TX/RX を PL に出して、フィルターを介して外の USB Uart と接続されています。これで、外の繋いである USB UART から(PC から) 入力すると Linux の UART に入力されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/99921/d8f916da-b5c5-42e9-2f8c-6a228b733947.png)

### echo のデザイン
階層化されたデザインは次の通りです。フィルターの letter_x_obj は小文字を大文字に、大文字を小文字にします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/99921/6e8d5813-616b-973a-5e25-1fe3c2fbf56b.png)

### 合成して実行
Linux 側で tty を 115200 にする必要があります。cat で入力待ちしてそとの USB Uart から文字を打ち込むと変換された文字を見ることが出来ます。

```text:stty
fpga@debian-fpga:~$ stty 115200 < /dev/ttyPS0
fpga@debian-fpga:~$ cat < /dev/ttyPS0
jgeil
jgeil
・ello
hello
HELLO
MYDER
mYJI
```

echo などで /dev/ttyPS0 へ出力することもできますが、ちゃんとオーバランを考慮していないので文字抜けが発生してしまいました。ここは改良の余地があります。

## Linux の UART を折り返す。
デザインを変更しました。Linux の UART からデータが入ってくると、calc0 で計算して UART へ折り返すようにしました。いままでのようにフィルターを介してもいいのですが、ここは足し算をして返すようにしました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/99921/e7c342fe-2fbd-5496-42c2-993a5fba69ff.png)

### 足し算のプログラム
もうほんとに足し算しかしてません。

```python:my_lib.py
> cat my_lib.py
def func(a , b):
    return a + b
```

### 呼び出しもと
これは queue を使っています。リクエストとして [len , ID , arg0 , arg1] のバイナリが来ることを期待しています。返答は [len, ID, rv] です。本当は json にしたかったのですが、時間がなくできませんでした。ということで bson という名前になっています。

```python:bson.py
import polyphony
from polyphony import is_worker_running
from polyphony.io import Port, Queue
from polyphony.typing import bit, bit8
from polyphony.timing import clksleep, clkfence, wait_value

from my_lib import func

@polyphony.module
class bson_server:
    def __init__(self):
        self.bson_req = Queue(bit8, 'in')
        self.bson_reply = Queue(bit8, 'out')

        self.append_worker(self.main_worker)

    def main_worker(self):
        while is_worker_running():
            len = self.bson_req.rd()
            id = self.bson_req.rd()
            a_0 = self.bson_req.rd()
            b_0 = self.bson_req.rd()
            rv = func(a_0, b_0)
            self.bson_reply.wr(2)
            self.bson_reply.wr(id)
            self.bson_reply.wr(rv)

@polyphony.testbench
def test(obj):
    bson_lst = [3, 10, 3, 4]

    for iter in bson_lst:
        obj.bson_req.wr(iter)

    for i in range(3):
        print(obj.bson_reply.rd())

if __name__ == '__main__':
    obj = bson_server()
    test(obj)

```

### Python で実行
シミュレーションとして Python で実行しておきます。

```text:pythonで実行
> python bson.py
2
10
7
```

### Polyphony でコンパイル
コンパイルすると bson_server_obj.v ができます。また polyphony_out.v ができるので fifo だけ抜き出して fifo.v とします。

## デザインに組み込む
bson_server_obj.v と fifo.v をデザインに組み込みます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/99921/a99b7276-3931-29b1-336e-3450373c9d7e.png)

あとは合成すれば出来上がり

## Linux でテスト
UART0 と 1 で混乱して動かないと思ってしまいました。この問題（オペミス）よっぽどおおいんですね。AR に書かれている。
https://japan.xilinx.com/support/answers/64339.html
「この場合のカーネル ブートは、実際は停止するわけではありません。」

これ DTS の順番を変えるだけで解決しそうな気がします、、、（未確認）

### Python でテスト
こっちの方が簡単です。

```text:Python3でシリアルへ読み書き
fpga@debian-fpga:~/uart$ python3
Python 3.5.3 (default, Jan 19 2017, 14:11:04)
[GCC 6.3.0 20170118] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> import termios
>>> import tty

>>> b = bytearray([3, 0x41, 5, 6])
>>> (print(b))
bytearray(b'\x03A\x05\x06')
>>> print(len(b))
4

>>> f = open('/dev/ttyPS0', 'w')
>>> print(f)
<_io.TextIOWrapper name='/dev/ttyPS0' mode='w' encoding='ANSI_X3.4-1968'>
>>> fds=f.fileno()
>>> tty.setraw(fds)
>>> print(fds)
4
>>> os.write(fds, b)
4
>>> x = os.read(fds, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 9] Bad file descriptor

>>> f = open('/dev/ttyPS0', 'r')
>>> fds=f.fileno()
>>> os.read(fds,4)
b'\x02A\x0b'
```

ポイントは raw モードにする事みたいです。

### C でテスト
tty デバイスですが tty として使うわけではないのでそこを考慮しないいといけません。送信列に 0x03 があって、これはシェル的には ^C に相当します。そのため 0x5E 0x43 (つまり ^C) が差し込まれたりして混乱しました。open 時の引数に O_NOCTTY を設定すると親の tty のコントロールから逃れることが出来ます。

次のプログラム上は raw モードにしてませんが、本来ならすべきです。このコマンドを起動する前に stty raw < /dev/ttyPS0 と stty 115200 < /dev/ttyPS0 を実行しています。

```c:rw.c
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>

int
main(int argc, char **argv)
{
        struct termios oterm;

        char bson_lst[] = {0x03, 0x41, 0x22, 0x44, '\n'};

        int i;
        char *file = argv[1];
        int fds;
        fds = open(file, O_RDWR | O_NOCTTY);
        if ( fds < 0 ) {
                perror("permission error?");
                return 1;
        }
        /*
                return 2;
        }
        oterm.c_lflag &= ~ICANON;
        if(tcsetattr(fds, TCSAFLUSH, &oterm) == -1){
                perror("tcgetattr failure");
                return 2;
        }

        oterm.c_lflag &= ~ICANON;
        if(tcsetattr(fds, TCSAFLUSH, &oterm) == -1){
                perror("tcgetattr failure");
                return 2;
        }
        */

        int rv;
        unsigned char rv_lst[4];
        for ( i = 0 ; i < 4; i++ ) {
                rv = write(fds, &bson_lst[i], 1);
                fprintf(stderr, "%d:%02x\n", rv, bson_lst[i]);
        }

        for ( i = 0 ; i < 3; i++ ) {
                rv = read(fds, rv_lst, 1);
                fprintf(stderr, "%d:%02x\n", rv, rv_lst[0]);
                if ( rv < 0 ) {
                        perror("he?");
                        return 3;
                }
        }
}
```

```text:実行結果
fpga@debian-fpga:~/uart$ stty 115200 < /dev/ttyPS0
fpga@debian-fpga:~/uart$ stty raw < /dev/ttyPS0
fpga@debian-fpga:~/uart$ stty < /dev/ttyPS0
speed 115200 baud; line = 0;
min = 1; time = 0;
-brkint -icrnl -imaxbel
-opost
-isig -icanon
fpga@debian-fpga:~/uart$ ./rw /dev/ttyPS0
1:03
1:41
1:22
1:44

1:02
1:41
1:66
```

参考までに書くと、実行結果として 0x22 + 0x44 の結果 0x66 が返ってきています。
0x22 + 0x44 は polyphony で記述した計算です。つまり、UART で書いた値に対して IP(FPGA 側のハードウェア) で計算した結果が戻ってきたという事になります。

将来的には json に対応したいと考えています。