FPGAの部屋 2012年03月
FC2ブログ

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

FPGAの部屋

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

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする14(AXI4スレーブIPのソース公開)

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする13(Writeはうまく行った)”で、Write側はうまく行ったと思われるキャラクタ・ディスプレイ・コントローラAXI4バス・スレーブIPのソースをブログに貼っておこうと思う。長いです。

cdc_axi_slave.vhd を下に貼っておく。なお、entity部分の宣言は はXilinx社の ar37425.zip から引用した。

-----------------------------------------------------------------------------
--
-- AXI Slave
--
-----------------------------------------------------------------------------
-- 2012/02/25 : S_AXI_AWBURST=1 (INCR) にのみ対応、AWSIZE, ARSIZE = 000 (1byte), 001 (2bytes), 010 (4bytes) のみ対応。

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;

--library unisim;
--use unisim.vcomponents.all;

entity cdc_axi_slave is
  generic (
    C_S_AXI_ID_WIDTH     : integer := 1;
    C_S_AXI_ADDR_WIDTH   : integer := 32;
    C_S_AXI_DATA_WIDTH   : integer := 32;
    C_S_AXI_AWUSER_WIDTH : integer := 1;
    C_S_AXI_ARUSER_WIDTH : integer := 1;
    C_S_AXI_WUSER_WIDTH  : integer := 1;
    C_S_AXI_RUSER_WIDTH  : integer := 1;
    C_S_AXI_BUSER_WIDTH  : integer := 1
    );
  port(
    -- System Signals
    ACLK    : in std_logic;
    ARESETN : in std_logic;

    -- Slave Interface Write Address Ports
    S_AXI_AWID     : in  std_logic_vector(C_S_AXI_ID_WIDTH-1 downto 0);
    S_AXI_AWADDR   : in  std_logic_vector(C_S_AXI_ADDR_WIDTH-1 downto 0);
    S_AXI_AWLEN    : in  std_logic_vector(8-1 downto 0);
    S_AXI_AWSIZE   : in  std_logic_vector(3-1 downto 0);
    S_AXI_AWBURST  : in  std_logic_vector(2-1 downto 0);
    S_AXI_AWLOCK   : in  std_logic_vector(2-1 downto 0);
    S_AXI_AWCACHE  : in  std_logic_vector(4-1 downto 0);
    S_AXI_AWPROT   : in  std_logic_vector(3-1 downto 0);
    S_AXI_AWREGION : in  std_logic_vector(4-1 downto 0);
    S_AXI_AWQOS    : in  std_logic_vector(4-1 downto 0);
    S_AXI_AWUSER   : in  std_logic_vector(C_S_AXI_AWUSER_WIDTH-1 downto 0);
    S_AXI_AWVALID  : in  std_logic;
    S_AXI_AWREADY  : out std_logic;

    -- Slave Interface Write Data Ports
    S_AXI_WID    : in  std_logic_vector(C_S_AXI_ID_WIDTH-1 downto 0);
    S_AXI_WDATA  : in  std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0);
    S_AXI_WSTRB  : in  std_logic_vector(C_S_AXI_DATA_WIDTH/8-1 downto 0);
    S_AXI_WLAST  : in  std_logic;
    S_AXI_WUSER  : in  std_logic_vector(C_S_AXI_WUSER_WIDTH-1 downto 0);
    S_AXI_WVALID : in  std_logic;
    S_AXI_WREADY : out std_logic;

    -- Slave Interface Write Response Ports
    S_AXI_BID    : out std_logic_vector(C_S_AXI_ID_WIDTH-1 downto 0);
    S_AXI_BRESP  : out std_logic_vector(2-1 downto 0);
    S_AXI_BUSER  : out std_logic_vector(C_S_AXI_BUSER_WIDTH-1 downto 0);
    S_AXI_BVALID : out std_logic;
    S_AXI_BREADY : in  std_logic;

    -- Slave Interface Read Address Ports
    S_AXI_ARID     : in  std_logic_vector(C_S_AXI_ID_WIDTH-1 downto 0);
    S_AXI_ARADDR   : in  std_logic_vector(C_S_AXI_ADDR_WIDTH-1 downto 0);
    S_AXI_ARLEN    : in  std_logic_vector(8-1 downto 0);
    S_AXI_ARSIZE   : in  std_logic_vector(3-1 downto 0);
    S_AXI_ARBURST  : in  std_logic_vector(2-1 downto 0);
    S_AXI_ARLOCK   : in  std_logic_vector(2-1 downto 0);
    S_AXI_ARCACHE  : in  std_logic_vector(4-1 downto 0);
    S_AXI_ARPROT   : in  std_logic_vector(3-1 downto 0);
    S_AXI_ARREGION : in  std_logic_vector(4-1 downto 0);
    S_AXI_ARQOS    : in  std_logic_vector(4-1 downto 0);
    S_AXI_ARUSER   : in  std_logic_vector(C_S_AXI_ARUSER_WIDTH-1 downto 0);
    S_AXI_ARVALID  : in  std_logic;
    S_AXI_ARREADY  : out std_logic;

    -- Slave Interface Read Data Ports
    S_AXI_RID    : out std_logic_vector(C_S_AXI_ID_WIDTH-1 downto 0);
    S_AXI_RDATA  : out std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0);
    S_AXI_RRESP  : out std_logic_vector(2-1 downto 0);
    S_AXI_RLAST  : out std_logic;
    S_AXI_RUSER  : out std_logic_vector(C_S_AXI_RUSER_WIDTH-1 downto 0);
    S_AXI_RVALID : out std_logic;
    S_AXI_RREADY : in  std_logic;
    
    -- TMDS Signals
    pixclk        : in    std_logic;
    TMDS_tx_clk_p :    out    std_logic;                    -- Clock
    TMDS_tx_clk_n :    out    std_logic;
    TMDS_tx_2_G_p :    out    std_logic;                    -- Green
    TMDS_tx_2_G_n :    out    std_logic;
    TMDS_tx_1_R_p :    out    std_logic;                    -- Red
    TMDS_tx_1_R_n :    out    std_logic;
    TMDS_tx_0_B_p :    out    std_logic;                    -- Blue
    TMDS_tx_0_B_n :    out    std_logic
    );

end cdc_axi_slave;

architecture implementation of cdc_axi_slave is
component dvi_disp 
    generic (
        PLL_CLKFBOUT_MULT : integer := 20;    -- VGA解像度 PLL VCO Freq 400MHz ~ 1000MHz
        PLL_CLKIN_PERIOD : real := 40.0;    -- VGA ピクセルクロック周期
        PLL_CLKOUT0_DIVIDE : integer := 2;    -- ピクセルクロックX10
        PLL_CLKOUT1_DIVIDE : integer := 20;    -- ピクセルクロック
        PLL_CLKOUT2_DIVIDE : integer := 10    -- ピクセルクロックX2
    );
    port (
        pixclk    :    in    std_logic;            -- pixel clock
        reset_in :    in     std_logic;            -- active high
        red_in :    in    std_logic_vector(7 downto 0);    -- RED入力
        green_in :    in    std_logic_vector(7 downto 0);    -- GREEN入力
        blue_in :    in    std_logic_vector(7 downto 0);    -- BLUE入力
        hsync :        in    std_logic;
        vsync :        in    std_logic;
        display_enable :    in std_logic;                -- 表示が有効
        TMDS_tx_clk_p :    out    std_logic;                    -- Clock
        TMDS_tx_clk_n :    out    std_logic;
        TMDS_tx_2_G_p :    out    std_logic;                    -- Green
        TMDS_tx_2_G_n :    out    std_logic;
        TMDS_tx_1_R_p :    out    std_logic;                    -- Red
        TMDS_tx_1_R_n :    out    std_logic;
        TMDS_tx_0_B_p :    out    std_logic;                    -- Blue
        TMDS_tx_0_B_n :    out    std_logic
    );
end component;

component CharDispCtrler
    port(
        axi4clk : in std_logic;
        pixclk : in std_logic;
        reset : in std_logic;
        
        processor_addr : in std_logic_vector(12 downto 0);
        processor_din : in std_logic_vector(15 downto 0);
        processor_dout : out std_logic_vector(15 downto 0);
        processor_we : in std_logic;
        
        VGA_RED : out std_logic_vector(2 downto 0);
        VGA_GREEN : out std_logic_vector(2 downto 0);
        VGA_BLUE : out std_logic_vector(2 downto 0);
        VGA_HSYNC : out std_logic;
        VGA_VSYNC : out std_logic;
        display_enable : out std_logic
    );
end component;
COMPONENT afifo_sm
  PORT (
    clk : IN STD_LOGIC;
    rst : IN STD_LOGIC;
    din : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
    wr_en : IN STD_LOGIC;
    rd_en : IN STD_LOGIC;
    dout : OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
    full : OUT STD_LOGIC;
    almost_full : OUT STD_LOGIC;
    overflow : OUT STD_LOGIC;
    empty : OUT STD_LOGIC;
    almost_empty : OUT STD_LOGIC;
    underflow : OUT STD_LOGIC
  );
END COMPONENT;

constant    AxBURST_FIXED    : std_logic_vector := "00";
constant    AxBURST_INCR    : std_logic_vector := "01";
constant    AxBURST_WRAP    : std_logic_vector := "10";

constant    RESP_OKAY        : std_logic_vector := "00";
constant    RESP_EXOKAY        : std_logic_vector := "01";
constant    RESP_SLVERR        : std_logic_vector := "10";
constant    RESP_DECERR        : std_logic_vector := "11";

-- for write transaction
type write_transaction_state is (idle_wr, awr_wait, awr_accept, wr_burst);
type write_response_state is (idle_wres, bvalid_assert);
type write_wready_state is (idle_wrdy, wready_assert);
signal wrt_cs : write_transaction_state;
signal wrres : write_response_state;
signal wrwr : write_wready_state;

-- for read transaction
type read_transaction_state is (idle_rd, arr_wait, arr_accept, rd_burst);
type read_last_state is (idle_rlast, rlast_assert);
signal rdt_cs : read_transaction_state;
signal rdlast : read_last_state;

signal reset_1d, reset_2d, reset : std_logic := '1';

signal awready         : std_logic;
signal wr_addr         : std_logic_vector(12 downto 0);
signal wr_bid         : std_logic_vector(0 downto 0);
signal wr_bresp     : std_logic_vector(1 downto 0);
signal wr_bvalid     : std_logic;

signal arready         : std_logic;
signal rd_addr         : std_logic_vector(12 downto 0);
signal rd_axi_count    : std_logic_vector(7 downto 0);
signal rd_cdc_count    : std_logic_vector(8 downto 0);
signal rdfifo_din    : std_logic_vector(15 downto 0);
signal rdfifo_wr_en    : std_logic;
signal rdfifo_rd_en    : std_logic;
signal rdfifo_full, rdfifo_empty    : std_logic;
signal rdfifo_almost_full, rdfifo_almost_empty : std_logic;
signal rdfifo_overflow, rdfifo_underflow : std_logic;
signal rvalid        : std_logic;
signal rlast        : std_logic;
signal cdc_addr        : std_logic_vector(12 downto 0);
signal vga_red, vga_green, vga_blue : std_logic_vector(2 downto 0);
signal vga_red8, vga_green8, vga_blue8 : std_logic_vector(7 downto 0);
signal vga_hsync, vga_vsync : std_logic;
signal display_enable : std_logic;
signal cdc_we : std_logic;
signal rdfifo_wr_en_node : std_logic;

begin
    -- ARESETN をACLK で同期化
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            reset_1d <= not ARESETN;
            reset_2d <= reset_1d;
        end if;
    end process;
    reset <= reset_2d;
    
    -- AXI4バス Write Transaction State Machine
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                wrt_cs <= idle_wr;
                awready <= '0';
            else
                case (wrt_cs) is
                    when idle_wr =>
                        if S_AXI_AWVALID='1' then -- S_AXI_AWVALID が1にアサートされた
                            if rdt_cs=idle_rd then -- Read Transaction が終了している(Writeの方が優先順位が高い)
                                wrt_cs <= awr_accept;
                                awready <= '1';
                            else -- Read Transaction が終了していないのでWait
                                wrt_cs <= awr_wait;
                            end if;
                        end if;
                    when awr_wait => -- Read Transaction の終了待ち
                        if rdt_cs=idle_rd or rdt_cs=arr_wait then -- Read Transaction が終了
                            wrt_cs <= awr_accept;
                            awready <= '1';
                        end if;
                    when awr_accept => -- S_AXI_AWREADY をアサート
                        wrt_cs <= wr_burst;
                        awready <= '0';
                    when wr_burst => -- Writeデータの転送
                        if S_AXI_WLAST='1' and S_AXI_WVALID='1' then -- Write Transaction 終了
                            wrt_cs <= idle_wr;
                        end if;
                end case;
            end if;
        end if;
    end process;
    S_AXI_AWREADY <= awready;
    S_AXI_WREADY <= '1' when wrt_cs=wr_burst else '0';
    cdc_we <= '1' when wrt_cs=wr_burst and S_AXI_WVALID='1' else '0';
    
    -- wr_addr の処理
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                wr_addr <= (others => '0');
            else
                if wrt_cs=awr_accept then
                    wr_addr <= S_AXI_AWADDR(14 downto 2); -- 32ビット幅データのため
                elsif wrt_cs=wr_burst and S_AXI_WVALID='1' then -- アドレスを1つ進める
                    wr_addr <= wr_addr + 1;
                end if;
            end if;
        end if;
    end process;
    
    -- wr_bid の処理
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                wr_bid <= "0";
            else
                if wrt_cs=awr_accept then
                    wr_bid <= S_AXI_AWID;
                end if;
            end if;
        end if;
    end process;
    S_AXI_BID <= wr_bid;
    
    -- wr_bresp の処理
    -- S_AXI_AWBURSTがINCRの時はOKAYを返す。それ以外はSLVERRを返す。
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                wr_bresp <= (others => '0');
            else
                if wrt_cs=awr_accept then
                    if S_AXI_AWBURST=AxBURST_INCR then -- バーストタイプがアドレス・インクリメントタイプ
                        wr_bresp <= RESP_OKAY; -- Write Transaction は成功
                    else
                        wr_bresp <= RESP_SLVERR; -- エラー
                    end if;
                end if;
            end if;
        end if;
    end process;
    S_AXI_BRESP <= wr_bresp;
    
    -- wr_bvalid の処理
    -- Write Transaction State Machineには含まない。axi_master のシミュレーションを見ると1クロックで終了しているので、長い間、Master側の都合でWaitしていることは考えない。
    -- 次のWrite転送まで遅延しているようであれば、Write Transaction State Machine に入れてブロックすることも考える必要がある。
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                wr_bvalid <= '0';
            else
                if S_AXI_WLAST='1' and S_AXI_WVALID='1' then -- Write Transaction 終了
                    wr_bvalid <= '1';
                elsif wr_bvalid='1' and S_AXI_BREADY='1' then -- wr_bvalid が1でMaster側のReadyも1ならばWrite resonse channel の転送も終了
                    wr_bvalid <= '0';
                end if;
            end if;
        end if;
    end process;
    S_AXI_BVALID <= wr_bvalid;
    S_AXI_BUSER <= (others => '0');
    
    
    -- AXI4バス Read Transaction State Machine
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                rdt_cs <= idle_rd;
                arready <= '0';
            else
                case (rdt_cs) is
                    when idle_rd =>
                        if S_AXI_ARVALID='1' then -- Read Transaction 要求
                            if wrt_cs=idle_wr and S_AXI_AWVALID='0' then -- Write Transaction State Machine がidle でWrite要求もない
                                rdt_cs <= arr_accept;
                                arready <= '1';
                            else -- Write Transaction が終了していないのでWait
                                rdt_cs <= arr_wait;
                            end if;
                        end if;
                    when arr_wait => -- Write Transaction の終了待ち
                        if wrt_cs=idle_wr and S_AXI_AWVALID='0' then -- Write Transaction State Machine がidle でWrite要求もない
                            rdt_cs <= arr_accept;
                            arready <= '1';
                        end if;
                    when arr_accept => -- S_AXI_ARREADY をアサート
                        rdt_cs <= rd_burst;
                        arready <= '0';
                    when rd_burst => -- Readデータの転送
                        if rd_axi_count=0 and rvalid='1' and S_AXI_RREADY='1' then -- Read Transaction 終了
                            rdt_cs <= idle_rd;
                        end if;
                end case;
            end if;
        end if;
    end process;
    S_AXI_ARREADY <= arready;
    
    -- rd_addr の処理
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                rd_addr <= (others => '0');
            else
                if rdt_cs=arr_accept then
                    rd_addr <= S_AXI_ARADDR(14 downto 2); -- 32ビット幅データのため
                elsif rdt_cs=rd_burst and rdfifo_almost_full='0' and rd_cdc_count/=0 then -- rdfifoに余裕があるときにはアドレスを1つ進める
                    rd_addr <= rd_addr + 1;
                end if;
            end if;
        end if;
    end process;
    
    -- Read用FIFOのインスタンス
    rdfifo : afifo_sm PORT MAP (
        clk => ACLK,
        rst => reset,
        din => rdfifo_din,
        wr_en => rdfifo_wr_en,
        rd_en => rdfifo_rd_en,
        dout => S_AXI_RDATA(15 downto 0),
        full => rdfifo_full,
        almost_full => rdfifo_almost_full,
        overflow => rdfifo_overflow,
        empty => rdfifo_empty,
        almost_empty => rdfifo_almost_empty,
        underflow => rdfifo_underflow
    );
    S_AXI_RDATA(31 downto 16) <= (others => '0');
    rvalid <= not rdfifo_empty;
    S_AXI_RVALID <= rvalid;
    
    rdfifo_wr_en_node <= '1' when rdt_cs=rd_burst and rdfifo_almost_full='0' and rd_cdc_count/=0 else '0';
    -- BlockRAMのReadは1クロック遅延するため、1クロック遅延させる。
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                rdfifo_wr_en <= '0';
            else
                rdfifo_wr_en <= rdfifo_wr_en_node;
            end if;
        end if;
    end process;
        
    rdfifo_rd_en <= '1' when rdt_cs=rd_burst and rvalid='1' and S_AXI_RREADY='1' else '0';
    
    -- rd_cdc_count の処理(CDC側のデータカウント)
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                rd_cdc_count <= (others => '0');
            else
                if rdt_cs=arr_accept then -- ロード
                    rd_cdc_count <= ('0'& S_AXI_ARLEN) + 1;
                elsif rdt_cs=rd_burst and rdfifo_almost_full='0' and rd_cdc_count/=0 then -- FIFOに余裕がある
                    rd_cdc_count <= rd_cdc_count - 1;
                end if;
            end if;
        end if;
    end process;
                    
    -- rd_axi_count の処理(AXIバス側のデータカウント)
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                rd_axi_count <= (others => '0');
            else
                if rdt_cs=arr_accept then -- rd_axi_count のロード
                    rd_axi_count <= S_AXI_ARLEN;
                elsif rdt_cs=rd_burst and rvalid='1' and S_AXI_RREADY='1' then -- Read Transaction が1つ終了
                    rd_axi_count <= rd_axi_count - 1;
                end if;
            end if;
        end if;
    end process;
    
    -- rdlast State Machine
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                rdlast <= idle_rlast;
                rlast <= '0';
            else
                case (rdlast) is
                    when idle_rlast =>
                        if rd_axi_count=1 and rvalid='1' and S_AXI_RREADY='1' then -- バーストする場合
                            rdlast <= rlast_assert;
                            rlast <= '1';
                        elsif rdt_cs=arr_accept and S_AXI_ARLEN=0 then -- 転送数が1の場合
                            rdlast <= rlast_assert;
                            rlast <= '1';
                        end if;
                    when rlast_assert => 
                        if rvalid='1' and S_AXI_RREADY='1' then -- Read Transaction 終了(rd_axi_count=0は決定)
                            rdlast <= idle_rlast;
                            rlast <= '0';
                        end if;
                end case;
            end if;
        end if;
    end process;
    S_AXI_RLAST <= rlast;
    
    -- S_AXI_RID, S_AXI_RUSER の処理
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                S_AXI_RID <= (others => '0');
            else
                if rdt_cs=arr_accept then
                    S_AXI_RID <= S_AXI_ARID;
                end if;
            end if;
        end if;
    end process;
    S_AXI_RUSER <= (others => '0');
    
    -- S_AXI_RRESP は、S_AXI_ARBURST がINCR の場合はOKAYを返す。それ以外はSLVERRを返す。
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                S_AXI_RRESP <= (others => '0');
            else
                if rdt_cs=arr_accept then
                    if S_AXI_ARBURST=AxBURST_INCR then
                        S_AXI_RRESP <= RESP_OKAY;
                    else
                        S_AXI_RRESP <= RESP_SLVERR;
                    end if;
                end if;
            end if;
        end if;
    end process;
    
    
    -- CharDispCtrler
    cdc_addr <= wr_addr when rdt_cs=idle_rd or rdt_cs=arr_wait else rd_addr;
    
    CDC_inst : CharDispCtrler port map (
        axi4clk         => ACLK,
        pixclk            => pixclk,
        reset            => reset,
        processor_addr    => cdc_addr,
        processor_din    => S_AXI_WDATA(15 downto 0),
        processor_dout    => rdfifo_din,
        processor_we    => cdc_we,
        VGA_RED            => vga_red,
        VGA_GREEN        => vga_green,
        VGA_BLUE        => vga_blue,
        VGA_HSYNC        => vga_hsync,
        VGA_VSYNC        => vga_vsync,
        display_enable    => display_enable
    );
    
    -- dvi_disp
    -- VGA解像度 PLL VCO Freq=400MHz ~ 1000MHz なので、25MHz入力クロックだと最初に20倍する必要がある。
    -- XGA 解像度だと、PLL_CLKFBOUT_MULT=20だと、PLL VCO Freq=1300MHzとなり、1000MHzを超えるので、10倍に変更する。
    dvi_disp_inst : dvi_disp generic map (
        -- PLL_CLKFBOUT_MULT    => 20,    -- VGA
        -- PLL_CLKIN_PERIOD    => 40.0,
        -- PLL_CLKOUT0_DIVIDE    => 2,
        -- PLL_CLKOUT1_DIVIDE    => 20,
        -- PLL_CLKOUT2_DIVIDE    => 10
        
        PLL_CLKFBOUT_MULT    => 20,    -- SVGA
        PLL_CLKIN_PERIOD    => 25.0,
        PLL_CLKOUT0_DIVIDE    => 2,
        PLL_CLKOUT1_DIVIDE    => 20,
        PLL_CLKOUT2_DIVIDE    => 10
        
        -- PLL_CLKFBOUT_MULT    => 10,    -- XGA
        -- PLL_CLKIN_PERIOD    => 15.4,
        -- PLL_CLKOUT0_DIVIDE    => 1,
        -- PLL_CLKOUT1_DIVIDE    => 10,
        -- PLL_CLKOUT2_DIVIDE    => 5
        
        -- PLL_CLKFBOUT_MULT    => 10,    -- SXGA(-3スピードグレードのみ)
        -- PLL_CLKIN_PERIOD        => 9.3,
        -- PLL_CLKOUT0_DIVIDE    => 1,
        -- PLL_CLKOUT1_DIVIDE    => 10,
        -- PLL_CLKOUT2_DIVIDE    => 5
    ) port map (
        pixclk    => pixclk,
        reset_in    => reset,
        red_in        => vga_red8,
        green_in    => vga_green8,
        blue_in        => vga_blue8,
        hsync        => vga_hsync,
        vsync        => vga_vsync,
        display_enable    => display_enable,
        TMDS_tx_clk_p    => TMDS_tx_clk_p,
        TMDS_tx_clk_n    => TMDS_tx_clk_n,
        TMDS_tx_2_G_p    => TMDS_tx_2_G_p,
        TMDS_tx_2_G_n    => TMDS_tx_2_G_n,
        TMDS_tx_1_R_p    => TMDS_tx_1_R_p,
        TMDS_tx_1_R_n    => TMDS_tx_1_R_n,
        TMDS_tx_0_B_p    => TMDS_tx_0_B_p,
        TMDS_tx_0_B_n    => TMDS_tx_0_B_n
    );
    
    vga_red8    <= vga_red & "00000";
    vga_green8    <= vga_green & "00000";
    vga_blue8     <= vga_blue & "00000";

