マイクロマウスのRX631(48ピン)に書き込めない

前回の記事から10日後に図1の基板が届きました。

図1:届いた基板
図1:届いた基板

早速、全ての電子部品をハンダ付けして、プログラムを書き込もうとしたのですが、接続すらできませんでした。 DCマウスを途中まで作ったときにRX631(64ピン)にプログラムを書き込んだ経験があったので、できて当然と思っていたのが間違いでした。やはり、ロボット製作では一つずつ動作確認しなければならないことを痛感しています。

結果的には、明確な原因はわからなかったのですが、発振子の配線部分を変更して、新しい基板を再発注することで接続できるようになりました。

この記事では、接続できるようになるまでの経緯を書きます。

環境

  • パソコン:Windows10 64 bit
    • 統合開発環境 e2studio 2021-01をインストール
    • コンパイラ Renesas CCRX v3.03.00をインストール
    • 書き込みソフト Renesas Flash Programmer V3.08.01(無償版) をインストール
  • ターゲットデバイス:R5F5631PDDFL

RX631と接続する方法

RX631とPCとの接続はUSBシリアル変換モジュールを用いて図2のように行いました。 USBシリアル変換モジュールは秋月で購入したFT234Xです。

図2:RX631とPCとの接続図
図2:RX631とPCとの接続図

書き込み基板は図3のようになっています。

図3:書き込み基板の写真
図3:書き込み基板の写真

RX631はリセット解除時のMD端子とPC7端子の電圧によって表のように動作モードが決まります。

動作モード
MD
PC7
内容
シングルチップモード
HIGH
---
プログラムが動作するモード
ブートモード
LOW
LOW
プログラムを書き換えられるモード

電源投入時に書き込み基板側でMD端子をLOWにすることで、動作モードをブートモードにします。

Renesas Flash Programmerを起動して、「ファイル」→「新しいプロジェクトを作成…」を選択して、以下を入力します(図4)。

  • マイクロコントローラ:RX63x
  • プロジェクト名:wakaba
  • 作成場所:(適当な場所)
  • ツール:COM port

図4:RFPのプロジェクトの作成
図4:RFPのプロジェクトの作成

これで「接続」を押すことで、RX631と接続ができます。

そのはずなのですが、図5のような画面になってしまい、接続が失敗します。 マジかよ…。

図5:接続ができないメッセージ
図5:接続ができないメッセージ

RX631とPCが接続できない原因について

RX631とPCが接続できない原因を頑張って調べました。 とりあえず、ブログ記事などを参考にしながら、原因となりうる箇所を一つずつ確認していきました。

  1. RXDとTXDが逆になっていないか確認
    → 接続確認ができているDCマウスと同じようにしているため、問題ないはず

  2. MD端子とPC7端子の電圧の確認
    → テスターで確認したところ、MD端子は0.0V、PC7端子は0.0Vとなっていたため、問題ないはず

  3. マイコンの電源電圧の確認
    → テスターで確認したところ、VCCは約3.3V、VSSは0.0Vとなっていたため、問題ないはず

  4. ハンダ不良がないか確認
    → 書き込みに関係ある配線についてテスターで導通しているか調べたため、問題ないはず

  5. メインクロックが発振しているか確認
    オシロスコープがないため、確認ができない

  6. RX631自体が破損している
    確認ができない

RX631が破損している以外では、メインクロックが発振していないことくらいしか原因が思いつかなかったので、メインクロックのことについて調べました。

メインクロックに関する調査

メインクロックは書き込みに関係ないと思っていたのですが、関係あるんですね。 たしかに、ユーザーズマニュアルのp.1873にフラッシュ書き換えにはメインクロックの入力が必要であると書いていました。

メインクロックに関して調べるうちに問題ありそうな箇所がいくつか見つかったので、列挙していきます。

  1. セラロックの向きが反対
    回路図で見るとセラロックは左右対称なので、接続方向がないと勝手に思っていたのですが、セラロックの仕様書を見ると向きがあるようです。 ハンダ付けしたセラロックを確認すると、EXTAL(入力)にセラロックの入力が接続されていました。 これが原因かと思って、セラロックを外して、接続方向を正して新しいセラロックを付けなおしました。 しかし、相変わらず接続は失敗しました。

  2. 内臓容量が参考値ではない
    ユーザーズマニュアルのp.261を見ると、内臓容量の参考値が10pF~22pFなのですが、自分のセラロック(CSTNE10M0GH5L000)は33pFでした。 ただ、昔書き込めたマウスのセラロックも33pFだったので、問題ないかなとは思うのですが...。

  3. セラロックがハンダ付けのときに破損した
    セラロックの仕様書を見ると、リフロー対応であり、ハンダごてを使用したハンダ付けは対応していないそうです。それなので、ハンダ付けのときに破損した可能性があります。 ただ、セラロックを付けなおしたとき、かなり丁寧にハンダ付けしたので、問題ないかなとは思うのですが...。

  4. 発振回路の近くに信号線が通過していた
    ユーザーズマニュアルのp.270を見ると、「発振回路の近くには信号線を通過させないでください」(図6)と書いていました。 以前のDCマウス製作時には意識していたのですが、今回は意識していませんでした。 配線図をみると、たしかに図7のように信号線が通過しています。

図6:RX631のユーザーズマニュアルp.270からの引用
図6:RX631のユーザーズマニュアルp.270からの引用

図7:発振回路に信号線が通過している
図7:発振回路に信号線が通過している

これを修正するには、基板を再発注して、部品も注文し直す必要があるので、少し踏ん切りがつきませんでした。

オシロスコープで発振子の動作確認

本当にメインクロックが原因か確かめるために、オシロスコープを購入して、発振回路の動作確認をしました。購入したオシロスコープは以下です。

かなり高かったですが、社会人になりましたし、オシロスコープは今後も使えると思いましたので購入しました。 表面実装用のプローブはあったほうがいいかなと思いましたが、付属のプローブでも測定はできました。 セラロックは以前使っていたCSTCE10M0G55に付けなおして、オシロスコープで電圧を測定しました。 結果を以下の表に示します。

基板
XTAL
EXTAL
書き込めたマウス基板 約10MHzで発振 約1.6 Vで定電圧
今回のマウス基板 約10MHzで発振 約1.6 Vで定電圧

また、今回のマウス基板のXTALとEXTALの測定結果を図8と図9に示します。

図8:XTALの測定結果
図8:XTALの測定結果

図9:EXTALの測定結果
図9:EXTALの測定結果

あれ…書き込めた回路と同じ動作をしているので、正常に動作しているようです。それなら、なぜ書き込めないのだろうか。

発振回路も正常に動作しているようなので、あとはRX631が破損している可能性しか残っていません。ハンダ付けのときにRX631を破損させてしまったのか。 明確な原因はわかりませんが、RX631などの部品を再度注文して、作りなおすことにしました。また、発振回路を通過する信号線についても気になったので、 基板についても再発注しました。

回路図と配線図の変更

回路図と配線図をいくつか変更して、再発注しました。

回路図

図10:再発注した基板の回路図
図10:再発注した基板の回路図
回路図PDF版

回路図の主な変更点は以下です。

  • プルアップ抵抗などの抵抗値の変更
  • CS信号線にプルアップ抵抗を付ける
  • セラロックを変更

ブートモード中にCS信号線がLOWになり、ICがアクティブな状態になって、PC7がHIGHになってしまうという記事を見たので、 CS信号線にプルアップ抵抗を付けました。また、セラロックを以前使っていたCSTCE10M0G55の代替品であるCSTNE10M0G550000に変更しました。 このセラロックは入出力が逆になっても問題ないようです。また、内臓容量は33pFですが、過去のDCマウスでは動いたし大丈夫かなと思い、これを選択しました。

配線図

図11:再発注した基板の配線図
図11:再発注した基板の配線図
配線図PDF版(縮尺:2.5倍)

配線図の主な変更点は以下です。

  • 発振回路の近くに信号線を通過させない
  • いくつかのパッドをハンダ付けしやすいようにする

