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スレーブの変換回路が必要です。
Avalon-MMスレーブとWISHBONEバスの変換
次のタイミングチャートは、Avalon-MMスレーブとWISHBONEバスの変換例です。上半分がAvalon MMスレーブ、下半分がWISHBONEバスの信号です。動作としては、Avalon-MMスレーブからシングルのリード・アクセスとライト・アクセスを連続して行っています。バス・プロトコル変換のポイントは、
- ストローブ信号
- アドレス
- データ・バス幅
- アクノリッジ信号
の4つです。
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



