Avalon-MMスレーブとWISHBONEの変換

FPGA

OpenCoresではWISHBONEバスというインターフェース・プロトコルの使用が奨励されており、 また、実際にWISHBONEバスが多く使われています。このため、OpenCoresのIPコアをQuartus II(Prime)のQsysにモジュールとして登録するには、WISHBONEバスをAvalon-MMに変換する必要があります。(もちろん、Qsysにモジュールとして登録しないで利用する方法もありますが)。

具体的な例

例えば、OpenCoresのUSBホストIPコア(USB 1.1 Host and Function IP core)をQsysにモジュールとして登録するには、Avalon-MMスレーブの変換回路が必要です。

USB Host IPコアの例

USB Host IPコアの例

Avalon-MMスレーブとWISHBONEバスの変換

次のタイミングチャートは、Avalon-MMスレーブとWISHBONEバスの変換例です。上半分がAvalon MMスレーブ、下半分がWISHBONEバスの信号です。動作としては、Avalon-MMスレーブからシングルのリード・アクセスとライト・アクセスを連続して行っています。バス・プロトコル変換のポイントは、

  • ストローブ信号
  • アドレス
  • データ・バス幅
  • アクノリッジ信号

の4つです。

Avalon-MM SlaveとWISHBONE BUSの変換

Avalon-MM SlaveとWISHBONEバスの変換

1.ストローブ信号の変換

Avalon-MMは、リード時とライト時のストローブ信号が分かれています(i_av_rとi_av_w)。一方、WISHBONE バスはリード/ライトでストローブ信号は共通です(strobe_i)。WISHBONEバスでは、ストローブが共通のため、その種別を表す信号we_iが必要になります。we_iは、Avalon-MMのi_av_wがそのまま利用できます。具体的なVerilogコードは、次のようになります。

assign strobe_i = i_av_w | i_av_r;
assign we_i = i_av_w;

2.アドレスの変換

OpenCoresのIPコアは、インターフェースが同じWISHBONEバスであっても、データバス幅やアドレスの扱いは異なります。例えば、データバス幅は、8ビットもあれば、32ビットもあります。アドレスの扱いも統一されていません。例えば、32ビットのデータバス幅では、本来バイト/ハーフワードの信号(アドレスの下位2ビット)は情報としては不要ですが、IPコアによってその扱いはまちまちです。USB ホスト IPコアの場合は、8ビットデータバス幅のWISHBONEバスです。このため、Avalon-MM側のバイトイネーブル信号をWISHBONEバスの下位2ビットに追加する必要があります。具体的なVerilogコードは次のとおりです。

wire [1:0] w_ba;
assign w_ba = i_av_be[1] ? 2'd1 :
              i_av_be[2] ? 2'd2 : 
              i_av_be[3] ? 2'd3 : 
                           2'd0 ;
// WISHBONEバスのアドレス
assign address_i = {i_av_adr,w_ba};

このような変換がわずらわしい場合は、32ビットのデータバスの上位24ビットを無効フィールドとして、Avalon-MMのデータバスの下位8ビットに直接マッピングする方法もあります。

3.データ・バス幅の変換

Avalon-MMスレーブ側が32ビット、WISHBONEバス側が8ビットの場合、バス幅の変換を行う必要があります。ライト・データは、Avalon-MMスレーブ側のアドレスの下位2ビットの値に応じて、32ビットから8ビットを選択してWISHBONEバス側のライトデータとします。一方のリードデータは、WISHBONEバス側の8ビットを単純に4回並べて32ビットに変換します。具体的なVerilogコードは次のとおりです。

wire [1:0] w_ba;
assign w_ba = i_av_be[1] ? 2'd1 :
              i_av_be[2] ? 2'd2 : 
              i_av_be[3] ? 2'd3 : 
                           2'd0 ;
