FC2カウンター FPGAの部屋 WishBone バスと AXI4-Lite インターフェースを変換する wb2axi4ls_conv.v とトップの i2cm_axi4ls.v
fc2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

WishBone バスと AXI4-Lite インターフェースを変換する wb2axi4ls_conv.v とトップの i2cm_axi4ls.v

今回は、”OpenCores.org の I2C controller core をシミュレーションする”で使用した OpenCores.org の I2C controller core を AXI4-Lite インターフェースに変更するための wb2axi4ls_conv.v とトップの i2cm_axi4ls.v を紹介する。

まずは、 wb2axi4ls_conv.v から貼っておく。

// wb2axi4ls_conv.v
// Convert Wishbone Bus Version I2C Master IP to AXI4-Lite Interface
// 2021/04/12 by marsee
//

`default_nettype none

module wb2axi4ls_conv # (
    parameter integer C_S_AXI_LITE_ADDR_WIDTH = 5, // Address width of the AXI Lite Interface
    parameter integer C_S_AXI_LITE_DATA_WIDTH = 32, // Data width of the AXI Lite Interface
    parameter integer WISHBONE_BUS_ADDR_WIDTH = 3,
    parameter integer WISHBONE_BUS_DATA_WIDTH = 8
)(
    input   wire                                    s_axi_lite_aclk,
    input   wire                                    axi_resetn,

    // AXI Lite Write Address Channel
    input   wire                                    s_axi_lite_awvalid,
    output  wire                                    s_axi_lite_awready,
    input   wire    [C_S_AXI_LITE_ADDR_WIDTH-1: 0]  s_axi_lite_awaddr,

    // AXI Lite Write Data Channel
    input   wire                                    s_axi_lite_wvalid,
    output  wire                                    s_axi_lite_wready,
    input   wire    [C_S_AXI_LITE_DATA_WIDTH-1: 0]  s_axi_lite_wdata,
    input   wire    [C_S_AXI_LITE_DATA_WIDTH/8-1:0] s_axi_lite_wstrb,

    // AXI Lite Write Response Channel
    output  wire    [1:0]                           s_axi_lite_bresp,
    output  wire                                    s_axi_lite_bvalid,
    input   wire                                    s_axi_lite_bready,

    // AXI Lite Read Address Channel
    input   wire                                    s_axi_lite_arvalid,
    output  wire                                    s_axi_lite_arready,
    input   wire    [C_S_AXI_LITE_ADDR_WIDTH-1: 0]  s_axi_lite_araddr,

    // AXI Lite Read Data Channel
    output  wire                                    s_axi_lite_rvalid,
    input   wire                                    s_axi_lite_rready,
    output  wire    [C_S_AXI_LITE_DATA_WIDTH-1: 0]  s_axi_lite_rdata,
    output  wire    [1:0]                           s_axi_lite_rresp,

    // WishBone Interface
    output  reg     [WISHBONE_BUS_ADDR_WIDTH-1:0]   wb_adr_i,
    output  wire    [WISHBONE_BUS_DATA_WIDTH-1:0]   wb_dat_i,
    input   wire    [WISHBONE_BUS_DATA_WIDTH-1:0]   wb_dat_o,
    output  wire                                    wb_we_i,
    output  wire                                    wb_stb_i,
    output  wire                                    wb_cyc_i,
    input   wire                                    wb_ack_o,
    input   wire                                    wb_inta_o
);

    // RESP の値の定義
    localparam  RESP_OKAY =     2'b00;
    localparam  RESP_EXOKAY =   2'b01;
    localparam  RESP_SLVERR =   2'b10;
    localparam  RESP_DECERR =   2'b11;

    localparam  IDLE_WR =           5'b00001,   // for wrt_cs
                DATA_WRITE_HOLD =   5'b00010,
                WB_WAIT_ACK_W =     5'b00100,
                BREADY_ASSERT =     5'b01000,
                BREADY_ASSERTED =   5'b10000;

    localparam  IDLE_RD =       3'b001,         //  for rdt_cs
                WB_WAIT_ACK_R = 3'b010,
                AR_DATA_WAIT =  3'b100;

    reg     [4:0]   wrt_cs = IDLE_WR;

    reg     [2:0]   rdt_cs = IDLE_RD;

    reg     reset_1d = 1'b0;
    reg     reset = 1'b0;
    reg     awready = 1'b1;
    reg     bvalid = 1'b0;
    reg     arready = 1'b1;
    reg     wready = 1'b0;
    reg     rvalid = 1'b0;
    wire    aclk;
    reg    [C_S_AXI_LITE_DATA_WIDTH-1:0]   rdata;

    reg     wb_cyc = 1'b0;
    reg     wb_we = 1'b0;

    assign aclk = s_axi_lite_aclk;
    // Synchronization of axi_resetn
    always @(posedge aclk) begin
        reset_1d <= ~axi_resetn;
        reset <= reset_1d;
    end

    // wb_adr_i
    always @(posedge aclk) begin
        if (reset) begin
            wb_adr_i = 3'b000;
        end else if (s_axi_lite_awvalid & s_axi_lite_awready) begin
            wb_adr_i = s_axi_lite_awaddr[4:2];
        end else if (s_axi_lite_arvalid & s_axi_lite_arready) begin
            wb_adr_i = s_axi_lite_araddr[4:2];
        end
    end
    
    // AXI4 Lite Slave Write Transaction State Machine
    always @(posedge aclk) begin
        if (reset) begin
            wrt_cs <= IDLE_WR;
            awready <= 1'b1;
            bvalid <= 1'b0;
            wready <= 1'b0;
            wb_we <= 1'b0;
        end else begin
            case (wrt_cs)
                IDLE_WR :
                    if (s_axi_lite_awvalid & ~s_axi_lite_wvalid) begin  // Write Transaction Start
                        wrt_cs <= DATA_WRITE_HOLD;
                        awready <= 1'b0;
                    end else if (s_axi_lite_awvalid & s_axi_lite_wvalid) begin  // Write Transaction Start with data
                        wrt_cs <= WB_WAIT_ACK_W;
                        awready <= 1'b0;
                        if (s_axi_lite_wstrb[0]) begin
                            wb_we <= 1'b1;
                        end
                    end
                DATA_WRITE_HOLD :
                    if (s_axi_lite_wvalid) begin    // Write data just valid
                        wrt_cs <= WB_WAIT_ACK_W;
                        if (s_axi_lite_wstrb[0]) begin
                            wb_we <= 1'b1;
                        end
                    end
                WB_WAIT_ACK_W :
                    if (wb_ack_o) begin
                        wrt_cs <= BREADY_ASSERT;
                        wready <= 1'b1;
                        wb_we <= 1'b0;
                    end
                BREADY_ASSERT: begin
                    wrt_cs <= BREADY_ASSERTED;
                    wready <= 1'b0;
                    bvalid <= 1'b1;
                end
                BREADY_ASSERTED :
                    if (s_axi_lite_bready) begin    // The write transaction was terminated.
                        wrt_cs <= IDLE_WR;
                        bvalid <= 1'b0;
                        awready <= 1'b1;
                    end
            endcase
        end
    end
    assign s_axi_lite_awready = awready;
    assign s_axi_lite_bvalid = bvalid;
    assign s_axi_lite_bresp = 2'b00;
    assign s_axi_lite_wready = wready;
    assign wb_we_i = wb_we;
    assign wb_dat_i = s_axi_lite_wdata[WISHBONE_BUS_DATA_WIDTH-1:0];

    // AXI4 Lite Slave Read Transaction State Machine
    always @(posedge aclk) begin
        if (reset) begin
            rdt_cs <= IDLE_RD;
            arready <= 1'b1;
            rvalid <= 1'b0;
            rdata <= 0;
        end else begin
            case (rdt_cs)
                IDLE_RD :
                    if (s_axi_lite_arvalid) begin
                        rdt_cs <= WB_WAIT_ACK_R;
                        arready <= 1'b0;
                    end
                WB_WAIT_ACK_R :
                    if(wb_ack_o) begin
                        rdata[WISHBONE_BUS_DATA_WIDTH-1:0] <= wb_dat_o;
                        rdata[C_S_AXI_LITE_DATA_WIDTH-1:WISHBONE_BUS_DATA_WIDTH] = 0;
                        rvalid <= 1'b1;
                        rdt_cs <= AR_DATA_WAIT;
                    end
                AR_DATA_WAIT :
                    if (s_axi_lite_rready) begin
                        rdt_cs <= IDLE_RD;
                        rvalid <= 1'b0;
                        arready <= 1'b1;
                    end
            endcase
        end
    end
    assign s_axi_lite_arready = arready;
    assign s_axi_lite_rvalid = rvalid;
    assign s_axi_lite_rresp = 2'b00;
    assign s_axi_lite_rdata = rdata;

    // wb_cyc_i
    always @(posedge aclk) begin
        if (reset) begin
            wb_cyc <= 1'b0;
        end else begin
            if (wrt_cs==IDLE_WR & s_axi_lite_awvalid & s_axi_lite_wvalid) begin // Write
                wb_cyc <= 1'b1;
            end else if (wrt_cs==DATA_WRITE_HOLD & s_axi_lite_wvalid) begin
                wb_cyc <= 1'b1;
            end else if (wrt_cs==WB_WAIT_ACK_W & wb_ack_o) begin
                wb_cyc <= 1'b0;
            end else if (rdt_cs==IDLE_RD & s_axi_lite_arvalid) begin // Read
                wb_cyc <= 1'b1;
            end else if (rdt_cs==WB_WAIT_ACK_R & wb_ack_o) begin
                wb_cyc <= 1'b0;
            end
        end
    end
    assign wb_cyc_i = wb_cyc;
    assign wb_stb_i = wb_cyc;
endmodule

`default_nettype wire


