FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

AXIのインターコネクトジェネレータを作ろう

現在設計中のCPUは、バスはAXIで、周辺のモジュールに接続される構成になっている。 この構成の場合、いろんなものをAXIで接続して、マスターが複数、スレーブが複数の中で相互に通信をするためのインタコネクトが必要になる。

Xilinxの場合は以下だ。

AXI Interconnect

こういうツールを使うのは非常に便利だと思うが、時としてかゆい所に手が届かないときがある。たとえば、特定のスレーブのFIFOの段数を変更したり、 スループットを変えてみたり。こういうのを柔軟に実現しようとなると、やはり自作したくなる。

という訳で、バスジェネレータを自作してみよう。何でもかんでも自分で作ってしまおうとする悪い癖があるが、これもAXIの勉強だと思ってやってみる。

方針としては、マスタとなるインタフェースのタイプ(Read/Write可?)、種類、あとスレーブとなるインタフェースの種類、数、タイプなどを指定すると、そのためのインタコネクトを生成する、テンプレートのようなものを構築してみる。 まずは、外側のインタフェースを構築してみよう。

github.com

まずはコンフィグできる要素として、以下を用意した。

master_if = Array [Array['IF', Array['R']],
                   Array['D',  Array['R', 'W']]]
slave_if = Array[Array['START', Array['R']],
                 Array['TEXT',  Array['R']],
                 Array['L1',    Array['R', 'W']],
                 Array['EX',    Array['R', 'W']]]

connect = Array[Array['IF', 'START', 'TEXT', 'L1', 'EX'],
                Array['D', 'START', 'TEXT', 'L1', 'EX']]

master_ifはマスタの種類、ここではIFとDを用意する(つまり、命令フェッチ用のマスタポートと、データアクセス用のマスタポートだ)。 slave_ifはスレーブの種類、ここではSTART(スタートアップROM)、TEXT(テキスト用ROM)、L1(L1RAM)、EX(外部アクセス用ポート)を用意する。 次に、connectは、マスタがどのポートに接続されているかを示している。ここでは、IFとDはSTART,TEXT,L1,EXのどれにも接続されているとする。

モジュールのインタフェース信号を出す

上記のような情報を登録すると、インタフェース信号を宣言するスクリプトを書いてみた。

ruby ./bus_if_axi.rb
less ./bus_if_axi.v
module bus_interface (
    // If_Pin "IF"
    //  If_Pin "IF": Channel ReadAddress
    input wire [4: 0] IF_SARID,
    input wire [31: 0] IF_SARADDR,
    input wire [7: 0] IF_SARLEN,
    input wire [2: 0] IF_SARSIZE,
    input wire [1: 0] IF_SARBURST,
    input wire IF_SARLOCK,
    input wire [3: 0] IF_SARCACHE,
    input wire [2: 0] IF_SARPROT,
    input wire IF_SARQOS,
    input wire IF_SARREGION,
    input wire IF_SARUSER,
    input wire IF_SARVALID,
    output wire IF_SARREADY,
    //  If_Pin "IF": Channel ReadData
    output wire [4: 0] IF_SRID,
    output wire [31: 0] IF_SRDATA,
    output wire IF_SRRESP,
    output wire IF_SRLAST,
    output wire IF_SRUSER,
    output wire IF_SRVALID,
    input wire IF_SRREADY,
    // If_Pin "D"
    //  If_Pin "D": Channel ReadAddress
    input wire [4: 0] D_SARID,
    input wire [31: 0] D_SARADDR,
    input wire [7: 0] D_SARLEN,
    input wire [2: 0] D_SARSIZE,
    input wire [1: 0] D_SARBURST,
    input wire D_SARLOCK,
    input wire [3: 0] D_SARCACHE,
    input wire [2: 0] D_SARPROT,
    input wire D_SARQOS,
    input wire D_SARREGION,
    input wire D_SARUSER,
    input wire D_SARVALID,
    output wire D_SARREADY,
    //  If_Pin "D": Channel ReadData
    output wire [4: 0] D_SRID,
    output wire [31: 0] D_SRDATA,
    output wire D_SRRESP,
    output wire D_SRLAST,
    output wire D_SRUSER,
    output wire D_SRVALID,
    input wire D_SRREADY,
    //  If_Pin "D": Channel WriteAddress
    input wire [4: 0] D_SAWID,
    input wire [31: 0] D_SAWADDR,
    input wire [7: 0] D_SAWLEN,
    input wire [2: 0] D_SAWSIZE,
    input wire [1: 0] D_SAWBURST,

こんな感じで、Verilogのインタフェースを生成するようにした。とりあえずは、外側のインタフェースはこれで完成したので、次にルータとアービタを作成していこう。