// ライト・データ
assign dada_i = (w_ba == 'd1) ? i_av_wd[15:8]:
                (w_ba == 'd2) ? i_av_wd[23:16]:
                (w_ba == 'd3) ? i_av_wd[31:24]:
                                i_av_wd[7:0];
// リード・データ
assign o_av_rd = {'d4{data_o}};

4.アクノリッジ信号の変換

Avalon-MMスレーブ側のアクノリッジ信号o_av_waitは、次のような論理になります。

  • 通常状態(IDLE状態)ではLレベル
  • i_av_rまたはi_av_wがHレベルになると、ウェイト状態(Hレベル)になって、WISHBONEバス側の応答を待つ
  • WISHBONEバス側のアクノリッジ信号(ack_o)を認識するとLレベルになる

具体的なVerilogコードは次のとおりです。

assign o_av_wait = !(!(i_av_r|i_av_w) & (r_state == P_IDLE) |
(r_state == P_ACK_OUT));

全コード

実際には、タイミングに余裕を持たせるために、Avalon-MMからのアクセスを一度サンプリングしています。

module fm_avalon_wb(
  clk_core,
  rst_x,
  // AVALON bus
  i_av_adr,
  i_av_be,
  i_av_r,
  o_av_rd,
  i_av_w,
  i_av_wd,
  o_av_wait,
  // WISHBONE
  strobe_i,
  we_i,
  address_i,
  ack_o,
  data_i,
  data_o
);
parameter P_AVALON_ADR_WIDTH='d8;
parameter P_AVALON_BE_WIDTH='d4;
parameter P_AVALON_DATA_WIDTH='d32;
parameter P_INTERNAL_ADR_WIDTH='d10;
parameter P_INTERNAL_DATA_WIDTH='d8;
input           clk_core;
input           rst_x;
// AVALON Bus
input  [P_AVALON_ADR_WIDTH-1:0]
                i_av_adr;
input  [P_AVALON_BE_WIDTH-1:0]
                i_av_be;
input           i_av_r;
output [P_AVALON_DATA_WIDTH-1:0]
                o_av_rd;
input           i_av_w;
input  [P_AVALON_DATA_WIDTH-1:0]
                i_av_wd;
output          o_av_wait;
// WISHBONE
output          strobe_i;
output          we_i;
output [P_INTERNAL_ADR_WIDTH-1:0]
                address_i;
input           ack_o;
output [P_INTERNAL_DATA_WIDTH-1:0]
                data_i;
input  [P_INTERNAL_DATA_WIDTH-1:0]
                data_o;

//////////////////////////////////
// state definition
//////////////////////////////////
localparam P_IDLE         = 2'h0;
localparam P_WAIT_ACK     = 2'h1;
localparam P_R_WAIT_RDATA = 2'h2;
localparam P_ACK_OUT      = 2'h3;
//////////////////////////////////
// reg
//////////////////////////////////
reg  [1:0]  r_state;
reg         r_req;
reg         r_wr;
reg  [P_INTERNAL_ADR_WIDTH-1:0]
            r_adrs;
reg  [P_INTERNAL_DATA_WIDTH-1:0]
            r_rdata;
reg  [P_INTERNAL_DATA_WIDTH-1:0]
            r_wd;
//////////////////////////////////
// wire
//////////////////////////////////
wire  [P_INTERNAL_ADR_WIDTH-1:0]
            w_adrs;
wire  [P_INTERNAL_DATA_WIDTH-1:0]
            w_rdata;
wire  [P_INTERNAL_BE_WIDTH-1:0]
            w_be;
wire  [P_INTERNAL_DATA_WIDTH-1:0]
            w_wd;
wire [1:0]  w_ba;
//////////////////////////////////
// assign
//////////////////////////////////
assign o_av_rd = {'d4{r_rdata}};
assign w_ba = i_av_be[1] ? 2'd1 :
              i_av_be[2] ? 2'd2 : 
              i_av_be[3] ? 2'd3 : 
                           2'd0 ;
assign w_adrs = {i_av_adr,w_ba};
assign w_wd = (w_ba == 'd1) ? i_av_wd[15:8]:
              (w_ba == 'd2) ? i_av_wd[23:16]:
              (w_ba == 'd3) ? i_av_wd[31:24]:
                              i_av_wd[7:0];
assign strobe_i = r_req;
assign we_i = r_wr;
assign address_i = r_adrs;
assign data_i = r_wd;
assign o_av_wait = !(!(i_av_r|i_av_w) & (r_state == P_IDLE) |
                    (r_state == P_ACK_OUT));
//////////////////////////////////
// state machine
//////////////////////////////////
always @(posedge clk_core or negedge rst_x) begin
  if (~rst_x) begin
    r_state <= P_IDLE;
    r_req <= 1'b0;
    r_wr <= 1'b0;
    r_adrs <= {P_INTERNAL_ADR_WIDTH{1'b0}};
    r_rdata <= {P_INTERNAL_DATA_WIDTH{1'b0}};
    r_wd <= {P_INTERNAL_DATA_WIDTH{1'b0}};
  end else begin
    case (r_state)
      P_IDLE: begin
        if (i_av_w) begin
          // write
          r_req <= 1'b1;
          r_wr <= 1'b1;    
          r_adrs <= w_adrs;
          r_wd <= w_wd;
          r_state <= P_WAIT_ACK;
        end else if (i_av_r) begin
          // read
          r_req <= 1'b1;
          r_wr <= 1'b0;    
          r_adrs <= w_adrs;
          r_state <= P_WAIT_ACK;
        end
      end
      P_WAIT_ACK: begin
        if (ack_o) begin
          r_req <= 1'b0;
          if (r_wr) begin
            // write
            r_state <= P_ACK_OUT;
          end else begin
            r_rdata <= data_o;
            r_state <= P_ACK_OUT;
          end
        end
      end
      P_ACK_OUT: begin
        r_state <= P_IDLE;
      end
    endcase
  end
end
endmodule
タイトルとURLをコピーしました