end implementation;


(2012/04/04:cdc_axi_slave.vhd のバグを修正)

下に、afifo_sm.xco の内容を示す。

##############################################################
#
# Xilinx Core Generator version 13.4
# Date: Sat Feb 25 06:45:30 2012
#
##############################################################
#
# This file contains the customisation parameters for a
# Xilinx CORE Generator IP GUI. It is strongly recommended
# that you do not manually alter this file as it may cause
# unexpected and unsupported behavior.
#
##############################################################
#
# Generated from component: xilinx.com:ip:fifo_generator:8.4
#
##############################################################
#
# BEGIN Project Options
SET addpads = false
SET asysymbol = true
SET busformat = BusFormatAngleBracketNotRipped
SET createndf = false
SET designentry = VHDL
SET device = xc6slx45
SET devicefamily = spartan6
SET flowvendor = Other
SET formalverification = false
SET foundationsym = false
SET implementationfiletype = Ngc
SET package = csg324
SET removerpms = false
SET simulationfiles = Behavioral
SET speedgrade = -2
SET verilogsim = false
SET vhdlsim = true
# END Project Options
# BEGIN Select
SELECT Fifo_Generator xilinx.com:ip:fifo_generator:8.4
# END Select
# BEGIN Parameters
CSET add_ngc_constraint_axi=false
CSET almost_empty_flag=true
CSET almost_full_flag=true
CSET aruser_width=1
CSET awuser_width=1
CSET axi_address_width=32
CSET axi_data_width=64
CSET axi_type=AXI4_Stream
CSET axis_type=FIFO
CSET buser_width=1
CSET clock_enable_type=Slave_Interface_Clock_Enable
CSET clock_type_axi=Common_Clock
CSET component_name=afifo_sm
CSET data_count=false
CSET data_count_width=5
CSET disable_timing_violations=false
CSET disable_timing_violations_axi=false
CSET dout_reset_value=0
CSET empty_threshold_assert_value=4
CSET empty_threshold_assert_value_axis=1022
CSET empty_threshold_assert_value_rach=1022
CSET empty_threshold_assert_value_rdch=1022
CSET empty_threshold_assert_value_wach=1022
CSET empty_threshold_assert_value_wdch=1022
CSET empty_threshold_assert_value_wrch=1022
CSET empty_threshold_negate_value=5
CSET enable_aruser=false
CSET enable_awuser=false
CSET enable_buser=false
CSET enable_common_overflow=false
CSET enable_common_underflow=false
CSET enable_data_counts_axis=false
CSET enable_data_counts_rach=false
CSET enable_data_counts_rdch=false
CSET enable_data_counts_wach=false
CSET enable_data_counts_wdch=false
CSET enable_data_counts_wrch=false
CSET enable_ecc=false
CSET enable_ecc_axis=false
CSET enable_ecc_rach=false
CSET enable_ecc_rdch=false
CSET enable_ecc_wach=false
CSET enable_ecc_wdch=false
CSET enable_ecc_wrch=false
CSET enable_handshake_flag_options_axis=false
CSET enable_handshake_flag_options_rach=false
CSET enable_handshake_flag_options_rdch=false
CSET enable_handshake_flag_options_wach=false
CSET enable_handshake_flag_options_wdch=false
CSET enable_handshake_flag_options_wrch=false
CSET enable_read_channel=false
CSET enable_read_pointer_increment_by2=false
CSET enable_reset_synchronization=true
CSET enable_ruser=false
CSET enable_tdata=false
CSET enable_tdest=false
CSET enable_tid=false
CSET enable_tkeep=false
CSET enable_tlast=false
CSET enable_tready=true
CSET enable_tstrobe=false
CSET enable_tuser=false
CSET enable_write_channel=false
CSET enable_wuser=false
CSET fifo_application_type_axis=Data_FIFO
CSET fifo_application_type_rach=Data_FIFO
CSET fifo_application_type_rdch=Data_FIFO
CSET fifo_application_type_wach=Data_FIFO
CSET fifo_application_type_wdch=Data_FIFO
CSET fifo_application_type_wrch=Data_FIFO
CSET fifo_implementation=Common_Clock_Distributed_RAM
CSET fifo_implementation_axis=Common_Clock_Block_RAM
CSET fifo_implementation_rach=Common_Clock_Block_RAM
CSET fifo_implementation_rdch=Common_Clock_Block_RAM
CSET fifo_implementation_wach=Common_Clock_Block_RAM
CSET fifo_implementation_wdch=Common_Clock_Block_RAM
CSET fifo_implementation_wrch=Common_Clock_Block_RAM
CSET full_flags_reset_value=1
CSET full_threshold_assert_value=15
CSET full_threshold_assert_value_axis=1023
CSET full_threshold_assert_value_rach=1023
CSET full_threshold_assert_value_rdch=1023
CSET full_threshold_assert_value_wach=1023
CSET full_threshold_assert_value_wdch=1023
CSET full_threshold_assert_value_wrch=1023
CSET full_threshold_negate_value=14
CSET id_width=4
CSET inject_dbit_error=false
CSET inject_dbit_error_axis=false
CSET inject_dbit_error_rach=false
CSET inject_dbit_error_rdch=false
CSET inject_dbit_error_wach=false
CSET inject_dbit_error_wdch=false
CSET inject_dbit_error_wrch=false
CSET inject_sbit_error=false
CSET inject_sbit_error_axis=false
CSET inject_sbit_error_rach=false
CSET inject_sbit_error_rdch=false
CSET inject_sbit_error_wach=false
CSET inject_sbit_error_wdch=false
CSET inject_sbit_error_wrch=false
CSET input_data_width=16
CSET input_depth=16
CSET input_depth_axis=1024
CSET input_depth_rach=16
CSET input_depth_rdch=1024
CSET input_depth_wach=16
CSET input_depth_wdch=1024
CSET input_depth_wrch=16
CSET interface_type=Native
CSET output_data_width=16
CSET output_depth=16
CSET overflow_flag=true
CSET overflow_flag_axi=false
CSET overflow_sense=Active_High
CSET overflow_sense_axi=Active_High
CSET performance_options=First_Word_Fall_Through
CSET programmable_empty_type=No_Programmable_Empty_Threshold
CSET programmable_empty_type_axis=Empty
CSET programmable_empty_type_rach=Empty
CSET programmable_empty_type_rdch=Empty
CSET programmable_empty_type_wach=Empty
CSET programmable_empty_type_wdch=Empty
CSET programmable_empty_type_wrch=Empty
CSET programmable_full_type=No_Programmable_Full_Threshold
CSET programmable_full_type_axis=Full
CSET programmable_full_type_rach=Full
CSET programmable_full_type_rdch=Full
CSET programmable_full_type_wach=Full
CSET programmable_full_type_wdch=Full
CSET programmable_full_type_wrch=Full
CSET rach_type=FIFO
CSET rdch_type=FIFO
CSET read_clock_frequency=1
CSET read_data_count=false
CSET read_data_count_width=5
CSET register_slice_mode_axis=Fully_Registered
CSET register_slice_mode_rach=Fully_Registered
CSET register_slice_mode_rdch=Fully_Registered
CSET register_slice_mode_wach=Fully_Registered
CSET register_slice_mode_wdch=Fully_Registered
CSET register_slice_mode_wrch=Fully_Registered
CSET reset_pin=true
CSET reset_type=Asynchronous_Reset
CSET ruser_width=1
CSET synchronization_stages=2
CSET synchronization_stages_axi=2
CSET tdata_width=64
CSET tdest_width=4
CSET tid_width=8
CSET tkeep_width=4
CSET tstrb_width=4
CSET tuser_width=4
CSET underflow_flag=true
CSET underflow_flag_axi=false
CSET underflow_sense=Active_High
CSET underflow_sense_axi=Active_High
CSET use_clock_enable=false
CSET use_dout_reset=true
CSET use_embedded_registers=false
CSET use_extra_logic=true
CSET valid_flag=false
CSET valid_sense=Active_High
CSET wach_type=FIFO
CSET wdch_type=FIFO
CSET wrch_type=FIFO
CSET write_acknowledge_flag=false
CSET write_acknowledge_sense=Active_High
CSET write_clock_frequency=1
CSET write_data_count=false
CSET write_data_count_width=5
CSET wuser_width=1
# END Parameters
# BEGIN Extra information
MISC pkg_timestamp=2011-10-22T06:08:52Z
# END Extra information
GENERATE
# CRC: 76cfed70


CharDispCtrlerは、”キャラクタ・ディスプレイ・コントローラのまとめ”からダウンロードすることが出来る。(Verilogです)

dvi_disp.vhd は、”DVI、HDMIの勉強4(キャラクタ・ディスプレイ・コントローラをDVI出力にする VHDL編1)”にVHDLソースコードを貼ってある。dvi_disp.vhd の下位階層のファイルについては、自分で作成したVHDLコードではないので、ブログに貼ることができない。VmodCAMのサンプルのVmodCAM_Ref_VGA Demo_13.zip をダウンロードして、ライブラリをいじってほしい。具体的には、インスタンス時のdigilentライブラリへのリンクは消して、VHDLファイルにcomponent宣言を追加した。
  1. 2012年03月29日 05:12 |
  2. AXI4 Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

擬似乱数、M系列を使う2

擬似乱数、M系列を使う1”の続き。

前回は8ビットのM系列を試してみたが、256回目で元のパターンに戻るので、ちょっと物足りないかな?と思ったので、16ビットのM系列の下8ビットを取ってみることにした。これならば、元のパターンに戻るのは、65536回目だ。

下に変更した下に、m_seq_test.v を示す。16ビットのM系列の下8ビットを取る mseq8_2 を増やした。

// M sequence test(m_seq_test.v)

`default_nettype none

module m_seq_test(
    input    wire         clk,
    input    wire        reset,
    output    reg [7:0]    mseq8,
    output    wire [7:0]    mseq8_2
);

    reg    [15:0]    mseq16;
    
    function [7:0] mseqf8_0 (input [7:0] din);
        reg xor_result;
        begin
            xor_result = din[7] ^ din[3] ^ din[2] ^ din[1];
            mseqf8_0 = {din[6:0], xor_result};
        end
    endfunction
    
    function [7:0] mseqf8_1 (input [7:0] din);
        reg xor_result;
        begin
            xor_result = din[7] ^ din[4] ^ din[2] ^ din[0];
            mseqf8_1 = {din[6:0], xor_result};
        end
    endfunction
    
    function [15:0] mseqf16 (input [15:0] din);
        reg xor_result;
        begin
            xor_result = din[15] ^ din[12] ^ din[10] ^ din[8] ^ din[7] ^ din[6] ^ din[3] ^ din[2];
            mseqf16 = {din[14:0], xor_result};
        end
    endfunction        
    
    always @(posedge clk) begin
        if (reset) 
            mseq8 <= 8'd1;
        else
            mseq8 <= mseqf8_0(mseq8);
    end
    
    always @(posedge clk) begin
        if (reset) 
            mseq16 <= 16'd1;
        else
            mseq16 <= mseqf16(mseq16);
    end
    assign mseq8_2 = mseq16[7:0];
    
endmodule

`default_nettype wire


16ビットのM系列のタップ位置は、95CC を選択した。15, 12, 10, 8, 7, 6, 3, 2ビット目のXOR をLSBに入れて左シフトしている。

m_seq_test.v をテストするm_seq_test_tb.v も変更した。下に示す。

`default_nettype none

`timescale 1ns / 1ps

module m_seq_test_tb;

    // Inputs
    reg clk = 1'b0;
    reg reset;
    wire [7:0] mseq8;
    wire [7:0] mseq8_2;

    // Instantiate the Unit Under Test (UUT)
    m_seq_test uut (
        .clk(clk), 
        .reset(reset), 
        .mseq8(mseq8),
        .mseq8_2(mseq8_2)
    );

    parameter        CLK_PERIOD = 10;
    parameter real    CLK_DUTY_CYCLE = 0.5;
    parameter        CLK_OFFSET = 0;
    parameter        START_STATE    = 1'b0;
    
    initial begin
        #CLK_OFFSET;
        forever begin
            clk = START_STATE;
            #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) clk = ~START_STATE;
            #(CLK_PERIOD*CLK_DUTY_CYCLE);
        end
    end

    // リセットが解除された後から、mseq8の値をクロックが立ち上がるごとにmseq8.txtに書き込む
    integer F_HANDLE;
    initial F_HANDLE = $fopen("mseq8.txt");

    always @(posedge clk) begin
        if (~reset) begin
            $fdisplay(F_HANDLE, "%b", mseq8);
            $fflush(F_HANDLE);
        end
    end

    // リセットが解除された後から、mseq8_2の値をクロックが立ち上がるごとにmseq8_2.txtに書き込む
    integer F_HANDLE2;
    initial F_HANDLE2 = $fopen("mseq8_2.txt");

    always @(posedge clk) begin
        if (~reset) begin
            $fdisplay(F_HANDLE2, "%b", mseq8_2);
            $fflush(F_HANDLE2);
        end
    end

    initial begin
        // Initialize Inputs
        reset = 1'b1;

        // Wait 100 ns for global reset to finish
        #100;
        
        // Add stimulus here
        reset = 1'b0;
        
        #660000;
        $fclose(F_HANDLE);
        $fclose(F_HANDLE2);
        $finish;
    end
      
endmodule

`default_nettype wire



mseq8_2.txt の一部を下に示す。

00000001
00000010
00000100
00001001
00010011
00100110
01001101
10011011
00110110
01101100


65536回目から65545回目にかけても同じパターンがあった。

8ビットのM系列ではオール0はシードが無くなってしまうのでダメだったが、16ビットのM系列の下8ビットを使用するならばオール0も可能だ。よって、mseq16_f ファンクションの下位8ビットをAXI4バスのバースト長の設定値として使うことにする。

  1. 2012年03月28日 05:18 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

擬似乱数、M系列を使う1

今度、作製するAXI4マスタIP用に擬似乱数としてM系列を使おうと思う。M系列は今から約30年前に大学の符号理論で習った。その時に教えて頂いた先生はもうすでに亡くなられた。今回は亡くなられた先生を偲びながら、M系列の性質について勉強しながら、Verilogファイルを作成して、シミュレーションで、その性質を検証してみようと思う。(元々、これについては、Veritakの作者の菅原様から教えて頂いた)

ウィキペディアのM系列の項目によると、”M系列(-けいれつ、m-sequence;maximal length sequence)とは、ガロア体における線形漸化式が生成する数列(sequence)のうち最長の周期(maximal length)を持つもの。”とある。M系列の生成多項式のページによると、”n ビットのシフトレジスタと フィードバックで生成される周期が 2^n - 1 の符号列”ということだ。 2^n - 1毎に自己相関のピークが来て、それ以外の自己相関値は低い。つまり乱数に近いという事になる。

擬似乱数としては、線形帰還シフトレジスタのフィボナッチ LFSRで生成する方式が有力らしい。
M系列の生成多項式のページにも書いてあるが、更にたくさんの例が、線型M系列符号発生器の結線表のページに書かれている。その内の mms8.txt を参考にした。mms8.txt の値の内の”8E, 95”を実装する。実装の仕方は、95を例にとると、95の2進表示は、"1001 0101"なので、bit7, bit4, bit2, bit0 のXORを取って、LSBにシフトする。

さて、この8ビットM系列の例を下に示す。なお、書き方は、Veritakの作者の菅原様から教えて頂いたVerilogコードを参考にさせて頂いた。下に、m_seq_test.v を示す。mseqf8_0 は 8EのM系列、mseqf8_1 は 95 のM系列だ。

// M sequence test(m_seq_test.v)

`default_nettype none

module m_seq_test(
    input    wire         clk,
    input    wire        reset,
    output    reg [7:0]    mseq8
);
    
    function [7:0] mseqf8_0 (input [7:0] din);
        reg xor_result;
        begin
            xor_result = din[7] ^ din[3] ^ din[2] ^ din[1];
            mseqf8_0 = {din[6:0], xor_result};
        end
    endfunction
    
    function [7:0] mseqf8_1 (input [7:0] din);
        reg xor_result;
        begin
            xor_result = din[7] ^ din[4] ^ din[2] ^ din[0];
            mseqf8_1 = {din[6:0], xor_result};
        end
    endfunction
    
    always @(posedge clk) begin
        if (reset) 
            mseq8 <= 8'd1;
        else
            mseq8 <= mseqf8_0(mseq8);
    end
    
endmodule

`default_nettype wire


”mseq8 <= 8'd1;”を初期値として代入しているが、ここが0だとずーと0になってしまう。初期値は0以外の数を代入する。

m_seq_test.v をテストするm_seq_test_tb.v を下に示す。今回は、リセットが解除された後から、mseq8の値をクロックが立ち上がるごとにmseq8.txtに書き込む。これで、mseq8.txtを見ればビットパターンがわかるわけだ。
なお、これらのVerilog 記述は、ファイル入出力システムタスクのページファイル入出力タスクのページを参考にさせて頂いた。

`default_nettype none

`timescale 1ns / 1ps

module m_seq_test_tb;

    // Inputs
    reg clk = 1'b0;
    reg reset;
    wire [7:0] mseq8;

    // Instantiate the Unit Under Test (UUT)
    m_seq_test uut (
        .clk(clk), 
        .reset(reset), 
        .mseq8(mseq8)
    );

    parameter        CLK_PERIOD = 10;
    parameter real    CLK_DUTY_CYCLE = 0.5;
    parameter        CLK_OFFSET = 0;
    parameter        START_STATE    = 1'b0;
    
    initial begin
        #CLK_OFFSET;
        forever begin
            clk = START_STATE;
            #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) clk = ~START_STATE;
            #(CLK_PERIOD*CLK_DUTY_CYCLE);
        end
    end

    // リセットが解除された後から、mseq8の値をクロックが立ち上がるごとにmseq8.txtに書き込む
    integer F_HANDLE;
    initial F_HANDLE = $fopen("mseq8.txt");

    always @(posedge clk) begin
        if (~reset) begin
            $fdisplay(F_HANDLE, "%b", mseq8);
            $fflush(F_HANDLE);
        end
    end

    initial begin
        // Initialize Inputs
        reset = 1'b1;

        // Wait 100 ns for global reset to finish
        #100;
        
        // Add stimulus here
        reset = 1'b0;
        
        #9900;
        $fclose(F_HANDLE);
        $finish;
    end
      
endmodule

`default_nettype wire


これでISimでシミュレーションを行った。
M_seq_1_120327.png

mseq8.txt をmseq8_0.txtとリネームしたファイルの一部を下に示す。

00000001
00000010
00000101
00001011
00010110
00101100
01011000
10110001
01100011
11000111


上のデータは1番目から10番目だが、256番目から265番目まで、511番目から520番目も同じデータだった。

次に、mseqf8_1ファンクションの1番目から10番目、256番目から265番目、511番目から520番目のデータを下に示す。

00000001
00000011
00000111
00001110
00011101
00111011
01110110
11101100
11011000
10110000


次回は、実際に使用するM系列を決定する。
  1. 2012年03月27日 22:15 |
  2. IP
  3. | トラックバック:0
  4. | コメント:2

AXI4マスタIPの作製(仕様の策定)

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする13(Writeはうまく行った)”でAXI4スレーブIPのWriteはうまく行った。Readの検証ができないので、キャラクタ・ディスプレイ・コントローラAXI4スレーブIPを駆動するAXI4マスタIPを作製することにした。

今回は仕様を決めようと思う。仕様としては、

・WriteとReadは交互に行うことにする。WriteとReadがオーバーラップする場合はシミュレーションで確かめたので良しとする。

・Write、Readするデータは0x0から0xFFFFまでとする。当然、キャラクタとして表示されない文字コードがあるが、それは良しとする。最初に書き込むデータは0x0として、次に書き込むデータは+1された0x1とする。その後も+1して行って0xFFFFになったら、0x0に戻す。

・1バーストから256バーストまでのバースト長のデータ転送を行う。バースト長はM系列乱数ジェネレータで生成する。

・Readでは、Writeと同じデータを読み出せることを確認する。データが正しい場合はOKのLEDを点灯する。データが間違っていた場合は、NGのLEDを点灯し、OKのLEDを消灯する。一度、NGのLEDを点灯したら、リセットするまで、そのまま保持する。

・WriteとReadの組みは0.2sec に1回行う。