発振回路の近くに信号線を通過させないこと以外はほとんど変えていないです。

新しい基板に部品を実装して接続

再発注して2週間後くらいに基板が届いたので、書き込みができる最低限の部品をハンダ付けしました。 はじめ、PCと接続したときに相変わらず「接続は失敗しました」と出て、マジかよ…と思いましたが、PC7のプルダウン抵抗をつけ忘れていました。 プルダウン抵抗をつけたら、図12のようになり、接続に成功しました。

図12:接続成功時のメッセージ
図12:接続成功時のメッセージ

本当によかった…。

おわりに

明確な原因がわからなくて、少し腑に落ちないですが、書き込めて本当によかったです。結局、RX631の破損だったのでしょうか? 謎です。次回は L チカやシリアル通信などの記事を書こうと思います。

ZYBOでシンセサイザー作成(3)波形テーブル(BRAM)による正弦波の合成

今回は、波形テーブル(BRAM)を使って正弦波を合成するIP (Intellectual Property) を製作します。前回まではハードウェア記述言語(HDL)を用いずにFPGAを使っていましたが、今回からHDLのSystemVerilog を用いていきたいと思います。

今回の記事で参考にした文献は以下の2つです。

[1] 波形テーブルの参考文献

[2] IP作成の参考文献

環境

  • パソコン:Windows10 64 bit
    • Vivado 2020.2 をインストール
    • Xilinx Vitis 2020.2 をインストール
  • ボード:Zybo Z7-20

波形テーブルによる合成について

SystemVerilogでは三角関数でさえ計算するのに苦労します。そこで、波形テーブル(BRAM)を使用して正弦波を合成します。一応、Xilinx社が三角関数を計算する IP を提供していますが、波形テーブルによる合成は様々な状況で応用が効くと思いますので、こちらの方法で合成します。

波形テーブルによる合成の例

波形テーブルによる合成では、図1のような1周期の波形をRAMの中に保存しておきます。図1の場合、2048個のデータを保存する必要があります。この波形テーブルから順次データを取り出すことによって任意周波数の波形を合成することができます。

図1:正弦波の波形テーブル
図1:正弦波の波形テーブル

例えば、サンプリングレート  f_s が48kHzの場合、周波数 f が12kHzの波形を合成したいなら、0番目、512番目、1024番目、1536番目、0番目...と取り出せばよいです。

任意周波数の正弦波の合成

周波数  f の信号を合成したい場合、データを取り出す波形テーブルの番号 p は以下の式で計算されます [1]。

\displaystyle{
p = {\rm mod} \left(\frac{nfN}{f_s}, N\right)
}

ここで、 f_sはサンプリング周波数、 N は波形テーブルのデータ数、nはサンプル番号です。 {\rm mod}(x, N) というのは「 x Nで割ったときの余り」です。この式をSystemVerilogで計算する必要があります。

正弦波合成 IP の製作

VivadoでBRAMを使った正弦波合成 IP を製作していきます。

正弦波合成 IPのフォルダの用意

まず、以下のようにフォルダを作成して、ファイルを配置していきます。 sin_gen.sv と SinTbl.coe はこちらの GitHub の「3rd」フォルダの中にあります。

ip_repo_synth
└── sin_gen
    ├── HDL
    │   └── sin_gen.sv
    └── ROMDATA
        └── SinTbl.coe

sin_gen.svの内容を以下に示します。sin_gen.svの内容については別の記事でしたいと思います。