次に、トップの i2cm_axi4ls.v を貼っておく。

`default_nettype none

// i2cm_axi4ls.v
// 2021/04/16 by marsee
//

module i2cm_axi4ls #(
    parameter integer C_S_AXI_LITE_ADDR_WIDTH = 12, // Address width of the AXI Lite Interface
    parameter integer C_S_AXI_LITE_DATA_WIDTH = 32 // Data width of the AXI Lite Interface
)(
    input   wire                                    s_axi_lite_aclk,
    input   wire                                    s_axi_lite_resetn,

    // AXI Lite Write Address Channel
    input   wire                                    s_axi_lite_awvalid,
    output  wire                                    s_axi_lite_awready,
    input   wire    [C_S_AXI_LITE_ADDR_WIDTH-1: 0]  s_axi_lite_awaddr,

    // AXI Lite Write Data Channel
    input   wire                                    s_axi_lite_wvalid,
    output  wire                                    s_axi_lite_wready,
    input   wire    [C_S_AXI_LITE_DATA_WIDTH-1: 0]  s_axi_lite_wdata,
    input   wire    [C_S_AXI_LITE_DATA_WIDTH/8-1:0] s_axi_lite_wstrb,

    // AXI Lite Write Response Channel
    output  wire    [1:0]                           s_axi_lite_bresp,
    output  wire                                    s_axi_lite_bvalid,
    input   wire                                    s_axi_lite_bready,

    // AXI Lite Read Address Channel
    input   wire                                    s_axi_lite_arvalid,
    output  wire                                    s_axi_lite_arready,
    input   wire    [C_S_AXI_LITE_ADDR_WIDTH-1: 0]  s_axi_lite_araddr,

    // AXI Lite Read Data Channel
    output  wire                                    s_axi_lite_rvalid,
    input   wire                                    s_axi_lite_rready,
    output  wire    [C_S_AXI_LITE_DATA_WIDTH-1: 0]  s_axi_lite_rdata,
    output  wire    [1:0]                           s_axi_lite_rresp,

    // I2C Bus
    output  wire                                    scl_o,
    output  wire                                    scl_t,
    input   wire                                    scl_i,
    output  wire                                    sda_o,
    output  wire                                    sda_t,
    input   wire                                    sda_i
    
    //inout   wire                                    scl,
    //inout   wire                                    sda
);

    // WishBone Interface
    wire    [2:0]   wb_adr_i;
    wire    [7:0]   wb_dat_i;
    wire    [7:0]   wb_dat_o;
    wire            wb_we_i;
    wire            wb_stb_i;
    wire            wb_cyc_i;
    wire            wb_ack_o;
    wire            wb_inta_o;
    wire            wb_rst_i;

    wb2axi4ls_conv #(
        .C_S_AXI_LITE_ADDR_WIDTH(C_S_AXI_LITE_ADDR_WIDTH),
        .C_S_AXI_LITE_DATA_WIDTH(C_S_AXI_LITE_DATA_WIDTH)
    ) wb2axi4c_i (
        .s_axi_lite_aclk(s_axi_lite_aclk),
        .axi_resetn(s_axi_lite_resetn),
        .s_axi_lite_awvalid(s_axi_lite_awvalid),
        .s_axi_lite_awready(s_axi_lite_awready),
        .s_axi_lite_awaddr(s_axi_lite_awaddr),
        .s_axi_lite_wvalid(s_axi_lite_wvalid),
        .s_axi_lite_wready(s_axi_lite_wready),
        .s_axi_lite_wdata(s_axi_lite_wdata),
        .s_axi_lite_wstrb(s_axi_lite_wstrb),
        .s_axi_lite_bresp(s_axi_lite_bresp),
        .s_axi_lite_bvalid(s_axi_lite_bvalid),
        .s_axi_lite_bready(s_axi_lite_bready),
        .s_axi_lite_arvalid(s_axi_lite_arvalid),
        .s_axi_lite_arready(s_axi_lite_arready),
        .s_axi_lite_araddr(s_axi_lite_araddr),
        .s_axi_lite_rvalid(s_axi_lite_rvalid),
        .s_axi_lite_rready(s_axi_lite_rready),
        .s_axi_lite_rdata(s_axi_lite_rdata),
        .s_axi_lite_rresp(s_axi_lite_rresp),
        .wb_adr_i(wb_adr_i),
        .wb_dat_i(wb_dat_i),
        .wb_dat_o(wb_dat_o),
        .wb_we_i(wb_we_i),
        .wb_stb_i(wb_stb_i),
        .wb_cyc_i(wb_cyc_i),
        .wb_ack_o(wb_ack_o),
        .wb_inta_o(wb_inta_o)
    );

    assign wb_rst_i = ~s_axi_lite_resetn;
    i2c_master_top i2cm_wb_i(
        .wb_clk_i(s_axi_lite_aclk),
        .wb_rst_i(wb_rst_i),
        .arst_i(1'b1),
        .wb_adr_i(wb_adr_i),
        .wb_dat_i(wb_dat_i),
        .wb_dat_o(wb_dat_o),
        .wb_we_i(wb_we_i),
        .wb_stb_i(wb_stb_i),
        .wb_cyc_i(wb_cyc_i),
        .wb_ack_o(wb_ack_o),
        .wb_inta_o(wb_inta_o),
        .scl_pad_i(scl_i),
        .scl_pad_o(scl_o),
        .scl_padoen_o(scl_t),
        .sda_pad_i(sda_i),
        .sda_pad_o(sda_o),
        .sda_padoen_o(sda_t)
    );
 endmodule

`default_nettype wire


なお、I2C の出力は、ブロックデザインで scl, sda を直接出力するとエラーになってしまう。そこで、 _t, _i, _o の IOBUF の入出力のポートを出した。
しかも、 scl_t, scl_i, scl_o, sda_t, sda_i, sda_o の名前にしないとラッパー・ファイルを作る時にIIC として推論してくれなかったので、この信号名にしたほうが良いと思う。(違うポート名にして、IP パッケージャーで関連付けしたのだが IIC として推論してくれなかった orz)
  1. 2021年04月23日 03:50 |
  2. FPGAを使用したシステム
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
https://marsee101.blog.fc2.com/tb.php/5214-65fd512c
この記事にトラックバックする(FC2ブログユーザー)