Edited at

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


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 に対応したいと考えています。