本記事の概要
前回の記事では、Xilinx社が提供するVivadoのIPパッケージャーという機能を用いて、自作のカスタムIPにAXIプロトコルの入出力インターフェースを追加する方法について解説し、シリアル出力回路を例にXilinx社のIP”AXI UART Lite”のAXI4-Liteポートと接続可能なカスタムIPを作成しました。
今回の記事では、このカスタムIPに順次”Hello World”という文字列を伝送するためのIPを紹介した後、実際にZybo上で動作確認をした結果をまとめます。
目標
本記事全体の目標は次の通りです:
自作のRTLモジュールにAXI4-Liteインタフェース(M側)を追加する
今回は、カスタムIPからAXI4-Liteを通じて、XilinxのIP”AXI UART Lite”のデータレジスタに値を書き込み、文字列“Hello world!!”を外部PCに出力させてみました。
Vivadoブロックデザインの作成
全体構成
ブロックデザインは以下のようにしました。
このブロックデザインの動作原理は以下のとおりです。
チャタリング除去に用いたRTLモジュール”debounce.v”は、「FPGAプログラミング大全」に記載されているコードをそのまま用いています。
以下では、私が自作した”word_send.v”のソースコードについて解説します。
RTLモジュール”word_send.v”
ソースコード
`timescale 1ns / 1ps
module word_send(
CLK
,RSTN
,btn_in
,AXI_DATA_en
,AXI_DATA
);
localparam PERIOD = 10000; //100us
localparam WORDNUM = 13;
//------------------------------------------------
input CLK;
input RSTN;
input btn_in;
output AXI_DATA_en;
output [7:0] AXI_DATA;
//------------------------------------------------
//-----------------------------------------------------------------------
// Generation of counter
//-----------------------------------------------------------------------
reg [23:0] count = 24'b0;
reg [3:0] wordcount = 4'b0;
reg load = 1'b0;
wire countend;
wire wordcountend;
always @( posedge CLK ) begin
if ( btn_in ) load <= 1'b1;
else if (wordcountend) load <= 1'b0;
end
always @( posedge CLK ) begin
if (countend) count <= 24'h000;
else if (load) count <= count + 24'h001;
end
assign countend = (count == PERIOD - 24'b1);
always @( posedge CLK ) begin
if (wordcountend) wordcount <= 4'b0;
else if (countend) wordcount <= wordcount + 4'b1;
end
assign wordcountend = (wordcount == WORDNUM) && countend;
//-----------------------------------------------------------------------
// WORD send
//-----------------------------------------------------------------------
assign AXI_DATA_en = (count == 24'h001);
assign AXI_DATA = hello_sentence(wordcount);
function [7:0] hello_sentence;
input [3:0] wordcount;
begin
case (wordcount)
4'd0 : hello_sentence = 8'h48; //H
4'd1 : hello_sentence = 8'h65; //e
4'd2 : hello_sentence = 8'h6c; //l
4'd3 : hello_sentence = 8'h6c; //l
4'd4 : hello_sentence = 8'h6f; //o
4'd5 : hello_sentence = 8'h20; //space
4'd6 : hello_sentence = 8'h57; //W
4'd7 : hello_sentence = 8'h6f; //o
4'd8 : hello_sentence = 8'h72; //r
4'd9 : hello_sentence = 8'h6c; //l
4'd10 : hello_sentence = 8'h64; //d
4'd11 : hello_sentence = 8'h21; //!
4'd12 : hello_sentence = 8'h21; //!
4'd13 : hello_sentence = 8'h0a; //LF, new line
default : hello_sentence = 8'h00;
endcase
end
endfunction
endmodule
タイミングチャート
ソースコードの説明を、word_send.vのタイミングチャートを使いながら説明します。
チャタリング除去後のボタン入力btn_inをトリガーにカウンターが回り始めます。
カウンターは(100マイクロ秒/バイト)×(送信するバイト数)だけ回ります。その間、load変数はHigh状態にアサートされています。
例えば、”Hello World!!(+改行)”は全部で13バイトなので1.3ミリ秒になります。
1バイト送信する100マイクロ秒の間に、AXI_DATA_ENがHigh状態にアサートされ、前回の記事で作成したカスタムIP”UART_OUT”に1文字分のデータを送信します。
ボーレートが115200bpsのとき、シリアル通信で1バイトの文字を送るのにかかる時間がおよそ87マイクロ秒なので、1語の送信間隔を100マイクロ秒だけ開ければ、余裕を持って送信することが可能です。
hello_sentenceという関数は、アスキーコード表を参考にして作成しています。
実装と動作確認
実機構成
ZyboとPCの接続は図のようにしています。
ZyboのPmodからシリアル信号を出力し、シリアルアダプターを用いてPCに読み込みました。
シリアルアダプターは、DSD TECH社のFTDI TTLアダプターを用いています。
値段も手頃で、3.3/5.0Vの両方に対応しているので使い勝手が良いと思います。ケースに入っているのも安心感がありますね。
ZyboのPmodはPmod JE(Standard Pmod)を選択しています。
下のZyboの写真におけるY17をRXD、T17をTXDにしました。
それぞれのPmodピンとシリアルアダプターのピンとをジャンパ線を使って接続しています。
接続の対応関係は以下の通りです。
制約ファイルにおけるピン配置
この接続を踏まえて、制約ファイルを作成しました。
##Clock signal
##IO_L11P_T1_SRCC_35
set_property PACKAGE_PIN L16 [get_ports sys_clock]
set_property IOSTANDARD LVCMOS33 [get_ports sys_clock]
create_clock -add -name sys_clk_pin -period 8.00 -waveform {0 4} [get_ports sys_clock]
##Buttons
##IO_L20N_T3_34
set_property PACKAGE_PIN R18 [get_ports {BTN}]
set_property IOSTANDARD LVCMOS33 [get_ports {BTN}]
##IO_L24N_T3_34
set_property PACKAGE_PIN P16 [get_ports {RST}]
set_property IOSTANDARD LVCMOS33 [get_ports {RST}]
##Pmod
#IO_L20P_T3_34
set_property PACKAGE_PIN T17 [get_ports {UART_TXD_IN}]
set_property IOSTANDARD LVCMOS33 [get_ports {UART_TXD_IN}]
#IO_L7N_T1_34
set_property PACKAGE_PIN Y17 [get_ports {UART_RXD_OUT}]
set_property IOSTANDARD LVCMOS33 [get_ports {UART_RXD_OUT}]
Pmodの接続に加えて、システムクロックとリセットポート、そしてトリガーをかけるためのボタンの接続ピンの配置を設定しました。
制約ファイルができましたら、[Generate Bitstream]で論理合成、配置配線、bitstreamファイルの作成までを一気に行います。
ZyboをPCに接続し、作成したbitファイルの書き込みまでを行い、シリアルアダプターをPCに接続しました。
Tera Termの設定
UART信号を確認するためのターミナル画面にTera Termを用いました。
Tera Termを立ち上げたら、シリアルを選択し、シリアルアダプターを接続しているポートを選択します。
ターミナル画面が立ち上がったら、[設定]>[端末]を選択します。
ここでは、改行コードの受信をLF、送信をCR+LF、そしてローカルエコーにチェックを入れます。
選択が終わったら、[OK]をクリックしましょう
次に、[設定]>[シリアルポート]を選択します。
スピードを、IP”AXI UART Lite”のボーレートと一致させます。今回は、115200です。
終わったら、[現在の接続を再設定]を選択します。
以上で、Tera Termの設定は完了です。
”Hello World!”出力確認
では、ボタン(BTN0)を押してみましょう
”Hello World!!”が無事に出力されました!
前々回、前回の記事から、M側のAXI4-Liteインタフェースを持つカスタムIPの作成を行い、今回の記事で実機での動作確認までを行うことができました。
プロセッサを使わずにAXI4-Lite経由で自由に書き込みや読み出しができるようになると、IP活用の幅が広がると思います。
最後までご覧いただきありがとうございました!
コメント