以上の仕様からAXI4マスタIPを作製する。
  1. 2012年03月25日 05:39 |
  2. AXI4 Master IPの作製
  3. | トラックバック:0
  4. | コメント:0

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする13(Writeはうまく行った)

windyさんから、貴重なアドバイスを頂いてキャラクタ・ディスプレイ・コントローラ AXI4バス・スレーブIPにMicroBlazeからアクセスすることができた。windyさん、ありがとうございました。助かりました。windyさんのアドバイスは”キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする11(インプリメント3)”のコメント欄を参照ください。

そうか、M_AXI_DCとM_AXI_IC は、キャッシュ直結のインターコネクトだったのか?そう言われてみれば、構成とすれば、そうなるか。MicroBlaze Processor Reference Guide Embedded Development Kit EDK 13.4 UG081 (v13.4) のFigure 2-1: MicroBlaze Core Block Diagram を見るとよく分かる。図を下に引用する。
CDC_axi_slave_53_120324.png

・M_AXI_DCとM_AXI_IC はD-CacheとI-Cache に接続されている。cdc_axi_slave をM_AXI_DC のみに接続した。
CDC_axi_slave_52_120324.png

・microblaze_0 をダブルクリックして、設定ダイアログを表示させて、CacheタブのData Cache Featureで、High Address を0xc7ffffff から0xcfffffff に変更した。
CDC_axi_slave_54_120324.png

・cdc_axi_slave_0 のベースアドレスを0xc8000000 に変更した。つまり、D-Cache の範囲に入れた。(2012/04/02:下図でcdc_axi_slave_0 のメモリ・サイズを16Kbytes にしているが、計算の結果これでは足りない。800/8 * 600/8 *4 ≒ 30Kbytes 必要だ)
CDC_axi_slave_55_120324.png

・これでインプリメントした。SDKのProgram FPGAが使えないので、ChipScope からFPGAをコンフィグレーションした。

・Project Navigator からExport Hardware Design To SDK with Bitstream でSDKを立ち上げて、Cプロジェクトを作成した。

・SDKで.elf ファイルの右クリックメニューからDebug As を選択して、デバックモードへ。

・XMDでキャラクタ・ディスプレイ・コントローラ AXI4バス・スレーブIPに mwr コマンドでデータを書き込んだ。
CDC_axi_slave_56_120324.png

・ディスプレイにも表示された。
CDC_axi_slave_57_120324.png

・mwr 0xc800000c 0x0000fc45 のAXI4バスのタイミングチャートを下に貼っておく。
CDC_axi_slave_58_120324.png

抜けいてる信号もあるが、今ある信号を見るかぎりは大丈夫そうだ。

Writeは大丈夫そうだが、Readがおかしいみたいだ。

・mrd 0xc8000000 を実行すると、ERROR: Cannot Read from targetになって、Readすることができない。
CDC_axi_slave_59_120324.png

ChipScope でS_AXI_ARVALID でトリガをかけてもトリガがかからない。キャラクタ・ディスプレイ・コントローラ AXI4バス・スレーブIPにアクセスが来ないみたいだ。

これで、AXI4スレーブIPはとりあえず終了として、次は、このキャラクタ・ディスプレイ・コントローラ AXI4バス・スレーブIPをテストするAXI4バス・マスタIPを作ろうと思う。
  1. 2012年03月24日 09:22 |
  2. AXI4 Slave IPの作製
  3. | トラックバック:0
  4. | コメント:2

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする12(現状)

キャラクタ・ディスプレイ・コントローラのAXI4バス・スレーブIPの作成はあまりうまく行ってないが、現状を書いておこうと思う。

まずは、AXI4バス・スレーブIPの名前がCDC_axi_slave で、CDCの大文字が入っているので、シミュレーションできなかと思って、一旦、CDC_axi_slave をデリートして、cdc_axi_slave に書き換えて、もう一度XPSにAdd IP してみた。そうして、シミュレーションしてみたが、やはり、cdc_axi_slave がないと言われてしまった。
CDC_axi_slave_46_120322.png

SDKのXilinx ToolsメニューからProgram FPGA を選択したら、エラーになってしまう。
CDC_axi_slave_48_120322.png

仕方がないので、iMPACT でコンフィグレーションしてから、SDKでchardisp_w_r.elf を右クリックメニューからDebug As -> Launch on Hardware を選択して、デバックモードへ移行する。
XMDを起動して、mwr 0x0001000 0x0000ffc1 でキャラクタ・ディスプレイ・コントローラのAXI4バス・スレーブIPにキャラクタ・データの書き込みを行うが、mrd 0x0001000 で読みだしてもオール0のままで書き込めていない。
CDC_axi_slave_47_120322.png

同じAXI4バス上にあるMCBには読み書きできる。

(現在は、microblaze_0 のAXI4バスのアドレス0x0001000 にキャラクタ・ディスプレイ・コントローラのAXI4バス・スレーブIPを接続している)
CDC_axi_slave_49_120322.png

ChipScope でS_AXI_AWVALID が1の時にトリガーをかけておいてもmwr 0x0001000 0x0000ffc1 でトリガーがかからない。
CDC_axi_slave_50_120322.png

どうやってデバックするか?を考えている。

(2010/03/24:追加)
cdc_axi_slave_v2_1_0.pao ファイルに、”lib cdc_axi_slave_v1_00_a afifo_sm.vhd vhdl”の行を追加してもシミュレーションはできません。
system_top_tb_beh.prj にはcdc_axi_slave.vhd の行もあったし、良く分かりません。つまりコンパイルできているはず。なんでエラボレートできないのだろうか?名前間違っている?

  1. 2012年03月22日 06:01 |
  2. AXI4 Slave IPの作製
  3. | トラックバック:0
  4. | コメント:5

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする11(インプリメント3)

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする10(インプリメント2)”でインプリメントすることができたので、今度はソフトウェアを作成して、キャラクタ・ディスプレイ・コントローラをテストする。

その前にMAPのレポートを下に貼っておく。

Release 13.4 Map O.87xd (nt)
Xilinx Mapping Report File for Design 'system_top'

Design Information
------------------
Command Line   : map -intstyle ise -p xc6slx45-csg324-2 -w -logic_opt off -ol
high -t 1 -xt 0 -register_duplication off -r 4 -global_opt off -mt off -ir off
-pr off -lc off -power off -o system_top_map.ncd system_top.ngd system_top.pcf 
Target Device  : xc6slx45
Target Package : csg324
Target Speed   : -2
Mapper Version : spartan6 -- $Revision: 1.55 $
Mapped Date    : MON 19 MAR 6:1:45 2012

Design Summary
--------------
Number of errors:      0
Number of warnings:   30
Slice Logic Utilization:
  Number of Slice Registers:                 4,171 out of  54,576    7%
    Number used as Flip Flops:               4,156
    Number used as Latches:                      0
    Number used as Latch-thrus:                  0
    Number used as AND/OR logics:               15
  Number of Slice LUTs:                      5,209 out of  27,288   19%
    Number used as logic:                    4,777 out of  27,288   17%
      Number using O6 output only:           3,550
      Number using O5 output only:             113
      Number using O5 and O6:                1,114
      Number used as ROM:                        0
    Number used as Memory:                     292 out of   6,408    4%
      Number used as Dual Port RAM:            108
        Number using O6 output only:             4
        Number using O5 output only:             1
        Number using O5 and O6:                103
      Number used as Single Port RAM:            4
        Number using O6 output only:             4
        Number using O5 output only:             0
        Number using O5 and O6:                  0
      Number used as Shift Register:           180
        Number using O6 output only:            83
        Number using O5 output only:             1
        Number using O5 and O6:                 96
    Number used exclusively as route-thrus:    140
      Number with same-slice register load:    123
      Number with same-slice carry load:        12
      Number with other load:                    5

Slice Logic Distribution:
  Number of occupied Slices:                 2,143 out of   6,822   31%
  Nummber of MUXCYs used:                      880 out of  13,644    6%
  Number of LUT Flip Flop pairs used:        6,114
    Number with an unused Flip Flop:         2,333 out of   6,114   38%
    Number with an unused LUT:                 905 out of   6,114   14%
    Number of fully used LUT-FF pairs:       2,876 out of   6,114   47%
    Number of unique control sets:             343
    Number of slice register sites lost
      to control set restrictions:           1,353 out of  54,576    2%

  A LUT Flip Flop pair for this architecture represents one LUT paired with
  one Flip Flop within a slice.  A control set is a unique combination of
  clock, reset, set, and enable signals for a registered element.
  The Slice Logic Distribution report is not meaningful if the design is
  over-mapped for a non-slice resource or if Placement fails.

IO Utilization:
  Number of bonded IOBs:                        97 out of     218   44%
    Number of LOCed IOBs:                       97 out of      97  100%
    IOB Flip Flops:                             11
    IOB Master Pads:                             4
    IOB Slave Pads:                              4

Specific Feature Utilization:
  Number of RAMB16BWERs:                        29 out of     116   25%
  Number of RAMB8BWERs:                          0 out of     232    0%
  Number of BUFIO2/BUFIO2_2CLKs:                 1 out of      32    3%
    Number used as BUFIO2s:                      1
    Number used as BUFIO2_2CLKs:                 0
  Number of BUFIO2FB/BUFIO2FB_2CLKs:             0 out of      32    0%
  Number of BUFG/BUFGMUXs:                       5 out of      16   31%
    Number used as BUFGs:                        5
    Number used as BUFGMUX:                      0
  Number of DCM/DCM_CLKGENs:                     0 out of       8    0%
  Number of ILOGIC2/ISERDES2s:                   6 out of     376    1%
    Number used as ILOGIC2s:                     6
    Number used as ISERDES2s:                    0
  Number of IODELAY2/IODRP2/IODRP2_MCBs:        24 out of     376    6%
    Number used as IODELAY2s:                    0
    Number used as IODRP2s:                      2
    Number used as IODRP2_MCBs:                 22
  Number of OLOGIC2/OSERDES2s:                  58 out of     376   15%
    Number used as OLOGIC2s:                     5
    Number used as OSERDES2s:                   53
  Number of BSCANs:                              1 out of       4   25%
  Number of BUFHs:                               0 out of     256    0%
  Number of BUFPLLs:                             1 out of       8   12%
  Number of BUFPLL_MCBs:                         1 out of       4   25%
  Number of DSP48A1s:                            3 out of      58    5%
  Number of ICAPs:                               0 out of       1    0%
  Number of MCBs:                                1 out of       2   50%
  Number of PCILOGICSEs:                         0 out of       2    0%
  Number of PLL_ADVs:                            2 out of       4   50%
  Number of PMVs:                                0 out of       1    0%
  Number of STARTUPs:                            0 out of       1    0%
  Number of SUSPEND_SYNCs:                       0 out of       1    0%

Average Fanout of Non-Clock Nets:                3.94

Peak Memory Usage:  407 MB
Total REAL time to MAP completion:  7 mins 6 secs 
Total CPU time to MAP completion:   7 mins 20 secs 


それでは、ソフトウェアを作るためにSDKを起動しよう。

・Project Navigator でsystem.xmp をクリックして、Export Hardware Desin To SDK with Bitstream をダブルクリックする。
CDC_axi_slave_40_120320.png

・SDKが立ち上がる。ワークスペースを聞いてくるので、Atlys_XPS_CDC_SVGA_134\SDK を指定した。

・SDKが立ち上がった。
CDC_axi_slave_41_120320.png

・FileメニューからNew -> Xilinx C Project を選択する。

・New Project ダイアログが立ち上がる。chardispctrl_test プロジェクトを作成する。Select Project Templete でEmpty Application を選択した。
CDC_axi_slave_42_120320.png

・Board Support Package project を作成する。
CDC_axi_slave_43_120320.png

・chardispctrl_test プロジェクトとempty_application_bsp_0 プロジェクトが生成された。

・・FileメニューからNew -> Source File を選択する。New Source File が出るので、Source file にchardispctrl_test.c を指定した。
CDC_axi_slave_45_120320.png

キャラクタ・ディスプレイ・コントローラの仕様は、”キャラクタ・ディスプレイ・コントローラの仕様の変更”に書いた。”赤 (R) の色情報は 15~13ビット目の3ビット、緑 (R) の色情報は 12~10ビット目の3ビット、青 (B) の色情報は 9~7ビット目の3ビットとする。6~0ビット目はキャラクタコードとする。”

・これを元に、ソフトウェアを作ろうとして、適当なソフトウェアを用意したが、SDKのXilinx ToolsメニューからProgram FPGA を選択したら、エラーになった。エラーを下に示す。

Program FPGA failed
Data2MEM failed.


・SDKからFPGAをコンフィグレーションできないので、iMPACTからコンフィグレーションした。

・SDKで.elf ファイルの右クリックメニューからDebug As を選択して、デバックモードへ。

・XMDで0x10000000 にデータをWriteしたが、Readはできなかった。

・microblaze_0 のAXI4バスに接続するとWrite/Readできないが、AXI4 Liteバスに接続すると、XMDのコマンドでWrite/Read することができて、画面をみてもキャラクタが正常に表示されている。

なぜAXI4バスに接続すると、Write/Readできないのかわからない?そのためシミュレーションを行うことにした。

(2010/03/21:追加)
シミュレーションは、キャラクタ・ディスプレイ・コントローラのAXI4バス・スレーブIPのCDC_axi_slaveのCDCが大文字のためシミュレーションがエラーになってしまった。ここは小文字にするべき。

ChipScope を入れて確かめてみることにする。
  1. 2012年03月20日 18:37 |
  2. AXI4 Slave IPの作製
  3. | トラックバック:0
  4. | コメント:4

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする10(インプリメント2)

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする9(インプリメント1)”の続き。

前回、XPSにキャラクタ・ディスプレイ・コントローラのAXI4バス・スレーブIP版を入れることができた。今回は設定をしていく。

・XPSのPortsタブをクリックし、clock_generator_0をダブルクリックする。

・現在、CLKOUT0、CLKOUT1、CLKOUT2を使用しているので、CLKOUT3をキャラクタ・ディスプレイ・コントローラのピクセルクロックとして設定する。40MHzに設定する。
CDC_axi_slave_31_120317.png

・CDC_axi_slave_0 のpixclk をclock_generator_0:CLKOUT3 に接続した。
CDC_axi_slave_32_120318.png

・CDC_axi_slave_0 のTMDS_tx_clk_p をExternal Portsに設定した。
CDC_axi_slave_33_120318.png

・TMDS出力をすべてExternal Portsに設定した。
CDC_axi_slave_34_120318.png

・Size に16Kを指定した。実際のメモリ量は8Kバイトなのだが、アドレスマップのデコードビットの関係で16Kバイトを選択した。Base Address に0x10000000 を指定した。。(2012/04/02:下図でcdc_axi_slave_0 のメモリ・サイズを16Kbytes にしているが、計算の結果これでは足りない。800/8 * 600/8 *4 ≒ 30Kbytes 必要だ)
CDC_axi_slave_35_120318.png

・Project メニューからDesign Rule Check を選択したら、エラーが消えた。
CDC_axi_slave_36_120318.png

・MCBの外部ピンの制約と、TMDS出力ピンの制約をsystem.ucf ファイルに加えた。
CDC_axi_slave_37_120318.png

・XPSを閉じて、Project Navigator Generate Top HDL Source した後でインプリメントした。(一旦、afifo_sm.ngc を忘れてエラーになってしまった。”XPSのカスタムIPにCore Generatorで生成したIPのネットリストを追加する”を参照して修正した)

・やはり、afifo_sm がTraslate フェーズで解決できないとエラーが出てしまう。Spartan-6で作ったはずなんだけど。。。
CDC_axi_slave_38_120319.png

うまく行きました。
やったことは、XPSのPoject メニューからRescan User Repositoriesを選択し、リポジトリを更新して、Poject メニューからClean All Generate Files を実行し、Project Navigator でProject メニューからCleanup Project Files を選択した。

・Project Navigator でインプリメントが完了した。
CDC_axi_slave_39_120319.png
  1. 2012年03月19日 05:28 |
  2. AXI4 Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

おかえり、はやぶさ(映画)を見てきました

今日は、はやぶさ映画3本目の”おかえり、はやぶさ”を見てきました。のぞみの失敗とかけて描かれていました。ロケットのCGも3D用に飛び出すと迫力があるように作られていた気がします。ここのCGは詳細に書きこまれていました。はやぶさ映画の3本を全部見ましたが、それぞれの良さがある気がします。
映画を見ていると、改めて、はやぶさの運用、本当にご苦労さまでしたと言いたいです。はやぶさ2は、すんなり行って帰ってきて欲しいです。
  1. 2012年03月17日 22:29 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする9(インプリメント1)

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8(BFMシミュレーション6)”で、シミュレーションを行なって不具合が出なかったので、今度はインプリメントしてみることにした。

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする2(Pcoreテンプレート生成)”で、CDC_AXI Slaveコアを生成してあったので、それを修正する。

・pcores\CDC_axi_slave_v1_00_a\data\CDC_axi_slave_v2_1_0.mpd にpixclk やTDMSポートを追加する。追加した定義を下に示す。

PORT pixclk = "", DIR = I
PORT TMDS_tx_clk_p = "", DIR = O
PORT TMDS_tx_clk_n = "", DIR = O
PORT TMDS_tx_2_G_p = "", DIR = O
PORT TMDS_tx_2_G_n = "", DIR = O
PORT TMDS_tx_1_R_p = "", DIR = O
PORT TMDS_tx_1_R_n = "", DIR = O
PORT TMDS_tx_0_B_p = "", DIR = O
PORT TMDS_tx_0_B_n = "", DIR = O


・pcores\CDC_axi_slave_v1_00_a\data\CDC_axi_slave_v2_1_0.pao に、HDLファイルを指定する。

lib CDC_axi_slave_v1_00_a CDC_axi_slave.vhd vhdl
lib CDC_axi_slave_v1_00_a dvi_disp.vhd vhdl
lib CDC_axi_slave_v1_00_a Digilent_RTL/Video.vhd vhdl
lib CDC_axi_slave_v1_00_a Digilent_RTL/DVITransmitter.vhd vhdl
lib CDC_axi_slave_v1_00_a Digilent_RTL/TMDSEncoder.vhd vhdl
lib CDC_axi_slave_v1_00_a Digilent_RTL/SerializerN_1.vhd vhdl
lib CDC_axi_slave_v1_00_a dvi_disp.vhd vhdl
lib CDC_axi_slave_v1_00_a CharDispCtrler.v verilog
lib CDC_axi_slave_v1_00_a char_gen_rom.v verilog
lib CDC_axi_slave_v1_00_a disp_timing.v verilog
lib CDC_axi_slave_v1_00_a frame_buffer.v verilog


・XPSのPoject メニューからRescan User Repositoriesを選択し、リポジトリを更新した。

・CDC_axi_slaveコアの右クリックメニューからAdd IPを選択する。
CDC_axi_slave_20_120317.png

・XPS Core Config ダイアログが立ち上がるが、ERROR:EDK:4149 - MUI File Parse Error.だった。
CDC_axi_slave_21_120317.png

・CDC_axi_slave_v2_1_0.mui を調べてみると、199行目のC_USE_ADVANCED_PORTS のENTITYが無いのが原因のようだ。
CDC_axi_slave_22_120317.png

・C_USE_ADVANCED_PORTS のENTITYを追加した。
CDC_axi_slave_23_120317.png

・もう一度、CDC_axi_slaveコアの右クリックメニューからAdd IPを選択する。
・XPS Core Config ダイアログが立ち上がる。Userタブの画面。C_USE_ADVANCED_PORTS は設定できない。
CDC_axi_slave_24_120317.png

・XPS Core Config ダイアログ(Systemタブ)
CDC_axi_slave_25_120317.png

・XPS Core Config ダイアログ(Interconnect Settings for BUSIF)
CDC_axi_slave_26_120317.png

・XPS Core Config ダイアログでOKボタンをクリックする。

・Instantiate and Connect IPダイアログで、microblaze_0 のラジオボタンをクリックしてから、OKボタンをクリックする。
CDC_axi_slave_27_120317.png

・そうすると、microblaze_0 のaxi4_lite バスに接続されてしまった。CDC_axi_slave_0 の+を展開して、axi4lite_0 をクリックする。
CDC_axi_slave_28_120317.png

・CDC_axi_slave_0.S_AXI Connection Dialog が開く。axi4_0 をクリックして、microblaze_0.MAXI_DC にチェックを入れて、OKボタンをクリックする。
CDC_axi_slave_29_120317.png

・CDC_axi_slave_0のAXI4バスが、axi4_0:microblaze_0.MAXI_DC に接続された。
CDC_axi_slave_30_120317.png

エラーも出ているが、後で修正を試みる。

(追加)
.mui、.paoファイルのファイルフォーマットは、Platform Specification Format Reference Manual を参照のこと。

  1. 2012年03月17日 09:02 |
  2. AXI4 Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8(BFMシミュレーション6)

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする7(BFMシミュレーション5)”で、チェッカを実装して、バスのプロトコルをテストすることができた。今回は、任意の回数繰り返して、チェッカにエラーが出ないかどうかを調べる。

テストベンチ (CDC_axi_slave_tb.v) を変更した。任意の回数ループするトランザクションは、8バースト、4バースト、2バースト、単発転送とする。それぞれデータ転送の最大のWait数を変えることにした。Readの値をovl_increment でチェックするため、Write トランザクションとRead トランザクションは、最後のトランザクションで同期する。Write トランザクションよりRead トランザクションの方が、遅延して開始する。
以上の様にテストベンチを変更した。変更部分のみを示す。

// CDC_axi_slave_tb.v

`default_nettype none

`timescale 100ps / 1ps

module CDC_axi_slave_tb;
    
    parameter DELAY    = 10;
    parameter NUMBER_OF_TEST = 100;    // テストする数


