UART を使う
Linux の UART ドライバと通信しようとおもいます。そのためにはどういうわけか u-boot から作り直さないといけないらしい。
先にデザイン
ざっくりとしたデザインを先に示します。async_trasnmitter というモジュールを使っています。これは verilog で書かれたもので python ではない。fpga4fun.com から拝借しています。
async_transmitter はダブルクリックで paramter を変更できます。基準のクロックが 125MHz なので初期設定の 50MHz から変更しておきます。
合成して実験
vio から 0x31 を送信する。あ、Linux が立ち上がったが気にしない。design_1_i/vio_0_probe_out0 に 1 を書いた瞬間に動き出します。
結果は 1 だらけになってとまりません。コントロールしてないから。
Zynq 側の UART をつかうと
参考までに書くと Zynq 側の UART0 を有効にする dtb を書いてみるとうまく Linux が動きません。当たり前ですが、u-boot で行われる初期化と Linux の初期化が不一致だと最悪、立ち上がらないようです。
なので上のデザインは PMOD から UART をだして USB-UART 経由で PC(Windows) と通信してます。
受信側追加
1の無限送信じゃ面白くないので echo バックさせる回路にします。
受信側も 125MHz にするパラメタ設定をします。そうしないと文字化けしますよ(経験者は語る)。
ピンの設定もつけておきましょう。
## PL System Clock
set_property -dict {PACKAGE_PIN L16 IOSTANDARD LVCMOS33} [get_ports clk]
create_clock -period 8.000 -name sys_clk_pin -waveform {0.000 4.000} -add [get_ports clk]
## LED
set_property -dict {PACKAGE_PIN M14 IOSTANDARD LVCMOS33} [get_ports led0]
## Buttons
set_property -dict {PACKAGE_PIN R18 IOSTANDARD LVCMOS33} [get_ports btn0]
set_property -dict {PACKAGE_PIN V12 IOSTANDARD LVCMOS33} [get_ports TxD_0]
set_property -dict {PACKAGE_PIN W16 IOSTANDARD LVCMOS33} [get_ports RxD_0]
うまく動きました。 Hello Hello と書いてあるのがエコーバックされた文字。そのまえの 1の羅列がひとつ前の処理。そして部分的に文字化けしているのが失敗した処理。
Polyphony を使う
ここまでは Polyphony つかってません。はい。使ってませんでした。フィルターとして大文字を小文字に小文字を大文字にするモジュールを作ります。
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
@polyphony.module
class letter_x:
def __init__(self):
self.tx_start = Port(bit, 'out', 0)
self.tx_data = Port(bit8, 'out', 0)
self.tx_busy = Port(bit, 'in')
self.rx_ready = Port(bit, 'in')
self.rx_data = Port(bit8, 'in')
self.append_worker(self.main_worker)
def main_worker(self):
while is_worker_running():
wait_value(1, self.rx_ready)
data = self.rx_data.rd()
if ( data > 0x40 ) & ( data < 0x60 ) :
data += 0x20
elif ( data > 0x60 ) & ( data < 0x80 ) :
data -= 0x20
wait_value(0, self.tx_busy)
self.tx_data.wr(data)
self.tx_start.wr(1)
clkfence()
self.tx_start.wr(0)
wait_value(0, self.tx_busy)
print(data)
@polyphony.testbench
def test(obj):
data_list = (0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21)
obj.tx_busy.wr(0)
obj.rx_ready.wr(0)
for iter in data_list:
obj.rx_data.wr(iter)
obj.rx_ready.wr(1)
clkfence()
obj.rx_ready.wr(0)
wait_value(1, obj.tx_start)
data = obj.tx_data.rd()
print(data)
if __name__ == '__main__':
obj = letter_x()
test(obj)
コンパイルして Vivado に埋め込み合成実行してみます(駆け足ですいません)。
分かりずらいですが Hello Good-Bye がエコーバックされて hELLO gOOD-bYE になりました。
ついでだから関数化する
worker だの clkfence だのが出てきてわかりづらいので、関数化/ライブラリ化してしまいます。
cat filter.py
def filter_func(data):
if ( data > 0x40 ) & ( data < 0x60 ) :
data += 0x20
elif ( data > 0x60 ) & ( data < 0x80 ) :
data -= 0x20
return data
使う側では import したうえで filter_func を呼べばいいだけです。これでややこしい部分は外に出せました。
from filter import filter_func
<略>
def main_worker(self):
while is_worker_running():
wait_value(1, self.rx_ready)
data = self.rx_data.rd()
data = filter_func(data)
wait_value(0, self.tx_busy)
self.tx_data.wr(data)
self.tx_start.wr(1)
clkfence()
self.tx_start.wr(0)
wait_value(0, self.tx_busy)
print(data)
略