module sin_gen(
    input logic clk96M,
    input logic reset,
    input logic [14:0] freq,
    output logic [15:0] dout
    );
    
    logic [20:0] cnt;      // Int 21bit
    logic [10:0] cnt2000;
    logic cnt2000_flag;
    logic [14:0] freq_ff;  // Int 15bit
    logic chg_freq_flag;
    logic [35:0] p_int;    // Int 36bit
    logic [39:0] p_int_ff; // Int 40bit 
    logic [50:0] p;        // Int 32bit  Decimal 19bit
    logic [10:0] p_ff;     // Int 11bit
    
    localparam tmp = 11'd1398; // Int 1bit, Decimal 10bit
    
    // cnt2000
    always_ff @(posedge clk96M) begin
        if(reset)
            cnt2000 <= 11'd0;
        else if(cnt2000_flag)
            cnt2000 <= 11'd0;
        else
            cnt2000 <= cnt2000 + 11'd1;
    end
    
    // cnt2000_flag
    assign cnt2000_flag = (cnt2000 == 11'd1999);
    
    // freq_ff
    always_ff @(posedge clk96M) begin
        if(reset)
            freq_ff <= 15'd0;
        else
            freq_ff <= freq;
    end
            
    // chg_freq_flag
    assign chg_freq_flag = (freq != freq_ff);
    
    // cnt
    always_ff @(posedge clk96M) begin
        if(reset)
            cnt <= 21'd0;
        else if(chg_freq_flag)
            cnt <= 21'd0;
        else if(cnt2000_flag)
            cnt <= cnt + 21'd1; 
        else
            cnt <= cnt;
    end 
    
    // p_int
    assign p_int = cnt*freq;
    
    // p_int_ff
    always_ff @(posedge clk96M) begin
        if(reset)
            p_int_ff <= 40'd0;
        else
            p_int_ff <= {p_int, 4'd0};
    end
            
    // p
    assign p = p_int_ff * tmp;
    
    // p_ff
    always_ff @(posedge clk96M) begin
        if(reset)
            p_ff <= 11'd0;
        else
            p_ff <= p[29:19];
    end
    
    // BRAM
    WAVE_TBL WAVE_TBL(
        .addra (p_ff),
        .douta (dout),
        .clka (clk96M)
    );
endmodule

SinTbl.coeの内容を以下に示します。振幅16384、波形テーブルのデータ数  N=2048 の正弦波の波形テーブルとなっています。Pythonを用いて符号付き整数で出力しました。

memory_initialization_radix=16;
memory_initialization_vector=
0000,
0032,
0065,
0097,
00c9,
…
ffce;

VivadoでIPの製作

プロジェクトを以下のようにして作成します。FPGAに書き込むためのプロジェクトではないので、制約ファイルは作らなくていいです。

  • 作業フォルダ:sin_wave_gen
  • プロジェクト名:sin_wave_gen

このプロジェクトでIPの製作を行い、IPのシミュレーションも行います。

プロジェクトができたら、「Tools」→「Create and Package New IP...」を実行します。

図2:「Create and Package New IP...」を実行
図2:「Create and Package New IP...」を実行

最初の画面(図3)では、そのまま「Next」をクリックします。

図3:そのまま「Next」をクリックする
図3:そのまま「Next」をクリックする

「Packaging Options」(図4)では、「Package a specified directrory」を選択して、「Next」をクリックします。これでフォルダを指定することでIPが作成されます。

図4:「Package a specified directory」を選択する
図4:「Package a specified directory」を選択する

IPのフォルダはさきほど作成した「sin_gen」を選択して、「Next」をクリックします(図5)。自分は「ip_repo_synth」というシンセサイザー用のIPリポジトリに「sin_gen」を置いてありますので、それを選択しました。

図5:IPのフォルダを選択
図5:IPのフォルダを選択

次の画面(図6)では、IPを編集するプロジェクト名の入力をします。ここはデフォルトのまま「Next」をクリックします。

図6:IPを編集するプロジェクト名の入力
図6:IPを編集するプロジェクト名の入力

最後の画面(図7)では、そのまま「Finish」をクリックします。HDLのSystemVerilogファイルにエラーがある場合、エラーが生じて、IPが作成できないので気をつけてください。

図7:「Finish」をクリックする
図7:「Finish」をクリックする

IP編集用のVivado画面が図8のように起動され、「sin_gen/HDL」フォルダ内の回路記述が自動で読み込まれます。起動した画面の「Sources」ペインでは「WAVE_TBL」に?マークがあり、プロジェクトにBRAMが不足しています。そのため、BRAMを追加していきます。

図8:IP編集用のVivado画面
図8:IP編集用のVivado画面

BRAMを追加するには「PROJECT MANAGER」→「IP Catalog」を実行します。IPカタログ内の「Memories & Storage Elements」→「RAMs & ROMs & BRAM」→「Block Memory Generator」をダブルクリックします。

図9:「Block Memory Generator」を実行
図9:「Block Memory Generator」を実行

BRAM生成用のウィザードではタブごとに設定を変更していきます。まず、「Basic」タブ(図10)では以下のように設定を変更します。

  • Component Name: WAVE_TBL
  • Memory Type: Single Port ROM

「Single Port ROM」とすることで、1ポートのROMとなります。RAMでもよいのですが、信号線が多くなるのでROMにしています。

図10:「Basic」タブでの設定
図10:「Basic」タブでの設定

「Port A Options」タブでは、以下のように設定を変更します。

  • Port A Depth: 2048
  • Enable Port Type: Always Enabled
  • Primitives Output Register: チェックOFF

「Port A Depth」で波形テーブルのデータ数を設定しています。「Enable Port Type」では「Always Enabled」にすることでイネーブル信号を不要としています。また「Primitives Output Register」をチェックOFFすることで出力にレジスタ(FF)を付けないようにしています。

図11:「Port A Options」タブでの設定
図11:「Port A Options」タブでの設定

「Other Options」タブでは、以下のように設定を変更します。

  • Load Init File: チェックON
  • Coe File: sin_gen/ROMDATA/SinTbl.coe を選択

これで、SinTbl.coeに記述してあるデータがBRAMにロードされます。

図12:「Other Options」タブでの設定
図12:「Other Options」タブでの設定

最後に、「Summary」タブで設定を確認します。以下のようになっていることを確認してください。

  • Memory Type: Single Port ROM
  • Block RAM resource(s)(18K BRAMs): 0
  • Block RAM resource(s)(36K BRAMs): 1
  • Total Port A Read Latency: 1 Cock Cycle(s)
  • Address Width A: 11

波形テーブルのデータ数を2048にすることで、36K BRAMsをちょうど1個使うようにしています。

図13:「Summary」タブで確認
図13:「Summary」タブで確認

保存場所を確認して(図14)、「OK」をクリックします。

図14:保存場所を確認
図14:保存場所を確認

また、「Create Output Products」の画面では、「Out of context per IP」を選択して、「Generate」をクリックします。「Out of context per IP」の場合、個々のIPごとに論理合成を行うので、全体の論理合成時間が短いというメリットがあります。一方、「Global」の場合、全体の論理合成時にIPを再合成するので、論理合成時間は長いが、生成ファイルが少ないという特徴があります。

図15:「Out of context per IP」を選択
図15:「Out of context per IP」を選択

これで、「Package IP」→「Files Groups」の「Merge changes from File Groups Wizard」をクリックして(図16)、「Synthesis(3)」となれば、問題ないです。

図16:「Package IP」の「File Groups」
図16:「Package IP」の「File Groups」

ただ、基本的にはVivadoの不具合のため「Synthesis(2)」となり、WAVE_TBLが読み込まれません(図17)。

図17:「Merge changes from File Groups Wizard」した後
図17:「Merge changes from File Groups Wizard」した後

対策方法としては、一度削除し追加することです。

  1. WAVE_TBLを「Remove File from Project...」する
  2. 「PROJECT MANAGER」→「Add Sources」からWAVE_TBLを追加
  3. 「Merge changes from File Groups Wizard」をクリックすると、「synthesis(3)」となる(図19)

「Add Sources」するときは「Add or create design sources」を選択して、「sin_gen/src/WAVE_TBL/WAVE_TBL.xci」を追加してください。また、Copy sources into IP DirectoryはチェックOFFしてください(図18)。

図18:「Copy sources into IP Directory」をチェックOFF
図18:「Copy sources into IP Directory」をチェックOFF

図19:「Synthesis(3)」となる
図19:「Synthesis(3)」となる

最後に「Package IP」→「Review and Package」の「Package IP」をクリックすることでIPが作成されます(図20)。

図20:「Package IP」をクリック
図20:「Package IP」をクリック

プロジェクトを閉じていいかのダイアログが出ますので(図21)、「OK」をクリックしてください。

図21:プロジェクトを閉じるかのダイアログ
図21:プロジェクトを閉じるかのダイアログ

以上でIPが作成され、元のVivadoプロジェクト画面に戻ります。

IP のシミュレーション

「sin_wave_gen」プロジェクトで IP「sin_gen」のシミュレーションをしていきます。

IP インテグレータで回路作成

IPインテグレータで回路を作っていきます。

  1. 「IP INTEGRATOR」→「Create Block Design」で「Diagram」を作成
  2. 「Add IP」で「sin_gen_v1_0」を追加
  3. 「sin_gen_v1_0」の全てのポートに対して「Make External」を実行
  4. ポート名を「_0」がないように変更

最終的な回路は図22のようになります。

図22:最終的な IP インテグレータ
図22:最終的な IP インテグレータ

回路ができたら、「Validate Design」をして、回路のチェックを行います。また、「Create HDL Wrapper」でラッパーを作成します。いつも通り「Copy generated wrapper to allow user edits」にしてください。

テストベンチの追加

回路のシミュレーションを行うためのファイルであるテストベンチをプロジェクトに追加します。追加するテストベンチ「sim_sin_gen.sv」はGitHubにあります。

このテストベンチをプロジェクトディレクトリの中に置いて、読み込みます。読み込む手順は以下です。

  1. 「PROJECT MANAGER」→「Add Sources」を実行
  2. 「Add or create simulation sources」を選択
  3. 「Add Files」で「sim_sin_gen.sv」を選択
  4. 「Scan and add RTL include files into project」と「Include all design sources for simulation」をチェックON(図23)
  5. 「Finish」をクリックする

図23:ファイル追加時のチェック
図23:ファイル追加時のチェック

シミュレーションの実行

シミュレーションを実行をします。シミュレーションは「SIMULATION」→「Run Simulation」→「Run Behavioral Simulation」で実行できます(図24)。シミュレーションファイルにエラーがある場合は、実行できないので気をつけてください。

図24:シミュレーションの実行
図24:シミュレーションの実行

シミュレーションの実行が上手くいくと図25のようになります。1nsまではシミュレーションが実行されています。「Run All」ボタンをクリックすることで、全てのシミュレーションが実行されます。

図25:シミュレーション時の画面
図25:シミュレーション時の画面

今回の場合は80msまで実行されます。「Zoom Fit」をクリックすることで、図26のように全体の結果をみることができます。

図26:全体のシミュレーション結果
図26:全体のシミュレーション結果

このままでは、doutが正弦波になっているかわかりづらいので、表示の仕方を以下のように変更します。

  • 「dout」を右クリックして、「Waveform Style」→「Analog」を選択
  • 「Waveform Style」→「Analog Setting」で「Y Range」を「Fixed」にして、Minを-18000、Maxを18000に変更(図27)
  • 「dout」を右クリックして、「Radix」→「Signed Decimal」を選択

図27:「Analog Setting」の画面
図27:「Analog Setting」の画面

表示の仕方を変更すると図28のようになります。正弦波になっていることがわかると思います。

図28:設定変更後の結果
図28:設定変更後の結果

おわりに

今回で正弦波を作成することができました。次回はパラシリ変換器を作成してスピーカーから音を出したいと思います。また、SystemVerilogの説明を全くしていないので、補足記事を書きたいと思います。

ZYBOでシンセサイザー作成(2)オーディオコーデックの使用

今回から徐々にシンセサイザーの作成を始めます。今回は、IICでオーディオコーデック(SSM2603)のレジスタ設定をして、コーデックで音を折り返します。

環境

  • パソコン:Windows10 64 bit
    • Vivado 2020.2 をインストール
    • Xilinx Vitis 2020.2 をインストール
  • ボード:Zybo Z7-20
  • 入力デバイス:パソコンのライン出力 or マイク(ELECOM HS-MC06BK)
  • 出力デバイス:イヤホン or スピーカー

システムの構成

今回作成するシステムの構成が図1です。「AXI GPIO」については省略しています。赤線が音データの経路です。「AXI IIC」を追加して、IIC通信で音を折り返すようにコーデック(SSM2603)を操作します。ADCとDACの電源は切っておきます。今回はDACを使いませんので、MUTEは使えませんが(DACの出力をミュートする機能であるため)、ミュート用のスイッチも次回のために付けておきます。

図1:音を折り返すシステムの構成
図1:音を折り返すシステムの構成

AXI IICとSWの追加(Vivado)

前回作成したLチカとHelloworldの回路に「AXI IIC」と「SW」の回路を追加します。

AXI IICの追加

「Open Block Design」で前回作成したダイアグラムを開きます。ダイアグラムが開いたら、「Add IP」で「AXI IIC」 を追加します(図2)。

図2:AXI IICの追加
図2:AXI IICの追加

追加したら、「Run Connection Automation」をクリックして自動接続をします。「IIC」と「S-AXI」をチェックONします。また「IIC」を選択して、「Select Board Part Interface」を「Custom」にして(図3)、「OK」をクリックします。
自動接続後は、ダイアグラムは図4のようになります。

図3:AXI IIC の自動接続の設定
図3:AXI IIC の自動接続の設定

図4:AXI IICの自動接続後のダイグラム
図4:AXI IICの自動接続後のダイグラム

つづいて、IICの出力ポート名を「iic_rtl」→「iic」に変更しておきます(図5)。
これで「AXI IIC」の接続は完了です。

図5:IICポート名の変更
図5:IICポート名の変更

注意:前回の記事のように「AXI IIC」の「+」部分を展開して、「Make External」をしてしまうと、双方向通信のためのIOBUFがラッパーファイルに追加されないため、自動接続の際にポートも作っています。

ミュート用のスイッチ(SW)追加

スイッチの追加といっても、スイッチの入力ポートとMUTE用の出力ポートをつなぐだけです。まず、ダイアグラムの何もないところで右クリックして、「Create Port...」を選択します(図6)。

図6:「Create Port...」コマンド
図6:「Create Port...」コマンド

スイッチの入力ポートは以下のように設定して(図7)、入力ポートを作成します。

  • Port Name:sw
  • Direction:Input
  • Type:Other
  • Create vector:チェックON   from 0 to 0

図7:「swポートの設定」
図7:「sw」ポートの設定

ミュートの出力ポートは以下のように設定して(図8)、出力ポートを作成します。

  • Port Name:ac_muten
  • Direction:Output
  • Type:Other
  • Create vector:チェックOFF

図8:「ac_muten」ポートの設定
図8:「ac_muten」ポートの設定

ポートが作成できたら、入力ポート「sw[0:0]」と出力ポート「ac_muten」を接続します(図9)。

図9:最終的なダイアグラム
図9:最終的なダイアグラム

ダイアグラムが作り終わったら、「Validate Design」で回路のチェックを行います。つづいて、Sourcesペインで「design_1」を右クリックして、「Create HDL Wrapper...」を選択します。ラッパーを作成するときは、「Copy generated wrapper to allow user edits」を選択します。

制約ファイルの修正

制約ファイル(*.xdc)の修正をします。作成したラッパー(design_1_wrapper.v)をみると、以下のような記述があります。

output [0:0]ac_muten;
inout iic_scl_io;
inout iic_sda_io;
output [3:0]led;
input [0:0]sw;

「led」以外が追加されたポートなので、4つのポートの定義を行います。

swのポート定義は12行目付近にありますので、「sw[0]」の定義のコメントを外します。

##Switches
set_property -dict { PACKAGE_PIN G15   IOSTANDARD LVCMOS33 } [get_ports { sw[0] }]; #IO_L19N_T3_VREF_35 Sch=sw[0]
#set_property -dict { PACKAGE_PIN P15   IOSTANDARD LVCMOS33 } [get_ports { sw[1] }]; #IO_L24P_T3_34 Sch=sw[1]
#set_property -dict { PACKAGE_PIN W13   IOSTANDARD LVCMOS33 } [get_ports { sw[2] }]; #IO_L4N_T0_34 Sch=sw[2]
#set_property -dict { PACKAGE_PIN T16   IOSTANDARD LVCMOS33 } [get_ports { sw[3] }]; #IO_L9P_T1_DQS_34 Sch=sw[3]

オーディオコーデックに関するポートの定義は44行目付近にありますので、「ac_muten」と「ac_scl」と「ac_sda」のコメントを外します。ポート名は「ac_scl」→「iic_scl_io」、「ac_sda」→「iic_sda_io」と変更します。

##Audio Codec
#set_property -dict { PACKAGE_PIN R19   IOSTANDARD LVCMOS33 } [get_ports { ac_bclk }]; #IO_0_34 Sch=ac_bclk
#set_property -dict { PACKAGE_PIN R17   IOSTANDARD LVCMOS33 } [get_ports { ac_mclk }]; #IO_L19N_T3_VREF_34 Sch=ac_mclk
set_property -dict { PACKAGE_PIN P18   IOSTANDARD LVCMOS33 } [get_ports { ac_muten }]; #IO_L23N_T3_34 Sch=ac_muten
#set_property -dict { PACKAGE_PIN R18   IOSTANDARD LVCMOS33 } [get_ports { ac_pbdat }]; #IO_L20N_T3_34 Sch=ac_pbdat
#set_property -dict { PACKAGE_PIN T19   IOSTANDARD LVCMOS33 } [get_ports { ac_pblrc }]; #IO_25_34 Sch=ac_pblrc
#set_property -dict { PACKAGE_PIN R16   IOSTANDARD LVCMOS33 } [get_ports { ac_recdat }]; #IO_L19P_T3_34 Sch=ac_recdat
#set_property -dict { PACKAGE_PIN Y18   IOSTANDARD LVCMOS33 } [get_ports { ac_reclrc }]; #IO_L17P_T2_34 Sch=ac_reclrc
set_property -dict { PACKAGE_PIN N18   IOSTANDARD LVCMOS33 } [get_ports { iic_scl_io }]; #IO_L13P_T2_MRCC_34 Sch=ac_scl
set_property -dict { PACKAGE_PIN N17   IOSTANDARD LVCMOS33 } [get_ports { iic_sda_io }]; #IO_L23P_T3_34 Sch=ac_sda

修正が終わったら、制約ファイルを保存してビットストリームを作成します。ビットストリームが作成できたら、「Export Hardware Platform」でVitisに渡す回路情報を作成します。

IICでコーデックのレジスタ設定(Vitis)

Vitisを起動して、IICでコーデックのレジスタ設定を行います。

プラットフォームプロジェクトの更新

ハードウェアが更新されたので、Vitisにそれを反映させます。Vitisでプラットフォームプロジェクトを選択して、マウス右ボタンから「Update Hardware Specification」を実行します(図10)。プラットフォームファイルを選択して、「OK」をクリックします(図11)。

図10:「Update Hardware Specification」コマンド
図10:「Update Hardware Specification」コマンド

図11:プラットフォームファイルの選択
図11:プラットフォームファイルの選択

あとは、アプリケーションプロジェクトを選択してビルドすれば、プラットフォームプロジェクトも再ビルドされます。

アプリケーションプロジェクトは前回「helloworld」という名前で作りましたが、今回からシンセサイザーをつくっていくので、「zybo_synthesizer」という名前で作りなおしました。テンプレートは前回と同じ「Empty Application」です。

プログラムの作成

プログラム名を「main.c」として、ソースコードを追加します。追加したら、以下のプログラムを記述します。「xiic.h」が定義されてないというエラーが出る場合は、プラットフォームプロジェクトが更新されていないので、更新しておいてください。記述したら、プロジェクトをビルドしてください。

#include "xparameters.h"
#include "xiic.h"
#include "xil_printf.h"
#include <sleep.h>

enum adauRegisterAddresses {
    R0_LEFT_ADC_VOL     = 0x00,
    R1_RIGHT_ADC_VOL    = 0x01,
    R2_LEFT_DAC_VOL     = 0x02,
    R3_RIGHT_DAC_VOL    = 0x03,
    R4_ANALOG_PATH      = 0x04,
    R5_DIGITAL_PATH     = 0x05,
    R6_POWER_MGMT       = 0x06,
    R7_DIGITAL_IF       = 0x07,
    R8_SAMPLE_RATE      = 0x08,
    R9_ACTIVE           = 0x09,
    R15_SOFTWARE_RESET  = 0x0F,
    R16_ALC_CONTROL_1   = 0x10,
    R17_ALC_CONTROL_2   = 0x11,
    R18_ALC_CONTROL_2   = 0x12
};

int CodecWrite(XIic*, u8 Address, u16 data);
XStatus CodecInit(XIic *Iic);

int main(void){
    XIic Iic;
    int status;
    xil_printf("IIC Start\n");

    // Codec Initialization
    status = CodecInit(&Iic);
    if(status != XST_SUCCESS) {
        xil_printf("Error Codec Initialization");
        return XST_FAILURE;
    }
    xil_printf("IIC Finished\n");
    return XST_SUCCESS;
}

// Write Audio Codec Register
int CodecWrite(XIic* Iic, u8 Address, u16 data){
    u8 Device_Address = 0x1A;       // Device ID
    UINTPTR BaseAddress = Iic->BaseAddress; // AXI IIC BaseAddress
    int num;                        // Number of Data sent

    // set write data
    u8 WR_data[2];
    Address = ((Address<<1) & 0xFE);
    WR_data[0] = Address + ((data>>8)&0x01);
    WR_data[1] = (data&0xFF);

    // send data
    num = XIic_Send(BaseAddress, Device_Address, WR_data, 2, XIIC_STOP);
    if(num!=2){
        xil_printf("Writing data Failed\r\n");
        return XST_FAILURE;
    }
    return XST_SUCCESS;
}

// Audio Codec Initialization
XStatus CodecInit(XIic* Iic){
    int status;

    // Initializes XIic instance.
    status = XIic_Initialize(Iic, XPAR_IIC_0_DEVICE_ID);
    if (status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    // Codec register settings
    CodecWrite(Iic, R6_POWER_MGMT,    0x3D); // power on
    CodecWrite(Iic, R0_LEFT_ADC_VOL,  0x97);
    CodecWrite(Iic, R1_RIGHT_ADC_VOL, 0x97);
    CodecWrite(Iic, R2_LEFT_DAC_VOL,  0x79);
    CodecWrite(Iic, R3_RIGHT_DAC_VOL, 0x79);
    CodecWrite(Iic, R4_ANALOG_PATH,   0x2B);
    CodecWrite(Iic, R5_DIGITAL_PATH,  0x08);
    CodecWrite(Iic, R7_DIGITAL_IF,    0x0A);
    CodecWrite(Iic, R8_SAMPLE_RATE,   0x00);
    usleep(80000);  // wait for charging capacity on the VMID pin
    CodecWrite(Iic, R9_ACTIVE,        0x01);  // digital core active
    CodecWrite(Iic, R6_POWER_MGMT,    0x2D);
    return XST_SUCCESS;
}

プログラムの説明

わかりにくいと思われるプログラム箇所について説明していきたいと思います。

レジスタアドレスとレジスタの役割の関連づけ

レジスタの役割がわかりにくいので、列挙型(enum)を使ってレジスタアドレスとレジスタの役割を関連付けています。

enum adauRegisterAddresses {
    R0_LEFT_ADC_VOL     = 0x00,
    R1_RIGHT_ADC_VOL    = 0x01,
    ︙
    R18_ALC_CONTROL_2   = 0x12
};

メイン関数

メイン関数では、大まかに以下のようなことを行っています。

  1. 「IIC Start」をシリアル転送
  2. 「CodecInit」関数を用いて、オーディオコーデックで音を折り返すように設定
  3. 「IIC Finished」をシリアル転送

1と3のシリアル転送についてはプログラムが動いたことをわかりやすくするために、記述しています。

コーデックの設定箇所は以下です。

// Codec Initialization
status = CodecInit(&Iic);
if(status != XST_SUCCESS) {
    xil_printf("Error Codec Initialization");
    return XST_FAILURE;
}

オーディオコーデックを初期化して、失敗したら中断するプログラムとなっています。

コーデックの初期化(CodecInit関数)

「CodecInit」関数では、大まかに以下のようなことを行っています。

  1. XIicインスタンス変数の初期化
  2. コーデックの各レジスタの書き込み

XIicインスタンス変数を初期化している箇所は以下です。

// Initializes XIic instance.
status = XIic_Initialize(Iic, XPAR_IIC_0_DEVICE_ID);
if (status != XST_SUCCESS) {
    return XST_FAILURE;
}

この記述でデバイスIDに基づいて、XIicインスタンス変数の「Iic」に「AXI IIC」の設定やアドレスなどが代入されます。XIic_Initialize 関数は Xilinx によって用意されている API 関数です。

コーデックのレジスタを書き込んでいる箇所は以下です。

 // Codec register settings
    CodecWrite(Iic, R6_POWER_MGMT,    0x3D); // power on
    ︙
    CodecWrite(Iic, R4_ANALOG_PATH,   0x2B);
    ︙
    usleep(80000);  // wait for charging capacity on the VMID pin
    CodecWrite(Iic, R9_ACTIVE,        0x01);  // digital core active
    CodecWrite(Iic, R6_POWER_MGMT,    0x2D);
    return XST_SUCCESS;
}

レジスタの設定はSSM2603のデータシートにおける「CONTROL REGISTER SEQUENCING」の手順どおりに行っています。 その手順が以下です。

  1. R6レジスタを書き込んで、「Out」ビットを除いて、使用するものに電源を入れる
  2. 設定が必要なレジスタに書き込む
  3. カップリングコンデンサに電源が供給されるまで待つ
  4. R9レジスタの「active」ビットとR6レジスタの「Out」ビットをセットする

1に関する記述箇所は以下です。

CodecWrite(Iic, R6_POWER_MGMT,    0x3D); // power on

モードを「Sidetone 」として使うので、R6レジスタの「MIC」、「CLKOUT」、「PWROFF」ビットを「0」にして電源ONします。

2に関する記述箇所は以下です。

CodecWrite(Iic, R4_ANALOG_PATH,   0x2B);

R4レジスタで「SIDETONE_EN」と「MICBOOST」をデフォルト値から変更しています。 それぞれのビットの説明は以下です。

  • SIDETONE_EN:「1」でサイドトーンを可能にさせる
  • MICBOOST:「1」でマイクのゲインを20dB上げる

「MICBOOST」を使わないと、音の折り返しがあるかわかりづらいので、「MICBOOST」をセットしました。 また、他のレジスタも書き込んでいますが、デフォルト値から変更していません。次回、音を再生するために書いておきました。

3に関する記述箇所は以下です。

usleep(80000);  // wait for charging capacity on the VMID pin

usleep関数を使って、80000 us 待機しています。

データシートによると

\displaystyle{
t = C \times 25000 / 3.5
}

だけ待機すればよいということでした。 CはVMINピンとGNDの間にあるコンデンサの容量です。ZYBO Z7-20の回路図を見ると、100nFと10uFのコンデンサがありますので、72142us だけ待てばよいということになります。念のために80000 us (80 ms) 待っています。

4に関する記述箇所は以下です。

CodecWrite(Iic, R9_ACTIVE,        0x01);  // digital core active
CodecWrite(Iic, R6_POWER_MGMT,    0x2D);

R9レジスタに0x01を書き込んで、R6レジスタを0x3D→0x2Dに変更することでオーディオコーデックを動作させています。

コーデックのレジスタの書き込み(CodecWrite関数)

レジスタにデータを書き込むには図12のようにしてデータを送信します。

図12:書き込みシーケンス
図12:書き込みシーケンス(SSM2608のデータシートから引用)

  • S/P = START/STOP BIT.
  • A0 = I2C R/W BIT.
  • A(S) = ACKNOWLEDGE BY SLAVE.

DEVICE ADDRESSの設定は以下で記述しています。

u8 Device_Address = 0x1A;       // Device ID

SSM2603のDEVICE ADDRESSは「CSB」ピンが「L」ならば0x1A (0b0011010)、「H」ならば0x1B (0b0011011)です。ZYBO z7の回路図を見ると、CSBピンはGNDに接続されていますので、0x1Aとしています。

[B0-B15]のRESISTER ADDRESSとRESISTER DATAの設定は以下で記述しています。

// set write date
u8 WR_data[2];
Address = ((Address<<1) & 0xFE);
WR_data[0] = Address + ((data>>8)&0x01);
WR_data[1] = (data&0xFF);

シフトとマスクを使って、アドレスとデータを形式にあうようにセットしています。

IICを使用した送信は以下の記述で行っています。

// send data
num = XIic_Send(BaseAddress, Device_Address, WR_data, 2, XIIC_STOP);

XIic_Send 関数は Xilinx によって用意されている API 関数です。

各引数は以下のようになっています。

  1. UINTPTR BaseAddress:AXI IICのベースアドレス
  2. u8 Address:デバイスID
  3. u8 * BufferPtr:送信するデータのアドレス
  4. unsigned ByteCount:送信するデータの数
  5. u8 Option:送信後の動作

プログラムの実行

プログラムの実行前に以下のことを行います。

  • ZyboのMIC INにマイクまたはPCのライン出力を接続
  • ZyboのHPH OUTにスピーカーまたはイヤホンを接続
  • ZyboとPCをUSBで接続
  • シリアルポートを接続(面倒くさい場合はしなくてもいいです)

MIC INにPCのライン出力を接続する場合は、MICBOOSTで音量がかなり大きくなるのでPCの音量を下げておいてください。自分は爆音でビビりました。

前回は「Debug」で回路構築してから、「resume」でプログラムを実行しましたが、「Run」をクリックすることで回路構築とプログラムの実行が同時に行うことができます(図13)。

図13:「Run」でプログラムの実行
図13:「Run」でプログラムの実行

マイクをつないでいる場合は、声を出したりするとスピーカーから音声が返ってきます(図14)。また、PCのライン出力につないだ場合も、Youtubeなどを再生するとスピーカーから音が返ってきます。

図14:音声を折り返している様子
図14:音声を折り返している様子

おわりに

今回で、オーディオコーデックを使うことができました。次回はBRAMを使用して正弦波合成を行い、シミュレーションで確認しようと思います。

参考文献

ZYBOでシンセサイザー作成(1)LチカとHelloWorld

ZYBOを使ってシンセサイザーを作成したいと思います。久しぶりにVivadoを使うので、まずはLチカとHello Worldからやってみます。主に今回の記事は以下の参考文献をもとに書きました。

環境

  • パソコン:Windows10 64 bit
    • Vivado 2020.2 をインストール
    • Xilinx Vitis 2020.2 をインストール
  • ボード:ZYBO Z7-20

Board ファイルのインストール

初期状態ではDigilentのボードであるZYBO-z7-20のボードファイルがないため、ボードファイルをインストールします。DigilentのボードファイルのダウンロードはInstalling Digilent Board Filesの「archive」をクリックすることでダウンロードすることができます。そのzipファイルを解凍して、そのフォルダの中の

「vivado-boards-master\new\board_files\zybo-z7-20」

のフォルダを

「C:\Xilinx\Vivado\<version>\data\boards\board_files」

の下に貼り付けすることでインストールできます。

プロジェクトの作成

まず、「Create New Project」をクリックして、プロジェクトを新規に作成します。

プロジェクトの作成画面1

「Create a New Vivado Project」はプロジェクトを作成するためにやることが書いてあるだけなので、そのまま「Next」をクリックします。

プロジェクトの作成画面2

「Project Name」では、プロジェクトの名前とプロジェクトの格納フォルダを指定します。私の場合は、

  • プロジェクト名:zybo_synthesizer
  • プロジェクトの格納フォルダ:D:/v20

として、「Create project subdirectory」にチェックしました。「Create project subdirectiory」にチェックONすることで、プロジェクトの格納フォルダの下にプロジェクト名のフォルダが作成され、その下にプロジェクトファイルが作成されます。

プロジェクトの作成画面3

「Project type」では、作成するプロジェクトのタイプを指定します。ここでは「RTL Project」を選択します。「Do not specify sources at this time」にもチェックONしておきます。チェックONしておくと、verilogファイルなどのソースファイルを追加する画面を省略します。あとで追加することもできるので基本的にチェックONしておきます。

プロジェクトの作成画面4

「Default Part」では、使用するFPGAバイスを選択します。ボードファイルのインストールをしていれば、「Boards」を選択することで、「Display Name」の中に「Zybo Z7-20」があると思います。「Zybo Z7-20」を選択して、「Next」をクリックします。

プロジェクトの作成画面5

「New Project Summary」では、プロジェクトの設定内容を確認して、問題なければ「Finish」をクリックします。

プロジェクトの作成画面6

回路の作成

Verilogファイルは書かないで、VivadoのIPインテグレータだけを使って、回路の作成をします。

IP インテグレータによる回路作成

IP インテグレータの起動

Flow Navigator から 「IP INTEGRATOR」の「Create Block Design」 をクリックします。 小さいウィンドウが出て、「Design Name」 や 「Directory」を変えることができますが、デフォルトのままでいいです。これで「Diagram」のタブができます。「Diagram」で GUI 的に回路作成ができます。

インテグレータによる回路作成1

IP の配置

Diagramで「ADD IP」をクリックすると、IP(機能ごとにまとまった回路)のリストが表示されますので、「ZYNQ7_Processing_System」をダブルクリックして配置します。また、同じようにして「AXI GPIO」も配置します。

インテグレータによる回路作成2

IP の接続

IPを配置したら、「Run Block Automation」をクリックします。Zynq の設定が表示されますので、「Apply Board Preset」にチェックがあることを確認して、「OK」をクリックします。「Apply Board Preset」にチェックONすることで、ボード上にあるDDRメモリなどにしたがって Zynq の設定をしてくれます。

インテグレータによる回路作成3

「AXI GPIO」をダブルクリックして、「AXI GPIO」の設定をします。「IP Configuration」タブで「All Outputs」にチェックをして、「GPIO Width」を 4 と設定します。「OK」をクリックして設定を終了します。

インテグレータによる回路作成4

「Run Connection Automation」をクリックして、Zynq と「AXI GPIO」を自動接続をします。接続の設定では、「GPIO」はチェックOFFし、「S-AXI」にはチェックONして、「OK」をクリックします。

インテグレータによる回路作成5
インテグレータによる回路作成6

「Run Connection Automation」を実行すると、「Processor System Reset」(リセット信号を生成する回路)と「AXI Interconnect」(AXI のハブ的な回路)が自動生成されて、IP間が接続されます。

インテグレータによる回路作成7

つづいて、「AXI GPIO」の「GPIO」端子の「+」部分をクリックすると、端子が展開されて「gpio_io_o[3:0]」が表示されます。これを右クリックして、メニューから「Make External」を実行することで、「AXI GPIO」の出力とFPGAの出力ポートを接続します。

インテグレータによる回路作成8

これで回路ができました。

制約ファイル(*.xdc)の読み込みと修正

制約ファイル(*.xdc)のダウンロード

FPGA端子の設定をする制約ファイルをダウンロードします。ZYBO Z7用の xdc ファイルは、Digilentページの「Documentaion」→「Master XDC Files」をクリックすると、Digilentの各ボードの制約ファイルがあるGItHubにとぶので、そこからZybo-Z7-Master.xdcをダウンロードします(私は一つのファイルだけダウンロードすることはできなかったので、他のボードの制約ファイルと一緒にダウンロードしました)。

制約ファイル(*.xdc)の読み込み

制約ファイルをプロジェクトに読み込むには、「Project MANAGER」の「Add Sources」をクリックし、「Add or create constraints」を選択します。続いて、さきほどダウンロードしたZybo-Z7-Master.xdcをプロジェクトファイル(*.xpr)と同じ階層にコピーして、それを読み込みます。「Copy constraints files into project」はチェックOFFにしておきます。チェックONにすることで、プロジェクトフォルダのどこかに制約ファイルがコピーされて置かれますが、深い階層に置かれてしまって、制約ファイルの修正や確認がしづらくなるので、私はこのようにしています。「Finish」で制約ファイルが読み込まれます。

制約ファイルの修正

次に、制約ファイルの修正をします。今回はLEDを使いたいので、LED設定箇所の27行目から30行目のコメントを以下のように外します。

##LEDs
set_property -dict { PACKAGE_PIN M14   IOSTANDARD LVCMOS33 } [get_ports { led[0] }]; #IO_L23P_T3_35 Sch=led[0]
set_property -dict { PACKAGE_PIN M15   IOSTANDARD LVCMOS33 } [get_ports { led[1] }]; #IO_L23N_T3_35 Sch=led[1]
set_property -dict { PACKAGE_PIN G14   IOSTANDARD LVCMOS33 } [get_ports { led[2] }]; #IO_0_35 Sch=led[2]
set_property -dict { PACKAGE_PIN D18   IOSTANDARD LVCMOS33 } [get_ports { led[3] }]; #IO_L3N_T0_DQS_AD1N_35 Sch=led[3]

ポート名の変更

制約ファイル上でポート名が「led」になっているので、IPインテグレータでもポート名を「led」にします。「gpio_io_o_0」を選択して、「External Port Properties」ペインで「Name」を「led」に変更します。

ポート名を変更

上位階層とビットストリームの作成

ダイアグラムのチェック

ダイアグラムが完成したら、右クリックのメニューから「Validate Design」を実行して、整合性のチェックをします。回路に問題がなければ、「Validation successful.」を表示します。

Validation Successful

もしかしたら、以下のようなクリティカルワーニングが発生するかもしれませんが、Digilentのボードファイルによる問題なので無視しても大丈夫です。

CRITICAL WARNING: [PSU-1]  Parameter : PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_0 has negative value -0.050 . PS DDR interfaces might fail when entering negative DQS skew values. 
CRITICAL WARNING: [PSU-2]  Parameter : PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_1 has negative value -0.044 . PS DDR interfaces might fail when entering negative DQS skew values. 
CRITICAL WARNING: [PSU-3]  Parameter : PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_2 has negative value -0.035 . PS DDR interfaces might fail when entering negative DQS skew values. 
CRITICAL WARNING: [PSU-4]  Parameter : PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_3 has negative value -0.100 . PS DDR interfaces might fail when entering negative DQS skew values. 

上位階層の作成

Sourcesペインにおいて、ダイアグラムで作成した階層「design_1」を選択し、右クリックから「Create HDL Wrapper...」を実行します。「Create HDL Wrapper...」によって、ダイアグラムで作った回路を使用するVerilogファイルが作成されます。「Options」では、「Copy generated wrapper to allow user edits 」を選択します。「Let Vivado manage wrapper and auto-update」を選択すると、自動的にラッパーが更新されるそうですが、経験的にポートが追加されないことがあったので、「Copy generated wrapper to allow user edits 」を選択します。

ビットストリームの作成

「PROGRAM AND DEBUG」の「Generate Bitstream」をクリックして、ビットストリームを作成します。論理合成と配置・配線をしていないので、「No Implementation Results Available」のダイアログが出ますが、「Yes」をクリックします。「Launch Runs」では、そのまま「OK」をクリックします。Number of jobsで使用するスレッドの数を指定できますが、デフォルトのままでいいと思います。

正常終了すると、右上のステータスが「write bitstream Complete」となり、次の処理を確認するダイアログが表示されます。「Open Implemented Design」を選択すると、回路規模などを確認することができます。普段、私は「Open Implemented Design」で回路規模を確認することが多いです。回路規模に興味がない人は「Cancel」をクリックで大丈夫です。

Bitstream_Generation_Completed

Zynq のプログラムを実行

Vitisを使って、Zynqのプログラムを実行していきます。

回路情報のコピーとVitis の起動

Vitisを起動する前にVivadoからVitisに対して回路情報を渡す必要があります。そのため、Vivadoでその作業をしてから、Vitis を起動します。

回路情報のコピー

メニューバーから「File」→「Export」→「Export Hardware...」を実行します。最初の画面では、「Next」をクリックします。

エクスポート画面1

「Output」では「Include bitstream」を選択します。これで、Vitisに回路情報を渡すことができます。

エクスポート画面2

「Files」では以下のように設定しました。

  • 名称:design_1_wrapper(デフォルト設定)
  • 保存先:D:/v20/zybo_synthesizer/zybo_synthesizer.vitis
エクスポート画面3

最後に「Finish」をクリックしてエクスポートします。

エクスポート画面4

Vitisの起動

メニューバーから「Tools」→「Launch Vitis IDE」でVitisを起動します。

Zynqのプログラム実行1

ワークスペースの場所は先ほど作成したzybo_synthesizer.vitisフォルダを指定し、「Launch」をクリックします。

Zynqのプログラム実行2

各種プロジェクトの作成

Vitisでは「プラットフォームプロジェクト」と「アプリケーションプロジェクト」の作成をする必要がありますので、これらのプロジェクトを作成していきます。

プラットフォームプロジェクトの作成

起動画面から「Create Platform Project」をクリックします。

Zynqのプログラム実行3

最初の画面では、プロジェクト名を入力します。プロジェクト名は「zybo_synthesizer_platform」としました。

Zynqのプログラム実行4

つぎは「Create a new platform from hardware (XSA)」タブで、Vivadoで作成したプラットフォームファイル(*.xsa)を指定します。「Finish」でプロジェクトが作成されます。

Zynqのプログラム実行5

アプリケーションプロジェクトの作成

メニューバーから「File」→「New」→「Application Project...」を実行します。

Zynqのプログラム実行6

最初の画面では、そのまま「Next」をクリックします。

Zynqのプログラム実行7

次の画面では、プラットフォームの選択をします。デフォルトでさきほど作成したプラットフォームが設定されていますので、確認してそのまま「Next」をクリックします。

Zynqのプログラム実行8

つづいて、アプリケーションプロジェクト名を入力する画面になります。アプリケーションプロジェクト名は「helloworld」としました。

Zynqのプログラム実行9

ドメインの選択では、そのまま「Next」をクリックします。

Zynqのプログラム実行10

テンプレートを選択する画面では、「Empty Application」を選択して、「Finish」をクリックします。

Zynqのプログラム実行11

プログラムの作成と実行

プログラムをC言語で作成して、シリアルターミナルなどを設定して、プログラムを実行します。

Cファイルの作成

アプリケーションプロジェクト内の「src」フォルダを右クリックして、「New」→「File」をクリックします。ファイル名を入力する画面では、ファイル名を「helloworld.c」として、「Finish」をクリックします。

Zynqのプログラム実行12
Zynqのプログラム実行13

プログラムの記述

プログラムはAPIを用いて記述します。「helloworld.c」に以下のプログラムを記述します。一応、プログラムが記述された「helloworld.c」はGitHubの「1st」フォルダに置いてあります(2021/5/12 追記)。

#include "xparameters.h" // 各IPのパラメータがあるファイル
#include "xgpio.h"       // AXI GPIO用のAPIがあるファイル
#include "xil_printf.h"  // xil_printfを使うためのファイル

int main(void){
    int status;
    XGpio Gpio;

    // Hello World をシリアル転送
    xil_printf("Hello World. \n");

    /* GPIO の初期化 */
    // Gpio変数の初期設定
    status = XGpio_Initialize(&Gpio, XPAR_GPIO_0_DEVICE_ID);
    if(status != XST_SUCCESS) return XST_FAILURE;

    // 入出力の設定
    // 2番目の引数:設定するインターフェース  1:GPIO 2:GPIO2
    // 3番目の引数:各ポートの入出力の設定  0:出力  1:入力
    XGpio_SetDataDirection(&Gpio, 1, 0x0); // 全て出力に設定

    // 信号レベルの設定
    // 2番目の引数:設定するインターフェース  1:GPIO 2:GPIO2
    // 3番目の引数:各ポートの出力レベルの設定   0:Low  1:High
    XGpio_DiscreteWrite(&Gpio, 1, 0xF); // 全てHighに設定
}

ビルド

アプリケーションプロジェクトの「helloworld」を選択して、メニューバーから「Project」→「Build Project」を実行して、プロジェクトのビルドを行います。

プロジェクトのビルド

PCとボードの接続

ZYBOのボード上のジャンパーピンを以下のように設定します。

ジャンパーピンの設定

こうすることで以下のように設定されます。

  • 電源供給方法:USBから供給
  • 回路情報やプログラムの実行方法:USB経由でPCから実施

この状態でPCとZYBOをMicro-USBで接続すると、赤色のLEDが点灯します。

デバッグの設定

デバッグ設定は、アプリケーションプロジェクト「helloworld」を選択して、虫マークのプルダウンメニューから「Debug As」→「Lunch on Hardware (Single Application Debug)」で実行する。

デバッグ設定

実行すると、FPGAに回路情報を渡して、デバッグ時の操作画面に変更されます。

FPGAプログラム中

シリアルターミナルの設定

シリアルターミナルの設定は、「Vitis Serial Terminal」タブの「+」をクリックし、COMポートと通信速度を設定して「OK」をクリックします。基本的には通信速度はデフォルトで、ポートは選択可能なものを選べばいいと思います。

シリアルターミナル
シリアルターミナルの設定

プログラムの実行

プログラムの実行は「Resume」ボタンをクリックすることで行われます。

resumeボタン

実行すると、シリアルターミナルには「Hello World」と表示されます。

シリアルターミナルの様子

また、4つのLEDが以下のように点灯します。

ZYBOがLEDを点灯している様子

おわりに

ZYBOでLチカとHello Worldをしました。まだ、Vitisに慣れないので、慣れていきたいと思います。

マイクロマウス(旧ハーフサイズ)始めました

マイクロマウス(旧:ハーフサイズ)を始めました。学部生のころ、マイクロマウスを製作するサークルに入っていたのですが、全くわからなくて挫折してしまいました。ただ、いまならできるかなと思ったのでマイクロマウスを始めました。とりあえずの目標は4 x 4迷路の走破です。現在、設計までは終えていて、機械部品と基板を発注しているところです。ちなみに私は機械系ではなく情報系の人間なので、優しい目で見ていただければ幸いです。

コンセプト

コンセプトは「作りやすいマウス」です。このコンセプトのため、全日本マイクロマウス大会のテクニカルデータを見て、IC やモータなどのキーパーツについては一般に使われているものを選びました。また、オシロスコープがないため、電気的トラブルがないようにはんだ付けしやすい IC を選びました。あとは自分で加工する必要がないように機体を設計しました。

機体情報

マイクロマウスの外観
機体名 wakaba
サイズ(長さ x 幅 x 高さ) 60.0mm x 45.0mm x 14.8mm
マイコン RX631 (48pin)
モーター Didel MK06-4.5
モータードライバー DRV8836
壁センサ OSI5FU3A11C & LTR-4206E
ジャイロセンサ MPU6000
エンコーダ AS5047P
ギア比 4.44 (40 : 9)
タイヤ径 14.8mm

機械設計にはFusion 360を使いました。機体は2輪となっています。 センサーホルダーを作って、センサーを固定するつもりです。 また、磁気式エンコーダを使えるようにモーターマウントや磁石マウントを設計しました。 加えて、ホイールはギア付きホイールとなっています。調べてもモジュール0.3で穴径が大きいギアは見つからなかったので、ブログでよく見られたギア付きホイールとしました。センサーホルダー、ホイール、モーターマウント、磁石マウントはDMM.makeに発注しました。素材はアクリル(Ultra Mode)です。

回路図

マイクロマウスの回路図

回路図PDF版

回路についてはサークルの先輩の回路やブログで見られた回路、マウス本の回路を参考にして設計しました。基本的にはコンセプト通りに半田付けしやすい足があるIC を選びました。ただ、モータードライバーのDRV8836とジャイロセンサーのMPU6000は足がないため頑張るしかないです。半田付けを久しぶりにやるので、不安で仕方がありません。

配線図

マイクロマウスの配線図

配線図PDF版(縮尺:2.5倍)

ベタGNDをする前の配線となっています。基本的には0.1524mm幅で配線しました。赤外線LEDやスピーカー、モータードライバーにおける配線幅については0.3048mmや0.4064mmを使用しました。羽のように繋がっている基板は磁気式エンコーダ用の基板です。あとでニッパーでカットして、真ん中の溝に嵌めるつもりです。また、線幅を変え忘れたので見にくいと思いますが、センサーホルダーを差し込むための溝もあります。基板についてはelecrowに発注しました。

おわりに

4月から就職なので、時間がなくなりそうですが少しずつ進めていきたいと思います。また、elecrowの注文でクレジットカード情報を入力していなかったため、面倒くさいことになっているので解決したいと思います。

追記(2021/9/15)

回路基板が届いたので、全ての部品をハンダ付けしましたが、プログラムの書き込みができませんでした。その際、基板の問題かと思い、回路基板を作りなおしています。それに関する詳細の記事が以下です。

また、作り直した基板でプログラムの書き込みは上手くいったのですが、AD変換が上手くいかなかったので、基板を更に作りなおしています。それに関する詳細の記事が以下です。

Cambridge Music Technologyでマルチトラックの音源を提供しているというハナシ

はじめに

機械学習などをやっていると困るのはデータを集めることだと思います。私も音源分離の機械学習をやったことがあるのですが、データを集めるのに苦労しました。音楽データの音源分離をする場合は、データセットMUSDB18が一番有名ではないかと思います。MUSDB18は150曲(約10時間)の音楽トラックデータセットで、曲ごとにボーカル、ベース、ドラム、その他の楽器に分けられたステムデータがあります。試しにモデルを動かしてみたいときはMUSDB18だけで十分だと思いますが、もっとモデルを学習させたいときがあるかと思います。

Cambridge Music Technology

Cambridge Music Techonologyではミキシングの練習をサポートするために無料でダウンロードできるマルチトラックを提供しています。

www.cambridge-mt.com

今見たところ471曲あるそうです。ボーカルが入っていない曲もありますので、全てのデータを学習に使うことはできませんが、学習データをかなり多く増やすことができます。学習の際の注意点としては、ステムデータではなくパラデータなので、学習前に自分でステムデータにする必要がある点です。そこは、自分で頑張りましょう。

ブログに掲載することに関して

今後、このブログでは音楽データに対する処理結果を載せたいと思うのですが、なかなか掲載できる音楽データは見つかりません。そこで、Cambridge Music Techhologyのサイト運営者の方に「信号処理技術を紹介するためにCambridge Music Techologyのデータで処理した結果をブログにアップロードできないか」と質問してみました。返信としては、「教育目的に該当するので掲載しても大丈夫です」とのことでした。また、「アーティストとCambridge Music Technologyのクレジットを付けてください」とのことでした。返信がきたときは、かなり嬉しかったです。本当にありがとうございます。

音源分離や自動ミキシングなどの処理結果を掲載する際は、音楽データを使わせていただきたいと思います。

お問い合わせフォーム プライバシーポリシー

© 2021 Setoti All rights reserved.