// 間は省略

    
    // test
    
    // Write Channel
    
    // wr_data生成、+1する
    reg        [15:0]    wcount;
    wire    [31:0]    wdata;
    
    always @(posedge ACLK) begin
        if (~ARESETN)
            wcount <= 0;
        else begin
            if (S_AXI_WVALID & S_AXI_WREADY)
                wcount <= wcount + 16'd1;
        end
    end
    assign wdata = {{16{1'b0}}, wcount};

    
    reg    WriteTaskBusy = 1'b0;
    integer wt_cnt;
    initial begin
        // Wait 100 ns for global reset to finish
        #1000;
        #5000;    // 500nsec Wait, PLL Locked
        
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        
        for (wt_cnt=0; wt_cnt<NUMBER_OF_TEST; wt_cnt=wt_cnt+1) begin
            WriteTaskBusy = 1'b1;
            MBFMi.AXI_Master_1Seq_Write(0, 32'h100, 8'd7, ASIZE_BT_4, ABURST_INCR, wdata, 0, 2);
            MBFMi.AXI_Master_1Seq_Write(0, 32'h200, 8'd3, ASIZE_BT_4, ABURST_INCR, wdata, 1, 3);
            MBFMi.AXI_Master_1Seq_Write(0, 32'h300, 8'd1, ASIZE_BT_4, ABURST_INCR, wdata, 1, 4);
            MBFMi.AXI_Master_1Seq_Write(0, 32'h400, 8'd0, ASIZE_BT_4, ABURST_INCR, wdata, 1, 5);
            
            WriteTaskBusy = 1'b0;
            while (ReadTestBusy) begin // Read 終了待ち
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
            end
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
        end
    end
    
    // Read Channel
    reg    ReadTestBusy = 1'b0;
    integer rd_cnt;
    initial begin
        // Wait 100 ns for global reset to finish
        #1000;
        #5000;    // 500nsec Wait, PLL Locked
        
        for (rd_cnt=0; rd_cnt<NUMBER_OF_TEST; rd_cnt=rd_cnt+1) begin
            
            ReadTestBusy = 1'b1;
            #1000;
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
            
            MBFMi.AXI_Master_1Seq_Read(0, 32'h100, 8'd7, ASIZE_BT_4, ABURST_INCR, 2);
            MBFMi.AXI_Master_1Seq_Read(0, 32'h200, 8'd3, ASIZE_BT_4, ABURST_INCR, 3);
            MBFMi.AXI_Master_1Seq_Read(0, 32'h300, 8'd1, ASIZE_BT_4, ABURST_INCR, 4);
            MBFMi.AXI_Master_1Seq_Read(0, 32'h400, 8'd0, ASIZE_BT_4, ABURST_INCR, 5);
            
            ReadTestBusy = 1'b0;
            while (WriteTaskBusy) begin // Write の終了待ち
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
            end
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
        end
    end
    
endmodule

// 下も省略


100回ループさせると、約113nsec で終了した。チェッカのエラーは出なかった。このチェックでは大丈夫そうだ。

下にシミュレーション波形を示す。

2012/10/25:修正、BFMがAXI非標準になっていたので、AXI_Master_BFM とOVL_Checker を修正しました。それに従って、Writeのシミュレーション波形も変更になっています。変更後のシミュレーション波形は、”キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8.1(BFMシミュレーション7)”を見てください。)

最初にWrite の全景を下に示す。左下のコンソールにチェッカのエラーが表示されていないことがわかる。
CDC_axi_slave_16_120314.png

Readの全景を下に示す。
CDC_axi_slave_17_120314.png

Write を拡大したものを下に示す。
CDC_axi_slave_18_120314.png

上のWrite と同じ時刻、時間間隔のRead を下に示す。
CDC_axi_slave_19_120314.png

これで、単体シミュレーション・テストは完了した。
次は、XPSでカスタムIPとして組み込むことにする。

(お知らせ)3月14日、15日は出張で神戸に行っているので、ブログの更新はお休みします。
  1. 2012年03月14日 04:46 |
  2. AXI4 Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする7(BFMシミュレーション5)

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする6(BFMシミュレーション4)”でデータ転送中にランダムなWaitを挟む機能をBFMに追加した。

今度は、OVLの様なチェッカを組み込む。OVLの機能の概略は、”OVL(Open Verification Library)を試してみる7(OVLチェッカの概要)”を参照のこと。
今回は、ovl_increment、ovl_handshake、ovl_never を使用する。

ovl_incrementは、バーストデータが+1されるのを監視する。ovl_handshake は、S_AXI_AWVALIDとS_AXI_AWREADY、S_AXI_ARVALIDとS_AXI_ARREADYの間の関係を監視する。ovl_never は、S_AXI_AWREADYとその他の信号の関係、S_AXI_ARREADYとその他の信号の間の関係を監視する。

ovl_increment は、データが+1 されるのを監視するので、バースト・データ転送の前にリセットをかけて、その後のデータが +1 されるのを確認しようとした。しかし、リセットが効かないというか、リセットしても、すべて通してチェックされるようで、データ転送の最初のデータでエラーが出てしまった。下にエラーメッセージを示す。

OVL_ERROR : OVL_INCREMENT : Error: Write data has not been incremented : Test expression is increased by a value other than specified : severity 1 : time 945000 : CDC_axi_slave_tb.OVLi.wr_data_check.ovl_error_t
OVL_ERROR : OVL_INCREMENT : Error: Read data has not been incremented : Test expression is increased by a value other than specified : severity 1 : time 1045000 : CDC_axi_slave_tb.OVLi.rd_data_check.ovl_error_t
OVL_ERROR : OVL_INCREMENT : Error: Write data has not been incremented : Test expression is increased by a value other than specified : severity 1 : time 1105000 : CDC_axi_slave_tb.OVLi.wr_data_check.ovl_error_t
OVL_ERROR : OVL_INCREMENT : Error: Write data has not been incremented : Test expression is increased by a value other than specified : severity 1 : time 1185000 : CDC_axi_slave_tb.OVLi.wr_data_check.ovl_error_t


これを回避するには、Writeするデータを最初のデータの +1 にするしか無いようなので、後で修正する。

ovl_handshake は、req_dropを0にして、reqが複数クロックアサートされてもエラーにならないようだ。max_ack_cycle などを指定しないと動かないようだ。一応、min_ack_cycle に1を、max_ack_cycle に32を設定した。

ovl_never は、普通に動作しているようだ。エラー条件を設定して、エラーになることを確認した。

チェッカ、OVL_Checker.v を下に示す。

2012/10/25:修正、BFMにAXI非標準の部分があったので、後で書き直しています。OVL Checker もそれに合わせて修正しています。OVL Checker の修正後のファイルは、”キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8.4(OVLチェッカのVerilog コード)”を見てください。下のファイルは修正前です)

// OVL_Checker.v

`default_nettype none

`timescale 100ps / 1ps

`include "std_ovl_defines.h"

module OVL_Checker (
    input    wire    ACLK,
    input    wire    ARESETN,

    input wire [0:0] S_AXI_AWID,
    input wire [31:0] S_AXI_AWADDR,
    input wire [7:0] S_AXI_AWLEN,
    input wire [2:0] S_AXI_AWSIZE,
    input wire [1:0] S_AXI_AWBURST,
    input wire [1:0] S_AXI_AWLOCK,
    input wire [3:0] S_AXI_AWCACHE,    // Normal Non-cacheable Non-bufferable
    input wire [2:0] S_AXI_AWPROT,
    input wire [3:0] S_AXI_AWREGION,
    input wire [3:0] S_AXI_AWQOS,
    input wire [0:0] S_AXI_AWUSER,
    input wire S_AXI_AWVALID,
    input wire [0:0] S_AXI_WID,
    input wire [31:0] S_AXI_WDATA,
    input wire [3:0] S_AXI_WSTRB,
    input wire S_AXI_WLAST,
    input wire [0:0] S_AXI_WUSER,
    input wire S_AXI_WVALID,
    input wire S_AXI_BREADY,
    input wire [0:0] S_AXI_ARID,
    input wire [31:0] S_AXI_ARADDR,
    input wire [7:0] S_AXI_ARLEN,
    input wire [2:0] S_AXI_ARSIZE,
    input wire [1:0] S_AXI_ARBURST,
    input wire [1:0] S_AXI_ARLOCK,
    input wire [3:0] S_AXI_ARCACHE, // Normal Non-cacheable Non-bufferable
    input wire [2:0] S_AXI_ARPROT,
    input wire [3:0] S_AXI_ARREGION,
    input wire [3:0] S_AXI_ARQOS,
    input wire [0:0] S_AXI_ARUSER,
    input wire S_AXI_ARVALID,
    input wire S_AXI_RREADY,

    input wire S_AXI_AWREADY,
    input wire S_AXI_WREADY,
    input wire [0:0] S_AXI_BID,
    input wire [1:0] S_AXI_BRESP,
    input wire [0:0] S_AXI_BUSER,
    input wire S_AXI_BVALID,
    input wire S_AXI_ARREADY,
    input wire [0:0] S_AXI_RID,
    input wire [31:0] S_AXI_RDATA,
    input wire [1:0] S_AXI_RRESP,
    input wire S_AXI_RLAST,
    input wire [0:0] S_AXI_RUSER,
    input wire S_AXI_RVALID
);

    wire [`OVL_FIRE_WIDTH-1:0] fire_wr_data, fire_rd_data;
    wire [`OVL_FIRE_WIDTH-1:0] fire_aw_hcheck, fire_ar_hcheck;
    wire [`OVL_FIRE_WIDTH-1:0] fire_aw_never, fire_ar_never;
    reg        [7:0]    countw, countr;
    
    parameter    idle_wts =        3'b001,
                wr_data_tran =    3'b010,
                wr_resp_tran =    3'b100;
    reg    [2:0]    wr_tran_cs;
    
    parameter    idle_rts =        1'b0,
                rd_data_tran =    1'b1;
    reg    rd_trans_cs;
    
    
    // Wirte Transaction データが+1されていることをチェックする(BFM Check)
    ovl_increment #(
        `OVL_ERROR,            // severity_level
        32,                        // width
        1,                        // value
        `OVL_ASSERT,            // property_type
        "Error: Write data has not been incremented", // msg
        `OVL_COVER_DEFAULT,        // coverage_level
        `OVL_POSEDGE,            // clock_edge
        `OVL_ACTIVE_HIGH,        // reset_polarity
        `OVL_GATE_CLOCK            // gating_type
    ) wr_data_check (
        ACLK,                    // clock
        ~ARESETN | (S_AXI_AWVALID & S_AXI_AWREADY),    // reset, Write のアドレス転送でリセット
        S_AXI_WVALID & S_AXI_WREADY,                    // enable
        S_AXI_WDATA,            // test_expr
        fire_wr_data            // fire    parameter
    );
    
    // Read Transaction データが+1されていることをチェックする(BFM Check)
    ovl_increment #(
        `OVL_ERROR,            // severity_level
        32,                        // width
        1,                        // value
        `OVL_ASSERT,            // property_type
        "Error: Read data has not been incremented", // msg
        `OVL_COVER_DEFAULT,        // coverage_level
        `OVL_POSEDGE,            // clock_edge
        `OVL_ACTIVE_HIGH,        // reset_polarity
        `OVL_GATE_CLOCK            // gating_type
    ) rd_data_check (
        ACLK,                    // clock
        ~ARESETN | (S_AXI_ARVALID & S_AXI_ARREADY),    // reset, Read のアドレス転送でリセット
        S_AXI_RVALID & S_AXI_RREADY,                    // enable
        S_AXI_RDATA,            // test_expr
        fire_rd_data            // fire    parameter
    );
    
    
    // S_AXI_AWVALID とS_AXI_AWREADY のハンドシェークのテスト
    ovl_handshake #(
        `OVL_ERROR,            // severity_level
        1,                    // min_ack_cycle
        32,                    // max_ack_cycle
        1,                    // req_drop
        1,                    // deassert_count
        1,                    // max_ack_length
        `OVL_ASSERT,        // property_type
        "Error: Handshake Error of S_AXI_AWREADY and S_AXI_AWVALID",
        `OVL_COVER_DEFAULT,    // coverage_level
        `OVL_POSEDGE,        // clock_edge
        `OVL_ACTIVE_LOW,    // reset_polarity
        `OVL_GATE_CLOCK        // gating_type
    ) aw_handshake_check (
        ACLK,                    // clock
        ARESETN,                // reset
        1'b1,                    // enable
        S_AXI_AWVALID,            // req
        S_AXI_AWREADY,            // ack
        fire_aw_hcheck            // fire parameter
    );
    
    // S_AXI_ARVALID とS_AXI_ARREADY のハンドシェークのテスト
    ovl_handshake #(
        `OVL_ERROR,            // severity_level
        1,                    // min_ack_cycle
        32,                    // max_ack_cycle
        1,                    // req_drop
        1,                    // deassert_count
        1,                    // max_ack_length
        `OVL_ASSERT,        // property_type
        "Error: Handshake Error of S_AXI_ARREADY and S_AXI_ARVALID",
        `OVL_COVER_DEFAULT,    // coverage_level
        `OVL_POSEDGE,        // clock_edge
        `OVL_ACTIVE_LOW,    // reset_polarity
        `OVL_GATE_CLOCK        // gating_type
    ) ar_handshake_check (
        ACLK,                    // clock
        ARESETN,                // reset
        1'b1,                    // enable
        S_AXI_ARVALID,            // req
        S_AXI_ARREADY,            // ack
        fire_ar_hcheck            // fire parameter
    );
    
    // Write, S_AXI_AWREADY がアサートされるときは、S_AXI_WVALID, S_AXI_WREADY, S_AXI_WLAST, S_AXI_BVALID, S_AXI_BREADY はアサートされない。
    ovl_never #(
        `OVL_ERROR,                // severity_level
        `OVL_ASSERT,            // property_type
        "Write, Assert Error of S_AXI_AWREADY",
        `OVL_COVER_DEFAULT,        // coverage_level
        `OVL_POSEDGE,            // clock edge
        `OVL_ACTIVE_LOW,        // reset_polarity
        `OVL_GATE_CLOCK            // gating_type
    ) aw_never_assert (
        ACLK,                    // clock_edge
        ARESETN,                // reset
        1'b1,                    // enable
        S_AXI_AWREADY & (S_AXI_WVALID | S_AXI_WREADY | S_AXI_WLAST | S_AXI_BVALID | S_AXI_BREADY),
        fire_aw_never
    );
    
    // Read, S_AXI_RREADY がアサートされるときは、 S_AXI_RVALID, S_AXI_RREADY, S_AXI_RLAST はアサートされない
    ovl_never #(
        `OVL_ERROR,                // severity_level
        `OVL_ASSERT,            // property_type
        "Read, Assert Error of S_AXI_RREADY",
        `OVL_COVER_DEFAULT,        // coverage_level
        `OVL_POSEDGE,            // clock edge
        `OVL_ACTIVE_LOW,        // reset_polarity
        `OVL_GATE_CLOCK            // gating_type
    ) ar_never_assert (
        ACLK,                    // clock_edge
        ARESETN,                // reset
        1'b1,                    // enable
        S_AXI_ARREADY & (S_AXI_RVALID | S_AXI_RREADY | S_AXI_RLAST),
        fire_ar_never
    );
    
    // Write の転送数をカウントして、 S_AXI_WLAST の出力を確認するアサーション
    always @(posedge ACLK) begin
        if (ARESETN == 1'b0)
            countw <= 0;
        else begin
            if (S_AXI_AWVALID & S_AXI_AWREADY) begin // countw へロード
                countw <= S_AXI_AWLEN;
            end else if (S_AXI_WVALID & S_AXI_WREADY) begin // データ転送
                if (countw==0) begin // データ転送終了
                    if (~S_AXI_WLAST) begin // countw==0 でS_AXI_WLASTが立たない
                        $display("%m: at time %t Error: countw==0 でS_AXI_WLASTが立たない",$time);
                    end
                end
            end
        end
    end
    
    // Read の転送数をカウントして、 S_AXI_RLAST の出力を確認するアサーション
    always @(posedge ACLK) begin
        if (ARESETN == 1'b0)
            countr <= 0;
        else begin
            if (S_AXI_ARVALID & S_AXI_ARREADY) begin // countw へロード
                countr <= S_AXI_ARLEN;
            end else if (S_AXI_RVALID & S_AXI_RREADY) begin // データ転送
                if (countr==0) begin // データ転送終了
                    if (~S_AXI_RLAST) begin // countw==0 でS_AXI_WLASTが立たない
                        $display("%m: at time %t Error: countr==0 でS_AXI_WLASTが立たない",$time);
                    end
                end
            end
        end
    end
    
    // Write 動作用ステートマシン
    always @(posedge ACLK) begin
        if (ARESETN == 1'b0)
            wr_tran_cs <= idle_wts;
        else begin
            case (wr_tran_cs)
                idle_wts :
                    if (S_AXI_AWREADY & (S_AXI_WVALID | S_AXI_WREADY | S_AXI_WLAST | S_AXI_BVALID | S_AXI_BREADY)) // エラー
                        $display("%m: at time %t S_AXI_AWREADY がアサートされた時に、その他のVALID, READY信号がアサートされた",$time);
                    else if (S_AXI_AWVALID & S_AXI_AWREADY) // アドレス転送終了
                        wr_tran_cs <= wr_data_tran;
                wr_data_tran :
                    if (S_AXI_AWREADY | S_AXI_BVALID | S_AXI_BREADY) // エラー
                        $display("%m: at time %t Write データ転送中に、S_AXI_AWREADY | S_AXI_BVALID | S_AXI_BREADY がアサートされた",$time);
                    else if (S_AXI_WVALID & S_AXI_WREADY & S_AXI_WLAST) // データ転送終了
                        wr_tran_cs <= wr_resp_tran;
                wr_resp_tran :
                    if (S_AXI_AWREADY | S_AXI_WVALID | S_AXI_WREADY | S_AXI_WLAST) // エラー
                        $display("%m: at time %t Write Response Channel 転送時に関連しない信号がアサートされた",$time);
                    else if (S_AXI_BVALID & S_AXI_BREADY) // Write Response Channel 転送終了
                        wr_tran_cs <= idle_wts;
            endcase
        end
    end
    
    // Read 動作用ステートマシン
    always @(posedge ACLK) begin
        if (ARESETN == 1'b0)
            rd_trans_cs <= idle_rts;
        else begin
            case (rd_trans_cs)
                idle_rts :
                    if (S_AXI_ARREADY & (S_AXI_RVALID | S_AXI_RREADY | S_AXI_RLAST)) // エラー
                        $display("%m: at time %t S_AXI_ARREADY がアサートされた時に、その他のVALID, READY信号がアサートされた",$time);
                    else if (S_AXI_ARVALID & S_AXI_ARREADY) // アドレス転送終了
                        rd_trans_cs <= rd_data_tran;
                rd_data_tran :
                    if (S_AXI_ARREADY) // エラー
                        $display("%m: at time %t Read データ転送中に、S_AXI_ARREADY がアサートされた",$time);
                    else if (S_AXI_RVALID & S_AXI_RREADY & S_AXI_RLAST) // データ転送終了
                        rd_trans_cs <= idle_rts;
            endcase
        end
    end
                        
endmodule    

`default_nettype wire


CDC_axi_slave_tb.v はOVL_Checker.v への接続ポートだけを追加してある。追加分を下に示す。

    // OVL Checker
    OVL_Checker OVLi (
        .ACLK(ACLK), 
        .ARESETN(ARESETN), 
        .S_AXI_AWID(S_AXI_AWID), 
        .S_AXI_AWADDR(S_AXI_AWADDR), 
        .S_AXI_AWLEN(S_AXI_AWLEN), 
        .S_AXI_AWSIZE(S_AXI_AWSIZE), 
        .S_AXI_AWBURST(S_AXI_AWBURST), 
        .S_AXI_AWLOCK(S_AXI_AWLOCK), 
        .S_AXI_AWCACHE(S_AXI_AWCACHE), 
        .S_AXI_AWPROT(S_AXI_AWPROT), 
        .S_AXI_AWREGION(S_AXI_AWREGION), 
        .S_AXI_AWQOS(S_AXI_AWQOS), 
        .S_AXI_AWUSER(S_AXI_AWUSER), 
        .S_AXI_AWVALID(S_AXI_AWVALID), 
        .S_AXI_AWREADY(S_AXI_AWREADY), 
        .S_AXI_WID(S_AXI_WID), 
        .S_AXI_WDATA(S_AXI_WDATA), 
        .S_AXI_WSTRB(S_AXI_WSTRB), 
        .S_AXI_WLAST(S_AXI_WLAST), 
        .S_AXI_WUSER(S_AXI_WUSER), 
        .S_AXI_WVALID(S_AXI_WVALID), 
        .S_AXI_WREADY(S_AXI_WREADY), 
        .S_AXI_BID(S_AXI_BID), 
        .S_AXI_BRESP(S_AXI_BRESP), 
        .S_AXI_BUSER(S_AXI_BUSER), 
        .S_AXI_BVALID(S_AXI_BVALID), 
        .S_AXI_BREADY(S_AXI_BREADY), 
        .S_AXI_ARID(S_AXI_ARID), 
        .S_AXI_ARADDR(S_AXI_ARADDR), 
        .S_AXI_ARLEN(S_AXI_ARLEN), 
        .S_AXI_ARSIZE(S_AXI_ARSIZE), 
        .S_AXI_ARBURST(S_AXI_ARBURST), 
        .S_AXI_ARLOCK(S_AXI_ARLOCK), 
        .S_AXI_ARCACHE(S_AXI_ARCACHE), 
        .S_AXI_ARPROT(S_AXI_ARPROT), 
        .S_AXI_ARREGION(S_AXI_ARREGION), 
        .S_AXI_ARQOS(S_AXI_ARQOS), 
        .S_AXI_ARUSER(S_AXI_ARUSER), 
        .S_AXI_ARVALID(S_AXI_ARVALID), 
        .S_AXI_ARREADY(S_AXI_ARREADY), 
        .S_AXI_RID(S_AXI_RID), 
        .S_AXI_RDATA(S_AXI_RDATA), 
        .S_AXI_RRESP(S_AXI_RRESP), 
        .S_AXI_RLAST(S_AXI_RLAST), 
        .S_AXI_RUSER(S_AXI_RUSER), 
        .S_AXI_RVALID(S_AXI_RVALID), 
        .S_AXI_RREADY(S_AXI_RREADY)
    );


シミュレーション波形は以前と同じだが、Write波形の全景を下に示す。
CDC_axi_slave_14_120313.png

Read波形の全景を示す。(Write波形と時間軸は同じだ)
CDC_axi_slave_15_120313.png
  1. 2012年03月13日 20:27 |
  2. AXI4 Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

ハンターマウンテン塩原スキー場に行って来ました

今日はハンターマウンテン塩原スキーから帰って来ました。本当は土日で蔵王温泉スキー場の宿を予約してあったんですが、親父が腎臓で2月から入院しているためキャンセルしました。スキーはキャンセルしたんですが、腎臓病は見舞いに行っても、健康にしか見えないし、大丈夫そう、ということで、日帰りで例によってハンターマウンテン塩原スキー場に行って来ました。
今日は行った時は曇っていたんですが、段々と晴れてきてとっても良いスキー日和になりました。スキー場の下のほうは昼ごろになるとベチャベチャしてきて春スキーになっちゃいましたが、上のほうは比較的良い雪でした。しかもアイスバーンがあまり有りません。ハンターマウンテン塩原にしてはとっても良かったです。積雪量は205cmでした。それに中学生は子供料金だったので、娘のリフト券を子供料金で買うことができました。
下の写真は午前中です。
Hunter_1_120311.jpg

下の写真が午後です。
Hunter_2_120311.jpg

いつの間にか息子も娘もスキーがうまくなっていました。息子は下まで一気に行きたいということで、下まで一気をやってました。体力的にかないません。
  1. 2012年03月11日 22:01 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

CIRCUITLABでお手軽に回路シミュレーション

@himamuraさんのツイートで知ったのだが、CircuitLabというWebブラウザ上で書ける回路図CADがあります。お手軽に回路図を書いて、シミュレーションすることができる。残念ながら、印刷はできないみたいだ。登録すると回路図を書いてセーブすることが出来る。ここに使い方の紹介ビデオがあった。これに沿ってCircuitLabを使ってみた。

1.まずは、CircuitLabのトップページのLaunch CircuitLab Editor ボタンをクリックしてCircuitLab Editor を立ち上げる。

2.Passive Elements の中のResister をクリックすると部品をつかめる。図面の適当な位置でもう一度クリックすると部品を図面に配置できる。
CircuitLab_1_120310.png

3.同じPassive Elements の中のCapacitor をクリックして図面に置く。
CircuitLab_2_120310.png

Rキーを押すと部品を回転できる。また、Editメニューから選択すると、水平方向、垂直方向に反転できる。

4.Signal Sources のVoltage Function Generator をクリックして図面に置く。
CircuitLab_3_120310.png

5.R1をダブルクリックして220Ωに変更する。R2をダブルクリックして330Ωに変更する。
CircuitLab_4_120310.png

4.V1をダブルクリックして、Shape をsquare に、DC Offsetを5Vに変更する。(DC Offset を変更するのを忘れて後で変更しました)
CircuitLab_5_120310.png

5.もう1つ回路図シンボルを置くのを忘れていた。Essentials からGNDをクリックして、V1の下の線に付けた置いた。
CircuitLab_6_120310.png

6.回路図シンボルの線の端をマウスでポイントすると、灰色の丸が出てくる。そこでマウスをクリックしてドラックすると線を引くことができる。
CircuitLab_7_120310.png

7.全部、線を引き終わった。
CircuitLab_8_120310.png

8.Essentials からName Node をクリックして回路図に置く。2つ置いて、入力 と出力 と名前を付ける。
CircuitLab_9_120310.png

9.下のSimulate ボタンをクリックする。
CircuitLab_10_120310.png

10.DCを選んで、回路図で”入力”ネットラベルと”出力”ネットラベルをクリックして解析ノードに加える。Run DC Solver をクリックするとDC解析される。
CircuitLab_11_120310.png

11.”入力”が 6.000V、”出力”が 3.666V とDC解析された。
CircuitLab_12_120310.png

12.Time Domain をクリックする。Stop Time に10m、Time Step に10u を入力する。回路図で”入力”ネットラベルと”出力”ネットラベルをクリックして解析ノードに加える。Run Time-Domain Simulation をクリックする。
CircuitLab_13_120310.png

13.Transient Analysis 結果が表示された。
CircuitLab_14_120310.png

14.コンデンサの値を0.5uF にした時のTransient Analysis 結果を下に示す。
CircuitLab_15_120310.png

作った回路は、登録するとセーブすることが出来る。これで、回路図を綺麗に印刷できる機能があれば良いと思う。
  1. 2012年03月10日 10:07 |
  2. CADツール
  3. | トラックバック:0
  4. | コメント:2

ISimでOVL V2.6を使用したシミュレーション(OVLライブラリのコンパイルと登録)

AXI4バスのBFMにデータ転送時にランダムなWaitを挟む機能を追加した。これをループさせてバグを検出したいのだが、シミュレーション波形を目で見ていると、とっても大変なので、OVL(Open Verification Language? Open Verification Library?)を使用して動作をチェックすることにした。なお、OVLの最新バージョンVer. 2.6 はここからダウンロードすることができる。

FPGAの部屋のOVLの資料を以下に示す。
アサーション事始め
ISimでOVLのVHDL, Verilog混在シミュレーション(OVLライブラリのコンパイルと登録)
ISimでOVLのVHDL, Verilog混在シミュレーション(ISimでシミュレーション)

ISimでOVLのシミュレーションを行うためには、OVLライブラリをコンパイルして、ライブラリに登録する必要がある。そのため、OVLの各チェッカをコンパイルするためにバッチを自作してある。そのための方法が”ISimでOVLのVHDL, Verilog混在シミュレーション(OVLライブラリのコンパイルと登録)”に書いてある。バッチファイルは、OVLチェッカの位置をフルパスで書いてあるので、それを変更してからISE13.4のコンパイラでコンパイルを行った。
下に手順を示す。

1.スタートメニュー -> アクセサリ -> コマンド プロンプトを立ち上げる。

2.ISE13.4のインストールフォルダに行って、13.4\ISE_DSフォルダの中の settings32.bat(Windows 64ビット版の場合はsettings64.bat)を実行する。
ISim_OVL_Sim_1_120309.png

3.OVL_Verilog_Lib_Comp.batとOVL_VHDL_Lib_Comp.bat の入っているフォルダに移動する。

4.OVL_Verilog_Lib_Comp.batを実行する。
ISim_OVL_Sim_2_120309.png

5.コンパイルが終了した。
ISim_OVL_Sim_3_120309.png

6.前回同様、isimフォルダに下にaccellera_ovl_vlog フォルダが出来て、Verilogのコンパイル済みのライブラリができた。
ISim_OVL_Sim_4_120309.png

7.OVL_VHDL_Lib_Comp.batを実行した。
ISim_OVL_Sim_5_120309.png

8.前回同様、isimフォルダに下にaccellera_ovl_vhdl フォルダが出来て、VHDLのコンパイル済みのライブラリができた。
ISim_OVL_Sim_6_120309.png

*コンパイルした2つのライブラリをISimに登録する。

9.Xilinx\13.4\ISE_DS\ISE\verilog\hdp\ntフォルダに、accellera_ovl_vlog フォルダをコピーする。
ISim_OVL_Sim_7_120309.png

10.Xilinx\13.4\ISE_DS\ISE\vhdl\hdp\ntフォルダに、accellera_ovl_vhdl フォルダをコピーする。
ISim_OVL_Sim_8_120309.png

11.Xilinx\13.4\ISE_DS\ISE\vhdl\hdp\ntフォルダに、xilinxsim.iniがあるので、これをエディタで開く。

12.accellera_ovl_vlog=$XILINX/verilog/hdp/nt/accellera_ovl_vlog と accellera_ovl_vhdl=$XILINX/vhdl/hdp/nt/accellera_ovl_vhdl の行を追加した。
ISim_OVL_Sim_9_120309.png

これで、accellera_ovl_vlogとaccellera_ovl_vhdlライブラリがISimで使用できるようになった。
  1. 2012年03月09日 05:12 |
  2. アサーション事始め
  3. | トラックバック:0
  4. | コメント:0

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする6(BFMシミュレーション4)

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする5(BFMシミュレーション3)”の続き。

今回は、バーストWrite, バーストReadの最中にマスタから任意のWaitを入れる機能を実現してみた。どうやったかというと、$unsigned($random)で整数のランダム値を生成して、それを設定値+1で割った余りを使ってWait数を決めることにした。そうすると0からtask に設定した値のまでのWaitがランダムに入るはずだ。これで、マスタ側でWaitしてもAXI4バスのスレーブIPがきちんとデータを出せるかどうかを検証することができる。
下に示すtask の最後の数が最大のWait数になる。

MBFMi.AXI_Master_1Seq_Write(0, 32'h100, 8'd4, ASIZE_BT_4, ABURST_INCR, 32'h12345678, 0, 2);
MBFMi.AXI_MASTER_WDC(32'h3333_4444, 4);
MBFMi.AXI_Master_1Seq_Read(0, 32'h100, 8'd4, ASIZE_BT_4, ABURST_INCR, 3);


MBFMi.AXI_Master_1Seq_Writeでは、最大Wait数が2、MBFMi.AXI_MASTER_WDCでは最大Wait数が4、MBFMi.AXI_Master_1Seq_Readでは最大Wait数が3となる。0を入れれば、今まで通りにWait無しのデータ転送になる。
Writeの波形を下に示す。これは、”MBFMi.AXI_Master_1Seq_Write(0, 32'h100, 8'd4, ASIZE_BT_4, ABURST_INCR, 32'h12345678, 0, 2);”の波形だ。
CDC_axi_slave_12_120308.png

余りランダムでない気もするが、Wait無しと2クロックWaitのWriteがあるのがわかると思う。

次に、Readの波形を下に示す。これは、”MBFMi.AXI_Master_1Seq_Read(0, 32'h100, 8'd4, ASIZE_BT_4, ABURST_INCR, 3);”の波形だ。
CDC_axi_slave_13_120308.png

Readの方がランダムな気がする。Waitが入っているのがわかる。

現在の、AXI4バスMaster BFM (AXI4_Master_BFM.v) を下に貼っておく。

2012/10/25:修正、またまたAXI非標準の部分があったので、書き直しています。この下のBFMはAXI非標準のままにしてあります。AXI非標準部分の修正後のファイルは、”キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8.3(AXI Master BFMのVerilogコード)”を見てください。)

// AXI4 bus Master Bus Fucntion Mode

`default_nettype none

`timescale 100ps / 1ps

module AXI4_Master_BFM #(
    parameter DELAY    = 10 )
(
    input    wire    ACLK,

    output reg [0:0] S_AXI_AWID = 0,
    output reg [31:0] S_AXI_AWADDR = 0,
    output reg [7:0] S_AXI_AWLEN = 0,
    output reg [2:0] S_AXI_AWSIZE = 0,
    output reg [1:0] S_AXI_AWBURST = 0,
    output reg [1:0] S_AXI_AWLOCK = 0,
    output reg [3:0] S_AXI_AWCACHE = 2,    // Normal Non-cacheable Non-bufferable
    output reg [2:0] S_AXI_AWPROT = 0,
    output reg [3:0] S_AXI_AWREGION = 0,
    output reg [3:0] S_AXI_AWQOS = 0,
    output reg [0:0] S_AXI_AWUSER = 0,
    output reg S_AXI_AWVALID = 0,
    output reg [0:0] S_AXI_WID = 0,
    output reg [31:0] S_AXI_WDATA = 0,
    output reg [3:0] S_AXI_WSTRB = 0,
    output reg S_AXI_WLAST = 0,
    output reg [0:0] S_AXI_WUSER = 0,
    output reg S_AXI_WVALID = 0,
    output reg S_AXI_BREADY = 0,
    output reg [0:0] S_AXI_ARID = 0,
    output reg [31:0] S_AXI_ARADDR = 0,
    output reg [7:0] S_AXI_ARLEN = 0,
    output reg [2:0] S_AXI_ARSIZE = 0,
    output reg [1:0] S_AXI_ARBURST = 0,
    output reg [1:0] S_AXI_ARLOCK = 0,
    output reg [3:0] S_AXI_ARCACHE = 2, // Normal Non-cacheable Non-bufferable
    output reg [2:0] S_AXI_ARPROT = 0,
    output reg [3:0] S_AXI_ARREGION = 0,
    output reg [3:0] S_AXI_ARQOS = 0,
    output reg [0:0] S_AXI_ARUSER = 0,
    output reg S_AXI_ARVALID = 0,
    output reg S_AXI_RREADY = 0,

    input wire S_AXI_AWREADY,
    input wire S_AXI_WREADY,
    input wire [0:0] S_AXI_BID,
    input wire [1:0] S_AXI_BRESP,
    input wire [0:0] S_AXI_BUSER,
    input wire S_AXI_BVALID,
    input wire S_AXI_ARREADY,
    input wire [0:0] S_AXI_RID,
    input wire [31:0] S_AXI_RDATA,
    input wire [1:0] S_AXI_RRESP,
    input wire S_AXI_RLAST,
    input wire [0:0] S_AXI_RUSER,
    input wire S_AXI_RVALID
);

    reg     [7:0]    awlen_hold = 0;
    reg     [0:0]    wid_hold = 0;
    reg     axi_w_transaction_active = 0;
    reg     axi_r_transaction_active = 0;
    reg     [7:0]    arlen_hold = 0;

    // Write Channel
    task AXI_Master_1Seq_Write;    // Write Address, Write Data, Write Response をシーケンシャルにオーバーラップせずに行う。
        input    [0:0]    awid;
        input    [31:0]    awaddr;
        input    [7:0]    awlen;
        input    [2:0]    awsize;
        input    [1:0]    awburst;
        input    [31:0]    wdata;
        input    [7:0]    wait_clk_bready;
        input    [7:0]    wmax_wait;
        begin
            AXI_MASTER_WAC(awid, awaddr, awlen, awsize, awburst);
            AXI_MASTER_WDC(wdata, wmax_wait);
            AXI_MASTER_WRC(wait_clk_bready);
        end
    endtask
    
    // Write Address Channel
    task AXI_MASTER_WAC;
        input    [0:0]    awid;
        input    [31:0]    awaddr;
        input    [7:0]    awlen;
        input    [2:0]    awsize;
        input    [1:0]    awburst;
        begin
            S_AXI_AWID        = awid;
            S_AXI_AWADDR    = awaddr;
            S_AXI_AWLEN        = awlen;
            S_AXI_AWSIZE    = awsize;
            S_AXI_AWBURST    = awburst;
            S_AXI_AWVALID    = 1'b1;

            if (axi_w_transaction_active == 1'b0) begin // AXI Write トランザクションが開始されている場合は戻る
                awlen_hold        = awlen; // Write Data Channel のためにバースト数を取っておく
                @(posedge ACLK);    // 次のクロックへ
                
                while (~S_AXI_AWREADY) begin    // S_AXI_AWREADY が1になるまで待つ
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end
                
                #DELAY;
                S_AXI_AWID         = 0;
                S_AXI_AWADDR    = 0;
                S_AXI_AWLEN     = 0;
                S_AXI_AWSIZE     = 0;
                S_AXI_AWBURST     = 0;
                S_AXI_AWVALID     = 1'b0;
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
                axi_w_transaction_active = 1'b1; // AXIトランザクション開始
            end
        end
    endtask
    
    // Write Data Channel
    task AXI_MASTER_WDC;    // WDATA は+1する
    // とりあえず、WSTRBはオール1にする
        input    [31:0]    wdata;
        input    [7:0]    wmax_wait;    // Write時の最大wait数
        integer    i, j, val;
        begin
            i = 0; j = 0;
            S_AXI_WSTRB = 4'b1111;
            
            while (i<=awlen_hold) begin
                if (wmax_wait == 0) // wmax_wait が0の時は$random を実行しない
                    val = 0;
                else
                    val = $unsigned($random) % (wmax_wait+1);

                if (val == 0) // waitなし
                    S_AXI_WVALID = 1'b1;
                else begin // waitあり
                    S_AXI_WVALID = 1'b0;
                    for (j=0; j<wmax_wait; j=j+1) begin
                        @(posedge ACLK);    // 次のクロックへ
                        #DELAY;
                    end
                    S_AXI_WVALID = 1'b1; // wait終了
                end
                    
                if (i == awlen_hold)
                    S_AXI_WLAST = 1'b1;
                else
                    S_AXI_WLAST = 1'b0;
                S_AXI_WDATA = wdata;
                wdata = wdata + 1;
                
                @(posedge ACLK);    // 次のクロックへ
                
                while (~S_AXI_WREADY) begin    // S_AXI_WREADY が0の時は1になるまで待つ
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end
                #DELAY;
                
                i = i + 1;
            end
            S_AXI_WVALID = 1'b0;
            S_AXI_WLAST = 1'b0;
            S_AXI_WSTRB = 4'b0000;
        end
    endtask
    
    // Write Response Channel
    task AXI_MASTER_WRC;    // wait_clk_bready
        input    [7:0]    wait_clk_bready;
        integer    i;
        begin
            for (i=0; i<wait_clk_bready; i=i+1) begin
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
            end
            
            S_AXI_BREADY = 1'b1;
            
                
            @(posedge ACLK);    // 次のクロックへ
            
            while (~S_AXI_BVALID) begin // S_AXI_BVALID が1になるまでWait
                #DELAY;
                @(posedge ACLK);    // 次のクロックへ
            end
            #DELAY;
            
            S_AXI_BREADY = 1'b0;
            
            axi_w_transaction_active = 1'b0; // AXIトランザクション終了
        end
    endtask 
    
    // Read Channel
    task AXI_Master_1Seq_Read; // Read Address, Read Data をシーケンシャルに行う。
        input    [0:0]    arid;
        input    [31:0]    araddr;
        input    [7:0]    arlen;
        input    [2:0]    arsize;
        input    [1:0]    arburst;
        input    [7:0]    rmax_wait;    // Read時の最大wait数
        begin
            AXI_MASTER_RAC(arid, araddr, arlen, arsize, arburst);
            AXI_MASTER_RDC(rmax_wait);
        end
    endtask

    // Read Address Channel    
    task AXI_MASTER_RAC;
        input    [0:0]    arid;
        input    [31:0]    araddr;
        input    [7:0]    arlen;
        input    [2:0]    arsize;
        input    [1:0]    arburst;
        begin
            S_AXI_ARID         = arid;
            S_AXI_ARADDR    = araddr;
            S_AXI_ARLEN        = arlen;
            S_AXI_ARSIZE    = arsize;
            S_AXI_ARBURST    = arburst;
            S_AXI_ARVALID     = 1'b1;
            
            if (axi_r_transaction_active == 1'b0) begin // AXI Read トランザクションが開始されている場合は戻る
                arlen_hold    =arlen; // Read Data Channel のためにバースト数を取っておく
                @(posedge ACLK);    // 次のクロックへ
                
                while (~S_AXI_ARREADY) begin    // S_AXI_ARREADY が1になるまで待つ
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end
                
                #DELAY;
                S_AXI_ARID         = 0;
                S_AXI_ARADDR    = 0;
                S_AXI_ARLEN     = 0;
                S_AXI_ARSIZE     = 0;
                S_AXI_ARBURST     = 0;
                S_AXI_ARVALID     = 1'b0;
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
                axi_r_transaction_active = 1'b1; // AXIトランザクション開始
            end
        end
    endtask
    
    // Read Data Channel
    task AXI_MASTER_RDC; // S_AXI_RLAST がアサートされるまでS_AXI_RREADY をアサートする
        input    [7:0]    rmax_wait;    // Read時の最大wait数
        integer i, val;
        begin    
            while (~(S_AXI_RLAST & S_AXI_RVALID & S_AXI_RREADY)) begin // S_AXI_RLAST & S_AXI_RVALID & S_AXI_RREADY で終了
                if (rmax_wait == 0) begin // rmax_wait が0の時は$random を実行しない
                    val = 0;
                    S_AXI_RREADY = 1'b1;
                end else begin
                    val = $unsigned($random) % (rmax_wait+1);
                    if (val == 0)
                        S_AXI_RREADY = 1'b1;
                    else
                        S_AXI_RREADY = 1'b0;
                end
                #DELAY;
                
                for (i=0; i<val; i=i+1) begin // ランダム値でWait、val=0の時はスキップ
                    @(posedge ACLK);    // 次のクロックへ
                    #DELAY;
                end
                
                S_AXI_RREADY = 1'b1;
                @(posedge ACLK);    // 次のクロックへ
                while (~S_AXI_RVALID) begin // S_AXI_RVALID が1になるまでWait
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end
                #DELAY;
            end
            #DELAY;
            
            S_AXI_RREADY = 1'b0;
            axi_r_transaction_active = 1'b0; // AXIトランザクション終了
        end
    endtask
    
endmodule

`default_nettype wire


次に、テストベンチ (CDC_axi_slave_tb.v) を貼っておく。

// CDC_axi_slave_tb.v

`default_nettype none

`timescale 100ps / 1ps

module CDC_axi_slave_tb;
    
    parameter DELAY    = 10;
    
    parameter    ASIZE_BT_4    = 3'd2;    // 32 bit width
    parameter    ASIZE_BT_2 = 3'd1;    // 16 bit width
    parameter    ASIZE_BT_1 = 3'd0;    // 8 bit width
    
    parameter    ABURST_FIXED    = 2'd0;
    parameter    ABURST_INCR    = 2'd1;
    parameter    ABURST_WRAP    = 2'd2;
    
    parameter    BRESP_OKAY        = 2'b00;
    parameter    BRESP_EXOKAY    = 2'b01;
    parameter    BRESP_SLVERR    = 2'b10;
    parameter    BRESP_DECERR    = 2'b11;

    // Inputs
    wire ACLK;
    wire ARESETN;
    wire [0:0] S_AXI_AWID;
    wire [31:0] S_AXI_AWADDR;
    wire [7:0] S_AXI_AWLEN;
    wire [2:0] S_AXI_AWSIZE;
    wire [1:0] S_AXI_AWBURST;
    wire [1:0] S_AXI_AWLOCK;
    wire [3:0] S_AXI_AWCACHE;
    wire [2:0] S_AXI_AWPROT;
    wire [3:0] S_AXI_AWREGION;
    wire [3:0] S_AXI_AWQOS;
    wire [0:0] S_AXI_AWUSER;
    wire S_AXI_AWVALID;
    wire [0:0] S_AXI_WID;
    wire [31:0] S_AXI_WDATA;
    wire [3:0] S_AXI_WSTRB;
    wire S_AXI_WLAST;
    wire [0:0] S_AXI_WUSER;
    wire S_AXI_WVALID;
    wire S_AXI_BREADY;
    wire [0:0] S_AXI_ARID;
    wire [31:0] S_AXI_ARADDR;
    wire [7:0] S_AXI_ARLEN;
    wire [2:0] S_AXI_ARSIZE;
    wire [1:0] S_AXI_ARBURST;
    wire [1:0] S_AXI_ARLOCK;
    wire [3:0] S_AXI_ARCACHE;
    wire [2:0] S_AXI_ARPROT;
    wire [3:0] S_AXI_ARREGION;
    wire [3:0] S_AXI_ARQOS;
    wire [0:0] S_AXI_ARUSER;
    wire S_AXI_ARVALID;
    wire S_AXI_RREADY;
    wire pixclk;

    // Outputs
    reg     S_AXI_AWREADY;
    reg     S_AXI_WREADY;
    reg     [0:0] S_AXI_BID;
    reg     [1:0] S_AXI_BRESP;
    wire     [0:0] S_AXI_BUSER = 0;
    reg     S_AXI_BVALID;
    reg     S_AXI_ARREADY;
    reg     [0:0] S_AXI_RID;
    reg     [31:0] S_AXI_RDATA;
    reg     [1:0] S_AXI_RRESP;
    reg     S_AXI_RLAST;
    wire     [0:0] S_AXI_RUSER = 0;
    reg     S_AXI_RVALID;
    wire TMDS_tx_clk_p;
    wire TMDS_tx_clk_n;
    wire TMDS_tx_2_G_p;
    wire TMDS_tx_2_G_n;
    wire TMDS_tx_1_R_p;
    wire TMDS_tx_1_R_n;
    wire TMDS_tx_0_B_p;
    wire TMDS_tx_0_B_n;

    wire S_AXI_AWREADY_d;
    wire S_AXI_WREADY_d;
    wire [0:0]    S_AXI_BID_d;
    wire [1:0]    S_AXI_BRESP_d;
    wire S_AXI_BVALID_d;
    wire S_AXI_ARREADY_d;
    wire [0:0]    S_AXI_RID_d;
    wire [31:0]    S_AXI_RDATA_d;
    wire [1:0]    S_AXI_RRESP_d;
    wire S_AXI_RLAST_d;
    wire S_AXI_RVALID_d;
    
    // Instantiate the Unit Under Test (UUT)
    CDC_axi_slave uut (
        .ACLK(ACLK), 
        .ARESETN(ARESETN), 
        .S_AXI_AWID(S_AXI_AWID), 
        .S_AXI_AWADDR(S_AXI_AWADDR), 
        .S_AXI_AWLEN(S_AXI_AWLEN), 
        .S_AXI_AWSIZE(S_AXI_AWSIZE), 
        .S_AXI_AWBURST(S_AXI_AWBURST), 
        .S_AXI_AWLOCK(S_AXI_AWLOCK), 
        .S_AXI_AWCACHE(S_AXI_AWCACHE), 
        .S_AXI_AWPROT(S_AXI_AWPROT), 
        .S_AXI_AWREGION(S_AXI_AWREGION), 
        .S_AXI_AWQOS(S_AXI_AWQOS), 
        .S_AXI_AWUSER(S_AXI_AWUSER), 
        .S_AXI_AWVALID(S_AXI_AWVALID), 
        .S_AXI_AWREADY(S_AXI_AWREADY_d), 
        .S_AXI_WID(S_AXI_WID), 
        .S_AXI_WDATA(S_AXI_WDATA), 
        .S_AXI_WSTRB(S_AXI_WSTRB), 
        .S_AXI_WLAST(S_AXI_WLAST), 
        .S_AXI_WUSER(S_AXI_WUSER), 
        .S_AXI_WVALID(S_AXI_WVALID), 
        .S_AXI_WREADY(S_AXI_WREADY_d), 
        .S_AXI_BID(S_AXI_BID_d), 
        .S_AXI_BRESP(S_AXI_BRESP_d), 
        .S_AXI_BUSER(S_AXI_BUSER), 
        .S_AXI_BVALID(S_AXI_BVALID_d), 
        .S_AXI_BREADY(S_AXI_BREADY), 
        .S_AXI_ARID(S_AXI_ARID), 
        .S_AXI_ARADDR(S_AXI_ARADDR), 
        .S_AXI_ARLEN(S_AXI_ARLEN), 
        .S_AXI_ARSIZE(S_AXI_ARSIZE), 
        .S_AXI_ARBURST(S_AXI_ARBURST), 
        .S_AXI_ARLOCK(S_AXI_ARLOCK), 
        .S_AXI_ARCACHE(S_AXI_ARCACHE), 
        .S_AXI_ARPROT(S_AXI_ARPROT), 
        .S_AXI_ARREGION(S_AXI_ARREGION), 
        .S_AXI_ARQOS(S_AXI_ARQOS), 
        .S_AXI_ARUSER(S_AXI_ARUSER), 
        .S_AXI_ARVALID(S_AXI_ARVALID), 
        .S_AXI_ARREADY(S_AXI_ARREADY_d), 
        .S_AXI_RID(S_AXI_RID_d), 
        .S_AXI_RDATA(S_AXI_RDATA_d), 
        .S_AXI_RRESP(S_AXI_RRESP_d), 
        .S_AXI_RLAST(S_AXI_RLAST_d), 
        .S_AXI_RUSER(S_AXI_RUSER), 
        .S_AXI_RVALID(S_AXI_RVALID_d), 
        .S_AXI_RREADY(S_AXI_RREADY), 
        .pixclk(pixclk), 
        .TMDS_tx_clk_p(TMDS_tx_clk_p), 
        .TMDS_tx_clk_n(TMDS_tx_clk_n), 
        .TMDS_tx_2_G_p(TMDS_tx_2_G_p), 
        .TMDS_tx_2_G_n(TMDS_tx_2_G_n), 
        .TMDS_tx_1_R_p(TMDS_tx_1_R_p), 
        .TMDS_tx_1_R_n(TMDS_tx_1_R_n), 
        .TMDS_tx_0_B_p(TMDS_tx_0_B_p), 
        .TMDS_tx_0_B_n(TMDS_tx_0_B_n)
    );
    always @* S_AXI_AWREADY <= #DELAY S_AXI_AWREADY_d;
    always @* S_AXI_WREADY <= #DELAY S_AXI_WREADY_d;
    always @* S_AXI_BID <= #DELAY S_AXI_BID_d;
    always @* S_AXI_BRESP <= #DELAY S_AXI_BRESP_d;
    always @* S_AXI_BVALID <= #DELAY S_AXI_BVALID_d;
    always @* S_AXI_ARREADY <= #DELAY S_AXI_ARREADY_d;
    always @* S_AXI_RID <= #DELAY S_AXI_RID_d;
    always @* S_AXI_RDATA <= #DELAY S_AXI_RDATA_d;
    always @* S_AXI_RRESP <= #DELAY S_AXI_RRESP_d;
    always @* S_AXI_RLAST <= #DELAY S_AXI_RLAST_d;
    always @* S_AXI_RVALID <= #DELAY S_AXI_RVALID_d;
    
    // clk_gen のインスタンス
    clk_gen #(
        .CLK_PERIOD(100),    // 10nsec, 100MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) ACLKi (
        .clk_out(ACLK)
    );
    
    clk_gen #(
        .CLK_PERIOD(250),    // 25nsec, 40MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) PIXCLKi (
        .clk_out(pixclk)
    );
    
    // reset_gen のインスタンス
    reset_gen #(
        .RESET_STATE(1'b0),
        .RESET_TIME(1000)    // 100nsec
    ) RESETi (
        .reset_out(ARESETN)
    );
    
    // AXI4_BFM のインスタンス
    AXI4_Master_BFM #(.DELAY(DELAY)) MBFMi(
        .ACLK(ACLK), 
        .S_AXI_AWID(S_AXI_AWID), 
        .S_AXI_AWADDR(S_AXI_AWADDR), 
        .S_AXI_AWLEN(S_AXI_AWLEN), 
        .S_AXI_AWSIZE(S_AXI_AWSIZE), 
        .S_AXI_AWBURST(S_AXI_AWBURST), 
        .S_AXI_AWLOCK(S_AXI_AWLOCK), 
        .S_AXI_AWCACHE(S_AXI_AWCACHE), 
        .S_AXI_AWPROT(S_AXI_AWPROT), 
        .S_AXI_AWREGION(S_AXI_AWREGION), 
        .S_AXI_AWQOS(S_AXI_AWQOS), 
        .S_AXI_AWUSER(S_AXI_AWUSER), 
        .S_AXI_AWVALID(S_AXI_AWVALID), 
        .S_AXI_AWREADY(S_AXI_AWREADY), 
        .S_AXI_WID(S_AXI_WID), 
        .S_AXI_WDATA(S_AXI_WDATA), 
        .S_AXI_WSTRB(S_AXI_WSTRB), 
        .S_AXI_WLAST(S_AXI_WLAST), 
        .S_AXI_WUSER(S_AXI_WUSER), 
        .S_AXI_WVALID(S_AXI_WVALID), 
        .S_AXI_WREADY(S_AXI_WREADY), 
        .S_AXI_BID(S_AXI_BID), 
        .S_AXI_BRESP(S_AXI_BRESP), 
        .S_AXI_BUSER(S_AXI_BUSER), 
        .S_AXI_BVALID(S_AXI_BVALID), 
        .S_AXI_BREADY(S_AXI_BREADY), 
        .S_AXI_ARID(S_AXI_ARID), 
        .S_AXI_ARADDR(S_AXI_ARADDR), 
        .S_AXI_ARLEN(S_AXI_ARLEN), 
        .S_AXI_ARSIZE(S_AXI_ARSIZE), 
        .S_AXI_ARBURST(S_AXI_ARBURST), 
        .S_AXI_ARLOCK(S_AXI_ARLOCK), 
        .S_AXI_ARCACHE(S_AXI_ARCACHE), 
        .S_AXI_ARPROT(S_AXI_ARPROT), 
        .S_AXI_ARREGION(S_AXI_ARREGION), 
        .S_AXI_ARQOS(S_AXI_ARQOS), 
        .S_AXI_ARUSER(S_AXI_ARUSER), 
        .S_AXI_ARVALID(S_AXI_ARVALID), 
        .S_AXI_ARREADY(S_AXI_ARREADY), 
        .S_AXI_RID(S_AXI_RID), 
        .S_AXI_RDATA(S_AXI_RDATA), 
        .S_AXI_RRESP(S_AXI_RRESP), 
        .S_AXI_RLAST(S_AXI_RLAST), 
        .S_AXI_RUSER(S_AXI_RUSER), 
        .S_AXI_RVALID(S_AXI_RVALID), 
        .S_AXI_RREADY(S_AXI_RREADY)
    );
    
    // test
    
    // Write Channel
    initial begin
        // Wait 100 ns for global reset to finish
        #1000;
        #5000;    // 500nsec Wait, PLL Locked
        
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        
        MBFMi.AXI_Master_1Seq_Write(0, 32'h100, 8'd4, ASIZE_BT_4, ABURST_INCR, 32'h12345678, 0, 2);
        MBFMi.AXI_Master_1Seq_Write(0, 32'h200, 8'd0, ASIZE_BT_4, ABURST_INCR, 32'h11223344, 1, 0);
        
        // アドレス転送のオーバーラップのサンプル
        // オーバーラップ後にもう一度、2度目のWrite Address Channel の転送を行う。
        MBFMi.AXI_MASTER_WAC(0, 32'h300, 8'd0, ASIZE_BT_4, ABURST_INCR);
        MBFMi.AXI_MASTER_WAC(0, 32'h400, 8'd0, ASIZE_BT_4, ABURST_INCR);
        MBFMi.AXI_MASTER_WDC(32'h1111_2222, 2);
        MBFMi.AXI_MASTER_WRC(0);
        MBFMi.AXI_MASTER_WAC(0, 32'h400, 8'd0, ASIZE_BT_4, ABURST_INCR);
        MBFMi.AXI_MASTER_WDC(32'h3333_4444, 4);
        MBFMi.AXI_MASTER_WRC(0);
    end
    
    // Read Channel
    initial begin
        // Wait 100 ns for global reset to finish
        #1000;
        #5000;    // 500nsec Wait, PLL Locked
        
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        
        MBFMi.AXI_Master_1Seq_Read(0, 32'h100, 8'd4, ASIZE_BT_4, ABURST_INCR, 3);
        MBFMi.AXI_Master_1Seq_Read(0, 32'h200, 8'd0, ASIZE_BT_4, ABURST_INCR, 4);
        
    end
    
endmodule

module clk_gen #(
    parameter         CLK_PERIOD = 100,
    parameter real    CLK_DUTY_CYCLE = 0.5,
    parameter        CLK_OFFSET = 0,
    parameter        START_STATE    = 1'b0 )
(
    output    reg        clk_out
);
    begin
        initial begin
            #CLK_OFFSET;
            forever
            begin
                clk_out = START_STATE;
                #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) clk_out = ~START_STATE;
                #(CLK_PERIOD*CLK_DUTY_CYCLE);
            end
        end
    end
endmodule

module reset_gen #(
    parameter    RESET_STATE = 1'b1,
    parameter    RESET_TIME = 100 )
(
    output    reg        reset_out
);
    begin
        initial begin
            reset_out = RESET_STATE;
            #RESET_TIME;
            reset_out = ~RESET_STATE;
        end
    end
endmodule

`default_nettype wire


これで、ランダムにWaitするデータ転送が実現できた。次は、Write, Read Transaction をfor ループで何十回も回せば、アドレス転送(AW... AR...)もWaitするし、データ転送もランダムにWaitするので、いろいろな転送パターンを試せる。これを目で見て良否を判断するのは大変で、やりたくない。そこで、OVLチェッカを付けて、自動的に良否を判定したい。エラーの時はエラー表示を出して、発火信号(Fire) を監視しようと思う。

OVL(Open Verification Language? Open Verification Library?)は、Accellera社から無料でダウンロードして使用することができる検証用ライブラリです。今回ダウンロードしたら、Ver. 2.6 になっていました。OVLを使用して、アサーションを使用した検証を行います。
  1. 2012年03月08日 05:42 |
  2. AXI4 Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする5(BFMシミュレーション3)

(注)この記事は完全に書き直しています。書き直しにあたっては、@Vengineer さんにご教授いただきました。ありがとうございました。追加:書きなおしたブログを消してしまったので、再度書き押しました。

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする4(BFMシミュレーション2)”のテストベンチ (CDC_axi_slave_tb.v) をテストベンチ部分とAXI4バスBFM 部分に分けた。それを下に示す。(なお、Testがトップで、その下にテストベンチがあり、そこにAXI4バスMaster BFM、クロック、リセット、スレーブIPをインスタンスする予定だったが、Project Navigator のシミュレーション用プロジェクトではテストベンチの上に何も入らないようなので、テストベンチにTestを含めた)

まずはAXI4バスMaster BFM 部分 (AXI4_Master_BFM.v) から下に示す。(`timescale 100ps / 1ps を書くのを忘れていました。)ファイル間で同じ値にしておくのに、なにか良い方法があるでしょうか?

2012/10/25:修正、またまたAXI非標準の部分があったので、書き直しています。この下のBFMはAXI非標準のままにしてあります。AXI非標準部分の修正後のファイルは、”キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする8.3(AXI Master BFMのVerilogコード)”を見てください。)

// AXI4 bus Master Bus Fucntion Mode

`default_nettype none

`timescale 100ps / 1ps

module AXI4_Master_BFM #(
    parameter DELAY    = 10 )
(
    input    wire    ACLK,

    output reg [0:0] S_AXI_AWID = 0,
    output reg [31:0] S_AXI_AWADDR = 0,
    output reg [7:0] S_AXI_AWLEN = 0,
    output reg [2:0] S_AXI_AWSIZE = 0,
    output reg [1:0] S_AXI_AWBURST = 0,
    output reg [1:0] S_AXI_AWLOCK = 0,
    output reg [3:0] S_AXI_AWCACHE = 2,    // Normal Non-cacheable Non-bufferable
    output reg [2:0] S_AXI_AWPROT = 0,
    output reg [3:0] S_AXI_AWREGION = 0,
    output reg [3:0] S_AXI_AWQOS = 0,
    output reg [0:0] S_AXI_AWUSER = 0,
    output reg S_AXI_AWVALID = 0,
    output reg [0:0] S_AXI_WID = 0,
    output reg [31:0] S_AXI_WDATA = 0,
    output reg [3:0] S_AXI_WSTRB = 0,
    output reg S_AXI_WLAST = 0,
    output reg [0:0] S_AXI_WUSER = 0,
    output reg S_AXI_WVALID = 0,
    output reg S_AXI_BREADY = 0,
    output reg [0:0] S_AXI_ARID = 0,
    output reg [31:0] S_AXI_ARADDR = 0,
    output reg [7:0] S_AXI_ARLEN = 0,
    output reg [2:0] S_AXI_ARSIZE = 0,
    output reg [1:0] S_AXI_ARBURST = 0,
    output reg [1:0] S_AXI_ARLOCK = 0,
    output reg [3:0] S_AXI_ARCACHE = 2, // Normal Non-cacheable Non-bufferable
    output reg [2:0] S_AXI_ARPROT = 0,
    output reg [3:0] S_AXI_ARREGION = 0,
    output reg [3:0] S_AXI_ARQOS = 0,
    output reg [0:0] S_AXI_ARUSER = 0,
    output reg S_AXI_ARVALID = 0,
    output reg S_AXI_RREADY = 0,

    input wire S_AXI_AWREADY,
    input wire S_AXI_WREADY,
    input wire [0:0] S_AXI_BID,
    input wire [1:0] S_AXI_BRESP,
    input wire [0:0] S_AXI_BUSER,
    input wire S_AXI_BVALID,
    input wire S_AXI_ARREADY,
    input wire [0:0] S_AXI_RID,
    input wire [31:0] S_AXI_RDATA,
    input wire [1:0] S_AXI_RRESP,
    input wire S_AXI_RLAST,
    input wire [0:0] S_AXI_RUSER,
    input wire S_AXI_RVALID
);

    reg     [7:0]    awlen_hold = 0;
    reg     [0:0]    wid_hold = 0;
    reg     axi_w_transaction_active = 0;
    reg     axi_r_transaction_active = 0;
    reg     [7:0]    arlen_hold = 0;

    // Write Channel
    task AXI_Master_1Seq_Write;    // Write Address, Write Data, Write Response をシーケンシャルにオーバーラップせずに行う。
        input    [0:0]    awid;
        input    [31:0]    awaddr;
        input    [7:0]    awlen;
        input    [2:0]    awsize;
        input    [1:0]    awburst;
        input    [31:0]    wdata;
        input    [7:0]    wait_clk_bready;
        begin
            AXI_MASTER_WAC(awid, awaddr, awlen, awsize, awburst);
            AXI_MASTER_WDC(wdata);
            AXI_MASTER_WRC(wait_clk_bready);
        end
    endtask
    
    // Write Address Channel
    task AXI_MASTER_WAC;
        input    [0:0]    awid;
        input    [31:0]    awaddr;
        input    [7:0]    awlen;
        input    [2:0]    awsize;
        input    [1:0]    awburst;
        begin
            S_AXI_AWID        = awid;
            S_AXI_AWADDR    = awaddr;
            S_AXI_AWLEN        = awlen;
            S_AXI_AWSIZE    = awsize;
            S_AXI_AWBURST    = awburst;
            S_AXI_AWVALID    = 1'b1;

            if (axi_w_transaction_active == 1'b0) begin // AXI Write トランザクションが開始されている場合は戻る
                awlen_hold        = awlen; // Write Data Channel のためにバースト数を取っておく
                @(posedge ACLK);    // 次のクロックへ
                
                while (~S_AXI_AWREADY) begin    // S_AXI_AWREADY が1になるまで待つ
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end
                
                #DELAY;
                S_AXI_AWID         = 0;
                S_AXI_AWADDR    = 0;
                S_AXI_AWLEN     = 0;
                S_AXI_AWSIZE     = 0;
                S_AXI_AWBURST     = 0;
                S_AXI_AWVALID     = 1'b0;
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
                axi_w_transaction_active = 1'b1; // AXIトランザクション開始
            end
        end
    endtask
    
    // Write Data Channel
    task AXI_MASTER_WDC;    // WDATA は+1する
    // とりあえず、WSTRBはオール1にする
        input    [31:0]    wdata;
        integer    i;
        begin
            i = 0;
            while (i<=awlen_hold) begin
                S_AXI_WVALID = 1'b1;
                S_AXI_WSTRB = 4'b1111;
                if (i == awlen_hold)
                    S_AXI_WLAST = 1'b1;
                else
                    S_AXI_WLAST = 1'b0;
                S_AXI_WDATA = wdata;
                wdata = wdata + 1;
                
                @(posedge ACLK);    // 次のクロックへ
                
                while (~S_AXI_WREADY) begin    // S_AXI_WREADY が0の時は1になるまで待つ
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end
                #DELAY;
                
                i = i + 1;
            end
            S_AXI_WVALID = 1'b0;
            S_AXI_WLAST = 1'b0;
            S_AXI_WSTRB = 4'b0000;
        end
    endtask
    
    // Write Response Channel
    task AXI_MASTER_WRC;    // wait_clk_bready
        input    [7:0]    wait_clk_bready;
        integer    i;
        begin
            for (i=0; i<wait_clk_bready; i=i+1) begin
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
            end
            
            S_AXI_BREADY = 1'b1;
            
                
            @(posedge ACLK);    // 次のクロックへ
            
            while (~S_AXI_BVALID) begin // S_AXI_BVALID が1になるまでWait
                #DELAY;
                @(posedge ACLK);    // 次のクロックへ
            end
            #DELAY;
            
            S_AXI_BREADY = 1'b0;
            
            axi_w_transaction_active = 1'b0; // AXIトランザクション終了
        end
    endtask 
    
    // Read Channel
    task AXI_Master_1Seq_Read; // Read Address, Read Data をシーケンシャルに行う。
        input    [0:0]    arid;
        input    [31:0]    araddr;
        input    [7:0]    arlen;
        input    [2:0]    arsize;
        input    [1:0]    arburst;
        begin
            AXI_MASTER_RAC(arid, araddr, arlen, arsize, arburst);
            AXI_MASTER_RDC();
        end
    endtask

    // Read Address Channel    
    task AXI_MASTER_RAC;
        input    [0:0]    arid;
        input    [31:0]    araddr;
        input    [7:0]    arlen;
        input    [2:0]    arsize;
        input    [1:0]    arburst;
        begin
            S_AXI_ARID         = arid;
            S_AXI_ARADDR    = araddr;
            S_AXI_ARLEN        = arlen;
            S_AXI_ARSIZE    = arsize;
            S_AXI_ARBURST    = arburst;
            S_AXI_ARVALID     = 1'b1;
            
            if (axi_r_transaction_active == 1'b0) begin // AXI Read トランザクションが開始されている場合は戻る
                arlen_hold    =arlen; // Read Data Channel のためにバースト数を取っておく
                @(posedge ACLK);    // 次のクロックへ
                
                while (~S_AXI_ARREADY) begin    // S_AXI_ARREADY が1になるまで待つ
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end
                
                #DELAY;
                S_AXI_ARID         = 0;
                S_AXI_ARADDR    = 0;
                S_AXI_ARLEN     = 0;
                S_AXI_ARSIZE     = 0;
                S_AXI_ARBURST     = 0;
                S_AXI_ARVALID     = 1'b0;
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
                axi_r_transaction_active = 1'b1; // AXIトランザクション開始
            end
        end
    endtask
    
    // Read Data Channel
    task AXI_MASTER_RDC; // S_AXI_RLAST がアサートされるまでS_AXI_RREADY をアサートする
        begin
            S_AXI_RREADY = 1'b1;
            
            @(posedge ACLK);    // 次のクロックへ
            while (~(S_AXI_RLAST & S_AXI_RVALID)) begin // S_AXI_RLAST & S_AXI_RVALID までS_AXI_RREADY を1にアサートする
                #DELAY;
                @(posedge ACLK);    // 次のクロックへ
            end
            #DELAY;
            
            S_AXI_RREADY = 1'b0;
            axi_r_transaction_active = 1'b0; // AXIトランザクション終了
        end
    endtask
    
endmodule

`default_nettype wire


次に、テストベンチ (CDC_axi_slave_tb.v) を下に示す。(バグってたので修正しました)

// CDC_axi_slave_tb.v

`default_nettype none

`timescale 100ps / 1ps

module CDC_axi_slave_tb;
    
    parameter DELAY    = 10;
    
    parameter    ASIZE_BT_4    = 3'd2;    // 32 bit width
    parameter    ASIZE_BT_2 = 3'd1;    // 16 bit width
    parameter    ASIZE_BT_1 = 3'd0;    // 8 bit width
    
    parameter    ABURST_FIXED    = 2'd0;
    parameter    ABURST_INCR    = 2'd1;
    parameter    ABURST_WRAP    = 2'd2;
    
    parameter    BRESP_OKAY        = 2'b00;
    parameter    BRESP_EXOKAY    = 2'b01;
    parameter    BRESP_SLVERR    = 2'b10;
    parameter    BRESP_DECERR    = 2'b11;

    // Inputs
    wire ACLK;
    wire ARESETN;
    wire [0:0] S_AXI_AWID;
    wire [31:0] S_AXI_AWADDR;
    wire [7:0] S_AXI_AWLEN;
    wire [2:0] S_AXI_AWSIZE;
    wire [1:0] S_AXI_AWBURST;
    wire [1:0] S_AXI_AWLOCK;
    wire [3:0] S_AXI_AWCACHE;
    wire [2:0] S_AXI_AWPROT;
    wire [3:0] S_AXI_AWREGION;
    wire [3:0] S_AXI_AWQOS;
    wire [0:0] S_AXI_AWUSER;
    wire S_AXI_AWVALID;
    wire [0:0] S_AXI_WID;
    wire [31:0] S_AXI_WDATA;
    wire [3:0] S_AXI_WSTRB;
    wire S_AXI_WLAST;
    wire [0:0] S_AXI_WUSER;
    wire S_AXI_WVALID;
    wire S_AXI_BREADY;
    wire [0:0] S_AXI_ARID;
    wire [31:0] S_AXI_ARADDR;
    wire [7:0] S_AXI_ARLEN;
    wire [2:0] S_AXI_ARSIZE;
    wire [1:0] S_AXI_ARBURST;
    wire [1:0] S_AXI_ARLOCK;
    wire [3:0] S_AXI_ARCACHE;
    wire [2:0] S_AXI_ARPROT;
    wire [3:0] S_AXI_ARREGION;
    wire [3:0] S_AXI_ARQOS;
    wire [0:0] S_AXI_ARUSER;
    wire S_AXI_ARVALID;
    wire S_AXI_RREADY;
    wire pixclk;

    // Outputs
    reg     S_AXI_AWREADY;
    reg     S_AXI_WREADY;
    reg     [0:0] S_AXI_BID;
    reg     [1:0] S_AXI_BRESP;
    wire     [0:0] S_AXI_BUSER = 0;
    reg     S_AXI_BVALID;
    reg     S_AXI_ARREADY;
    reg     [0:0] S_AXI_RID;
    reg     [31:0] S_AXI_RDATA;
    reg     [1:0] S_AXI_RRESP;
    reg     S_AXI_RLAST;
    wire     [0:0] S_AXI_RUSER = 0;
    reg     S_AXI_RVALID;
    wire TMDS_tx_clk_p;
    wire TMDS_tx_clk_n;
    wire TMDS_tx_2_G_p;
    wire TMDS_tx_2_G_n;
    wire TMDS_tx_1_R_p;
    wire TMDS_tx_1_R_n;
    wire TMDS_tx_0_B_p;
    wire TMDS_tx_0_B_n;

    wire S_AXI_AWREADY_d;
    wire S_AXI_WREADY_d;
    wire [0:0]    S_AXI_BID_d;
    wire [1:0]    S_AXI_BRESP_d;
    wire S_AXI_BVALID_d;
    wire S_AXI_ARREADY_d;
    wire [0:0]    S_AXI_RID_d;
    wire [31:0]    S_AXI_RDATA_d;
    wire [1:0]    S_AXI_RRESP_d;
    wire S_AXI_RLAST_d;
    wire S_AXI_RVALID_d;
    
    // Instantiate the Unit Under Test (UUT)
    CDC_axi_slave uut (
        .ACLK(ACLK), 
        .ARESETN(ARESETN), 
        .S_AXI_AWID(S_AXI_AWID), 
        .S_AXI_AWADDR(S_AXI_AWADDR), 
        .S_AXI_AWLEN(S_AXI_AWLEN), 
        .S_AXI_AWSIZE(S_AXI_AWSIZE), 
        .S_AXI_AWBURST(S_AXI_AWBURST), 
        .S_AXI_AWLOCK(S_AXI_AWLOCK), 
        .S_AXI_AWCACHE(S_AXI_AWCACHE), 
        .S_AXI_AWPROT(S_AXI_AWPROT), 
        .S_AXI_AWREGION(S_AXI_AWREGION), 
        .S_AXI_AWQOS(S_AXI_AWQOS), 
        .S_AXI_AWUSER(S_AXI_AWUSER), 
        .S_AXI_AWVALID(S_AXI_AWVALID), 
        .S_AXI_AWREADY(S_AXI_AWREADY_d), 
        .S_AXI_WID(S_AXI_WID), 
        .S_AXI_WDATA(S_AXI_WDATA), 
        .S_AXI_WSTRB(S_AXI_WSTRB), 
        .S_AXI_WLAST(S_AXI_WLAST), 
        .S_AXI_WUSER(S_AXI_WUSER), 
        .S_AXI_WVALID(S_AXI_WVALID), 
        .S_AXI_WREADY(S_AXI_WREADY_d), 
        .S_AXI_BID(S_AXI_BID_d), 
        .S_AXI_BRESP(S_AXI_BRESP_d), 
        .S_AXI_BUSER(S_AXI_BUSER), 
        .S_AXI_BVALID(S_AXI_BVALID_d), 
        .S_AXI_BREADY(S_AXI_BREADY), 
        .S_AXI_ARID(S_AXI_ARID), 
        .S_AXI_ARADDR(S_AXI_ARADDR), 
        .S_AXI_ARLEN(S_AXI_ARLEN), 
        .S_AXI_ARSIZE(S_AXI_ARSIZE), 
        .S_AXI_ARBURST(S_AXI_ARBURST), 
        .S_AXI_ARLOCK(S_AXI_ARLOCK), 
        .S_AXI_ARCACHE(S_AXI_ARCACHE), 
        .S_AXI_ARPROT(S_AXI_ARPROT), 
        .S_AXI_ARREGION(S_AXI_ARREGION), 
        .S_AXI_ARQOS(S_AXI_ARQOS), 
        .S_AXI_ARUSER(S_AXI_ARUSER), 
        .S_AXI_ARVALID(S_AXI_ARVALID), 
        .S_AXI_ARREADY(S_AXI_ARREADY_d), 
        .S_AXI_RID(S_AXI_RID_d), 
        .S_AXI_RDATA(S_AXI_RDATA_d), 
        .S_AXI_RRESP(S_AXI_RRESP_d), 
        .S_AXI_RLAST(S_AXI_RLAST_d), 
        .S_AXI_RUSER(S_AXI_RUSER), 
        .S_AXI_RVALID(S_AXI_RVALID_d), 
        .S_AXI_RREADY(S_AXI_RREADY), 
        .pixclk(pixclk), 
        .TMDS_tx_clk_p(TMDS_tx_clk_p), 
        .TMDS_tx_clk_n(TMDS_tx_clk_n), 
        .TMDS_tx_2_G_p(TMDS_tx_2_G_p), 
        .TMDS_tx_2_G_n(TMDS_tx_2_G_n), 
        .TMDS_tx_1_R_p(TMDS_tx_1_R_p), 
        .TMDS_tx_1_R_n(TMDS_tx_1_R_n), 
        .TMDS_tx_0_B_p(TMDS_tx_0_B_p), 
        .TMDS_tx_0_B_n(TMDS_tx_0_B_n)
    );
    always @* S_AXI_AWREADY <= #DELAY S_AXI_AWREADY_d;
    always @* S_AXI_WREADY <= #DELAY S_AXI_WREADY_d;
    always @* S_AXI_BID <= #DELAY S_AXI_BID_d;
    always @* S_AXI_BRESP <= #DELAY S_AXI_BRESP_d;
    always @* S_AXI_BVALID <= #DELAY S_AXI_BVALID_d;
    always @* S_AXI_ARREADY <= #DELAY S_AXI_ARREADY_d;
    always @* S_AXI_RID <= #DELAY S_AXI_RID_d;
    always @* S_AXI_RDATA <= #DELAY S_AXI_RDATA_d;
    always @* S_AXI_RRESP <= #DELAY S_AXI_RRESP_d;
    always @* S_AXI_RLAST <= #DELAY S_AXI_RLAST_d;
    always @* S_AXI_RVALID <= #DELAY S_AXI_RVALID_d;
    
    // clk_gen のインスタンス
    clk_gen #(
        .CLK_PERIOD(100),    // 10nsec, 100MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) ACLKi (
        .clk_out(ACLK)
    );
    
    clk_gen #(
        .CLK_PERIOD(250),    // 25nsec, 40MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) PIXCLKi (
        .clk_out(pixclk)
    );
    
    // reset_gen のインスタンス
    reset_gen #(
        .RESET_STATE(1'b0),
        .RESET_TIME(1000)    // 100nsec
    ) RESETi (
        .reset_out(ARESETN)
    );
    
    // AXI4_BFM のインスタンス
    AXI4_Master_BFM #(.DELAY(DELAY)) MBFMi(
        .ACLK(ACLK), 
        .S_AXI_AWID(S_AXI_AWID), 
        .S_AXI_AWADDR(S_AXI_AWADDR), 
        .S_AXI_AWLEN(S_AXI_AWLEN), 
        .S_AXI_AWSIZE(S_AXI_AWSIZE), 
        .S_AXI_AWBURST(S_AXI_AWBURST), 
        .S_AXI_AWLOCK(S_AXI_AWLOCK), 
        .S_AXI_AWCACHE(S_AXI_AWCACHE), 
        .S_AXI_AWPROT(S_AXI_AWPROT), 
        .S_AXI_AWREGION(S_AXI_AWREGION), 
        .S_AXI_AWQOS(S_AXI_AWQOS), 
        .S_AXI_AWUSER(S_AXI_AWUSER), 
        .S_AXI_AWVALID(S_AXI_AWVALID), 
        .S_AXI_AWREADY(S_AXI_AWREADY), 
        .S_AXI_WID(S_AXI_WID), 
        .S_AXI_WDATA(S_AXI_WDATA), 
        .S_AXI_WSTRB(S_AXI_WSTRB), 
        .S_AXI_WLAST(S_AXI_WLAST), 
        .S_AXI_WUSER(S_AXI_WUSER), 
        .S_AXI_WVALID(S_AXI_WVALID), 
        .S_AXI_WREADY(S_AXI_WREADY), 
        .S_AXI_BID(S_AXI_BID), 
        .S_AXI_BRESP(S_AXI_BRESP), 
        .S_AXI_BUSER(S_AXI_BUSER), 
        .S_AXI_BVALID(S_AXI_BVALID), 
        .S_AXI_BREADY(S_AXI_BREADY), 
        .S_AXI_ARID(S_AXI_ARID), 
        .S_AXI_ARADDR(S_AXI_ARADDR), 
        .S_AXI_ARLEN(S_AXI_ARLEN), 
        .S_AXI_ARSIZE(S_AXI_ARSIZE), 
        .S_AXI_ARBURST(S_AXI_ARBURST), 
        .S_AXI_ARLOCK(S_AXI_ARLOCK), 
        .S_AXI_ARCACHE(S_AXI_ARCACHE), 
        .S_AXI_ARPROT(S_AXI_ARPROT), 
        .S_AXI_ARREGION(S_AXI_ARREGION), 
        .S_AXI_ARQOS(S_AXI_ARQOS), 
        .S_AXI_ARUSER(S_AXI_ARUSER), 
        .S_AXI_ARVALID(S_AXI_ARVALID), 
        .S_AXI_ARREADY(S_AXI_ARREADY), 
        .S_AXI_RID(S_AXI_RID), 
        .S_AXI_RDATA(S_AXI_RDATA), 
        .S_AXI_RRESP(S_AXI_RRESP), 
        .S_AXI_RLAST(S_AXI_RLAST), 
        .S_AXI_RUSER(S_AXI_RUSER), 
        .S_AXI_RVALID(S_AXI_RVALID), 
        .S_AXI_RREADY(S_AXI_RREADY)
    );
    
    // test
    
    // Write Channel
    initial begin
        // Wait 100 ns for global reset to finish
        #1000;
        #5000;    // 500nsec Wait, PLL Locked
        
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        
        MBFMi.AXI_Master_1Seq_Write(0, 32'h100, 8'd4, ASIZE_BT_4, ABURST_INCR, 32'h12345678, 0);
        MBFMi.AXI_Master_1Seq_Write(0, 32'h200, 8'd0, ASIZE_BT_4, ABURST_INCR, 32'h11223344, 1);
        
        // アドレス転送のオーバーラップのサンプル
        // オーバーラップ後にもう一度、2度目のWrite Address Channel の転送を行う。
        MBFMi.AXI_MASTER_WAC(0, 32'h300, 8'd0, ASIZE_BT_4, ABURST_INCR);
        MBFMi.AXI_MASTER_WAC(0, 32'h400, 8'd0, ASIZE_BT_4, ABURST_INCR);
        MBFMi.AXI_MASTER_WDC(32'h1111_2222);
        MBFMi.AXI_MASTER_WRC(0);
        MBFMi.AXI_MASTER_WAC(0, 32'h400, 8'd0, ASIZE_BT_4, ABURST_INCR);
        MBFMi.AXI_MASTER_WDC(32'h3333_4444);
        MBFMi.AXI_MASTER_WRC(0);
    end
    
    // Read Channel
    initial begin
        // Wait 100 ns for global reset to finish
        #1000;
        #5000;    // 500nsec Wait, PLL Locked
        
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        
        MBFMi.AXI_Master_1Seq_Read(0, 32'h100, 8'd4, ASIZE_BT_4, ABURST_INCR);
        MBFMi.AXI_Master_1Seq_Read(0, 32'h200, 8'd0, ASIZE_BT_4, ABURST_INCR);
        
    end
    
endmodule

module clk_gen #(
    parameter         CLK_PERIOD = 100,
    parameter real    CLK_DUTY_CYCLE = 0.5,
    parameter        CLK_OFFSET = 0,
    parameter        START_STATE    = 1'b0 )
(
    output    reg        clk_out
);
    begin
        initial begin
            #CLK_OFFSET;
            forever
            begin
                clk_out = START_STATE;
                #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) clk_out = ~START_STATE;
                #(CLK_PERIOD*CLK_DUTY_CYCLE);
            end
        end
    end
endmodule

module reset_gen #(
    parameter    RESET_STATE = 1'b1,
    parameter    RESET_TIME = 100 )
(
    output    reg        reset_out
);
    begin
        initial begin
            reset_out = RESET_STATE;
            #RESET_TIME;
            reset_out = ~RESET_STATE;
        end
    end
endmodule

`default_nettype wire


これでProject Navigator からISimを起動したらシミュレーションが前回と同じように(やっと)できた。
CDC_axi_slave_11_120304.png

これで、テストベンチに改良を加えていく。

(追記)Verilog コード中にブロッキング代入文とノン・ブロッキング代入文が混在しているので、家に帰ったら修正します。
  1. 2012年03月06日 12:26 |
  2. AXI4 Slave IPの作製
  3. | トラックバック:0
  4. | コメント:0

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする4(BFMシミュレーション2)

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする3(BFMシミュレーション)”の続き。

前回、AXI4バス・マスタBFMのWrite側を作ってテストしたので、今度は、AXI4バス・マスタBFMのRead側を作成してテストした。

下にシミュレーション波形1を示す。S_AXI_ARVALIDが 1 にアサートされてから、S_AXI_ARREADY が 1 にアサートされるのに時間がかかっているのは、その間にWrite Transaction が実行されているからだ。キャラクタ・ディスプレイ・コントローラという回路の性質を考えるとReadよりもWriteの方が多いことが考えられる。そのため、Write優先でアービトレーションを行なっている。ちなみに、キャラクタ・ディスプレイ・コントローラのプロセッサから操作できるポートは1つで、それをWrite Transaction とRead Transaction が共用で使用する。
CDC_axi_slave_10_120304.png

上の図では、この前の最初のWrite 同様に、0x100番地から5バーストのRead を行なっている。Writeで書いたアドレスを読み出しているので、前回のWriteで書いたデータの下16ビットが出てきていることが確認できると思う。

下に、Write, Read Transaction の全体のシミュレーション波形を示す。
CDC_axi_slave_11_120304.png

Write Transaction を下に示す。

1.0x100番地から、12345678の値をWriteし、+1しながら5バーストWrite
2.0x200番地に、11223344の値をWrite
3.オーバーラップのサンプル、0x300番地に11112222の値をWrite
4.オーバーラップのサンプル、0x400番地に33334444の値をWrite


Read Transaction を下に示す。

1.0x100番地から、00005678の値を読み出せた。その後+1しながらReadバーストし、最後の値は0000567Cだった。
2.0x200番地から00003344の値を読み出せた。


Readした値の上16ビットがないのは、仕様となっている。32ビット幅のうちの下16ビットを色データとキャラクタコードに使用している。

さて、現時点でのテストベンチを下に貼る。Write とRead は別々のinitial文で書いてある。並列に実行できるようするためだ。
Write の通常のTransactionのために、AXI_Master_1Seq_Writeタスクを用意してある。オーバーラップのテストのためには、AXI_MASTER_WAC、AXI_MASTER_WDC、AXI_MASTER_WRCタスクを別々に書く必要がある。
Readには、AXI_Master_1Seq_Readタスクを用意してある。

`default_nettype none

`timescale 100ps / 1ps

////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer:
//
// Create Date:   03:54:02 02/29/2012
// Design Name:   CDC_axi_slave
// Module Name:   H:/HDL/FndtnISEWork/Spartan6/Atlys/Atlys_XPS_CDC_SVGA_134/pcores/CDC_axi_slave_v1_00_a/CDC_axi_slave/CDC_axi_slave_tb.v
// Project Name:  afifo_sm
// Target Device:  
// Tool versions:  
// Description: 
//
// Verilog Test Fixture created by ISE for module: CDC_axi_slave
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
////////////////////////////////////////////////////////////////////////////////

module CDC_axi_slave_tb;

    parameter    DELAY    = 10;
    
    parameter    ASIZE_BT_4    = 3'd2;    // 32 bit width
    parameter    ASIZE_BT_2 = 3'd1;    // 16 bit width
    parameter    ASIZE_BT_1 = 3'd0;    // 8 bit width
    
    parameter    ABURST_FIXED    = 2'd0;
    parameter    ABURST_INCR    = 2'd1;
    parameter    ABURST_WRAP    = 2'd2;
    
    parameter    BRESP_OKAY        = 2'b00;
    parameter    BRESP_EXOKAY    = 2'b01;
    parameter    BRESP_SLVERR    = 2'b10;
    parameter    BRESP_DECERR    = 2'b11;

    // Inputs
    reg ACLK;
    reg ARESETN;
    reg [0:0] S_AXI_AWID;
    reg [31:0] S_AXI_AWADDR;
    reg [7:0] S_AXI_AWLEN;
    reg [2:0] S_AXI_AWSIZE;
    reg [1:0] S_AXI_AWBURST;
    reg [1:0] S_AXI_AWLOCK;
    reg [3:0] S_AXI_AWCACHE;
    reg [2:0] S_AXI_AWPROT;
    reg [3:0] S_AXI_AWREGION;
    reg [3:0] S_AXI_AWQOS;
    reg [0:0] S_AXI_AWUSER;
    reg S_AXI_AWVALID;
    reg [0:0] S_AXI_WID;
    reg [31:0] S_AXI_WDATA;
    reg [3:0] S_AXI_WSTRB;
    reg S_AXI_WLAST;
    reg [0:0] S_AXI_WUSER;
    reg S_AXI_WVALID;
    reg S_AXI_BREADY;
    reg [0:0] S_AXI_ARID;
    reg [31:0] S_AXI_ARADDR;
    reg [7:0] S_AXI_ARLEN;
    reg [2:0] S_AXI_ARSIZE;
    reg [1:0] S_AXI_ARBURST;
    reg [1:0] S_AXI_ARLOCK;
    reg [3:0] S_AXI_ARCACHE;
    reg [2:0] S_AXI_ARPROT;
    reg [3:0] S_AXI_ARREGION;
    reg [3:0] S_AXI_ARQOS;
    reg [0:0] S_AXI_ARUSER;
    reg S_AXI_ARVALID;
    reg S_AXI_RREADY;
    reg pixclk;

    // Outputs
    wire S_AXI_AWREADY;
    wire S_AXI_WREADY;
    wire [0:0] S_AXI_BID;
    wire [1:0] S_AXI_BRESP;
    wire [0:0] S_AXI_BUSER;
    wire S_AXI_BVALID;
    wire S_AXI_ARREADY;
    wire [0:0] S_AXI_RID;
    wire [31:0] S_AXI_RDATA;
    wire [1:0] S_AXI_RRESP;
    wire S_AXI_RLAST;
    wire [0:0] S_AXI_RUSER;
    wire S_AXI_RVALID;
    wire TMDS_tx_clk_p;
    wire TMDS_tx_clk_n;
    wire TMDS_tx_2_G_p;
    wire TMDS_tx_2_G_n;
    wire TMDS_tx_1_R_p;
    wire TMDS_tx_1_R_n;
    wire TMDS_tx_0_B_p;
    wire TMDS_tx_0_B_n;

    wire S_AXI_AWREADY_d;
    wire S_AXI_WREADY_d;
    wire [0:0]    S_AXI_BID_d;
    wire [1:0]    S_AXI_BRESP_d;
    wire S_AXI_BVALID_d;
    wire S_AXI_ARREADY_d;
    wire [0:0]    S_AXI_RID_d;
    wire [31:0]    S_AXI_RDATA_d;
    wire [1:0]    S_AXI_RRESP_d;
    wire S_AXI_RLAST_d;
    wire S_AXI_RVALID_d;
    reg     [7:0]    awlen_hold = 0;
    reg     axi_w_transaction_active = 0;
    reg     axi_r_transaction_active = 0;
    reg     [7:0]    arlen_hold = 0;
    
    // Instantiate the Unit Under Test (UUT)
    CDC_axi_slave uut (
        .ACLK(ACLK), 
        .ARESETN(ARESETN), 
        .S_AXI_AWID(S_AXI_AWID), 
        .S_AXI_AWADDR(S_AXI_AWADDR), 
        .S_AXI_AWLEN(S_AXI_AWLEN), 
        .S_AXI_AWSIZE(S_AXI_AWSIZE), 
        .S_AXI_AWBURST(S_AXI_AWBURST), 
        .S_AXI_AWLOCK(S_AXI_AWLOCK), 
        .S_AXI_AWCACHE(S_AXI_AWCACHE), 
        .S_AXI_AWPROT(S_AXI_AWPROT), 
        .S_AXI_AWREGION(S_AXI_AWREGION), 
        .S_AXI_AWQOS(S_AXI_AWQOS), 
        .S_AXI_AWUSER(S_AXI_AWUSER), 
        .S_AXI_AWVALID(S_AXI_AWVALID), 
        .S_AXI_AWREADY(S_AXI_AWREADY_d), 
        .S_AXI_WID(S_AXI_WID), 
        .S_AXI_WDATA(S_AXI_WDATA), 
        .S_AXI_WSTRB(S_AXI_WSTRB), 
        .S_AXI_WLAST(S_AXI_WLAST), 
        .S_AXI_WUSER(S_AXI_WUSER), 
        .S_AXI_WVALID(S_AXI_WVALID), 
        .S_AXI_WREADY(S_AXI_WREADY_d), 
        .S_AXI_BID(S_AXI_BID_d), 
        .S_AXI_BRESP(S_AXI_BRESP_d), 
        .S_AXI_BUSER(S_AXI_BUSER), 
        .S_AXI_BVALID(S_AXI_BVALID_d), 
        .S_AXI_BREADY(S_AXI_BREADY), 
        .S_AXI_ARID(S_AXI_ARID), 
        .S_AXI_ARADDR(S_AXI_ARADDR), 
        .S_AXI_ARLEN(S_AXI_ARLEN), 
        .S_AXI_ARSIZE(S_AXI_ARSIZE), 
        .S_AXI_ARBURST(S_AXI_ARBURST), 
        .S_AXI_ARLOCK(S_AXI_ARLOCK), 
        .S_AXI_ARCACHE(S_AXI_ARCACHE), 
        .S_AXI_ARPROT(S_AXI_ARPROT), 
        .S_AXI_ARREGION(S_AXI_ARREGION), 
        .S_AXI_ARQOS(S_AXI_ARQOS), 
        .S_AXI_ARUSER(S_AXI_ARUSER), 
        .S_AXI_ARVALID(S_AXI_ARVALID), 
        .S_AXI_ARREADY(S_AXI_ARREADY_d), 
        .S_AXI_RID(S_AXI_RID_d), 
        .S_AXI_RDATA(S_AXI_RDATA_d), 
        .S_AXI_RRESP(S_AXI_RRESP_d), 
        .S_AXI_RLAST(S_AXI_RLAST_d), 
        .S_AXI_RUSER(S_AXI_RUSER), 
        .S_AXI_RVALID(S_AXI_RVALID_d), 
        .S_AXI_RREADY(S_AXI_RREADY), 
        .pixclk(pixclk), 
        .TMDS_tx_clk_p(TMDS_tx_clk_p), 
        .TMDS_tx_clk_n(TMDS_tx_clk_n), 
        .TMDS_tx_2_G_p(TMDS_tx_2_G_p), 
        .TMDS_tx_2_G_n(TMDS_tx_2_G_n), 
        .TMDS_tx_1_R_p(TMDS_tx_1_R_p), 
        .TMDS_tx_1_R_n(TMDS_tx_1_R_n), 
        .TMDS_tx_0_B_p(TMDS_tx_0_B_p), 
        .TMDS_tx_0_B_n(TMDS_tx_0_B_n)
    );
    assign #DELAY S_AXI_AWREADY = S_AXI_AWREADY_d;
    assign #DELAY S_AXI_WREADY = S_AXI_WREADY_d;
    assign #DELAY S_AXI_BID = S_AXI_BID_d;
    assign #DELAY S_AXI_BRESP = S_AXI_BRESP_d;
    assign #DELAY S_AXI_BVALID = S_AXI_BVALID_d;
    assign #DELAY S_AXI_ARREADY = S_AXI_ARREADY_d;
    assign #DELAY S_AXI_RID = S_AXI_RID_d;
    assign #DELAY S_AXI_RDATA = S_AXI_RDATA_d;
    assign #DELAY S_AXI_RRESP = S_AXI_RRESP_d;
    assign #DELAY S_AXI_RLAST = S_AXI_RLAST_d;
    assign #DELAY S_AXI_RVALID = S_AXI_RVALID_d;
    
    parameter ACLK_PERIOD = 100;
    parameter real ACLK_DUTY_CYCLE = 0.5;
    parameter ACLK_OFFSET = 0;

    initial    // Clock process for clk
    begin
        #ACLK_OFFSET;
        forever
        begin
            ACLK = 1'b0;
            #(ACLK_PERIOD-(ACLK_PERIOD*ACLK_DUTY_CYCLE)) ACLK = 1'b1;
            #(ACLK_PERIOD*ACLK_DUTY_CYCLE);
        end
    end
    
    parameter PIXCLK_PERIOD = 250;
    parameter real PIXCLK_DUTY_CYCLE = 0.5;
    parameter PIXCLK_OFFSET = 0;

    initial    // Clock process for clk
    begin
        #PIXCLK_OFFSET;
        forever
        begin
            pixclk = 1'b0;
            #(PIXCLK_PERIOD-(PIXCLK_PERIOD*PIXCLK_DUTY_CYCLE)) pixclk = 1'b1;
            #(PIXCLK_PERIOD*PIXCLK_DUTY_CYCLE);
        end
    end
    
    // Write Channel
    initial begin
        // Initialize Inputs
        ARESETN = 0;
        S_AXI_AWID = 0;
        S_AXI_AWADDR = 0;
        S_AXI_AWLEN = 0;
        S_AXI_AWSIZE = 0;
        S_AXI_AWBURST = 0;
        S_AXI_AWVALID = 0;
        S_AXI_AWLOCK = 0;
        S_AXI_AWCACHE = 2;    // Normal Non-cacheable Non-bufferable 
        S_AXI_AWPROT = 0;
        S_AXI_AWREGION = 0;
        S_AXI_AWQOS = 0;
        S_AXI_AWUSER = 0;

        S_AXI_WID = 0;
        S_AXI_WDATA = 0;
        S_AXI_WSTRB = 0;
        S_AXI_WLAST = 0;
        S_AXI_WUSER = 0;
        S_AXI_WVALID = 0;
        S_AXI_BREADY = 0;
        pixclk = 0;

        // Wait 100 ns for global reset to finish
        #1000;
        
        // Add stimulus here
        ARESETN = 1'b1;
        
        #5000    // 500nsec Wait
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        
        AXI_Master_1Seq_Write(0, 32'h100, 8'd4, ASIZE_BT_4, ABURST_INCR, 32'h12345678, 0);
        AXI_Master_1Seq_Write(0, 32'h200, 8'd0, ASIZE_BT_4, ABURST_INCR, 32'h11223344, 1);
        
        // アドレス転送のオーバーラップのサンプル
        // オーバーラップ後にもう一度、2度目のWrite Address Channel の転送を行う。
        AXI_MASTER_WAC(0, 32'h300, 8'd0, ASIZE_BT_4, ABURST_INCR);
        AXI_MASTER_WAC(0, 32'h400, 8'd0, ASIZE_BT_4, ABURST_INCR);
        AXI_MASTER_WDC(32'h1111_2222);
        AXI_MASTER_WRC(0);
        AXI_MASTER_WAC(0, 32'h400, 8'd0, ASIZE_BT_4, ABURST_INCR);
        AXI_MASTER_WDC(32'h3333_4444);
        AXI_MASTER_WRC(0);
        
    end
    
    // Read Channel
    initial begin
        // Initialize Inputs
        S_AXI_ARID = 0;
        S_AXI_ARADDR = 0;
        S_AXI_ARLEN = 0;
        S_AXI_ARSIZE = 0;
        S_AXI_ARBURST = 0;
        S_AXI_ARLOCK = 0;
        S_AXI_ARCACHE = 2;    // Normal Non-cacheable Non-bufferable 
        S_AXI_ARPROT = 0;
        S_AXI_ARREGION = 0;
        S_AXI_ARQOS = 0;
        S_AXI_ARUSER = 0;
        S_AXI_ARVALID = 0;
        S_AXI_RREADY = 0;

        // Wait 100 ns for global reset to finish
        #1000;
        
        // Add stimulus here
        
        #5000    // 500nsec Wait
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        
        AXI_Master_1Seq_Read(0, 32'h100, 8'd4, ASIZE_BT_4, ABURST_INCR);
        AXI_Master_1Seq_Read(0, 32'h200, 8'd0, ASIZE_BT_4, ABURST_INCR);
        
    end
    
    //
    // AXI Master Bus Function Model
    //
    
    // Write Channel
    task AXI_Master_1Seq_Write;    // Write Address, Write Data, Write Response をシーケンシャルにオーバーラップせずに行う。
        input    [0:0]    awid;
        input    [31:0]    awaddr;
        input    [7:0]    awlen;
        input    [2:0]    awsize;
        input    [1:0]    awburst;
        input    [31:0]    wdata;
        input    [7:0]    wait_clk_bready;
        begin
            AXI_MASTER_WAC(awid, awaddr, awlen, awsize, awburst);
            AXI_MASTER_WDC(wdata);
            AXI_MASTER_WRC(wait_clk_bready);
        end
    endtask
    
    // Write Address Channel
    task AXI_MASTER_WAC;
        input    [0:0]    awid;
        input    [31:0]    awaddr;
        input    [7:0]    awlen;
        input    [2:0]    awsize;
        input    [1:0]    awburst;
        begin
            S_AXI_AWID        = awid;
            S_AXI_AWADDR    = awaddr;
            S_AXI_AWLEN        = awlen;
            S_AXI_AWSIZE    = awsize;
            S_AXI_AWBURST    = awburst;
            S_AXI_AWVALID    = 1'b1;

            if (axi_w_transaction_active == 0) begin // AXI Write トランザクションが開始されている場合は戻る
                awlen_hold        = awlen; // Write Data Channel のためにバースト数を取っておく
                @(posedge ACLK);    // 次のクロックへ
                
                while (~S_AXI_AWREADY) begin    // S_AXI_AWREADY が1になるまで待つ
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end
                
                #DELAY;
                S_AXI_AWID         = 0;
                S_AXI_AWADDR    = 0;
                S_AXI_AWLEN     = 0;
                S_AXI_AWSIZE     = 0;
                S_AXI_AWBURST     = 0;
                S_AXI_AWVALID     = 1'b0;
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
                axi_w_transaction_active = 1; // AXIトランザクション開始
            end
        end
    endtask
    
    // Write Data Channel
    task AXI_MASTER_WDC;    // WDATA は+1する
    // とりあえず、WSTRBはオール1にする
        input    [31:0]    wdata;
        integer    i;
        begin
            i = 0;
            while (i<=awlen_hold) begin
                S_AXI_WVALID = 1'b1;
                S_AXI_WSTRB = 4'b1111;
                if (i == awlen_hold)
                    S_AXI_WLAST = 1'b1;
                else
                    S_AXI_WLAST = 1'b0;
                S_AXI_WDATA = wdata;
                wdata = wdata + 1;
                
                @(posedge ACLK);    // 次のクロックへ
                
                while (~S_AXI_WREADY) begin    // S_AXI_WREADY が0の時は1になるまで待つ
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end
                #DELAY;
                
                i = i + 1;
            end
            S_AXI_WVALID = 1'b0;
            S_AXI_WLAST = 1'b0;
            S_AXI_WSTRB = 4'b0000;
        end
    endtask
    
    // Write Response Channel
    task AXI_MASTER_WRC;    // wait_clk_bready
        input    [7:0]    wait_clk_bready;
        integer    i;
        begin
            for (i=0; i<wait_clk_bready; i=i+1) begin
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
            end
            
            S_AXI_BREADY = 1'b1;
                
            @(posedge ACLK);    // 次のクロックへ
            
            while (~S_AXI_BVALID) begin // S_AXI_BVALID が1になるまでWait
                #DELAY;
                @(posedge ACLK);    // 次のクロックへ
            end
            #DELAY;
            
            S_AXI_BREADY = 1'b0;
            
            axi_w_transaction_active = 0; // AXIトランザクション終了
        end
    endtask 
    
    // Read Channel
    task AXI_Master_1Seq_Read; // Read Address, Read Data をシーケンシャルに行う。
        input    [0:0]    arid;
        input    [31:0]    araddr;
        input    [7:0]    arlen;
        input    [2:0]    arsize;
        input    [1:0]    arburst;
        begin
            AXI_MASTER_RAC(arid, araddr, arlen, arsize, arburst);
            AXI_MASTER_RDC();
        end
    endtask

    // Read Address Channel    
    task AXI_MASTER_RAC;
        input    [0:0]    arid;
        input    [31:0]    araddr;
        input    [7:0]    arlen;
        input    [2:0]    arsize;
        input    [1:0]    arburst;
        begin
            S_AXI_ARID         = arid;
            S_AXI_ARADDR    = araddr;
            S_AXI_ARLEN        = arlen;
            S_AXI_ARSIZE    = arsize;
            S_AXI_ARBURST    = arburst;
            S_AXI_ARVALID     = 1'b1;
            
            if (axi_r_transaction_active == 0) begin // AXI Read トランザクションが開始されている場合は戻る
                arlen_hold    =arlen; // Read Data Channel のためにバースト数を取っておく
                @(posedge ACLK);    // 次のクロックへ
                
                while (~S_AXI_ARREADY) begin    // S_AXI_ARREADY が1になるまで待つ
                    #DELAY;
                    @(posedge ACLK);    // 次のクロックへ
                end
                
                #DELAY;
                S_AXI_ARID         = 0;
                S_AXI_ARADDR    = 0;
                S_AXI_ARLEN     = 0;
                S_AXI_ARSIZE     = 0;
                S_AXI_ARBURST     = 0;
                S_AXI_ARVALID     = 1'b0;
                @(posedge ACLK);    // 次のクロックへ
                #DELAY;
                axi_r_transaction_active = 1; // AXIトランザクション開始
            end
        end
    endtask
    
    // Read Data Channel
    task AXI_MASTER_RDC; // S_AXI_RLAST がアサートされるまでS_AXI_RREADY をアサートする
        begin
            S_AXI_RREADY = 1'b1;
            
            @(posedge ACLK);    // 次のクロックへ
            while (~(S_AXI_RLAST & S_AXI_RVALID)) begin // S_AXI_RLAST & S_AXI_RVALID までS_AXI_RREADY を1にアサートする
                #DELAY;
                @(posedge ACLK);    // 次のクロックへ
            end
            #DELAY;
            
            S_AXI_RREADY = 1'b0;
            axi_r_transaction_active = 0; // AXIトランザクション終了
        end
    endtask
    
endmodule

`default_nettype wire


どうだろうか?細かくコメントを書いておいたので、わかるかと思うのだが?
何か、ここをこうしたほうが良いとか?、全く別の方法でシミュレーションすべきだとか?ご意見がありましたらよろしくお願いします。
次は、このAXI4バスの簡易マスタBFMを含んだテストベンチをもっと詳細なテストができるように書き換えていく。
(なかなかAXI4バスのマスタ・デバイスまで行けないのだが、重要だと思うので、もう少しこのシミュレーションを改良してみることにする)
  1. 2012年03月04日 05:19 |
  2. AXI4 Slave IPの作製
  3. | トラックバック:0
  4. | コメント:4

戦火の馬を見てきた(映画)

今日は奥さんと”戦火の馬”を見てきました。良かったです。”マイウェイ 12000キロの真実”より戦争シーンは少なかったと思います。感動しました。良い映画だと思います。
馬の動きや目の動きとかはCG使ってますよね?でなければ、馬が演技した?たぶんCGでしょう?
  1. 2012年03月03日 21:06 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする3(BFMシミュレーション)

キャラクタ・ディスプレイ・コントローラをAXI4スレーブにする2(Pcoreテンプレート生成)”の続き。

最終的にはXPSのAXI4バスのカスタム・スレーブIPを作るのが目的なのだが、XPS上でのシミュレーションは全体のシミュレーションとなってコストが高い。よって、その前にAXI4バスの接続をするHDLファイルレベルで単体テストを行った。

まずは、カスタム・スレーブIP(pcore) のトップのVHDLファイル、CDC_axi_slave.vhd を作成して、それをトップとするProject Navigator のプロジェクトを作成した。その際に、Digilent社のDVI信号出力用のVHDLファイルをプロジェクトに入れたが、digilentライブラリだと面倒なので、workライブラリに変更した。これは、下位モジュールのインスタンス部分のdigilentをworkに変更し、DVITransmitter.vhd のlibrary digilent; をコメントアウトした。
更に、テストベンチ (CDC_axi_slave_tb.v) を作成した。これは、taskを使うためにVerilogで記述した。
下に作成したProject Navigator のプロジェクトを示す。
CDC_axi_slave_6_120302.png

テストベンチには、AXI4のマスタ・デバイスの簡単なBus Function Model(BFM) を記述してある。AXIバスのBFMはXilinx社から提供されているが、別オプションとなっていて料金を払わないと使用できない。そのため、簡単なテスト用のtask を自分で書いたわけだ。メーカー製のBFMは良く出来ているので、それを使用するために理解するのが難しいが、自分で作ったBFMならば、それも心配ない。その代わり、自分が勘違いしているとそれを検証することができない。本来は他人に作ってもらうのが良いのだが、そうも言ってられない。
とりあえず、AXI4マスタのWrite側だけできたのシミュレーションをやってみることにした。

さて、そのままISimでシミュレーションすると、コンパイルは通ったのだが、シミュレーション実行時にエラーになってしまった。PLLのAttribute Syntax Error だそうだ。
CDC_axi_slave_5_120301.png

これは、”DVI、HDMIの勉強4(キャラクタ・ディスプレイ・コントローラをDVI出力にする VHDL編1)”のdvi_disp.vhd を修正することにより回避できた。具体的には、

・PLL_BASE の RESET_ON_LOSS_OF_LOCK をComponent宣言とインスタンス部分でコメントアウトした。
・BUFPLL の ENABLE_SYNC をComponent宣言とインスタンス部分でコメントアウトした。


上記2つの対策を取ったら、シミュレーション実行時のエラーは回避できた。インプリメントはできるのに、どうしてだろうか?

下にバグフィックス後のシミュレーション波形を示す。S_AXI_AWLENを4に設定してしまったので、5バーストとなっている。4バーストに設定するつもりで間違ってしまった。
CDC_axi_slave_7_120302.png

今のところ、簡易AXIバス・マスタBFM もカスタム・スレーブIPも動いているようだ。簡易AXIバス・マスタBFM の機能的にバースト時のWaitはかけられない仕様だ。

次に、単発のトランザクションを下に示す。この場合は、Write Responce Channel の応答を1クロック遅延してある。
CDC_axi_slave_8_120302.png

Write Responce Channel の応答を数クロック遅延する機能は付けてあるのだが、データをバースト中のWait挿入機能はつけていない。どうやってサポートすれば良いか?考え中だからだ。
task に何番目がどのくらいWaitと入力するのではなく、後ろに擬似的な回路を作って、それのレイテンシやスループットを変更できるようにした方が良いと思う。

次は、Write Address Channelがオーバラップしている状態のシミュレーション結果。今回のAXI4バス・スレーブはオーバーラップに対応していないので、トランザクションが終了するまでWaitさせる。
CDC_axi_slave_9_120302.png

AXI4バスの簡易BFMは、データ・バースト時のWaitの入れ方とS_AXI_AWSIZEが2のみ対応(32ビット幅バス)という欠点があるが、とりあえずはこれで行こうと思う。
すぐにRead 側のAXI4バスのBFMを作ろうと思う。

(修正)タイミングチャート図のS_AXI_AWCACHEの値を2 (Normal Non-cacheable Non-bufferable) に変更した。
  1. 2012年03月02日 05:46 |
  2. AXI4 Slave IPの作製
  3. | トラックバック:0
  4. | コメント:4