FC2カウンター FPGAの部屋 2019年09月26日
FC2ブログ

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

FPGAの部屋

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

Vivado HLS でAXI4 Lite のレジスタのHDL コードを生成しよう

レジスタを生成できるツールがあるようだが、Vivado HLS でも同様に AXI4 Lite インターフェースのレジスタを C ソースコードから生成することができる。これを使って、 AXI4 Lite インターフェースのレジスタを Verilog HDL と VHDL のソースコードとして簡単に生成することができるので紹介する。

紹介するVivado HLS のプロジェクトは”Ultra96のDisplayPortを使用するためのIPを作成する2(pattern_gen_axis IP)”の pattern_gen_axis IP とする。

pattern_gen_axis IP の C ソースコードの一部分を抜き出す。

int pattern_gen_axis(hls::stream<ap_axis<32,1,1,1> >& outs,
        int v_size, int h_size,
        ap_uint<1> &init_done, ap_uint<1> &init_done_out
){
#pragma HLS INTERFACE ap_none register port=init_done_out
#pragma HLS INTERFACE s_axilite port=init_done
#pragma HLS INTERFACE s_axilite port=v_size
#pragma HLS INTERFACE s_axilite port=h_size
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE s_axilite port=return


上のソースコードを見ると、ブロックレベルのインターフェースは s_axilite で、init_done と v_size, h_size が s_axilite つまりAXI4 Lite インターフェースとなっている。
これを合成すると、solution1/syn の verilog ディレクトリと vhdl ディレクトリにHDL ファイルが生成される。
vivado_hls_reg_1_190926.png

verilog を見てみると、

pattern_gen_axis.v
pattern_gen_axis_AXILiteS_s_axi.v


があるのが分かる。pattern_gen_axis.v がトップのVerilog HDL ファイルだが、そのAXI4 Lite インターフェース部分は、pattern_gen_axis_AXILiteS_s_axi.v にまとめられている。
pattern_gen_axis_AXILiteS_s_axi.v を見ると、とても読みやすいのが分かるだろうか?
pattern_gen_axis_AXILiteS_s_axi.v を示す。

// ==============================================================
// File generated on Tue Jan 22 04:34:10 JST 2019
// Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC v2018.3 (64-bit)
// SW Build 2405991 on Thu Dec  6 23:36:41 MST 2018
// IP Build 2404404 on Fri Dec  7 01:43:56 MST 2018
// Copyright 1986-2018 Xilinx, Inc. All Rights Reserved.
// ==============================================================
`timescale 1ns/1ps
module pattern_gen_axis_AXILiteS_s_axi
#(parameter
    C_S_AXI_ADDR_WIDTH = 6,
    C_S_AXI_DATA_WIDTH = 32
)(
    // axi4 lite slave signals
    input  wire                          ACLK,
    input  wire                          ARESET,
    input  wire                          ACLK_EN,
    input  wire [C_S_AXI_ADDR_WIDTH-1:0] AWADDR,
    input  wire                          AWVALID,
    output wire                          AWREADY,
    input  wire [C_S_AXI_DATA_WIDTH-1:0] WDATA,
    input  wire [C_S_AXI_DATA_WIDTH/8-1:0] WSTRB,
    input  wire                          WVALID,
    output wire                          WREADY,
    output wire [1:0]                    BRESP,
    output wire                          BVALID,
    input  wire                          BREADY,
    input  wire [C_S_AXI_ADDR_WIDTH-1:0] ARADDR,
    input  wire                          ARVALID,
    output wire                          ARREADY,
    output wire [C_S_AXI_DATA_WIDTH-1:0] RDATA,
    output wire [1:0]                    RRESP,
    output wire                          RVALID,
    input  wire                          RREADY,
    // user signals
    output wire [10:0]                   v_size_V,
    output wire [10:0]                   h_size_V,
    output wire [0:0]                    init_done_V
);
//------------------------Address Info-------------------
// 0x00 : reserved
// 0x04 : reserved
// 0x08 : reserved
// 0x0c : reserved
// 0x10 : Data signal of v_size_V
//        bit 10~0 - v_size_V[10:0] (Read/Write)
//        others   - reserved
// 0x14 : reserved
// 0x18 : Data signal of h_size_V
//        bit 10~0 - h_size_V[10:0] (Read/Write)
//        others   - reserved
// 0x1c : reserved
// 0x20 : Data signal of init_done_V
//        bit 0  - init_done_V[0] (Read/Write)
//        others - reserved
// 0x24 : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)

//------------------------Parameter----------------------
localparam
    ADDR_V_SIZE_V_DATA_0    = 6'h10,
    ADDR_V_SIZE_V_CTRL      = 6'h14,
    ADDR_H_SIZE_V_DATA_0    = 6'h18,
    ADDR_H_SIZE_V_CTRL      = 6'h1c,
    ADDR_INIT_DONE_V_DATA_0 = 6'h20,
    ADDR_INIT_DONE_V_CTRL   = 6'h24,
    WRIDLE                  = 2'd0,
    WRDATA                  = 2'd1,
    WRRESP                  = 2'd2,
    WRRESET                 = 2'd3,
    RDIDLE                  = 2'd0,
    RDDATA                  = 2'd1,
    RDRESET                 = 2'd2,
    ADDR_BITS         = 6;

//------------------------Local signal-------------------
    reg  [1:0]                    wstate = WRRESET;
    reg  [1:0]                    wnext;
    reg  [ADDR_BITS-1:0]          waddr;
    wire [31:0]                   wmask;
    wire                          aw_hs;
    wire                          w_hs;
    reg  [1:0]                    rstate = RDRESET;
    reg  [1:0]                    rnext;
    reg  [31:0]                   rdata;
    wire                          ar_hs;
    wire [ADDR_BITS-1:0]          raddr;
    // internal registers
    reg  [10:0]                   int_v_size_V = 'b0;
    reg  [10:0]                   int_h_size_V = 'b0;
    reg  [0:0]                    int_init_done_V = 'b0;

//------------------------Instantiation------------------

//------------------------AXI write fsm------------------
assign AWREADY = (wstate == WRIDLE);
assign WREADY  = (wstate == WRDATA);
assign BRESP   = 2'b00;  // OKAY
assign BVALID  = (wstate == WRRESP);
assign wmask   = { {8{WSTRB[3]}}, {8{WSTRB[2]}}, {8{WSTRB[1]}}, {8{WSTRB[0]}} };
assign aw_hs   = AWVALID & AWREADY;
assign w_hs    = WVALID & WREADY;

// wstate
always @(posedge ACLK) begin
    if (ARESET)
        wstate <= WRRESET;
    else if (ACLK_EN)
        wstate <= wnext;
end

// wnext
always @(*) begin
    case (wstate)
        WRIDLE:
            if (AWVALID)
                wnext = WRDATA;
            else
                wnext = WRIDLE;
        WRDATA:
            if (WVALID)
                wnext = WRRESP;
            else
                wnext = WRDATA;
        WRRESP:
            if (BREADY)
                wnext = WRIDLE;
            else
                wnext = WRRESP;
        default:
            wnext = WRIDLE;
    endcase
end

// waddr
always @(posedge ACLK) begin
    if (ACLK_EN) begin
        if (aw_hs)
            waddr <= AWADDR[ADDR_BITS-1:0];
    end
end

//------------------------AXI read fsm-------------------
assign ARREADY = (rstate == RDIDLE);
assign RDATA   = rdata;
assign RRESP   = 2'b00;  // OKAY
assign RVALID  = (rstate == RDDATA);
assign ar_hs   = ARVALID & ARREADY;
assign raddr   = ARADDR[ADDR_BITS-1:0];

// rstate
always @(posedge ACLK) begin
    if (ARESET)
        rstate <= RDRESET;
    else if (ACLK_EN)
        rstate <= rnext;
end

// rnext
always @(*) begin
    case (rstate)
        RDIDLE:
            if (ARVALID)
                rnext = RDDATA;
            else
                rnext = RDIDLE;
        RDDATA:
            if (RREADY & RVALID)
                rnext = RDIDLE;
            else
                rnext = RDDATA;
        default:
            rnext = RDIDLE;
    endcase
end

// rdata
always @(posedge ACLK) begin
    if (ACLK_EN) begin
        if (ar_hs) begin
            rdata <= 1'b0;
            case (raddr)
                ADDR_V_SIZE_V_DATA_0: begin
                    rdata <= int_v_size_V[10:0];
                end
                ADDR_H_SIZE_V_DATA_0: begin
                    rdata <= int_h_size_V[10:0];
                end
                ADDR_INIT_DONE_V_DATA_0: begin
                    rdata <= int_init_done_V[0:0];
                end
            endcase
        end
    end
end


//------------------------Register logic-----------------
assign v_size_V    = int_v_size_V;
assign h_size_V    = int_h_size_V;
assign init_done_V = int_init_done_V;
// int_v_size_V[10:0]
always @(posedge ACLK) begin
    if (ARESET)
        int_v_size_V[10:0] <= 0;
    else if (ACLK_EN) begin
        if (w_hs && waddr == ADDR_V_SIZE_V_DATA_0)
            int_v_size_V[10:0] <= (WDATA[31:0] & wmask) | (int_v_size_V[10:0] & ~wmask);
    end
end

// int_h_size_V[10:0]
always @(posedge ACLK) begin
    if (ARESET)
        int_h_size_V[10:0] <= 0;
    else if (ACLK_EN) begin
        if (w_hs && waddr == ADDR_H_SIZE_V_DATA_0)
            int_h_size_V[10:0] <= (WDATA[31:0] & wmask) | (int_h_size_V[10:0] & ~wmask);
    end
end

// int_init_done_V[0:0]
always @(posedge ACLK) begin
    if (ARESET)
        int_init_done_V[0:0] <= 0;
    else if (ACLK_EN) begin
        if (w_hs && waddr == ADDR_INIT_DONE_V_DATA_0)
            int_init_done_V[0:0] <= (WDATA[31:0] & wmask) | (int_init_done_V[0:0] & ~wmask);
    end
end


//------------------------Memory logic-------------------

endmodule


どうですか?読みやすいですよね?
という具合に、AXI4 Lite のレジスタ記述が欲しい場合は、レジスタにしたい名称をトップの関数の引数にして、
#pragma HLS INTERFACE s_axilite port=<レジスタにしたい名称>
ディレクティブを与えて、適当な演算を書いて合成すれば、XXXX_AXILiteS_s_axi.v や XXXX_AXILiteS_s_axi.vhd ができるので、それを抜き出して使ってAXI4 Lite のレジスタ記述のあるHDL 回路を作成しましょう。

ただし、Vivado HLS で作れる回路だったら、そのまま C で記述して作ったほうが簡単で確実なのは言うまでもない。これを使用するのはカメラのインターフェース回路にレジスタを付けるというような場合のみとなるだろう。
そして、使用する場合はライセンセンスを守って使いましょう
  1. 2019年09月26日 06:27 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0