LoginSignup
1
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-12-24

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

echo のデザイン

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

image.png

合成して実行

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

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

足し算のプログラム

もうほんとに足し算しかしてません。

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

呼び出しもと

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

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 で実行しておきます。

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

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

Linux でテスト

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

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

Python でテスト

こっちの方が簡単です。

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 を実行しています。

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;
                }
        }
}
実行結果
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 に対応したいと考えています。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2