FC2カウンター FPGAの部屋 2013年10月

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

FPGAの部屋

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

AXI4 Master Interfaceモジュールの作製1(仕様の検討)

今までのカメラ・コントローラやビットマップ・ディスプレイ・コントローラは、複数のアドレス転送を許さなかったので、今回は複数のアドレス転送を許す汎用AXI4 Master Interfaceモジュールを作製することにした。
(2013/10/31:書き換え、ユーザー回路とAXI4 Busのクロックドメインを分けた)
(2013/11/03 : read_fifo_rd_ena を追加)
(2013/11/26: write_data_empty ポートを追加)

CoreGen のFIFO IPを埋め込む関係上、64ビットデータバス幅の axi4_master_inf.v を作って、32ビット幅データバスにも対応させる予定だ。別々に作ろうと思う。

AXI4 Bus側とユーザー回路側は非同期FIFOで結ばれていて、それそれ独自のクロックで動作する。また、ユーザー回路のRead 側と Write 側は別々のクロックで動作することができる。
下に AXI4 Master Interface (axi4_master_inf.v) のブロック図を示す。
axi4_master_inf_1_131029.png

AXI4 Master Interface は、それぞれ4つのFIFOインターフェースで構成されている。

read_adfifo : AXI4 Master Read のアドレス転送用FIFOで、16深度の分散RAMを使用するFIFOとする。
read_fifo : AX4 Master Read のデータ転送用FIFOで、512深度のBlockRAMを使用するFIFOとする。
write_adfifo : AX4 Master Write のアドレス転送用FIFOで、16深度の分散RAMを使用するFIFOとする。
write_fifo : AX4 Master Write のデータ転送用FIFOで、512深度のBlockRAMを使用するFIFOとする。

4つのFIFOのクロックドメインは、Read側が read_clk, Write側が write_clkだ。リセットは、Read側が read_reset, Write側が write_reset だ。
4つのFIFOは独立に動作する。当然、read_adfifo と read_fifo は組で、read_adfifo にアドレスやバースト長(arlen)を書くと、AXI4 Master Readトランザクションを生成してAXI4バスに投げる。その後、Readアドレスに相当するReadデータを受け取る。AXI Slave から送られてきたReadデータは read_fifo に入ってきて、データが入ってくるとempty_n が 1 になり、Readデータがあることをユーザー回路に知らせる。
もう1つの組は、write_adfifo と write_fifo だ。Read同様に、write_adfifo にアドレスやバースト長(awlen)を書くと、AXI4 Master Writeトランザクションを生成してAXI4バスに投げる。その後、ユーザー回路が書き込んだwrite_data と wstrb(byte enable) をAXI Slave に送る。

ポートの説明
Readクロック、リセット

read_clk : ユーザー回路 Read 側のクロック
read_reset : ユーザー回路 Read 側のリセット


Readアドレス転送

read_adfifo_wr_ena : read_adfifoへのWrite Enable、1にすると read_adfifo へのWriteする。
read_adfifo_addr : Readアドレスを入力する(32ビット幅)
read_adfifo_arlen : データ転送数-1 を入力する(8ビット幅)
read_adfifo_full : read_adfifo の fullフラグ


Readデータ転送

read_fifo_rd_ena : read_fifoの Read Enable
read_fifo_read_data : read_data(32ビット幅または64ビット幅)
read_fifo_empty_n : read_fifoの empty フラグ(active low)、1の時にReadデータがある
read_fifo_almost_empty_n : read_fifo の almost emptyフラグ(active low)、後1つ Read すると empty になる


Writeクロック、リセット

write_clk : ユーザー回路 write 側のクロック
write_reset : ユーザー回路 write 側のリセット


Writeアドレス転送

wirte_adfifo_wr_ena : write_adfifo へのWrite Enable、1にすると write_adfifo へのWriteする
write_adfifo_addr : Writeアドレスを入力する(32ビット幅)
write_adfifo_arlen : データ転送数-1 を入力する(8ビット幅)
write_adfifo_full : write_adfifo の fullフラグ

Writeデータ転送

wirte_fifo_wr_ena : write_fifo へのWrite Enable、1にすると write_fifo へのWriteする
write_fifo_wirte_data : write_data(32ビット幅または64ビット幅)
write_fifo_wstrb : byte enable (4ビットまたは、8ビット)
write_fifo_full : write_fifo の fullフラグ
write_fifo_almost_full : write_fifo の almost full フラグ、後1つで full
write_fifo_empty : write_fifo のデータが空になった。クロックはwrite_clkに載せ替え済み。

アドレス転送はアドレスとデータ転送長をFIFOにWriteするのは、2クロックに1回を想定している。
  1. 2013年10月29日 19:34 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

AXI4 Master用自作AXI4 Slave BFM

lap_filter_axim_cam_fb_if.v をシミュレーションする2”をシミュレーションする際にAXI4 Master用自作AXI4 Slave BFM (Bus Functional Model) を使用した。今までの自作BFMでは、Writeした値をReadするという検証戦略だったため、Readしている時にはWrite出来ないという制約が付いていた。今回はそれではシミュレーションするのに不都合があったため、その制約を取り払った。

下にAXI4 Master用自作AXI4 Slave BFMの axi_slave_BFM.vhd を貼っておく。アドレス転送をネストすることは出来ない1つずつ受ける仕様だ。

-----------------------------------------------------------------------------
--
-- AXI Master用 Slave Bus Function Mode (BFM)
-- axi_slave_BFM.vhd
--
-----------------------------------------------------------------------------
-- 2012/02/25 : M_AXI_AWBURST=1 (INCR) にのみ対応、AWSIZE, ARSIZE = 000 (1byte), 001 (2bytes), 010 (4bytes) のみ対応。
-- 2012/07/04 : READ_ONLY_TRANSACTION を追加。Read機能のみでも+1したデータを出力することが出来るように変更した。

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_misc.all;

package m_seq_bfm_pack is
    function M_SEQ16_BFM_F(mseq16in : std_logic_vector
        )return std_logic_vector;
end package m_seq_bfm_pack;
package body m_seq_bfm_pack is
    function M_SEQ16_BFM_F(mseq16in : std_logic_vector
        )return std_logic_vector is
            variable mseq16 : std_logic_vector(15 downto 0);
            variable xor_result : std_logic;
    begin
        xor_result := mseq16in(15) xor mseq16in(12) xor mseq16in(10) xor mseq16in(8) xor mseq16in(7) xor mseq16in(6) xor mseq16in(3) xor mseq16in(2);
        mseq16 := mseq16in(14 downto 0) & xor_result;
        return mseq16;
    end M_SEQ16_BFM_F;
end m_seq_bfm_pack;


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

library work;
use work.m_seq_bfm_pack.all;

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

entity axi_slave_bfm is
  generic (
    C_M_AXI_ID_WIDTH     : integer := 1;
    C_M_AXI_ADDR_WIDTH   : integer := 32;
    C_M_AXI_DATA_WIDTH   : integer := 32;
    C_M_AXI_AWUSER_WIDTH : integer := 1;
    C_M_AXI_ARUSER_WIDTH : integer := 1;
    C_M_AXI_WUSER_WIDTH  : integer := 1;
    C_M_AXI_RUSER_WIDTH  : integer := 1;
    C_M_AXI_BUSER_WIDTH  : integer := 1;
    
    C_M_AXI_TARGET        : integer := 0;
    C_OFFSET_WIDTH        : integer := 10; -- 割り当てるRAMのアドレスのビット幅
    C_M_AXI_BURST_LEN    : integer := 256;
    
    WRITE_RANDOM_WAIT    : integer := 1; -- Write Transaction のデータ転送の時にランダムなWaitを発生させる=1, Waitしない=0
    READ_RANDOM_WAIT    : integer := 0; -- Read Transaction のデータ転送の時にランダムなWaitを発生させる=1, Waitしない=0
    READ_ONLY_TRANSACTION :    integer := 0 -- Read, Write Transaciton 双方を使用する = 0(RAMにWriteしたものをReadする)、Read Transaciton のみ使用する = 1(データは+1したデータをReadデータとして使用する
    );
  port(
    -- System Signals
    ACLK    : in std_logic;
    ARESETN : in std_logic;

    -- Master Interface Write Address Ports
    M_AXI_AWID     : in  std_logic_vector(C_M_AXI_ID_WIDTH-1 downto 0);
    M_AXI_AWADDR   : in  std_logic_vector(C_M_AXI_ADDR_WIDTH-1 downto 0);
    M_AXI_AWLEN    : in  std_logic_vector(8-1 downto 0);
    M_AXI_AWSIZE   : in  std_logic_vector(3-1 downto 0);
    M_AXI_AWBURST  : in  std_logic_vector(2-1 downto 0);
    -- M_AXI_AWLOCK   : in  std_logic_vector(2-1 downto 0);
    M_AXI_AWLOCK   : in  std_logic_vector(1 downto 0);
    M_AXI_AWCACHE  : in  std_logic_vector(4-1 downto 0);
    M_AXI_AWPROT   : in  std_logic_vector(3-1 downto 0);
    M_AXI_AWQOS    : in  std_logic_vector(4-1 downto 0);
    M_AXI_AWUSER   : in  std_logic_vector(C_M_AXI_AWUSER_WIDTH-1 downto 0);
    M_AXI_AWVALID  : in  std_logic;
    M_AXI_AWREADY  : out std_logic;

    -- Master Interface Write Data Ports
    M_AXI_WDATA  : in  std_logic_vector(C_M_AXI_DATA_WIDTH-1 downto 0);
    M_AXI_WSTRB  : in  std_logic_vector(C_M_AXI_DATA_WIDTH/8-1 downto 0);
    M_AXI_WLAST  : in  std_logic;
    M_AXI_WUSER  : in  std_logic_vector(C_M_AXI_WUSER_WIDTH-1 downto 0);
    M_AXI_WVALID : in  std_logic;
    M_AXI_WREADY : out std_logic;

    -- Master Interface Write Response Ports
    M_AXI_BID    : out std_logic_vector(C_M_AXI_ID_WIDTH-1 downto 0);
    M_AXI_BRESP  : out std_logic_vector(2-1 downto 0);
    M_AXI_BUSER  : out std_logic_vector(C_M_AXI_BUSER_WIDTH-1 downto 0);
    M_AXI_BVALID : out std_logic;
    M_AXI_BREADY : in  std_logic;

    -- Master Interface Read Address Ports
    M_AXI_ARID     : in  std_logic_vector(C_M_AXI_ID_WIDTH-1 downto 0);
    M_AXI_ARADDR   : in  std_logic_vector(C_M_AXI_ADDR_WIDTH-1 downto 0);
    M_AXI_ARLEN    : in  std_logic_vector(8-1 downto 0);
    M_AXI_ARSIZE   : in  std_logic_vector(3-1 downto 0);
    M_AXI_ARBURST  : in  std_logic_vector(2-1 downto 0);
    M_AXI_ARLOCK   : in  std_logic_vector(2-1 downto 0);
    M_AXI_ARCACHE  : in  std_logic_vector(4-1 downto 0);
    M_AXI_ARPROT   : in  std_logic_vector(3-1 downto 0);
    M_AXI_ARQOS    : in  std_logic_vector(4-1 downto 0);
    M_AXI_ARUSER   : in  std_logic_vector(C_M_AXI_ARUSER_WIDTH-1 downto 0);
    M_AXI_ARVALID  : in  std_logic;
    M_AXI_ARREADY  : out std_logic;

    -- Master Interface Read Data Ports
    M_AXI_RID    : out std_logic_vector(C_M_AXI_ID_WIDTH-1 downto 0);
    M_AXI_RDATA  : out std_logic_vector(C_M_AXI_DATA_WIDTH-1 downto 0);
    M_AXI_RRESP  : out std_logic_vector(2-1 downto 0);
    M_AXI_RLAST  : out std_logic;
    M_AXI_RUSER  : out std_logic_vector(C_M_AXI_RUSER_WIDTH-1 downto 0);
    M_AXI_RVALID : out std_logic;
    M_AXI_RREADY : in  std_logic
    );

end axi_slave_bfm;

architecture implementation of axi_slave_bfm is

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";

constant    DATA_BUS_BYTES     : natural := C_M_AXI_DATA_WIDTH/8; -- データバスのビット幅
constant    ADD_INC_OFFSET    : natural := natural(log(real(DATA_BUS_BYTES), 2.0));

-- RAMの生成
constant    SLAVE_ADDR_NUMBER    : integer := 2**(C_OFFSET_WIDTH - ADD_INC_OFFSET);
type ram_array_def is array (SLAVE_ADDR_NUMBER-1 downto 0) of std_logic_vector(C_M_AXI_DATA_WIDTH-1 downto 0);
signal ram_array : ram_array_def := (others => (others => '0'));

-- 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;
signal addr_inc_step_wr : integer := 1;
signal awready         : std_logic;
signal wr_addr         : std_logic_vector(C_OFFSET_WIDTH-1 downto 0);
signal wr_bid         : std_logic_vector(C_M_AXI_ID_WIDTH-1 downto 0);
signal wr_bresp     : std_logic_vector(1 downto 0);
signal wr_bvalid     : std_logic;
signal m_seq16_wr    : std_logic_vector(15 downto 0);
signal wready        : std_logic;
type wready_state is (idle_wready, assert_wready, deassert_wready);
signal cs_wready : wready_state;
signal cdc_we : std_logic;

-- 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 addr_inc_step_rd : integer := 1;
signal arready         : std_logic;
signal rd_addr         : std_logic_vector(C_OFFSET_WIDTH-1 downto 0);
signal rd_axi_count    : std_logic_vector(7 downto 0);
signal rvalid        : std_logic;
signal rlast        : std_logic;
signal m_seq16_rd    : std_logic_vector(15 downto 0);
type rvalid_state is (idle_rvalid, assert_rvalid, deassert_rvalid);
signal cs_rvalid : rvalid_state;
signal read_data_count : std_logic_vector(C_M_AXI_DATA_WIDTH-1 downto 0);

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

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 M_AXI_AWVALID='1' then -- M_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 の終了待ち
                        wrt_cs <= awr_accept;
                        awready <= '1';
                    when awr_accept => -- M_AXI_AWREADY をアサート
                        wrt_cs <= wr_burst;
                        awready <= '0';
                    when wr_burst => -- Writeデータの転送
                        if M_AXI_WLAST='1' and M_AXI_WVALID='1' and wready='1' then -- Write Transaction 終了
                            wrt_cs <= idle_wr;
                        end if;
                end case;
            end if;
        end if;
    end process;
    M_AXI_AWREADY <= awready;
    
    -- m_seq_wr、16ビットのM系列を計算する
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                m_seq16_wr <= (0 => '1', others => '0');
            else
                if WRITE_RANDOM_WAIT=1 then -- Write Transaction 時にランダムなWaitを挿入する
                    if wrt_cs=wr_burst and M_AXI_WVALID='1' then
                        m_seq16_wr <= M_SEQ16_BFM_F(m_seq16_wr);
                    end if;
                else -- Wait無し
                    m_seq16_wr <= (others => '0');
                end if;
            end if;
        end if;
    end process;
                
    -- wready の処理、M系列を計算して128以上だったらWaitする。
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                cs_wready <= idle_wready;
                wready <= '0';
            else
                case (cs_wready) is
                    when idle_wready =>
                        if wrt_cs=awr_accept then -- 次はwr_burst
                            if m_seq16_wr(7)='0' then -- wready='1'
                                cs_wready <= assert_wready;
                                wready <= '1';
                            else -- m_seq16_wr(7)='1' then -- wready='0'
                                cs_wready <= deassert_wready;
                                wready <= '0';
                            end if;
                        end if;
                    when assert_wready => -- 一度wreadyがアサートされたら、1つのトランザクションが終了するまでwready='1'
                        if wrt_cs=wr_burst and M_AXI_WLAST='1' and M_AXI_WVALID='1' then -- 終了
                            cs_wready <= idle_wready;
                            wready <= '0';
                        elsif wrt_cs=wr_burst and M_AXI_WVALID='1' then -- 1つのトランザクション終了。
                            if m_seq16_wr(7)='1' then
                                cs_wready <= deassert_wready;
                                wready <= '0';
                            end if;
                        end if;
                    when deassert_wready =>
                        if m_seq16_wr(7)='0' then -- wready='1'
                            cs_wready <= assert_wready;
                            wready <= '1';
                        end if;
                end case;
            end if;
        end if;
    end process;
    
    M_AXI_WREADY <= wready;
    cdc_we <= '1' when wrt_cs=wr_burst and wready='1' and M_AXI_WVALID='1' else '0';
    
    -- addr_inc_step_wr の処理
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                addr_inc_step_wr <= 1;
            else
                if wrt_cs=awr_accept then
                    case (M_AXI_AWSIZE) is
                        when "000" => -- 8ビット転送
                            addr_inc_step_wr <= 1;
                        when "001" => -- 16ビット転送
                            addr_inc_step_wr <= 2;
                        when "010" => -- 32ビット転送
                            addr_inc_step_wr <= 4;
                        when "011" => -- 64ビット転送
                            addr_inc_step_wr <= 8;
                        when "100" => -- 128ビット転送
                            addr_inc_step_wr <= 16;
                        when "101" => -- 256ビット転送
                            addr_inc_step_wr <= 32;
                        when "110" => -- 512ビット転送
                            addr_inc_step_wr <= 64;
                        when others => --"111" => -- 1024ビット転送
                            addr_inc_step_wr <= 128;
                    end case;
                end if;
            end if;
        end if;
    end process;
    
    -- 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 <= M_AXI_AWADDR(C_OFFSET_WIDTH-1 downto 0);
                elsif wrt_cs=wr_burst and M_AXI_WVALID='1' and wready='1' then -- アドレスを進める
                    wr_addr <= wr_addr + addr_inc_step_wr;
                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 <= M_AXI_AWID;
                end if;
            end if;
        end if;
    end process;
    M_AXI_BID <= wr_bid;
    
    -- wr_bresp の処理
    -- M_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 M_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;
    M_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 M_AXI_WLAST='1' and M_AXI_WVALID='1' and wready='1' then -- Write Transaction 終了
                    wr_bvalid <= '1';
                elsif wr_bvalid='1' and M_AXI_BREADY='1' then -- wr_bvalid が1でMaster側のReadyも1ならばWrite resonse channel の転送も終了
                    wr_bvalid <= '0';
                end if;
            end if;
        end if;
    end process;
    M_AXI_BVALID <= wr_bvalid;
    M_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 M_AXI_ARVALID='1' then -- Read Transaction 要求
                            if wrt_cs=idle_wr and M_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 M_AXI_AWVALID='0' then -- Write Transaction State Machine がidle でWrite要求もない
                            rdt_cs <= arr_accept;
                            arready <= '1';
                        end if;
                    when arr_accept => -- M_AXI_ARREADY をアサート
                        rdt_cs <= rd_burst;
                        arready <= '0';
                    when rd_burst => -- Readデータの転送
                        if rd_axi_count=0 and rvalid='1' and M_AXI_RREADY='1' then -- Read Transaction 終了
                            rdt_cs <= idle_rd;
                        end if;
                end case;
            end if;
        end if;
    end process;
    M_AXI_ARREADY <= arready;

    -- m_seq_rd、16ビットのM系列を計算する
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                m_seq16_rd <= (others => '1'); -- Writeとシードを変更する
            else
                if READ_RANDOM_WAIT=1 then -- Read Transaciton のデータ転送でランダムなWaitを挿入する場合
                    if rdt_cs=rd_burst and M_AXI_RREADY='1' then
                        m_seq16_rd <= M_SEQ16_BFM_F(m_seq16_rd);
                    end if;
                else -- Wati無し
                    m_seq16_rd <= (others => '0');
                end if;
            end if;
        end if;
    end process;
                
    -- rvalid の処理、M系列を計算して128以上だったらWaitする。
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                cs_rvalid <= idle_rvalid;
                rvalid <= '0';
            else
                case (cs_rvalid) is
                    when idle_rvalid =>
                        if rdt_cs=arr_accept then -- 次はrd_burst
                            if m_seq16_rd(7)='0' then -- rvalid='1'
                                cs_rvalid <= assert_rvalid;
                                rvalid <= '1';
                            else -- m_seq16_rd(7)='1' then -- rvalid='0'
                                cs_rvalid <= deassert_rvalid;
                                rvalid <= '0';
                            end if;
                        end if;
                    when assert_rvalid => -- 一度rvalidがアサートされたら、1つのトランザクションが終了するまでrvalid='1'
                        if rdt_cs=rd_burst and rlast='1' and M_AXI_RREADY='1' then -- 終了
                            cs_rvalid <= idle_rvalid;
                            rvalid <= '0';
                        elsif rdt_cs=rd_burst and M_AXI_RREADY='1' then -- 1つのトランザクション終了。
                            if m_seq16_rd(7)='1' then
                                cs_rvalid <= deassert_rvalid;
                                rvalid <= '0';
                            end if;
                        end if;
                    when deassert_rvalid =>
                        if m_seq16_rd(7)='0' then -- rvalid='1'
                            cs_rvalid <= assert_rvalid;
                            rvalid <= '1';
                        end if;
                end case;
            end if;
        end if;
    end process;
    
    M_AXI_RVALID <= rvalid;
    
    -- addr_inc_step_rd の処理
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                addr_inc_step_rd <= 1;
            else
                if rdt_cs=arr_accept then
                    case (M_AXI_ARSIZE) is
                        when "000" => -- 8ビット転送
                            addr_inc_step_rd <= 1;
                        when "001" => -- 16ビット転送
                            addr_inc_step_rd <= 2;
                        when "010" => -- 32ビット転送
                            addr_inc_step_rd <= 4;
                        when "011" => -- 64ビット転送
                            addr_inc_step_rd <= 8;
                        when "100" => -- 128ビット転送
                            addr_inc_step_rd <= 16;
                        when "101" => -- 256ビット転送
                            addr_inc_step_rd <= 32;
                        when "110" => -- 512ビット転送
                            addr_inc_step_rd <= 64;
                        when others => -- "111" => -- 1024ビット転送
                            addr_inc_step_rd <= 128;
                    end case;
                end if;
            end if;
        end if;
    end process;
    
    -- 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 <= M_AXI_ARADDR(C_OFFSET_WIDTH-1 downto 0);
                elsif rdt_cs=rd_burst and M_AXI_RREADY='1' and rvalid='1' then
                    rd_addr <= rd_addr + addr_inc_step_rd;
                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 <= M_AXI_ARLEN;
                elsif rdt_cs=rd_burst and rvalid='1' and M_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 M_AXI_RREADY='1' then -- バーストする場合
                            rdlast <= rlast_assert;
                            rlast <= '1';
                        elsif rdt_cs=arr_accept and M_AXI_ARLEN=0 then -- 転送数が1の場合
                            rdlast <= rlast_assert;
                            rlast <= '1';
                        end if;
                    when rlast_assert => 
                        if rvalid='1' and M_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;
    M_AXI_RLAST <= rlast;
    
    -- M_AXI_RID, M_AXI_RUSER の処理
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                M_AXI_RID <= (others => '0');
            else
                if rdt_cs=arr_accept then
                    M_AXI_RID <= M_AXI_ARID;
                end if;
            end if;
        end if;
    end process;
    M_AXI_RUSER <= (others => '0');
    
    -- M_AXI_RRESP は、M_AXI_ARBURST がINCR の場合はOKAYを返す。それ以外はSLVERRを返す。
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                M_AXI_RRESP <= (others => '0');
            else
                if rdt_cs=arr_accept then
                    if M_AXI_ARBURST=AxBURST_INCR then
                        M_AXI_RRESP <= RESP_OKAY;
                    else
                        M_AXI_RRESP <= RESP_SLVERR;
                    end if;
                end if;
            end if;
        end if;
    end process;
    
    -- RAM
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if cdc_we='1' then
                for i in 0 to C_M_AXI_DATA_WIDTH/8-1 loop
                    if M_AXI_WSTRB(i)='1' then -- Byte Enable
                        ram_array(CONV_INTEGER(wr_addr(C_OFFSET_WIDTH-1 downto ADD_INC_OFFSET)))(i*8+7 downto i*8) <= M_AXI_WDATA(i*8+7 downto i*8);
                    end if;
                end loop;
            end if;
        end if;
    end process;

    -- Read Transaciton のみの場合のReadデータ(Transction 毎に+1)
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                read_data_count <= (others => '0');
            else
                if rdt_cs=rd_burst and rvalid='1' and M_AXI_RREADY='1' then -- Read Transaction が1つ終了
                    read_data_count <= read_data_count + 1;
                end if;
            end if;
        end if;
    end process;
    
    M_AXI_RDATA <= ram_array(CONV_INTEGER(rd_addr(C_OFFSET_WIDTH-1 downto ADD_INC_OFFSET))) when READ_ONLY_TRANSACTION=0 else read_data_count;
    
end implementation;


  1. 2013年10月29日 04:04 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

lap_filter_axim_cam_fb_if.v をシミュレーションする2

lap_filter_axim_cam_fb_if.v” をシミュレーションする”でlap_filter_axim_cam_fb_if.vのAXI4 Master Readをシミュレーションしてみたが、今度は、AXI4 Master Write をシミュレーションしてみた。

Single Write と 2バーストWriteトランザクションを発生させてみた。シミュレーション波形を下に示す。
Vivado_HLS_IP_3_131027.png

このAXI4 Master Writeアクセスを発生するのに、必要なテストベンチの一部を示す。

    // Read Write Test
    initial begin
        USER_dataout = 0;
        USER_address = 0;
        USER_size = 0;
        USER_req_din = 0;
        USER_req_write = 0;
        
        @(posedge ARESETN)
        #1;
        @(posedge ACLK);
        #1;
        USER_address = 32'h12340000;    // read request
        USER_size = 32'd800;
        USER_req_din = 1'b0;
        USER_req_write = 1'b1;
        
        @(posedge ACLK);
        #1;
        USER_address = 32'h56780000;    // write request
        USER_dataout = 32'h12345678;
        USER_size = 32'd1;
        USER_req_din = 1'b1;
        USER_req_write = 1'b1;
        
        @(posedge ACLK);
        #1;
        USER_address = 32'h89AB0000;    // write request
        USER_dataout = 32'h11223344;
        USER_size = 32'd2;
        USER_req_din = 1'b1;
        USER_req_write = 1'b1;
        
        @(posedge ACLK);
        #1;
        USER_dataout = 32'h22334455;
        
        @(posedge ACLK);
        #1;
        USER_req_din = 1'b0;
        USER_req_write = 1'b0;
    end


AXI4 Master Writeも使えそうだ。
  1. 2013年10月28日 03:54 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

テーブル作り2

テーブル作り”の続きです。

今日は天気が良いので、アクリルニスを塗りました。足は丸棒でボルト付きなので、仮止めして塗っています。
Table_5_131027.jpg

Table_6_131027.jpg

天板です。後2回ほど塗りたいと思います。
Table_7_131027.jpg

乾いたら、240番程度の紙やすりでこすってから、もう一度塗ります。
  1. 2013年10月27日 18:08 |
  2. 木工
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2013.3 で作ったラプラシアン・フィルタAXI4 Master IPを使う2

”Vivado HLS 2013.3 で作ったラプラシアン・フィルタAXI4 Master IPを使う1”でVivado HLS 2013.3 でC言語から合成したIPが動かないということを書いた。今回は、予め入れておいたChipScope を使用して、ラプラシアン・フィルタAXI4 Master IPの2つのAXI4 Masterバスと1つのAXI4 Lite Slaveバスの波形を見てみた。

まずは、レジスタを設定するAXI4 Lite Slave から見ていこう。
まずはキャプチャされた全体を下に示す。
HLS_lap_fil_axim_43_131026.png

次は最初のWriteしている部分を見ていこう。
HLS_lap_fil_axim_44_131026.png

AXI4 Lite SlaveアクセスのWriteは以下の通り。

1.最初の5つのWriteは初期化
2.画像データのアドレス(0x19107000)
3.ラプラシアン・フィルタ用の画像データのアドレス(0x192DC000)
4.ap_startのWrite
3.2つのデータのvld


その後は、2ビット目の ap_done が立たないかどうかをポーリングしている。面白いのは、RDATAから 2回1、1回0が読めることだ。
HLS_lap_fil_axim_45_131026.png

次は、画像データが入っているCAM_FBのReadを見ていこう。
下に、1ライン分(800ワード、1ワード=4バイト)のReadアクセスを示す。
HLS_lap_fil_axim_46_131026.png

RDATAを見ると、いろいろなデータが読めているのがわかる。

最初のアドレス転送を引き伸ばしてみてみよう。
HLS_lap_fil_axim_47_131026.png

最初のトランザクションは、0x19107000番地から256ワードのバースト転送であることがわかる。0x19107000はAXI4 Lite Slave で書かれた画像データのスタートアドレスだ。

次に、ラプラシアン・フィルタを掛けた後のデータのWriteアクセスを見ていく。
HLS_lap_fil_axim_48_131026.png

WDATAを見ると、すべてのデータが0になっている。これはおかしい。

最初の部分を拡大すると、0x192DC000番地から256ワードのWriteのアドレス転送を行っている。これは、AXI4 Lite Slave で書かれたラプラシアン・フィルタ用の画像データのアドレスだ。
HLS_lap_fil_axim_49_131026.png

今まで見てきたように、Vivado HLS 2013.3 で生成されたラプラシアン・フィルタIPには2つの問題がある。

1.ap_doneが 1 にならない。
2.ラプラシアン・フィルタのWriteデータが全て 0 になっている。


とりあえず、シミュレーションがうまくいかないのと合わせて、まだバグがある感じがする?
  1. 2013年10月26日 05:49 |
  2. Co-design
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2013.3 で作ったラプラシアン・フィルタAXI4 Master IPを使う1

Vivado HLS 2013.3でラプラシアン・フィルタ関数をaxi masterモジュールにする”でC言語からHDLに合成したラプラシアン・フィルタIPをDigilent LinuxのXPSプロジェクトに組み込んで、動作を確認したが、結果から言うと動作しなかった。

前回、C言語からHDLに合成したラプラシアン・フィルタIPをDigilent LinuxのXPSプロジェクトの pcores フォルダに入れなおして、Project Navigator から論理合成、インプリメント、ビットストリームの生成を行った。

ハードウェアとビットストリームをエクスポートして、SDKを立ちあげ、ソフトウェアをリビルドした。

AXI4 Master 版ラプラシアン・フィルタを動作されるソフトウェアを実行したが、ap_done が帰ってこない。Vivado 2013.2 の時とソフト的には同様の結果だった。

AXI4 Master版ラプラシアン・フィルタIPの制御用ソフトウェアを貼ってなかったようなので、貼っておく
lap_filter_axim.c から下に示す。

// lap_filter_axim.c
// AXI4 Master のVivado HLS出力ハードウェア・バージョン
// RGBをYに変換後にラプラシアンフィルタを掛ける。
// ピクセルのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 2013/10/15

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <ctype.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/kernel.h>
#include "lap_fil_axim_uty.h"

#define LAP_FILTER_AXIM_HW_ADDRESS    0x49000000    // ラプラシアン・フィルタのAXI4 Masterハードウェアのアドレス

#define HORIZONTAL_PIXEL_WIDTH    800
#define VERTICAL_PIXEL_WIDTH    600
#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)

#define BUFSIZE    1024

#define MEASURE_COUNT    5000

int conv_rgb2y(int rgb);
int chkhex(char *str);
volatile unsigned *setup_io(off_t mapped_addr, unsigned int *buf_addr);

int main()
{
    FILE *fd;
    unsigned int bitmap_dc_reg_addr;
    unsigned int read_num;
    volatile unsigned *bm_disp_cnt_reg;
    unsigned int fb_addr, next_frame_addr;
    int return_val;
    struct timeval start_time, end_time;
    unsigned int rmmap_cnt=0, wmmap_cnt=0;
    unsigned int lap_fil_hw, *lap_fil_hw_addr;
    char buf[BUFSIZE], *token;
    unsigned int val;
    unsigned int bitmap_buf;

    gettimeofday(&start_time, NULL);    // プログラム起動時の時刻を記録

    // fb_start_addr.txt の内容をパイプに入れる
    memset(buf, '\0'sizeof(buf)); // buf すべてに\0 を入れる
    // fb_start_addr.txt を開く
    fd = popen("cat /Apps/fb_start_addr.txt""r");
    if (fd != NULL){
        read_num = fread(buf, sizeof(unsigned char), BUFSIZE, fd);
        if (read_num > 0){
            sscanf(buf, "%x\n", &fb_addr);
        }
    }
    pclose(fd);

    // ラプラシアンフィルタの結果を入れておくフレーム・バッファ
    next_frame_addr = ((fb_addr + (ALL_PIXEL_VALUE*4)) & (~(int)(PAGE_SIZE-1))) + PAGE_SIZE;

    // Vivado HLS で作製したラプラシアン・フィルタIPのアドレスを取得
    lap_fil_hw_addr = setup_io((off_t)LAP_FILTER_AXIM_HW_ADDRESS, &lap_fil_hw);

    lap_fil_initialize(lap_fil_hw_addr);    // ラプラシアン・フィルタIPの初期化とap_start
    // ラプラシアン・フィルタAXI4 Master IPスタート
    return_val = laplacian_fil_hw(lap_fil_hw_addr, fb_addr, next_frame_addr);
    printf("return Value = %d\n", return_val);
    
    munmap((unsigned int *)lap_fil_hw_addr, BLOCK_SIZE);
    free((unsigned int *)lap_fil_hw);

    // bitmap-disp-cntrler-axi-master のアドレスを取得
    memset(buf, '\0'sizeof(buf)); // buf すべてに\0 を入れる
    // ls /sys/devices/axi.0 の内容をパイプに入れる
    fd = popen("ls /sys/devices/axi.0""r");
    if (fd != NULL){
        read_num = fread(buf, sizeof(unsigned char), BUFSIZE, fd);
        if (read_num > 0){
            token = buf;
            if ((token=strtok(token, ".\n")) != NULL){
                do {
                    if (chkhex(token)){ // 16進数
                        sscanf(token, "%x", &val);
                    } else {
                        if (strcmp(token, "bitmap-disp-cntrler-axi-master") == 0)
                            bitmap_dc_reg_addr = val;
                    }
                }while((token=strtok(NULL, ".\n")) != NULL);
            }
        }
    }
    pclose(fd);

    // ラプラシアンフィルタの掛かった画像のスタートアドレスを bitmap-disp-cntrler-axi-master にセット
    bm_disp_cnt_reg = setup_io((off_t)bitmap_dc_reg_addr, &bitmap_buf);
    *bm_disp_cnt_reg = next_frame_addr;

    munmap((unsigned int *)bm_disp_cnt_reg, BLOCK_SIZE);
    free((unsigned int *)bitmap_buf);

    gettimeofday(&end_time, NULL);
    printf("rmmap_cnt = %d\n", rmmap_cnt);
    printf("wmmap_cnt = %d\n", wmmap_cnt);
    if (end_time.tv_usec < start_time.tv_usec) {
        printf("total time = %d.%6.6d sec\n", end_time.tv_sec - start_time.tv_sec - 11000000 + end_time.tv_usec - start_time.tv_usec);
    }
    else {
        printf("total time = %d.%6.6d sec\n", end_time.tv_sec - start_time.tv_sec, end_time.tv_usec - start_time.tv_usec);
    }
    return(0);
}


// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8// 256で割る

    return(y);
}

//
// Set up a memory regions to access GPIO
//
volatile unsigned *setup_io(off_t mapped_addr, unsigned int *buf_addr)
// void setup_io()
{
    int  mem_fd;
    char *gpio_mem, *gpio_map;

   /* open /dev/mem */
   if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
      printf("can't open /dev/mem \n");
      printf("mapped_addr = %x\n", mapped_addr);
      exit (-1);
   }

   /* mmap GPIO */

   // Allocate MAP block
   if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
      printf("allocation error \n");
      exit (-1);
   }
    *buf_addr = gpio_mem;    // mallocしたアドレスをコピー

   // Make sure pointer is on 4K boundary
   if ((unsigned long)gpio_mem % PAGE_SIZE)
     gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE);

   // Now map it
   gpio_map = (unsigned char *)mmap(
      (caddr_t)gpio_mem,
      BLOCK_SIZE,
      PROT_READ|PROT_WRITE,
      MAP_SHARED|MAP_FIXED,
      mem_fd,
      mapped_addr
   );

   if ((long)gpio_map < 0) {
      printf("mmap error %d\n", (int)gpio_map);
      printf("mapped_addr = %x\n", mapped_addr);
      exit (-1);
   }

   close(mem_fd); // /dev/mem のクローズ

   // Always use volatile pointer!
   // gpio = (volatile unsigned *)gpio_map;
   return((volatile unsigned *)gpio_map);

// setup_io

// 文字列が16進数かを調べる
int chkhex(char *str){
    while (*str != '\0'){
        if (!isxdigit(*str))
            return 0;
        str++;
    }
    return 1;
}


Vivado HLS で生成された xlap_filter_axim_hw.h を下に示す。

// ==============================================================
// File generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC
// Version: 2013.2
// Copyright (C) 2013 Xilinx Inc. All rights reserved.
// 
// ==============================================================

// LiteS
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Control signal of cam_addr
//        bit 0  - cam_addr_ap_vld (Read/Write/COH)
//        bit 1  - cam_addr_ap_ack (Read)
//        others - reserved
// 0x14 : Data signal of cam_addr
//        bit 31~0 - cam_addr[31:0] (Read/Write)
// 0x18 : Control signal of lap_addr
//        bit 0  - lap_addr_ap_vld (Read/Write/SC)
//        others - reserved
// 0x1c : Data signal of lap_addr
//        bit 31~0 - lap_addr[31:0] (Read/Write)
// 0x20 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)

#define XLAP_FILTER_AXIM_LITES_ADDR_AP_CTRL       0x00
#define XLAP_FILTER_AXIM_LITES_ADDR_GIE           0x04
#define XLAP_FILTER_AXIM_LITES_ADDR_IER           0x08
#define XLAP_FILTER_AXIM_LITES_ADDR_ISR           0x0c
#define XLAP_FILTER_AXIM_LITES_ADDR_CAM_ADDR_CTRL 0x10
#define XLAP_FILTER_AXIM_LITES_ADDR_CAM_ADDR_DATA 0x14
#define XLAP_FILTER_AXIM_LITES_BITS_CAM_ADDR_DATA 32
#define XLAP_FILTER_AXIM_LITES_ADDR_LAP_ADDR_CTRL 0x18
#define XLAP_FILTER_AXIM_LITES_ADDR_LAP_ADDR_DATA 0x1c
#define XLAP_FILTER_AXIM_LITES_BITS_LAP_ADDR_DATA 32
#define XLAP_FILTER_AXIM_LITES_ADDR_AP_RETURN     0x20
#define XLAP_FILTER_AXIM_LITES_BITS_AP_RETURN     32


次に、lap_fil_axim_uty.h を下に示す。


/*
* lap_fil_axim_uty.h
*
* Created on: 2013/10/15
* Author: Masaaki
*/

#ifndef LAP_FIL_AXIM_UTY_H_
#define LAP_FIL_AXIM_UTY_H_

void lap_fil_initialize(unsigned int *lap_fil_hw_addr);
int laplacian_fil_hw(unsigned int *lap_fil_hw_addr, unsigned int *cam_fb, unsigned int *lap_fb);

#endif /* LAP_FIL_AXIM_UTY_H_ */


最後に、lap_fil_axim_uty.c を下に示す。

/* * lap_fil_axim_uty.c * *  Created on: 2013/10/15 *      Author: Masaaki */

#include "xlap_filter_axim_hw.h"

#define AP_START_BIT_POS        1    // ap_start のビット位置 bit0
#define AP_DONE_BIT_POS            2    // ap_done のビット位置 bit1
#define AP_AUTO_RESTART_BIT_POS    0x80    // auto_restart のビット位置 bit7
#define VLD_BIT_POS                1

void lap_fil_initialize(unsigned int *lap_fil_hw_addr)
{
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_AP_CTRL) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_GIE) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_IER) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_CAM_ADDR_CTRL) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_LAP_ADDR_CTRL) = 0;
}

// ラプラシアン・フィルタ・スタート
int laplacian_fil_hw(unsigned int *lap_fil_hw_addr, unsigned int cam_fb, unsigned int lap_fb)
{
    int ap_status, ap_done;
    int ap_return;
    int cam_fb_read, lap_fb_read;
    int cam_addr_vld, lap_addr_vld;

    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_CAM_ADDR_DATA) = cam_fb;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_LAP_ADDR_DATA) = lap_fb;
    cam_fb_read = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_CAM_ADDR_DATA);
    lap_fb_read = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_LAP_ADDR_DATA);

    // ap_start enable
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_AP_CTRL) = AP_START_BIT_POS;

     // vld enable
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_CAM_ADDR_CTRL) = VLD_BIT_POS;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_LAP_ADDR_CTRL) = VLD_BIT_POS;

    // wait ap_done
    do{
        ap_status = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_AP_CTRL);
        ap_done = ap_status & AP_DONE_BIT_POS;
        cam_addr_vld = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_CAM_ADDR_CTRL);
        lap_addr_vld = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_LAP_ADDR_CTRL);
    }while(!ap_done);
    
    // ap_return read
    ap_return = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_AP_RETURN);
    //printf("ap_return = %d\n", ap_return);
    return(ap_return);
}

  1. 2013年10月26日 04:25 |
  2. Co-design
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2013.3でラプラシアン・フィルタ関数をaxi masterモジュールにする

昨日、Xilinxのサイトに、Vivado 2013.3 と ISE14.7 が出ているということをツイッターで教えてもらったので、早速、Vivado 2013.3 をインストールした。そして、再度、Vivado HLSでラプラシアン・フィルタ関数をaxi masterモジュールにしてみた。

Vivado HLS 2013.2 でラプラシアン・フィルタ関数をaxi masterモジュールにした記事を以下に示す。

Vivado HLSでラプラシアン・フィルタ関数をaxi masterモジュールにする1
Vivado HLSでラプラシアン・フィルタ関数をaxi masterモジュールにする2


Vivado HLS 2013.3 でも、C Simulation と C/RTL Cosimulation はうまく行かなかった。
下にC Synthesis のレポートを示す。更にXPS の pcore へのIP化も行っている。
HLS_lap_fil_axim_42_131025.png

前回、Vivado HLS 2013.2 のC Synthesis のレポートを示す。
HLS_lap_fil_axim_4_131011.png

前回のリソース使用量と比べるとFFは増えたが、LUTが減ったのがわかる。

前回のIPの構成と比べると、conv_rgby.v と laplacian_fil.v が増えているのがわかる。
前回のIPの構成を下の図に示す。
HLS_lap_fil_axim_7_131012.png

今回のIPを使用して、もう一度、AXI4 Master のラプラシアン・フィルタを試してみたいと思う。

ラプラシアン・フィルタのCソースを少し変更したので、貼っておく。キャストが違っていて、warnningがでていたので、その点を修正した。
(2013/12/21:ラプラシアン・フィルタ関数にバグが有ったので変更した)

// laplacian_filter.c
// lap_filter_axim()

#include <stdio.h>
#include <string.h>

#define HORIZONTAL_PIXEL_WIDTH    800
#define VERTICAL_PIXEL_WIDTH    600
#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y(int rgb);

int lap_filter_axim(int *cam_addr, int *lap_addr, volatile int *cam_fb, volatile int *lap_fb)
{
    #pragma HLS INTERFACE ap_hs port=cam_addr
    #pragma HLS INTERFACE ap_vld port=lap_addr
    #pragma HLS RESOURCE variable=cam_addr core=AXI4LiteS metadata="-bus_bundle LiteS"
    #pragma HLS RESOURCE variable=lap_addr core=AXI4LiteS metadata="-bus_bundle LiteS"
    #pragma HLS RESOURCE variable=return core=AXI4LiteS metadata="-bus_bundle LiteS"
    
    #pragma HLS INTERFACE ap_bus port=cam_fb depth=480000
    #pragma HLS INTERFACE ap_bus port=lap_fb depth=480000
    #pragma HLS RESOURCE variable=cam_fb core=AXI4M
    #pragma HLS RESOURCE variable=lap_fb core=AXI4M

    unsigned int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
    unsigned int lap_buf[HORIZONTAL_PIXEL_WIDTH];
    int x, y;
    int lap_fil_val;
    int a, b;
    int fl, sl, tl;
    unsigned int offset;
    
    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    for (y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (y==0 || y==VERTICAL_PIXEL_WIDTH-1){ // 縦の境界の時の値は0とする
                lap_fil_val = 0;
            }else if (x==0 || x==HORIZONTAL_PIXEL_WIDTH-1){ // 横の境界の時も値は0とする
                lap_fil_val = 0;
            }else{
                if (x == 1){ // ラインの最初でラインの画素を読み出す
                    if (y == 1){ // 最初のラインでは3ライン分の画素を読み出す
                        for (a=0; a<3; a++){ // 3ライン分
                            offset = *cam_addr/sizeof(int);
                            memcpy(line_buf[a], (const int*)(cam_fb+offset+(a*HORIZONTAL_PIXEL_WIDTH)), HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                            for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                                line_buf[a][b] = conv_rgb2y(line_buf[a][b]);    // カラーから白黒へ
                            }
                        }
                    }
                } else { // 最初のラインではないので、1ラインだけ読み込む。すでに他の2ラインは読み込まれている
                    offset = *cam_addr/sizeof(int);
                    memcpy(line_buf[(y+1)%3], (const int*)(cam_fb+offset+((y+1)*HORIZONTAL_PIXEL_WIDTH)), HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                    for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                        line_buf[(y+1)%3][b] = conv_rgb2y(line_buf[(y+1)%3][b]);    // カラーから白黒へ
                    }
                }
                fl = (y-1)%3;    // 最初のライン, y=1 012, y=2 120, y=3 201, y=4 012
                sl = y%3;        // 2番めのライン
                tl = (y+1)%3;    // 3番目のライン
                lap_fil_val = laplacian_fil(line_buf[fl][x-1], line_buf[fl][x], line_buf[fl][x+1], line_buf[sl][x-1], line_buf[sl][x], line_buf[sl][x+1], line_buf[tl][x-1], line_buf[tl][x], line_buf[tl][x+1]);
            }
            lap_buf[x] = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる
        }
        offset = *lap_addr/sizeof(int);
        memcpy((int *)(lap_fb+offset+(y*HORIZONTAL_PIXEL_WIDTH)), (const int*)lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
    }
    return(1);
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8// 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil_soft(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
     // return(abs(-x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2)); // 元の実装
    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = 0;
    else if (y>255)
        y = 255;
    return(y);
}

  1. 2013年10月25日 04:29 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

lap_filter_axim_cam_fb_if.v をシミュレーションする

Vivado HLSでラプラシアン・フィルタ関数をaxi masterモジュールにする2”で、Vivado HLSでIPとして出力された Verilog HDLファイルを見たが、使えそうな機能を持ったモジュールが結構あった。その中から、lap_filter_axim_cam_fb_if.v をシミュレーションしてみることにした。

lap_filter_axim_cam_fb_if.v は、どうやらユーザー・インターフェースにアドレスなどを入れると、AXI4 Master としてマスタのトランザクションを発生する回路のようだ。lap_filter_axim_cam_fb_if.v に自作のAXI4 BFMを付けて、シミュレーションを行った。
下にシミュレーション用のProject Navigator のプロジェクトを示す。
Vivado_HLS_IP_1_131025.png

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

0x12340000番地から、800バーストで32ビットデータをRead出来た。このモジュール単体としても使えそうだ。

自作AXI4 BFMは、”AXI4バスに接続するビットマップ・ディスプレイ・コントローラの作製3(単体シミュレーション)”の辺のものを使用している。

下にテストベンチの一部を貼っておく。

    // Read Write Test
    initial begin
        USER_dataout = 0;
        USER_address = 0;
        USER_size = 0;
        USER_req_din = 0;
        USER_req_write = 0;
        
        @(posedge ARESETN)
        #1;
        @(posedge ACLK);
        #1;
        USER_address = 32'h12340000;    // read request
        USER_size = 32'd800;
        USER_req_din = 1'b0;
        USER_req_write = 1'b1;
        
        @(posedge ACLK);
        #1;
        USER_req_din = 1'b0;
        USER_req_write = 1'b0;
    end

  1. 2013年10月25日 04:04 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

ラプラシアン・フィルタのAXI4 Master IPを作製する1(仕様の検討)

Vivado HLSでラプラシアン・フィルタをAXI Master で構成するC言語のソースを書いて、IPとしてZedBoardのLinux用ハードウェアに実装してみたが、動作しなかった。そこで、Vivado HLSが出力したIPのトップファイルのポート構成はそのままで、自分でHDLを書いて、ラプラシアン・フィルタのAXI4 Master IPを完成させてみることにした。

関連するブログ記事を列挙する。

Vivado HLSでラプラシアン・フィルタ関数をaxi masterモジュールにする1
Vivado HLSでラプラシアン・フィルタ関数をaxi masterモジュールにする2(Vivado HLSの出力したIPのトップファイルのポート構成はここに書いてあります)
Vivado HLSで作ったラプラシアン・フィルタAXI4 Master IPを使う1
Vivado HLSで作ったラプラシアン・フィルタAXI4 Master IPを使う2
Vivado HLSで作ったラプラシアン・フィルタAXI4 Master IPを使う3


通常では、ビットマップ・ディスプレイ・コントローラのピクセルクロック、もしくはカメラ側ですでにラプラシアン・フィルタを掛ける時は、カメラ・コントローラのピクセルクロックでラプラシアン・フィルタをリアルタイムに掛けるのが通常だと思う。今回は、あくまでC言語から高位合成で合成されたHDLをイメージして、現在の動作周波数が100MHzのAXI4バスクロックの元にシーケンシャルに実行するHDLを作製して、その性能を見てみようと思う。

通常のHDLで実装する時は、1ピクセル画像データが来たら、今までためてきた2ラインのピクセルデータと現在のラインの前の2つのピクセルデータを使って、ラプラシアン・フィルタの値を計算して保存する。現在のピクセルデータはラインバッファに格納する。このように1クロックごとにいろいろな処理を並列して行う。
ラプラシアン・フィルタのハードウェア構成のブロック図を下に示す。
edge_detect_11_091111.png

さて、上のブロック図はカメラから来たピクセルデータを直接受け取って、ラプラシアン・フィルタを掛けている。通常ハードウェアとして実行する場合には、厳密にピクセルデータを変換するフィルタとして書くことができる。それは、時間を制御することができるからだ。それでは、時間軸を書くことが出来ない通常のC言語でハードウェアを書く場合は、一旦メモリ(BRAMを含む)から読んできたデータを処理する以外にないだろう?と思う。
今回はハードウェアでもそれに習って書くことにする。つまり、

1.フレーム・バッファから1ラインRead
2.Readしたピクセルデータを使ってラプラシアン・フィルタを掛けてラインバッファに格納する
3.ラプラシアン・フィルタの値が入ったラインバッファを異なるフレーム・バッファにWrite


この3つをシーケンシャルに実行してみようと思う。

このように実装しても、現在のAXIバスの動作周波数は100MHzで、SVGAのピクセルクロックの40MHzよりも十分に高いので、フレームレートは結構行くのではないだろうか?と思う。

これができたら、今度は、1.2.3.を3つをスレッドとみなして、スレッドの並列化を行ってみよう。これはC言語で記述できる1ポートのRAMを使ってもReadで使用するラインバッファを4つにして、ラプラシアン・フィルタの結果を格納するラインバッファを2つ使えば問題無いと思う。それぞれの処理の終了時にバリア同期を掛ける。この様な実装がHLSのハードウェア化記述としては、より正しいのでは?と思っている。

(注)”画像のフレームバッファ2(ライフサイクルをシミュレーション)”に示すように、動画に乱れが出ないかどうかは別問題です。あくまで静止画に於いて、フレームレートで変換できるのを目指しています。動画で画像の乱れが無いように実装するにはあくまでHDLで実装する必要があると思います。
  1. 2013年10月23日 05:43 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSのaxi_stream_side_channel_dataを試す

前回は、”Vivado HLSのaxi_stream_no_side_channel_dataを試す1

今回は、axi_stream_side_channel_dataを試すことにする。

・Vivado HLSを立ちあげて、Open Example Project をクリックした。

・axi_stream_side_channel_data を選択して、Next> ボタンをクリックした。
Vivado_HLS_AXIS_12_131022.png

・Location を選択して、Finishボタンをクリックした。
Vivado_HLS_AXIS_13_131022.png

・Vivado HLSが立ち上がった。
Vivado_HLS_AXIS_14_131022.png

このCソースは、また強烈な書き方だね。example()関数の引数にもろにap_axis が書いてある。ap_axis構造体は ap_axi_sdata.h に書いてあって、下のような宣言になっていた。

template<int D,int U,int TI,int TD>
  struct ap_axis{
    ap_int<D>    data;
    ap_uint<D/8> keep;
    ap_uint<D/8> strb;
    ap_uint<U>   user;
    ap_uint<1>   last;
    ap_uint<TI>  id;
    ap_uint<TD>  dest;
  };


AXI4 Stream の信号がそのまま構造体になっていた。

・C Simulation を行った。
Vivado_HLS_AXIS_15_131022.png

・次に、C Synthesis を行った。レイテンシが 51クロックでインターバルが52クロックだった。この状態では、かなり遅い。
Vivado_HLS_AXIS_16_131022.png

・インターフェースは、ap_ctrl_hs 1つと ap_fifoがたくさんある。
Vivado_HLS_AXIS_17_131022.png

・C/RTL Co-Simulation をSystemC で行った。成功した。
Vivado_HLS_AXIS_18_131022.png

・pcoreとして出力した。トップのVerilog HDLファイルを見ると、AとBのAXI Stream ができている。ap_start などはそのまま出ている。
Vivado_HLS_AXIS_19_131022.png

AXI4 Stream 用の Vivado HLSの Cコードを書くと、どうしても時間の概念が入ってきてしまうと思う。
3x3のフィルタを実装するとなると、少なくとも2個のラインパッファが必要になる。”Vivado Design Suite ユーザーガイド 高位合成 UG902 (v2013.2) 2013 年 6 月 19 日”の 177ページの”表 2-3 : ストレージコア”に載っている RAM_2P_BRAM などを使う必要があるのか?
でも、Cコードでそうするくらいならば HDL を書いたほうがマシなんじゃないだろうか?
Cでのラプラシアン・フィルタの実装としては、前にやったようにC言語の基本となるメモリベースのAXI4 Master で memcpy()で1ラインずつ DMA転送してもらってラプラシアン・フィルタを実行するのが基本的な考え方だと思う。AXI4 Stream を使って実装すると、C言語のソフトウェア言語の書き方から外れてしまって、HDLで実装したほうがマシということになりはしないだろうか?

AXI4 Master での実装は、たぶんバグ?のためにうまくいかないようなので、HDLでラプラシアン・フィルタを書くことにする。
  1. 2013年10月22日 04:52 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:2

連絡用メールアドレスの変更

Webページの左にある連絡用メールアドレスを gooメールの有料化に伴って、gooメールから infoseekメールに変更しました。よろしくお願いします。
infoseekメールって、Microsoft社のOutlook.com になってました。容量無制限です。
  1. 2013年10月22日 03:45 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

ガーミンのランナー用GPSトレーナーForeAthlete 10Jを購入しました

走っている時の瞬間タイムが見たくて、アマゾンでガーミンのランナー用GPSトレーナーForeAthlete 10Jを購入しました。
garmin_foreathlete_10J_2_131021.jpg

心拍計はいらなかったので、分秒/km だけでも見られれば良いと思って、GPS付きで一番安いのを購入しました。
今日、お昼に走ってみましたが、とっても具合が良かったです。後半はつかれている感じでも、だいぶスピードが出ていることが分かりました。中盤は約6分/kmに抑えていました。
GARMIN Connectの画面です。
garmin_foreathlete_10J_131021.png

何分くらいの時に、何分/km のペースで走っていたかが分かります。高度変化も分かりますね。
トレーニングやレースの時に今現在のペースがリアルタイムで見られるのはとっても良いです。ペースの予定が立てやすいので、オーバーペースやスローペースにならなくてすみますね。良い買い物でした。最高のペースは 5:18/km でした。
  1. 2013年10月21日 21:25 |
  2. スポーツ
  3. | トラックバック:0
  4. | コメント:0

AXI4バスでのAWCACHE、ARCACHEの値

ZedBoardでHDMI出力3(ChipScope AXI Monitor でAXIバスを観察2)”では

、char_write_axi_master IP、つまりAXIバスのWrite マスタを追加した時には、AWCACHE = 3、つまりBufferableにすると、BRESPに”10”、SLVERR (Slave error) が帰ってきた。

と書いたが、”Zynq-7000 All Programmable SoC Seminar 2013”では、AWCACHE、ARCACHEどちらも3、AWCACHE, ARCACHE = 3'b011 が推奨という話だったので、実際にやってみた。実験してみたのは、現在使用しているLinuxを起動してるハードウェアだ。以前のハードウェアをISE14.6に変換して ChipScope を入れようとしたのだが、どうしてもエラーになって入らなかった。うまく入ったのが、このハードウェアだけだったのだ。

その結果、Write, Read 共にレスポンス信号にSLVERRが帰ってくることもなく、正常に動作した。う~ん。以前はSLVERRだったんだが、途中から変わったのか?とにかく、AWCACHE、ARCACHEどちらも3、AWCACHE, ARCACHE = 3'b011 でも問題なしだった。前の実験を間違えたのか?それともISE14.6で不具合が修正されたのか?よくわからないが、ISE14.6だと問題ないようだ。
次に、その時のカメラ・インターフェース回路の ChipScope の波形を下に示す(Write 側)。
AXI_CACHE_1_131020.png

0x19121238番地に、1転送8バイト単位で2バーストWriteしている。例によって、BVALIDのアサートがとっても遅い。最後のデータ転送が完了してから54クロック、つまり540nsec 後にBVALIDがアサートされている。
肝心のAWCACHEは 3 (Normal Non-cacheable Bufferable, Xilinxの推奨値) で転送を行ったところ、BRESPに 0 つまり、OKEYが帰ってきている。問題ない。

次に、ビットマップ・ディスプレイ・コントローラの ChipScope の波形を下に示す。(Read 側)
AXI_CACHE_2_131020.png

0x19153000番地から、1転送8バイト単位で、64バーストReadしている。ARCACHE を 3 (Normal Non-cacheable Bufferable, Xilinxの推奨値) にしても、RRESPはずっと 0 で問題ない。

このように、少なくともISE14.6 の環境では、AWCACHE、ARCACHEの値は 3 (Normal Non-cacheable Bufferable, Xilinxの推奨値) でも問題ないことがわかった。Xilinxの推奨値でもあることだし、今度からは、AWCACHE、ARCACHEの値は 3 にしようと思う。
  1. 2013年10月21日 04:03 |
  2. AXI4バス
  3. | トラックバック:0
  4. | コメント:0

自分の写真を彫った光るアクリルサインを作りました

先週は、FPGAの部屋のイラストが光るアクリルサインを作って、とっても良いと自分では気に入っていました。
今日は、自分の写真を2値化して彫って、光るアクリルサインにしてきました。正直、ちょっと微妙です。CorelDraw の画面ではもっと良かったんですが、彫って次元が上がると、あまり見栄えがしないみたいです。Make に出そうかどうか?迷っています。どうでしょうか?絵の才能の無い私には、写真がかっこよくアクリルサインにできるととっても良いのですが、まだ工夫が足りないようです。CorelDraw の画面上では行ける。。。いいじゃん!と思ったのですが、アクリルにレーザー加工機で彫ってみると、また違う印象になりました。
acrylic_signs_2_131020.jpg

皆さん、いかがでしょうか?
  1. 2013年10月20日 20:36 |
  2. Make出展
  3. | トラックバック:0
  4. | コメント:0

テーブル作り

親からテーブル作りを請け負って、テーブルを作っています。

板厚25mmの赤松合板が残っていたので、それを使って天板を作っています。幅は400mm だったのですが、500mmは欲しいということで、余っていた板を2枚くっつけました。1枚板の方が良かったんですが、2枚の短い板しか残ってなかったんです。残念。。。
Table_1_131020.jpg

昨日、板をはいだ(貼り付けた)ので、今日クランプを外しました。
Table_2_131020.jpg

板の両面を120番の紙やすりで磨いてから、240番の紙やすりで仕上げました。トリマで板の縁も綺麗に丸めました。上の写真との板の縁の違いがわかりますか?
Table_3_131020.jpg

裏面の仕上げです。
Table_4_131020.jpg

次は塗装なのですが、今日は雨が降っているので、塗装は止めました。

次の工程は、アクリルニス塗装です。大体、3度塗り位します。それから、足の金具を天板に取り付けて丸棒の足(60Φ x 700mm)を取り付けます。丸棒の足は既成品でもうボルトが埋め込まれています。それを天板に付けた金具にねじこみます。足を適当な長さに切って出来上がりです。
  1. 2013年10月20日 10:59 |
  2. 木工
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSのaxi_stream_no_side_channel_dataを試す1

先週の金曜日の Zynq-7000 All Programmable SoC Seminar 2013 でAXI Master より、AXI Streamで実装したほうが、Vivado HLSで正常にコンパイル、シミュレーション出来そうだということを聞いてきた。早速、Example をやってみようと思った。

最初にやるのは、Vivado HLSのaxi_stream_no_side_channel_data というExampleだ。それでは早速やってみよう。

・Vivado HLSを立ちあげて、Open Example Project をクリックした。
Vivado_HLS_AXIS_1_131020.png

・axi_stream_no_side_channel_data を選択して、Next> ボタンをクリックした。
Vivado_HLS_AXIS_2_131020.png

・Location を選択して、Finishボタンをクリックした。
Vivado_HLS_AXIS_3_131020.png

・Vivado HLSが立ち上がった。
Vivado_HLS_AXIS_4_131020.png

・C Simulation を行った。
Vivado_HLS_AXIS_5_131020.png

・次に、C Synthesis を行った。リソースの結果を示す。ほとんどリソースが使用されていない。
Vivado_HLS_AXIS_6_131020.png

・Summary を表示させると、ap_ctrl_hs と ap_fifo インターフェースが実装されるようだ。
Vivado_HLS_AXIS_7_131020.png

Vivado Design Suite ユーザーガイド 高位合成 UG902 (v2013.2) 2013 年 6 月 19 日”(以下、マニュアル)で調べてみると、図 1-39 : ap_ctrl_hs インターフェイスのビヘイビア にタイミングチャートが載っていた。それを引用させていただく。
Vivado_HLS_AXIS_8_131020.png

ap_ctrl_hs は入力データをバーストで読みだして返り値を返すというバスのようだ。ap_startを 1 にして、ap_idleが 0 になると、入力データが読み出せるようになる。

次に、ap_fifo を見ていく。ap_fifoはシーケンシャルなRead, Writeのために使用する(当たり前か?)。78ページの 図 1-43 : ap_fifo インターフェイスの動作 のタイミングチャートを引用させていただく。
Vivado_HLS_AXIS_9_131020.png

この Example では、Aが入力で、Bが出力ということがわかる。

・C/RTL Co-Simulation をSystemC で行った。成功した。
Vivado_HLS_AXIS_10_131020.png

・pcoreとして出力した。トップのVerilog HDLファイルを見ると、AとBのAXI Stream ができている。ap_start などはそのまま出ているので、AXI Lite Slaveとして実装したいところだ。
Vivado_HLS_AXIS_11_131020.png
  1. 2013年10月20日 05:48 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Zynq-7000 All Programmable SoC Seminar 2013

昨日、Zynq-7000 All Programmable SoC Seminar 2013に行ってきました。
ためになるセミナだったと思います。いろいろと新しいことも聞けました。

・「ザイリンクスUltraScale™ 真の次世代アーキテクチャ」
・Vivado® HLS (高位合成ツール)とZynq®-7000 All Programmable SoC全てをCで設計するテクニック
・Zynq-7000 All Programmable SoC搭載のARM CPUの活用 ~複雑なデザインの構築を可能にする性能 向上テクニック~
・Zynq-7000 All Programmable SoCユーザロジックから見たProcessing System活用テクニック


上の4つのセミナを聞きましたが、Workshopは1つも聞いていません。

・「ザイリンクスUltraScale™ 真の次世代アーキテクチャ」
Xilinxの方が、7シリーズでは、一気にArtix(安価なFPGA)を含めて、一気に 28nmプロセスに移行していましたが、今後は、そのようなことはないそうです。つまり10年くらいの長いスパンで 28nmプロセスのFPGAを売り続けるそうです。Artixは 20nmプロセスにも移行せずに 28nmプロセスのままだそうです。Zynq、Kintex、Virtexは 20nmプロセスで作るそうですが、28nmプロセス製品のファミリーも増やすそうです。あくまで 28nmプロセスをベースにするようなことを言ってました。Virtexは16nmプロセスのも作るそうです。やはり、Kintexで小さい規模のFPGAがなかなか出てこないのを考えると、もうプロセスを微細化してもしょうがないんでしょうね?たぶん。IOパッドの面積は相対的に大きくなるし。。。そのようなことをちらっと仰っていました。
内部構造の改良については、ASICのようにするとのことでした。

・ロジックエレメントのルーティングトラックを4x4にスライスが並んでいるところに各スライスに行くFast Track Routes を新設するそうです。つまり1つのライス辺り15本のルートを確保してクロスバーの様に接続できるんでしょう?

・クロッキングは今までは、グローバルクロックが真ん中に走っていましが、今度はマルチリージョンのクロックにするそうです。クロック領域を分けてクロックを供給しますが、複数のリージョンをまたがったクロックの接続もできるとのことでした。チップが大きくなるとグローバルクロックは限界が来るんでしょうね?

・DSPユニットも25x18入力から、27x18入力にするそうです。これだと54ビット入力に2個のDSPで対応できるそうです。今までは3個使う必要があって無駄だったそうです。

・BRAMのカスケードがBRAM外部のロジックリソースを使わずに、BRAMブロックに組込んであるロジックを使って出来るようです。リソースの使用率の軽減と動作スピードを改善できるとのことです。

・100G EthernetやGen4 PCIeとかに対応するそうです。

とにかく、本音が聞けた気がします。今まで無い有意義なセミナでした。面白かったです。

・Vivado® HLS (高位合成ツール)とZynq®-7000 All Programmable SoC全てをCで設計するテクニック
Vivado HLSの概要はもうすでに知っていたので退屈だったんですが、途中からデザイン構築の流れ辺りからよく聞いてました。
デザインはまずARMのソフトウェアのみで作って、プロファイリングしてハードウェア化を検討するとのことでした。全く同感です。SDKにプロファイリングの機能があるそうです。後で試してみたいと思います。
ボトルネックを見つけて、それをハードウェア化とのことです。HLS化の過程もよくまとまってました。
最後にOpenCVについての情報もありました。概要はよくわかりました
。OpenCVやりたいです、まずはZedBoardのLinuxにインストールする必要がありますね。Ubuntu に変えたいと思ってきました。
展示のところで発表者にVivado HLSのことを聞いてきました。AXI Master の辺りはバグありそうなので、AXI DMAを使って、HLS部分はAXI Stream で実装したほうが良さそうだそうです。いろいろとソフトとハードの負荷分散に付いても検討してみたいと思いました。

・Zynq-7000 All Programmable SoC搭載のARM CPUの活用 ~複雑なデザインの構築を可能にする性能 向上テクニック~
以前、セミナでお世話になったNさんが講義でした。
最初はプロセッサの説明でした。キャッシュやTLBや割り込みの種類(レベルトリガ、エッジトリガ)、インタラプトハンドラの説明でした。これはハード・エンジニアの方でプロセッサに詳しくない方向けなんでしょうか?あまり、キャッシュやTLBの図が簡単すぎて、かえってよくわからないじゃないだろうか?と思いながら見てました。
次は、Zynqのプロセッサを2つ使う方法ですね。短時間ですが、具体的なやり方を説明してくれて良かったです。
その次は、Xil_L2CacheFlush() などのキャッシュ操作の説明でした。SIMDエンジンNEONの使い方を説明してくれて、最後にOSの説明がありました。

・Zynq-7000 All Programmable SoCユーザロジックから見たProcessing System活用テクニック
この方の話し方はあまり好きじゃないんですが(すいません。。。)、話す内容はノウハウ満載でとても気に入っています。
ツールやZynqのPSとPLを接続するバスのことについて詳しく説明していただきました。やはり、IP Integrator は今のところバグがあるので、おすすめしないそうです。私も同感です。
2つHPポートを使う場合は、HP0とHP2にするとか、そんな感じです。しかし、PS部にレベルシフタがあって、デフォルトオフと言うのは知りませんでした。FSBLでONにするそうです。ONにしないとまずいそうなので、デバックの場合は、JTAGでSDKからビットストリームをダウンロードして、ソフトウェアを流す前にONにしてくれているのだと思います。QoSの話とかもあって、あまりAXIバスの優先順位を考えたことがなかったですが、考慮の対象にしようと思いました。
AXIバスのAWCHACHE とARCHACHEですが、Xilinxの推奨値は0'b0011 (Normal Non-cacheable Bufferable) だそうです。以前、私がやった時には、Readは大丈夫だったんですが、Write はSLVERR が帰ってきました。もう一度やってみようと思います。それでいまのところ、私は、AWCHACHE とARCHACHEを0'b0010 (Normal Non-cacheable Non-bufferable)にしています。もう一度、確かめてみます。

AXIバスのカスタムIPを作る方法として、次の3つを説明していました。

・IPIF(これってスレーブしか出来ませんよね?)を使う
AR37425をテンプレートにして自作する
・Vivado HLSで作る


私は、Xilinxだと自分でカスタムIP作ったほうが良いと思っています。IPIFだとラッパーをはさむのでレイテンシが遅くなりますし、たぶん?マスタは作れません。(2013/10/24:更新 マスタも作れるようです。)更に、マスタ2ポート、スレーブ1ポートを持つカスタムIPは無理です。AXIバスのカスタムIPをネイティブに作れるようになれば、とっても有用だと思っています。それを感じたので、1年以上前からAXIバスの勉強を初めました。それにAXIバスで作れば、Alteraでも使えるかもしれません。
私は、ネイティブなAXIバスのカスタムIPの作り方を含めたZynqのセミナをちょっと拡大した内部的に開こうとしています。セミナ資料も100枚くらい作りました。後80枚くらい作ろうと思っています。ネイティブなAXIバスのカスタムIPの作り方を覚えると、とっても有利だと思います。機能を少し限定すれば、作るのは難しくありません。

という訳で、ノウハウやツールの使い方などを説明していただいて、とっても有用なセミナでした。Xilinx社の方、東京エレクトロンデバイスのセミナ講師の方々、ありがとうございました。そして、お疲れ様でした。また、こんなセミナに行きたいです。また、よろしくお願いいたします。

(2013/10/21:追記)
ISE14.6を使用して、AWCACHE、ARCACHEの値を 3 にして、AXIバスのデータ転送を行ってみました。Write もRead もBRESP、RRESPの返り値が 0 で問題ないことが分かりました。”AXI4バスでのAWCACHE、ARCACHEの値”を参照下さい。
  1. 2013年10月19日 05:41 |
  2. その他のFPGAの話題
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSで作ったラプラシアン・フィルタAXI4 Master IPを使う3

Vivado HLSで作ったラプラシアン・フィルタAXI4 Master IPを使う2”の続き。

前回は、Vivado HLSで作ったAXI4 Lite Slave と AXI4 Master の 2種類のAXIバスを実装するラプラシアン・フィルタ IPをXPSのカスタムIPとして、SDKからのソフトウェア制御で使用してみたら動作しなかった(ap_done がアサートされなかった)という問題が合った。
今回は、XPSプロジェクトに入れてあったChipScope AXI Monitor を使用してAXI4バスのトランザクションを見ることにした。

早速、ChipScope Analyzer を立上て、CDCファイルをインポートしてデータポートの名前を確定した。
最初に、lap_filter_axim_top_0のAXI4 Lite Slave のバス・トランザクションを観察した。下にその波形を示す。
HLS_lap_fil_axim_38_131017.png

もう一度、AXI4 Lite Slave バスのアドレスマップを示す。

//------------------------Address Info-------------------
// 0x00 : Control signals
// bit 0 - ap_start (Read/Write/COH)
// bit 1 - ap_done (Read/COR)
// bit 2 - ap_idle (Read)
// bit 3 - ap_ready (Read)
// bit 7 - auto_restart (Read/Write)
// others - reserved
// 0x04 : Global Interrupt Enable Register
// bit 0 - Global Interrupt Enable (Read/Write)
// others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
// bit 0 - Channel 0 (ap_done)
// bit 1 - Channel 1 (ap_ready)
// others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
// bit 0 - Channel 0 (ap_done)
// bit 1 - Channel 1 (ap_ready)
// others - reserved
// 0x10 : Control signal of cam_addr
// bit 0 - cam_addr_ap_vld (Read/Write/COH)
// bit 1 - cam_addr_ap_ack (Read)
// others - reserved
// 0x14 : Data signal of cam_addr
// bit 31~0 - cam_addr[31:0] (Read/Write)
// 0x18 : Control signal of lap_addr
// bit 0 - lap_addr_ap_vld (Read/Write/SC)
// others - reserved
// 0x1c : Data signal of lap_addr
// bit 31~0 - lap_addr[31:0] (Read/Write)
// 0x20 : Data signal of ap_return
// bit 31~0 - ap_return[31:0] (Read)
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


レジスタをアクセスする部分のソフトウェアを下に示す。

    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_CAM_ADDR_DATA) = cam_fb;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_LAP_ADDR_DATA) = lap_fb;
    cam_fb_read = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_CAM_ADDR_DATA);
    lap_fb_read = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_LAP_ADDR_DATA);

    // ap_start enable
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_AP_CTRL) = AP_START_BIT_POS;

     // vld enable
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_CAM_ADDR_CTRL) = VLD_BIT_POS;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAP_FILTER_AXIM_LITES_ADDR_LAP_ADDR_CTRL) = VLD_BIT_POS;


問題なくRead, Writeできているようだ。

次に、M_AXI_CAM_FB を見てみよう。
HLS_lap_fil_axim_39_131017.png

バーストでReadアクセスを行っている。
最初のアドレス転送の部分を拡大してみよう。
HLS_lap_fil_axim_40_131017.png

最初のアドレス開始位置は0x19107000 でレジスタに書いた値と同じだ。
カメラのフレーム・バッファのReadは問題ないようだ。

ラプラシアン・フィルタのフレーム・バッファへのWriteはどうだろう?
M_AXI_LAP_FB AXIバスのAWVALID でトリガを掛けてもトリガが掛からなかった。
HLS_lap_fil_axim_41_131017.png

ラプラシアン・フィルタのフレーム・バッファへのWriteは発生していないようだ。もう少し調査する必要がある。
  1. 2013年10月17日 05:13 |
  2. Co-design
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSで作ったラプラシアン・フィルタAXI4 Master IPを使う2

Vivado HLSで作ったラプラシアン・フィルタAXI4 Master IPを使う1”の続き。

・前回はXPSプロジェクトを完成させたので、Project Navigator に戻って論理合成、インプリメントを行ったが、問題なく終了した。
HLS_lap_fil_axim_28_131015.png

・例によって、Design ウインドウのHierarchy から system_i を選んで、Processes ウインドウのExport Hardware Design To SDK with Bitstream をダブルクリックしてハードウェアをエクスポートした。
HLS_lap_fil_axim_29_131015.png

・続いて、SDKが立ち上がる。lap_filter_axim_top_0 が 0x49000000 にマップされていた。
HLS_lap_fil_axim_30_131015.png

・Project メニューからClean... を選択して、すべてのプロジェクトをコンパイルすると、FSBLとstandalone_bsp がエラーだった。

・FSBLとstandalone_bsp を消去して、FSBL と FSBL_bspを新規作成した。

・lag_fil_hls_axim プロジェクトを新規作成した。

・lap_fil_hls_axim.c, lap_fil_axim_uty.c, lap_fil_axim_uty.h, を作製した。

・xlap_filter_axim_hw.h を Vivado_HLS\lap_filter_axim\solution1\impl\drivers\lap_filter_axim_top_v1_00_a\src からコピーした。
HLS_lap_fil_axim_31_131016.png

・SDカードに入れるブートイメージを作製した。
HLS_lap_fil_axim_32_131016.png

・u-boot.bin が出来た。
HLS_lap_fil_axim_33_131016.png

・BOOT.bin に書き換えた。
HLS_lap_fil_axim_34_131016.png

・BOOT.bin をSDカードに入れて、Linuxをブート。

・lap_fil_hls_axim のデバック・コンフィギュレーションを作って、デバック。
HLS_lap_fil_axim_35_131016.png

・デバックモードになった。
HLS_lap_fil_axim_36_131016.png

デバックしているが、いまのところ ap_done が上がらずに原因を調査中。

(追記)
Vivado HLSでのSystemC のCo-Simulation をしたところエラーになった。
HLS_lap_fil_axim_37_131016.png

@E [SIM-304] Aborting co-simulation: C TB simulation failed.
@E [SIM-320] Generating test vectors failed.
”@E [SIM-4] *** C/RTL co-simulation finished: FAIL ***


Generating test vectors failed”ということで、よく原因がわからない?

(2013/10/26:追記)
AXI4 Master版ラプラシアン・フィルタIPの制御用ソフトウェアの全ソースは、”Vivado HLS 2013.3 で作ったラプラシアン・フィルタAXI4 Master IPを使う1”を参照。
  1. 2013年10月16日 05:18 |
  2. Co-design
  3. | トラックバック:0
  4. | コメント:0

iPhone4SのiOS7でのJogNoteの不具合

私はジョギングをしていて、iPhone4S でJogNote(ジョグノート)を使って、走った距離やタイムなどを図っています。
最近、たぶん、iOS7にしてからジョグノートの距離計測がおかしくなりました。何キロ走っても0.3km 程度しか表示されません。おかしいです。
Runtasic を入れても同じ状況なので、GPSがおかしいと思いました。
ネットを検索くしたところ、”iOS7 related issues - App not tracking when running in the background or not tracking at all. Troubleshooting guide. ”がヒットしました。
1)はOKでしたが、2)が問題でした。

設定アプリの一般 -> Appのバックグラウンド更新 画面の Appのバックグラウンド更新のチェックを入れるそうです。
iPhone_1_131014.jpg

チェックが入りました。これで大丈夫だと思いますが、まだ確かめていません。
iPhone_2_131014.jpg

ともかく、これで大丈夫でしょう。ジョグノートがうまく動かないと困ります。。。
  1. 2013年10月14日 10:50 |
  2. スマートフォン
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSで作ったラプラシアン・フィルタAXI4 Master IPを使う1

Vivado HLSでラプラシアン・フィルタ関数をaxi masterモジュールにする2”で作ったAXI4 Lite Slave と AXI4 Master の 2種類のAXIバスを実装するラプラシアン・フィルタ IPをXPSのカスタムIPとして使って見ようと思う。

Verilog HDLソースコードをちらっと見たが、ap_start をイネーブルにすると、AXI4 Master アクセスが動作する仕様のような気がする。そうでないと、フレーム・バッファのアドレスを書く前にAXI4 Master アクセスであらぬアドレスにラプラシアン・フィルタの出力データを書かれると非常にまずい。下に、AXI4 Lite Slave バスのアドレスマップを示す。

//------------------------Address Info-------------------
// 0x00 : Control signals
// bit 0 - ap_start (Read/Write/COH)
// bit 1 - ap_done (Read/COR)
// bit 2 - ap_idle (Read)
// bit 3 - ap_ready (Read)
// bit 7 - auto_restart (Read/Write)
// others - reserved
// 0x04 : Global Interrupt Enable Register
// bit 0 - Global Interrupt Enable (Read/Write)
// others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
// bit 0 - Channel 0 (ap_done)
// bit 1 - Channel 1 (ap_ready)
// others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
// bit 0 - Channel 0 (ap_done)
// bit 1 - Channel 1 (ap_ready)
// others - reserved
// 0x10 : Control signal of cam_addr
// bit 0 - cam_addr_ap_vld (Read/Write/COH)
// bit 1 - cam_addr_ap_ack (Read)
// others - reserved
// 0x14 : Data signal of cam_addr
// bit 31~0 - cam_addr[31:0] (Read/Write)
// 0x18 : Control signal of lap_addr
// bit 0 - lap_addr_ap_vld (Read/Write/SC)
// others - reserved
// 0x1c : Data signal of lap_addr
// bit 31~0 - lap_addr[31:0] (Read/Write)
// 0x20 : Data signal of ap_return
// bit 31~0 - ap_return[31:0] (Read)
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


・最初に、D:\HDL\FndtnISEWork\Zynq-7000\ZedBoard\Zed_OOB_Design2_HLS\hw\xps_proj\pcores にVivdo HLSで生成した lap_filter_axim_top_v1_00_a をコピーした。
HLS_lap_fil_axim_8_131014.png

・XPSで、Project メニューからRescan User Repositories を実行しなくても、lap_filter_axim_top IP がすでに、XPSのProject Local PCoresの中に入っていた。
HLS_lap_fil_axim_9_131014.png

・lap_filter_axim_top IP をクリックして、Add IPを選択した。
HLS_lap_fil_axim_10_131014.png

・ダイアログが出たので、Yes ボタンをクリックした。
HLS_lap_fil_axim_11_131014.png

・XPS Core Config ダイアログが出た。C_M_AXI_CAM_FB_CACHE_VALUE と C_M_AXI_LAP_FB_CACHE_VALUE が 0b0011 だ。以前、AXI4 Master モジュールを作った時に0011の Normal Non-cacheable Bufferable だと、BRESPが10でSLVERRになってバス転送が失敗してしまったのだが、大丈夫なのだろうか?とりあえずは、他の設定が違うとこれでも良いかもしれないので、デフォルトのままとした。
HLS_lap_fil_axim_12_131014.png

・XPSプロジェクトに lap_filter_axim_top_0 が入った。
HLS_lap_fil_axim_14_131014.png

・PSのA_AXI_HP3ポートが余っているので、これを生かそう。ZynqタブでHigh Parformance AXI 32b64b Slave Prots をクリックした。
HLS_lap_fil_axim_15_131014.png

・Enable S_AXI_HP3 interface にチェックを入れた。後はデフォルトのまま、OKボタンをクリックした。
HLS_lap_fil_axim_16_131014.png

・AXI Interconnect を追加しよう。Bus and Bridge から選んでAdd IPした。
HLS_lap_fil_axim_17_131014.png

・いつものダイアログ
HLS_lap_fil_axim_18_131014.png

・XPS Core Config の画面。デフォルトのまま、OKボタンをクリックした。
HLS_lap_fil_axim_19_131014.png

・axi_interconnetc_4 が生成された。S_AXI_HP3も生きたのがわかる。
HLS_lap_fil_axim_20_131014.png

・lpa_filter_axim_top_0 のM_AXI_CAM_FB とM_AXI_LAP_FB をaxi_interconnetc_4 に接続した。
HLS_lap_fil_axim_21_131014.png

・PSのS_AXI_HP3もaxi_interconnetc_4 に接続し、lpa_filter_axim_top_0 のM_AXI_CAM_FB とM_AXI_LAP_FB と接続した。
HLS_lap_fil_axim_22_131014.png

・axi_interconnetc_4 のINTERCONNECT_ACLK とINTERCONNECT_ARESETN が接続されていなかったので、以下の図のように接続した。
HLS_lap_fil_axim_23_131014.png

・Address タブで、lap_filter_axim_top_0 のアドレスを 0x49000000 にマップした。
HLS_lap_fil_axim_24_131014.png

・chipscope_axi_monitor を lap_filter_axim_top_0 の 3つのAXIバスに接続した。
HLS_lap_fil_axim_25_131014.png

・Project メニューから Design Rule Check を実行したら、エラーがでた。

・エラー内容は、S_AXI_HP3_ACLKがつながっていないというエラーだった。早速、接続した。
HLS_lap_fil_axim_26_131014.png

・これで Design Rule Check もOKになった。

・Project メニューからClean All Gnerated Files を選んで、すべての生成ファイルを削除した。
HLS_lap_fil_axim_27_131014.png

これでXPSでの作業は終了だ。
  1. 2013年10月14日 10:05 |
  2. Co-design
  3. | トラックバック:0
  4. | コメント:0

FPGAの部屋の光るアクリルサイン

今日、FPGA-CAFE/FabLab Tsukubaで、FPGAの部屋の光るアクリルサインを作ってきました。なかなか、よく出来たと思います。こういったイラストやロゴを光らせたほうが良いようです。Maker Faire Tokyo 2013 で、展示したいと思います。
更に、光るパターンを変更できるようにしたいと思います。CdSが付いているので、応答時間が遅いですが、信号取れるかもしれません?
Make_2013_1_131013.jpg
  1. 2013年10月13日 20:25 |
  2. Make出展
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSでラプラシアン・フィルタ関数をaxi masterモジュールにする2

Vivado HLSでラプラシアン・フィルタ関数をaxi masterモジュールにする1”の続き。

前回、Vivado HLS の Export RTL で EDK用の pcore を作ってみたが、カメラ画像フレーム・バッファ読み込み用アドレスとラプラシアン・フィルタの結果を書き出すフレーム・バッファのアドレスは、parameter 指定の固定アドレスだったので、AXI4 Lite Slave でAXI4 Master のRead/Writeするアドレスを渡す仕様に変更した。変更する際には、Xilinx User Community Forums”Vivado HLS AXI4 address assignment”を参考にした。

新しい laplacian_filter.c を下に示す。AXI4 Lite Slave と AXI4 Master の 2種類のAXIバスを実装している。
(2013/12/21:ラプラシアン・フィルタ関数にバグが有ったので変更した)

// laplacian_filter.c
//

#include <stdio.h>
#include <string.h>

#define HORIZONTAL_PIXEL_WIDTH    800
#define VERTICAL_PIXEL_WIDTH    600
#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y(int rgb);

int lap_filter_axim(int *cam_addr, int *lap_addr, volatile int *cam_fb, volatile int *lap_fb)
{
    #pragma HLS INTERFACE ap_hs port=cam_addr
    #pragma HLS INTERFACE ap_vld port=lap_addr
    #pragma HLS RESOURCE variable=cam_addr core=AXI4LiteS metadata="-bus_bundle LiteS"
    #pragma HLS RESOURCE variable=lap_addr core=AXI4LiteS metadata="-bus_bundle LiteS"
    #pragma HLS RESOURCE variable=return core=AXI4LiteS metadata="-bus_bundle LiteS"
    
    #pragma HLS INTERFACE ap_bus port=cam_fb depth=480000
    #pragma HLS INTERFACE ap_bus port=lap_fb depth=480000
    #pragma HLS RESOURCE variable=cam_fb core=AXI4M
    #pragma HLS RESOURCE variable=lap_fb core=AXI4M

    unsigned int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
    unsigned int lap_buf[HORIZONTAL_PIXEL_WIDTH];
    int x, y;
    int lap_fil_val;
    int a, b;
    int fl, sl, tl;
    unsigned int offset;
    
    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    for (y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (y==0 || y==VERTICAL_PIXEL_WIDTH-1){ // 縦の境界の時の値は0とする
                lap_fil_val = 0;
            }else if (x==0 || x==HORIZONTAL_PIXEL_WIDTH-1){ // 横の境界の時も値は0とする
                lap_fil_val = 0;
            }else{
                if (x == 1){ // ラインの最初でラインの画素を読み出す
                    if (y == 1){ // 最初のラインでは3ライン分の画素を読み出す
                        for (a=0; a<3; a++){ // 3ライン分
                            offset = *cam_addr/sizeof(int);
                            memcpy(line_buf[a], (const int*)(cam_fb+offset+(a*HORIZONTAL_PIXEL_WIDTH)), HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                            for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                                line_buf[a][b] = conv_rgb2y(line_buf[a][b]);    // カラーから白黒へ
                            }
                        }
                    }
                } else { // 最初のラインではないので、1ラインだけ読み込む。すでに他の2ラインは読み込まれている
                    offset = *cam_addr/sizeof(int);
                    memcpy(line_buf[(y+1)%3], (const int*)(cam_fb+offset+((y+1)*HORIZONTAL_PIXEL_WIDTH)), HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                    for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                        line_buf[(y+1)%3][b] = conv_rgb2y(line_buf[(y+1)%3][b]);    // カラーから白黒へ
                    }
                }
                fl = (y-1)%3;    // 最初のライン, y=1 012, y=2 120, y=3 201, y=4 012
                sl = y%3;        // 2番めのライン
                tl = (y+1)%3;    // 3番目のライン
                lap_fil_val = laplacian_fil(line_buf[fl][x-1], line_buf[fl][x], line_buf[fl][x+1], line_buf[sl][x-1], line_buf[sl][x], line_buf[sl][x+1], line_buf[tl][x-1], line_buf[tl][x], line_buf[tl][x+1]);
            }
            lap_buf[x] = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる
        }
        offset = *lap_addr/sizeof(int);
        memcpy((const int*)(lap_fb+offset+(y*HORIZONTAL_PIXEL_WIDTH)), lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
    }
    return(1);
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8// 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil_soft(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
     // return(abs(-x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2)); // 元の実装
    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = 0;
    else if (y>255)
        y = 255;
    return(y);
}


新しい lap_filter_tb.c を下に示す。
(2013/12/21:ラプラシアン・フィルタ関数にバグが有ったので変更した)

// Testbench of laplacian_filter.c
// M系列データをハードウェアとソフトウェアで、ラプラシアン・フィルタを掛けて、それを比較する
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define HORIZONTAL_PIXEL_WIDTH    40
#define VERTICAL_PIXEL_WIDTH    20
//#define HORIZONTAL_PIXEL_WIDTH    800
//#define VERTICAL_PIXEL_WIDTH    600
#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

int laplacian_fil_soft(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y_soft(int rgb);
int lap_filter_axim(int cam_addr, int lap_addr, volatile int *cam_fb, volatile int *lap_fb);    // hardware
void laplacian_filter_soft(volatile int *cam_fb, volatile int *lap_fb); // software

int mseq_po[ALL_PIXEL_VALUE];
int hw_lap_po[ALL_PIXEL_VALUE];
int sf_lap_po[ALL_PIXEL_VALUE];

int main()
{
//    int *mseq_po, *hw_lap_po, *sf_lap_po;
    int *s, *h;
    int x, y;
    unsigned int lfsr = 1;
    int cam_addr, lap_addr;
    
    // ピクセルデータ領域にM系列データを入力
    for (y=0, s=mseq_po; y<VERTICAL_PIXEL_WIDTH; y++){
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xd0000001u); /* taps 32 31 29 1 */
            // ”線形帰還シフトレジスタ ”参照 : http://ja.wikipedia.org/wiki/%E7%B7%9A%E5%BD%A2%E5%B8%B0%E9%82%84%E3%82%B7%E3%83%95%E3%83%88%E3%83%AC%E3%82%B8%E3%82%B9%E3%82%BF
            *s = lfsr;
            s++;
        }
    }
    
    cam_addr = (int)mseq_po;
    lap_addr = (int)hw_lap_po;
    lap_filter_axim(cam_addr, lap_addr, (volatile int *)0, (volatile int *)0);    // ハードウェアのラプラシアン・フィルタ
    laplacian_filter_soft(mseq_po, sf_lap_po);    // ソフトウェアのラプラシアン・フィルタ
    
    // ハードウェアとソフトウェアのラプラシアン・フィルタの値のチェック
    for (y=0, h=hw_lap_po, s=sf_lap_po; y<VERTICAL_PIXEL_WIDTH; y++){
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (*h != *s){
                printf("ERROR HW and SW results mismatch x = %d, y = %d, HW = %d, SW = %d\n", x, y, *h, *s);
                return(1);
            } else {
                h++;
                s++;
            }
        }
    }
    printf("Success HW and SW results match\n");
}

void laplacian_filter_soft(volatile int *cam_fb, volatile int *lap_fb)
{
    unsigned int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
    unsigned int lap_buf[HORIZONTAL_PIXEL_WIDTH];
    int x, y;
    int lap_fil_val;
    int a, b;
    int fl, sl, tl;

    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    for (y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        fl = (y-1)%3;    // 最初のライン, y=1 012, y=2 120, y=3 201, y=4 012
        sl = y%3;        // 2番めのライン
        tl = (y+1)%3;    // 3番目のライン
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (y==0 || y==VERTICAL_PIXEL_WIDTH-1){ // 縦の境界の時の値は0とする
                lap_fil_val = 0;
            }else if (x==0 || x==HORIZONTAL_PIXEL_WIDTH-1){ // 横の境界の時も値は0とする
                lap_fil_val = 0;
            }else{
                 if (x == 1){ // ラインの最初でラインの画素を読み出す
                    if (y == 1){ // 最初のラインでは3ライン分の画素を読み出す
                        for (a=0; a<3; a++){ // 3ライン分
                            memcpy(&line_buf[a][0], (const int*)&cam_fb[a*(HORIZONTAL_PIXEL_WIDTH)], HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                            for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                                line_buf[a][b] = conv_rgb2y(line_buf[a][b]);    // カラーから白黒へ
                            }
                        }
                    } else { // 最初のラインではないので、1ラインだけ読み込む。すでに他の2ラインは読み込まれている
                         memcpy(line_buf[(y+1)%3], (const int*)&cam_fb[(y+1)*(HORIZONTAL_PIXEL_WIDTH)], HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                        for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                            line_buf[(y+1)%3][b] = conv_rgb2y(line_buf[(y+1)%3][b]);    // カラーから白黒へ
                        }
                    }
                }
                lap_fil_val = laplacian_fil(line_buf[fl][x-1], line_buf[fl][x], line_buf[fl][x+1], line_buf[sl][x-1], line_buf[sl][x], line_buf[sl][x+1], line_buf[tl][x-1], line_buf[tl][x], line_buf[tl][x+1]);
            }
            lap_buf[x] = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる
        }
        memcpy(&lap_fb[y*(HORIZONTAL_PIXEL_WIDTH)], (const int*)lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
    }
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y_soft(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8// 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil_soft(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
    int y;

    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = 0;
    else if (y>255)
        y = 255;
    return(y);
}


Vivado HLS で XPS の pcore として生成した。
HLS_lap_fil_axim_7_131012.png
(2013/10/13: フォルダをC:\Users\Masaaki\Documents\Vivado_HLS\lap_filter_aximに変更しました)

これでXPSの pcore として生成したVerilog HDLのトップファイル (laplacian_filter_top.v) の一部を下に示す。
AXI4 Master と AXI4 Lite Slave バスのみとなった。


// ==============================================================
// File generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC
// Version: 2013.2
// Copyright (C) 2013 Xilinx Inc. All rights reserved.
//
// ==============================================================

`timescale 1 ns / 1 ps
module lap_filter_axim_top (
m_axi_cam_fb_AWID,
m_axi_cam_fb_AWADDR,
m_axi_cam_fb_AWLEN,
m_axi_cam_fb_AWSIZE,
m_axi_cam_fb_AWBURST,
m_axi_cam_fb_AWLOCK,
m_axi_cam_fb_AWCACHE,
m_axi_cam_fb_AWPROT,
m_axi_cam_fb_AWQOS,
m_axi_cam_fb_AWUSER,
m_axi_cam_fb_AWVALID,
m_axi_cam_fb_AWREADY,
m_axi_cam_fb_WDATA,
m_axi_cam_fb_WSTRB,
m_axi_cam_fb_WLAST,
m_axi_cam_fb_WUSER,
m_axi_cam_fb_WVALID,
m_axi_cam_fb_WREADY,
m_axi_cam_fb_BID,
m_axi_cam_fb_BRESP,
m_axi_cam_fb_BUSER,
m_axi_cam_fb_BVALID,
m_axi_cam_fb_BREADY,
m_axi_cam_fb_ARID,
m_axi_cam_fb_ARADDR,
m_axi_cam_fb_ARLEN,
m_axi_cam_fb_ARSIZE,
m_axi_cam_fb_ARBURST,
m_axi_cam_fb_ARLOCK,
m_axi_cam_fb_ARCACHE,
m_axi_cam_fb_ARPROT,
m_axi_cam_fb_ARQOS,
m_axi_cam_fb_ARUSER,
m_axi_cam_fb_ARVALID,
m_axi_cam_fb_ARREADY,
m_axi_cam_fb_RID,
m_axi_cam_fb_RDATA,
m_axi_cam_fb_RRESP,
m_axi_cam_fb_RLAST,
m_axi_cam_fb_RUSER,
m_axi_cam_fb_RVALID,
m_axi_cam_fb_RREADY,
m_axi_lap_fb_AWID,
m_axi_lap_fb_AWADDR,
m_axi_lap_fb_AWLEN,
m_axi_lap_fb_AWSIZE,
m_axi_lap_fb_AWBURST,
m_axi_lap_fb_AWLOCK,
m_axi_lap_fb_AWCACHE,
m_axi_lap_fb_AWPROT,
m_axi_lap_fb_AWQOS,
m_axi_lap_fb_AWUSER,
m_axi_lap_fb_AWVALID,
m_axi_lap_fb_AWREADY,
m_axi_lap_fb_WDATA,
m_axi_lap_fb_WSTRB,
m_axi_lap_fb_WLAST,
m_axi_lap_fb_WUSER,
m_axi_lap_fb_WVALID,
m_axi_lap_fb_WREADY,
m_axi_lap_fb_BID,
m_axi_lap_fb_BRESP,
m_axi_lap_fb_BUSER,
m_axi_lap_fb_BVALID,
m_axi_lap_fb_BREADY,
m_axi_lap_fb_ARID,
m_axi_lap_fb_ARADDR,
m_axi_lap_fb_ARLEN,
m_axi_lap_fb_ARSIZE,
m_axi_lap_fb_ARBURST,
m_axi_lap_fb_ARLOCK,
m_axi_lap_fb_ARCACHE,
m_axi_lap_fb_ARPROT,
m_axi_lap_fb_ARQOS,
m_axi_lap_fb_ARUSER,
m_axi_lap_fb_ARVALID,
m_axi_lap_fb_ARREADY,
m_axi_lap_fb_RID,
m_axi_lap_fb_RDATA,
m_axi_lap_fb_RRESP,
m_axi_lap_fb_RLAST,
m_axi_lap_fb_RUSER,
m_axi_lap_fb_RVALID,
m_axi_lap_fb_RREADY,
s_axi_LiteS_AWADDR,
s_axi_LiteS_AWVALID,
s_axi_LiteS_AWREADY,
s_axi_LiteS_WDATA,
s_axi_LiteS_WSTRB,
s_axi_LiteS_WVALID,
s_axi_LiteS_WREADY,
s_axi_LiteS_BRESP,
s_axi_LiteS_BVALID,
s_axi_LiteS_BREADY,
s_axi_LiteS_ARADDR,
s_axi_LiteS_ARVALID,
s_axi_LiteS_ARREADY,
s_axi_LiteS_RDATA,
s_axi_LiteS_RRESP,
s_axi_LiteS_RVALID,
s_axi_LiteS_RREADY,
interrupt,
aresetn,
aclk
);

parameter C_M_AXI_CAM_FB_ID_WIDTH = 1;
parameter C_M_AXI_CAM_FB_ADDR_WIDTH = 32;
parameter C_M_AXI_CAM_FB_DATA_WIDTH = 32;
parameter C_M_AXI_CAM_FB_AWUSER_WIDTH = 1;
parameter C_M_AXI_CAM_FB_ARUSER_WIDTH = 1;
parameter C_M_AXI_CAM_FB_WUSER_WIDTH = 1;
parameter C_M_AXI_CAM_FB_RUSER_WIDTH = 1;
parameter C_M_AXI_CAM_FB_BUSER_WIDTH = 1;
parameter C_M_AXI_CAM_FB_USER_DATA_WIDTH = 32;
parameter C_M_AXI_CAM_FB_TARGET_ADDR = 32'h00000000;
parameter C_M_AXI_CAM_FB_USER_VALUE = 1'b0;
parameter C_M_AXI_CAM_FB_PROT_VALUE = 3'b010;
parameter C_M_AXI_CAM_FB_CACHE_VALUE = 4'b0000;
parameter C_M_AXI_LAP_FB_ID_WIDTH = 1;
parameter C_M_AXI_LAP_FB_ADDR_WIDTH = 32;
parameter C_M_AXI_LAP_FB_DATA_WIDTH = 32;
parameter C_M_AXI_LAP_FB_AWUSER_WIDTH = 1;
parameter C_M_AXI_LAP_FB_ARUSER_WIDTH = 1;
parameter C_M_AXI_LAP_FB_WUSER_WIDTH = 1;
parameter C_M_AXI_LAP_FB_RUSER_WIDTH = 1;
parameter C_M_AXI_LAP_FB_BUSER_WIDTH = 1;
parameter C_M_AXI_LAP_FB_USER_DATA_WIDTH = 32;
parameter C_M_AXI_LAP_FB_TARGET_ADDR = 32'h00000000;
parameter C_M_AXI_LAP_FB_USER_VALUE = 1'b0;
parameter C_M_AXI_LAP_FB_PROT_VALUE = 3'b010;
parameter C_M_AXI_LAP_FB_CACHE_VALUE = 4'b0000;
parameter C_S_AXI_LITES_ADDR_WIDTH = 6;
parameter C_S_AXI_LITES_DATA_WIDTH = 32;
parameter RESET_ACTIVE_LOW = 1;

  1. 2013年10月12日 08:25 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSでラプラシアン・フィルタ関数をaxi masterモジュールにする1

今回は、ラプラシアン・フィルタ関数自体をVivado HLSで、AXI4 Master を使用したIPとして生成して見ようと思う。

まずは、laplacian_filter.c を Vivado HLS でコンパイルできるように書き換えた。
次に、シミュレーション用のテストベンチを作製した。

Vivado HLSでCのシミュレーションを行ったところ、CPUが100%になったきり、なかなか終わらなかったので、DebugモードでCのシミュレーションを行った。
HLS_lap_fil_axim_2_131011.png

Debugモードでブレークポイントを入れながらCのシミュレーションを行うと、うまく行った。なぜだろうか?とっても大きな規模のシミュレーションだとは思うのだが。。。
(2013/10/13: フォルダをC:\Users\Masaaki\Documents\Vivado_HLS\lap_filter_aximに変更しました)
HLS_lap_fil_axim_1_131011.png

HLS_lap_fil_axim_3_131011.png

C Synthesis もやってみた。
min Latency が 1450801 クロック、クロックが10nsec だと14.5msec だ。
max Latency が 6932170801 クロックで 69.3sec だ。
かなり開きがある?これはなぜだろうか?
HLS_lap_fil_axim_4_131011.png

Interface を見ると、指定した ap_bus もあるが、ap_ctl_hs もある。AXI4 Masterバスだけでなく、アドレスや開始を設定する AXI4 Lite Slave バスも必要なのだが、ソレも作ってくれるのだろうか? pragma での指定が必要だろうか?
HLS_lap_fil_axim_5_131011.png

Export RTL で EDK用の pcore を作ってみたが、カメラ画像フレーム・バッファ読み込み用アドレスとラプラシアン・フィルタの結果を書き出すフレーム・バッファのアドレスは、parameter 指定の固定アドレスのようだ。これをAXI4 Lite Slave でAXI4 Master のRead/Writeするアドレスを渡す必要がある。次回修正を試みる。
HLS_lap_fil_axim_6_131011.png

現在の、Vivado HLS合成用の laplacian_filter.c とテストベンチの lap_filter_tb.c を貼っておく。
まずは、laplacian_filter.c から。(2013/11/30:修正)

// laplacian_filter.c
//

#include <stdio.h>
#include <string.h>

#define HORIZONTAL_PIXEL_WIDTH    800
#define VERTICAL_PIXEL_WIDTH    600
#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y(int rgb);

void laplacian_filter(volatile int *cam_fb, volatile int *lap_fb)
{
    #pragma HLS INTERFACE ap_bus port=cam_fb depth=480000
    #pragma HLS INTERFACE ap_bus port=lap_fb depth=480000
    #pragma HLS RESOURCE variable=cam_fb core=AXI4M
    #pragma HLS RESOURCE variable=lap_fb core=AXI4M

    unsigned int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
    unsigned int lap_buf[HORIZONTAL_PIXEL_WIDTH];
    int x, y;
    int lap_fil_val;
    int a, b;
    int fl, sl, tl;
    
    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    for (y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (y==0 || y==VERTICAL_PIXEL_WIDTH-1){ // 縦の境界の時の値は0とする
                lap_fil_val = 0;
            }else if (x==0 || x==HORIZONTAL_PIXEL_WIDTH-1){ // 横の境界の時も値は0とする
                lap_fil_val = 0;
            }else{
                if (x == 1){ // ラインの最初でラインの画素を読み出す
                    if (y == 1){ // 最初のラインでは3ライン分の画素を読み出す
                        for (a=0; a<3; a++){ // 3ライン分
                            memcpy(line_buf[a], (const int*)(cam_fb+a*HORIZONTAL_PIXEL_WIDTH), HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                            for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                                line_buf[a][b] = conv_rgb2y(line_buf[a][b]);    // カラーから白黒へ
                            }
                        }
                    }
                } else { // 最初のラインではないので、1ラインだけ読み込む。すでに他の2ラインは読み込まれている
                    memcpy(line_buf[(y+1)%3], (const int*)(cam_fb+((y+1)*HORIZONTAL_PIXEL_WIDTH)), HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                    for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                        line_buf[(y+1)%3][b] = conv_rgb2y(line_buf[(y+1)%3][b]);    // カラーから白黒へ
                    }
                }
                fl = (y-1)%3;    // 最初のライン, y=1 012, y=2 120, y=3 201, y=4 012
                sl = y%3;        // 2番めのライン
                tl = (y+1)%3;    // 3番目のライン
                lap_fil_val = laplacian_fil(line_buf[fl][x-1], line_buf[fl][x], line_buf[fl][x+1], line_buf[sl][x-1], line_buf[sl][x], line_buf[sl][x+1], line_buf[tl][x-1], line_buf[tl][x], line_buf[tl][x+1]);
            }
            lap_buf[x] = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる
        }
        memcpy((const *)(lap_fb+(y*HORIZONTAL_PIXEL_WIDTH)), lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
    }
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8// 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
    // return(abs(-x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2)); // 元の実装
    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = 0;
    else if (y>255)
        y = 255;
    return(y);
}


次に、テストベンチの lap_filter_tb.c を貼る。(2013/11/30:修正)(2015/07/26:修正)

// Testbench of laplacian_filter.c
// M系列データをハードウェアとソフトウェアで、ラプラシアン・フィルタを掛けて、それを比較する
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define HORIZONTAL_PIXEL_WIDTH    40
#define VERTICAL_PIXEL_WIDTH    20
//#define HORIZONTAL_PIXEL_WIDTH    800
//#define VERTICAL_PIXEL_WIDTH    600
#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

int laplacian_fil_soft(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y_soft(int rgb);
int lap_filter_axim(int cam_addr, int lap_addr, volatile int *cam_fb, volatile int *lap_fb);    // hardware
void laplacian_filter_soft(volatile int *cam_fb, volatile int *lap_fb); // software

int mseq_po[ALL_PIXEL_VALUE];
int hw_lap_po[ALL_PIXEL_VALUE];
int sf_lap_po[ALL_PIXEL_VALUE];

int main()
{
//    int *mseq_po, *hw_lap_po, *sf_lap_po;
    int *s, *h;
    int x, y;
    unsigned int lfsr = 1;
    int cam_addr, lap_addr;
    
    // ピクセルデータ領域にM系列データを入力
    for (y=0, s=mseq_po; y<VERTICAL_PIXEL_WIDTH; y++){
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xd0000001u); /* taps 32 31 29 1 */
            // ”線形帰還シフトレジスタ ”参照 : http://ja.wikipedia.org/wiki/%E7%B7%9A%E5%BD%A2%E5%B8%B0%E9%82%84%E3%82%B7%E3%83%95%E3%83%88%E3%83%AC%E3%82%B8%E3%82%B9%E3%82%BF
            *s = lfsr;
            s++;
        }
    }
    
    cam_addr = (int)mseq_po;
    lap_addr = (int)hw_lap_po;
    lap_filter_axim(cam_addr, lap_addr, (volatile int *)0, (volatile int *)0);    // ハードウェアのラプラシアン・フィルタ
    laplacian_filter_soft(mseq_po, sf_lap_po);    // ソフトウェアのラプラシアン・フィルタ
    
    // ハードウェアとソフトウェアのラプラシアン・フィルタの値のチェック
    for (y=0, h=hw_lap_po, s=sf_lap_po; y<VERTICAL_PIXEL_WIDTH; y++){
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (*h != *s){
                printf("ERROR HW and SW results mismatch x = %d, y = %d, HW = %d, SW = %d\n", x, y, *h, *s);
                return(1);
            } else {
                h++;
                s++;
            }
        }
    }
    printf("Success HW and SW results match\n");
}

void laplacian_filter_soft(volatile int *cam_fb, volatile int *lap_fb)
{
    unsigned int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
    unsigned int lap_buf[HORIZONTAL_PIXEL_WIDTH];
    int x, y;
    int lap_fil_val;
    int a, b;
    int fl, sl, tl;

    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    for (y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        fl = (y-1)%3;    // 最初のライン, y=1 012, y=2 120, y=3 201, y=4 012
        sl = y%3;        // 2番めのライン
        tl = (y+1)%3;    // 3番目のライン
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (y==0 || y==VERTICAL_PIXEL_WIDTH-1){ // 縦の境界の時の値は0とする
                lap_fil_val = 0;
            }else if (x==0 || x==HORIZONTAL_PIXEL_WIDTH-1){ // 横の境界の時も値は0とする
                lap_fil_val = 0;
            }else{
                 if (x == 1){ // ラインの最初でラインの画素を読み出す
                    if (y == 1){ // 最初のラインでは3ライン分の画素を読み出す
                        for (a=0; a<3; a++){ // 3ライン分
                            memcpy(&line_buf[a][0], (const int*)&cam_fb[a*(HORIZONTAL_PIXEL_WIDTH)], HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                            for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                                line_buf[a][b] = conv_rgb2y(line_buf[a][b]);    // カラーから白黒へ
                            }
                        }
                    } else { // 最初のラインではないので、1ラインだけ読み込む。すでに他の2ラインは読み込まれている
                         memcpy(line_buf[(y+1)%3], (const int*)&cam_fb[(y+1)*(HORIZONTAL_PIXEL_WIDTH)], HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                        for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                            line_buf[(y+1)%3][b] = conv_rgb2y(line_buf[(y+1)%3][b]);    // カラーから白黒へ
                        }
                    }
                }
                lap_fil_val = laplacian_fil(line_buf[fl][x-1], line_buf[fl][x], line_buf[fl][x+1], line_buf[sl][x-1], line_buf[sl][x], line_buf[sl][x+1], line_buf[tl][x-1], line_buf[tl][x], line_buf[tl][x+1]);
            }
            lap_buf[x] = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる
        }
        memcpy(&lap_fb[y*(HORIZONTAL_PIXEL_WIDTH)], (const int*)lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
    }
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y_soft(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8// 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil_soft(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
    int y;

    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = 0;
    else if (y>255)
        y = 255;
    return(y);
}

  1. 2013年10月11日 04:48 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSで作ったラプラシアン・フィルタIPを使ってみる5

Vivado HLSで作ったラプラシアン・フィルタIPを使ってみる4”の続き。

前回はCソースコードをブログに貼った。今回は、ChipScope を使用して、AXI4 Lite Slave バスのバス・トランザクションを見ていこう。

最初に、大事な、chipscope_icon_0 の control0, control1, control2 の割り当てから確認する。下に図に示す順番だ。
HLS_lap_filter_24_131009.png

ChipScope Pro を立ちあげて、CDCファイルをインポートした。3つ分インポート。
HLS_lap_filter_25_131009.png

HLS_lap_filter_26_131009.png

HLS_lap_filter_27_131009.png

ラプラシアン・フィルタIPのAXI4 Lite Slave バスを見た。
HLS_lap_filter_28_131009.png

Oカーソルの時点で、x0y0のデータがラプラシアン・フィルタIPのレジスタにWriteされている。9個のデータがWriteされ、その次に、vld 信号を1にするレジスタWrite がやはり9回続く。その後、ap_done の状態を見るReadが1回(1回で済んでいるようだ)。その後のRead はラプラシアン・フィルタの結果をReadしている。1回のラプラシアン・フィルタIPのアクセスが終了してから、次のx0y0のデータWrite までに 396クロックかかっている。1クロックは100MHzで10nsec なので、3.96usec かかっていることになる。

次に、IOアクセス(メモリ・マップされているので、メモリ・マップドIO)全体を見てみよう。
HLS_lap_filter_29_131009.png

IOアクセス全体では、307クロック、3.07usec だった。ソフトウェアを含めた。1回のラプラシアン・フィルタIPのアクセスが終了時間は、3.96usec だったので、IOアクセスの割合は、3.07 / 3.96 = 77.5%となった。

次に、IOアクセスを拡大してみてみることにする。
HLS_lap_filter_30_131009.png

1つのAXI4 Lite Slave Write アクセスから、次のそれまでは、15クロック、つまり、150nsec 掛かっている。
その内の実際のAXI4 Slave Write アクセスは3クロックのみだ。間隔が開きすぎている。ARMプロセッサのIOアクセスが遅いのか?

Oカーソルの位置が、ap_done を読んでいるAXI4 Lite Slave のRead アクセスで、Xカーソルの位置が、ラプラシアン・フィルタの結果を読んでいるAXI4 Lite Slave のRead アクセスだ。やはり、間隔は 150nsec だ。
HLS_lap_filter_31_131009.png

Vivado HLSで作ったラプラシアン・フィルタIPを使ってみる4”の lap_filter_uty.c の

//x0y0_read = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y0_DATA);

のコメントを外して実行した時のChipScope の結果を下に示す。ピンクの四角で囲ったのが、上のRead アクセスとなる。
HLS_lap_filter_32_131009.png

これならば、特に遅くなる要素はないはず。。。なぜ、0.9秒も遅くなってしまうのだろうか?
  1. 2013年10月09日 05:34 |
  2. Co-design
  3. | トラックバック:0
  4. | コメント:2

Vivado HLSで作ったラプラシアン・フィルタIPを使ってみる4

Vivado HLSで作ったラプラシアン・フィルタIPを使ってみる3”の続き。

今回はCソースコードを貼っておく。
なお、Vivado HLSでラプラシアン・フィルタIP用のドライバが生成されていたが、使い方がまだよくわからないため、自作した。

まずは、lap_filter_hls.c このCソースコードの修正箇所は、laplacian_fil_hw()を呼び出すところだけだ。

// lap_filter_hls.c
// RGBをYに変換後にラプラシアンフィルタを掛ける。
// ピクセルのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 2013/09/16

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <ctype.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/kernel.h>
#include "lap_filter_uty.h"

#define LAP_FILTER_HW_ADDRESS    0x48000000    // ラプラシアン・フィルタのハードウェアのアドレス

#define HORIZONTAL_PIXEL_WIDTH    800
#define VERTICAL_PIXEL_WIDTH    600
#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)

#define BUFSIZE    1024

#define MEASURE_COUNT    5000

int laplacian_fil(unsigned int *lap_fil_hw_addr, int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y(int rgb);
int chkhex(char *str);
volatile unsigned *setup_io(off_t mapped_addr, unsigned int *buf_addr);
void Xil_DCacheInvalidateLine(unsigned int adr);
void Xil_DCacheFlushLine(unsigned int adr);

int main()
{
    FILE *fd;
    int xy[3][3];
    char buf[BUFSIZE], *token;
    unsigned int read_num;
    unsigned int bitmap_dc_reg_addr;
    volatile unsigned *bm_disp_cnt_reg;
    unsigned int fb_addr, next_frame_addr;
    unsigned int val;
    int lap_fil_val;
    int x, y;
    int *r_pixel, *w_pixel;
    unsigned int r_addr, w_addr;
    unsigned int r_addr_page, w_addr_page;
    unsigned int r_addr_page_pre=0, w_addr_page_pre=0;
    unsigned int r_addr_offset, w_addr_offset;
    unsigned int r_buf, w_buf, bitmap_buf;
    struct timeval start_time, temp1, temp2, end_time;
    unsigned int rmmap_cnt=0, wmmap_cnt=0;
    unsigned int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
    int a, b;
    int fl, sl, tl;
    unsigned int lap_fil_hw, *lap_fil_hw_addr;

    gettimeofday(&start_time, NULL);    // プログラム起動時の時刻を記録

    // fb_start_addr.txt の内容をパイプに入れる
    memset(buf, '\0', sizeof(buf)); // buf すべてに\0 を入れる
    // fb_start_addr.txt を開く
    fd = popen("cat /Apps/fb_start_addr.txt", "r");
    if (fd != NULL){
        read_num = fread(buf, sizeof(unsigned char), BUFSIZE, fd);
        if (read_num > 0){
            sscanf(buf, "%x\n", &fb_addr);
        }
    }
    pclose(fd);

    // ラプラシアンフィルタの結果を入れておくフレーム・バッファ
    next_frame_addr = ((fb_addr + (ALL_PIXEL_VALUE*4)) & (~(int)(PAGE_SIZE-1))) + PAGE_SIZE;

    // Vivado HLS で作製したラプラシアン・フィルタIPのアドレスを取得
    lap_fil_hw_addr = setup_io((off_t)LAP_FILTER_HW_ADDRESS, &lap_fil_hw);

    lap_fil_initialize(lap_fil_hw_addr);    // ラプラシアン・フィルタIPの初期化とap_start

    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    for (y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (y==0 || y==VERTICAL_PIXEL_WIDTH-1){ // 縦の境界の時の値は0とする
                lap_fil_val = 0;
            }else if (x==0 || x==HORIZONTAL_PIXEL_WIDTH-1){ // 横の境界の時も値は0とする
                lap_fil_val = 0;
            }else{
                if (y == 1 && x == 1){ // 最初のラインの最初のピクセルでは2ライン分の画素を読み出す
                    for (a=0; a<2; a++){ // 2ライン分
                        for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                            r_addr = fb_addr+((y+(a-1))*HORIZONTAL_PIXEL_WIDTH+b)*4;
                            r_addr_page = r_addr & (~(int)(PAGE_SIZE-1));
                            r_addr_offset = r_addr & ((int)(PAGE_SIZE-1));
                            if (r_addr_page != r_addr_page_pre){    // 以前のページと違うのでunmmap してページの物理アドレスを取り直す
                               if (r_addr_page_pre != 0){    // 初めの場合はmmap()していないので、munmap()しない
                                    munmap(r_pixel, BLOCK_SIZE);
                                    free((unsigned int *)r_buf);
                                }
                                r_pixel = setup_io((off_t)r_addr_page, &r_buf);

                                rmmap_cnt++;
                                r_addr_page_pre = r_addr_page;
                            }
                            line_buf[a][b] = *(volatile int *)((unsigned int)r_pixel + r_addr_offset);
                            line_buf[a][b] = conv_rgb2y(line_buf[a][b]);
                        }
                    }
                }
                if (x == 1) {    // ラインの最初なので、2つのピクセルを読み込む
                    for (b=0; b<2; b++){ // ライン
                        r_addr = fb_addr+((y+1)*HORIZONTAL_PIXEL_WIDTH+b)*4;
                        r_addr_page = r_addr & (~(int)(PAGE_SIZE-1));
                        r_addr_offset = r_addr & ((int)(PAGE_SIZE-1));
                        if (r_addr_page != r_addr_page_pre){    // 以前のページと違うのでunmmap してページの物理アドレスを取り直す
                           if (r_addr_page_pre != 0){    // 初めの場合はmmap()していないので、munmap()しない
                                munmap(r_pixel, BLOCK_SIZE);
                                free((unsigned int *)r_buf);
                            }
                            r_pixel = setup_io((off_t)r_addr_page, &r_buf);

                            rmmap_cnt++;
                            r_addr_page_pre = r_addr_page;
                        }
                        line_buf[(y+1)%3][b] = *(volatile int *)((unsigned int)r_pixel + r_addr_offset);
                        // (y+1)%3 は、使用済みのラインがに読み込む、y=2 の時 line[0], y=3の時 line[1], y=4の時 line[2]
                        line_buf[(y+1)%3][b] = conv_rgb2y(line_buf[(y+1)%3][b]);
                    }
                }

                // 1つのピクセルを読み込みながらラプラシアン・フィルタを実行する
                r_addr = fb_addr+((y+1)*HORIZONTAL_PIXEL_WIDTH+(x+1))*4; // ラプラシアン・フィルタに必要な最後のピクセルを読み込む
                r_addr_page = r_addr & (~(int)(PAGE_SIZE-1));
                r_addr_offset = r_addr & ((int)(PAGE_SIZE-1));
                if (r_addr_page != r_addr_page_pre){    // 以前のページと違うのでunmmap してページの物理アドレスを取り直す
                   if (r_addr_page_pre != 0){    // 初めの場合はmmap()していないので、munmap()しない
                        munmap(r_pixel, BLOCK_SIZE);
                        free((unsigned int *)r_buf);
                    }
                    r_pixel = setup_io((off_t)r_addr_page, &r_buf);

                    rmmap_cnt++;
                    r_addr_page_pre = r_addr_page;
                }
                line_buf[(y+1)%3][x+1] = *(volatile int *)((unsigned int)r_pixel + r_addr_offset);
                // (y+1)%3 は、使用済みのラインがに読み込む、y=2 の時 line[0], y=3の時 line[1], y=4の時 line[2]
                line_buf[(y+1)%3][x+1] = conv_rgb2y(line_buf[(y+1)%3][x+1]);

                fl = (y-1)%3;    // 最初のライン, y=1 012, y=2 120, y=3 201, y=4 012
                sl = y%3;        // 2番めのライン
                tl = (y+1)%3;    // 3番目のライン
                lap_fil_val = laplacian_fil(lap_fil_hw_addr, line_buf[fl][x-1], line_buf[fl][x], line_buf[fl][x+1], line_buf[sl][x-1], line_buf[sl][x], line_buf[sl][x+1], line_buf[tl][x-1], line_buf[tl][x], line_buf[tl][x+1]);
            }
            w_addr = next_frame_addr+(y*HORIZONTAL_PIXEL_WIDTH + x)*4;
            w_addr_page = w_addr & (~(int)(PAGE_SIZE-1));
            w_addr_offset = w_addr & ((int)(PAGE_SIZE-1));
            if (w_addr_page != w_addr_page_pre){    // 以前のページと違うのでunmmap してページの物理アドレスを取り直す
                if (w_addr_page_pre != 0){    // 初めの場合はmmap()していないので、munmap()しない
                    munmap(w_pixel, BLOCK_SIZE);
                    free((unsigned int *)w_buf);
                }
                w_pixel = setup_io((off_t)w_addr_page, &w_buf);
                wmmap_cnt++;
                w_addr_page_pre = w_addr_page;
            }
            *(volatile int *)((unsigned int)w_pixel + w_addr_offset) = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val ;
            //printf("x = %d  y = %d\n", x, y);
        }
        a++;
    }
    munmap((unsigned int *)r_addr_page, BLOCK_SIZE);
    free((unsigned int *)r_buf);
    munmap((unsigned int *)w_addr_page, BLOCK_SIZE);
    free((unsigned int *)w_buf);
    munmap((unsigned int *)lap_fil_hw_addr, BLOCK_SIZE);
    free((unsigned int *)lap_fil_hw);

    // bitmap-disp-cntrler-axi-master のアドレスを取得
    memset(buf, '\0', sizeof(buf)); // buf すべてに\0 を入れる
    // ls /sys/devices/axi.0 の内容をパイプに入れる
    fd = popen("ls /sys/devices/axi.0", "r");
    if (fd != NULL){
        read_num = fread(buf, sizeof(unsigned char), BUFSIZE, fd);
        if (read_num > 0){
            token = buf;
            if ((token=strtok(token, ".\n")) != NULL){
                do {
                    if (chkhex(token)){ // 16進数
                        sscanf(token, "%x", &val);
                    } else {
                        if (strcmp(token, "bitmap-disp-cntrler-axi-master") == 0)
                            bitmap_dc_reg_addr = val;
                    }
                }while((token=strtok(NULL, ".\n")) != NULL);
            }
        }
    }
    pclose(fd);

    // ラプラシアンフィルタの掛かった画像のスタートアドレスを bitmap-disp-cntrler-axi-master にセット
    bm_disp_cnt_reg = setup_io((off_t)bitmap_dc_reg_addr, &bitmap_buf);
    *bm_disp_cnt_reg = next_frame_addr;

    munmap((unsigned int *)bm_disp_cnt_reg, BLOCK_SIZE);
    free((unsigned int *)bitmap_buf);

    gettimeofday(&end_time, NULL);
    printf("rmmap_cnt = %d\n", rmmap_cnt);
    printf("wmmap_cnt = %d\n", wmmap_cnt);
    if (end_time.tv_usec < start_time.tv_usec) {
        printf("total time = %d.%d sec\n", end_time.tv_sec - start_time.tv_sec - 1, 1000000 + end_time.tv_usec - start_time.tv_usec);
    }
    else {
        printf("total time = %d.%d sec\n", end_time.tv_sec - start_time.tv_sec, end_time.tv_usec - start_time.tv_usec);
    }
    return(0);
}


// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8; // 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil(unsigned int *lap_fil_hw_addr, int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
    //return(abs(-x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2));
    return(laplacian_fil_hw(lap_fil_hw_addr, x0y0, x1y0, x2y0, x0y1, x1y1, x2y1, x0y2, x1y2, x2y2));
}

//
// Set up a memory regions to access GPIO
//
volatile unsigned *setup_io(off_t mapped_addr, unsigned int *buf_addr)
// void setup_io()
{
    int  mem_fd;
    char *gpio_mem, *gpio_map;

   /* open /dev/mem */
   if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
      printf("can't open /dev/mem \n");
      printf("mapped_addr = %x\n", mapped_addr);
      exit (-1);
   }

   /* mmap GPIO */

   // Allocate MAP block
   if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
      printf("allocation error \n");
      exit (-1);
   }
    *buf_addr = gpio_mem;    // mallocしたアドレスをコピー

   // Make sure pointer is on 4K boundary
   if ((unsigned long)gpio_mem % PAGE_SIZE)
     gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE);

   // Now map it
   gpio_map = (unsigned char *)mmap(
      (caddr_t)gpio_mem,
      BLOCK_SIZE,
      PROT_READ|PROT_WRITE,
      MAP_SHARED|MAP_FIXED,
      mem_fd,
      mapped_addr
   );

   if ((long)gpio_map < 0) {
      printf("mmap error %d\n", (int)gpio_map);
      printf("mapped_addr = %x\n", mapped_addr);
      exit (-1);
   }

   close(mem_fd); // /dev/mem のクローズ

   // Always use volatile pointer!
   // gpio = (volatile unsigned *)gpio_map;
   return((volatile unsigned *)gpio_map);

} // setup_io

// 文字列が16進数かを調べる
int chkhex(char *str){
    while (*str != '\0'){
        if (!isxdigit(*str))
            return 0;
        str++;
    }
    return 1;
}


次に、lap_filter_uty.h を下に示す。

// axi lite slave utility routine of laplacian filter
// 2013/10/07
//

void lap_fil_initialize(unsigned int *lap_fil_hw_addr);
int laplacian_fil_hw(unsigned int *lap_fil_hw_addr, int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);


最後に、lap_filter_uty.c を下に示す。

// axi lite slave utility routine of laplacian filter
// 2013/10/07
//

#include "xlaplacian_filter_hw.h"

#define AP_START_BIT_POS    1    // ap_start のビット位置 bit0
#define AP_DONE_BIT_POS        2    // ap_done のビット位置 bit1

void lap_fil_initialize(unsigned int *lap_fil_hw_addr)
{
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_AP_CTRL) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_GIE) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_IER) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y0_CTRL) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X1Y0_CTRL) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X2Y0_CTRL) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y1_CTRL) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X1Y1_CTRL) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X2Y1_CTRL) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y2_CTRL) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X1Y2_CTRL) = 0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X2Y2_CTRL) = 0;
}
    
// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil_hw(unsigned int *lap_fil_hw_addr, int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
    int ap_status, ap_done;
    int ap_return;
    int x0y0_read;
    
    // data Write
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y0_DATA) = x0y0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X1Y0_DATA) = x1y0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X2Y0_DATA) = x2y0;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y1_DATA) = x0y1;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X1Y1_DATA) = x1y1;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X2Y1_DATA) = x2y1;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y2_DATA) = x0y2;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X1Y2_DATA) = x1y2;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X2Y2_DATA) = x2y2;

    //x0y0_read = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y0_DATA);
    
    // ap_start enable
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_AP_CTRL) = AP_START_BIT_POS;

     // vld enable
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y0_CTRL) = 1;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X1Y0_CTRL) = 1;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X2Y0_CTRL) = 1;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y1_CTRL) = 1;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X1Y1_CTRL) = 1;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X2Y1_CTRL) = 1;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y2_CTRL) = 1;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X1Y2_CTRL) = 1;
    *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X2Y2_CTRL) = 1;
    
    // wait ap_done
    do{
        ap_status = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_AP_CTRL);
        ap_done = ap_status & AP_DONE_BIT_POS;
    }while(!ap_done);
    
    // ap_return read
    ap_return = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_AP_RETURN);
    //printf("ap_return = %d\n", ap_return);
    return(abs(ap_return));
}


lap_filter_uty.c で、laplacian_fil_hw() の”//x0y0_read = *(volatile int *)((unsigned int)lap_fil_hw_addr + (unsigned int)XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y0_DATA);”のコメントを外すと、ラプラシアン・フィルタの実行時間が1.9秒程度から2.8秒程度に遅くなる。なぜなのか?がとっても気になる。
  1. 2013年10月09日 03:44 |
  2. Co-design
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSで作ったラプラシアン・フィルタIPを使ってみる3

Vivado HLSで作ったラプラシアン・フィルタIPを使ってみる2”の続き。

まずは、XPSプロジェクトを少し修正した。カメラ・コントローラとビットマップ・ディスプレイ・コントローラのAXI4 Lite Slave の ChipScope AXI Monitor を削除して、ラプラシアン・フィルタIP に ChipScope AXI Monitor を入れた。
HLS_lap_filter_23_131008.png

これで、ISEに戻って、インプリメント、ビットストリームの生成を行って、SDKでBOOT.bin を再生成して、SDカードに入れてブートしてみたが、問題なくブートできた。

次に、ラプラシアン・フィルタIPを使用する様に、ラプラシアン・フィルタのソフトウェアを変更した (lap_filter_hls.c)。

次に進む前に、ラプラシアン・フィルタIPのハードウェア情報が記述されている xlaplacian_filter_hw.h を下に示す。

// ==============================================================
// File generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC
// Version: 2013.2
// Copyright (C) 2013 Xilinx Inc. All rights reserved.
//
// ==============================================================

// BUS_A
// 0x00 : Control signals
// bit 0 - ap_start (Read/Write/COH)
// bit 1 - ap_done (Read/COR)
// bit 2 - ap_idle (Read)
// bit 3 - ap_ready (Read)
// bit 7 - auto_restart (Read/Write)
// others - reserved
// 0x04 : Global Interrupt Enable Register
// bit 0 - Global Interrupt Enable (Read/Write)
// others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
// bit 0 - Channel 0 (ap_done)
// bit 1 - Channel 1 (ap_ready)
// others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
// bit 0 - Channel 0 (ap_done)
// bit 1 - Channel 1 (ap_ready)
// others - reserved
// 0x10 : Control signal of x0y0
// bit 0 - x0y0_ap_vld (Read/Write/COH)
// bit 1 - x0y0_ap_ack (Read)
// others - reserved
// 0x14 : Data signal of x0y0
// bit 31~0 - x0y0[31:0] (Read/Write)
// 0x18 : Control signal of x1y0
// bit 0 - x1y0_ap_vld (Read/Write/SC)
// others - reserved
// 0x1c : Data signal of x1y0
// bit 31~0 - x1y0[31:0] (Read/Write)
// 0x20 : Control signal of x2y0
// bit 0 - x2y0_ap_vld (Read/Write/SC)
// others - reserved
// 0x24 : Data signal of x2y0
// bit 31~0 - x2y0[31:0] (Read/Write)
// 0x28 : Control signal of x0y1
// bit 0 - x0y1_ap_vld (Read/Write/SC)
// others - reserved
// 0x2c : Data signal of x0y1
// bit 31~0 - x0y1[31:0] (Read/Write)
// 0x30 : Control signal of x1y1
// bit 0 - x1y1_ap_vld (Read/Write/SC)
// others - reserved
// 0x34 : Data signal of x1y1
// bit 31~0 - x1y1[31:0] (Read/Write)
// 0x38 : Control signal of x2y1
// bit 0 - x2y1_ap_vld (Read/Write/SC)
// others - reserved
// 0x3c : Data signal of x2y1
// bit 31~0 - x2y1[31:0] (Read/Write)
// 0x40 : Control signal of x0y2
// bit 0 - x0y2_ap_vld (Read/Write/SC)
// others - reserved
// 0x44 : Data signal of x0y2
// bit 31~0 - x0y2[31:0] (Read/Write)
// 0x48 : Control signal of x1y2
// bit 0 - x1y2_ap_vld (Read/Write/SC)
// others - reserved
// 0x4c : Data signal of x1y2
// bit 31~0 - x1y2[31:0] (Read/Write)
// 0x50 : Control signal of x2y2
// bit 0 - x2y2_ap_vld (Read/Write/SC)
// others - reserved
// 0x54 : Data signal of x2y2
// bit 31~0 - x2y2[31:0] (Read/Write)
// 0x58 : Data signal of ap_return
// bit 31~0 - ap_return[31:0] (Read)
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)

#define XLAPLACIAN_FILTER_BUS_A_ADDR_AP_CTRL 0x00
#define XLAPLACIAN_FILTER_BUS_A_ADDR_GIE 0x04
#define XLAPLACIAN_FILTER_BUS_A_ADDR_IER 0x08
#define XLAPLACIAN_FILTER_BUS_A_ADDR_ISR 0x0c
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y0_CTRL 0x10
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y0_DATA 0x14
#define XLAPLACIAN_FILTER_BUS_A_BITS_X0Y0_DATA 32
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X1Y0_CTRL 0x18
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X1Y0_DATA 0x1c
#define XLAPLACIAN_FILTER_BUS_A_BITS_X1Y0_DATA 32
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X2Y0_CTRL 0x20
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X2Y0_DATA 0x24
#define XLAPLACIAN_FILTER_BUS_A_BITS_X2Y0_DATA 32
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y1_CTRL 0x28
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y1_DATA 0x2c
#define XLAPLACIAN_FILTER_BUS_A_BITS_X0Y1_DATA 32
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X1Y1_CTRL 0x30
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X1Y1_DATA 0x34
#define XLAPLACIAN_FILTER_BUS_A_BITS_X1Y1_DATA 32
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X2Y1_CTRL 0x38
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X2Y1_DATA 0x3c
#define XLAPLACIAN_FILTER_BUS_A_BITS_X2Y1_DATA 32
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y2_CTRL 0x40
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X0Y2_DATA 0x44
#define XLAPLACIAN_FILTER_BUS_A_BITS_X0Y2_DATA 32
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X1Y2_CTRL 0x48
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X1Y2_DATA 0x4c
#define XLAPLACIAN_FILTER_BUS_A_BITS_X1Y2_DATA 32
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X2Y2_CTRL 0x50
#define XLAPLACIAN_FILTER_BUS_A_ADDR_X2Y2_DATA 0x54
#define XLAPLACIAN_FILTER_BUS_A_BITS_X2Y2_DATA 32
#define XLAPLACIAN_FILTER_BUS_A_ADDR_AP_RETURN 0x58
#define XLAPLACIAN_FILTER_BUS_A_BITS_AP_RETURN 32

COH = Clear on Handshake の ap_start とx0y0_ap_vld ~ x2y2_ap_vld は振る舞いが違っていた。
laplacian_filter_BUS_A_if.v を見ると、ap_start は、ready が来ると、auto_restartの値をロードしていた。x0y0_ap_vld ~ x2y2_ap_vld は 1 にセットされた次のクロックで 0 にリセットされていた。

// ap_start
always @(posedge ACLK) begin
    if (~ARESETN)
        ap_start <= 1'b0;
    else if (w_hs && waddr == ADDR_AP_CTRL && WSTRB[0] && WDATA[0])
        ap_start <= 1'b1;
    else if (O_ap_ready)
        ap_start <= auto_restart; // clear on handshake/auto restart
end


// _x0y0_ap_vld
always @(posedge ACLK) begin
    if (~ARESETN)
        _x0y0_ap_vld <= 1'b0;
    else if (w_hs && waddr == ADDR_X0Y0_CTRL && WSTRB[0] && WDATA[0])
        _x0y0_ap_vld <= 1'b1;
    else if (I_x0y0_ap_ack)
        _x0y0_ap_vld <= 1'b0; // clear on handshake
end


それに応じてソフトウェアを修正して、動作させることが出来た。更に、”時間・時刻処理について(4)”を参照させて頂いて、gettimeofday()の使用に関するバグを修正した。

Vivado HLSで作製したラプラシアン・フィルタIPを使用したラプラシアン・フィルタ・ソフトウェアの実行時間は以下のようになった。

zynq> ./lap_filter_hls.elf
rmmap_cnt = 469
wmmap_cnt = 469
total time = 1.945109 sec
zynq> ./lap_filter_hls.elf
rmmap_cnt = 469
wmmap_cnt = 469
total time = 1.943671 sec
zynq> ./lap_filter_hls.elf
rmmap_cnt = 469
wmmap_cnt = 469
total time = 1.944409 sec
zynq> ./lap_filter_hls.elf
rmmap_cnt = 469
wmmap_cnt = 469
total time = 1.944262 sec
zynq> ./lap_filter_hls.elf
rmmap_cnt = 469
wmmap_cnt = 469
total time = 1.944169 sec


約1.94秒となった。完全にソフトウェアで実行した前回の 1.94 / 0.39 ≒ 5 倍になった。
予想通り遅くなってしまった。ラプラシアン・フィルタの9個の要素をレジスタに書いたり、ハンドシェークをするのに時間がかかってしまったのだと思う。これは後で、ChipScopeを使用して検証したい。
  1. 2013年10月08日 05:56 |
  2. Co-design
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSでラプラシアン・フィルタ式のみをaxi lite slaveモジュールにする3

Vivado HLSでラプラシアン・フィルタ式のみをaxi lite slaveモジュールにする2”の続き。

記事が前後してしまったが、Vivado HLSで生成されたVerilog HDLコードをシミュレーションしてみようと思う。
生成されたVerilogはC言語のテストベンチでCo-Simulation してみたがタイミングチャートにして表示する方法がわからなかった。そこで、ISEのプロジェクトを作って、ISimでシミュレーションしてみた。

ISEでプロジェクトを作り、テストベンチを作った。
HLS_lap_filter_21_131006.png

ISimでのシミュレーション結果、レイテンシは 2クロック、インターバルは 1クロックなのが確認できた。
HLS_lap_filter_22_131006.png

作製したテストベンチ lap_fil_tb.v を下に示す。

`timescale 1ns / 1ps

module lap_fil_tb;

    // Inputs
    reg ap_clk = 1'b0;
    reg ap_rst;
    reg ap_start;
    reg x0y0_ap_vld;
    reg x1y0_ap_vld;
    reg x2y0_ap_vld;
    reg x0y1_ap_vld;
    reg x1y1_ap_vld;
    reg x2y1_ap_vld;
    reg x0y2_ap_vld;
    reg x1y2_ap_vld;
    reg x2y2_ap_vld;
    reg [31:0] x0y0;
    reg [31:0] x1y0;
    reg [31:0] x2y0;
    reg [31:0] x0y1;
    reg [31:0] x1y1;
    reg [31:0] x2y1;
    reg [31:0] x0y2;
    reg [31:0] x1y2;
    reg [31:0] x2y2;

    // Outputs
    wire ap_done;
    wire ap_idle;
    wire ap_ready;
    wire x0y0_ap_ack;
    wire [31:0] ap_return;

    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
            ap_clk = START_STATE;
            #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) ap_clk = ~START_STATE;
            #(CLK_PERIOD*CLK_DUTY_CYCLE);
        end
    end

    // Instantiate the Unit Under Test (UUT)
    laplacian_filter uut (
        .ap_clk(ap_clk), 
        .ap_rst(ap_rst), 
        .ap_start(ap_start), 
        .ap_done(ap_done), 
        .ap_idle(ap_idle), 
        .ap_ready(ap_ready), 
        .x0y0_ap_vld(x0y0_ap_vld), 
        .x1y0_ap_vld(x1y0_ap_vld), 
        .x2y0_ap_vld(x2y0_ap_vld), 
        .x0y1_ap_vld(x0y1_ap_vld), 
        .x1y1_ap_vld(x1y1_ap_vld), 
        .x2y1_ap_vld(x2y1_ap_vld), 
        .x0y2_ap_vld(x0y2_ap_vld), 
        .x1y2_ap_vld(x1y2_ap_vld), 
        .x2y2_ap_vld(x2y2_ap_vld), 
        .x0y0(x0y0), 
        .x0y0_ap_ack(x0y0_ap_ack), 
        .x1y0(x1y0), 
        .x2y0(x2y0), 
        .x0y1(x0y1), 
        .x1y1(x1y1), 
        .x2y1(x2y1), 
        .x0y2(x0y2), 
        .x1y2(x1y2), 
        .x2y2(x2y2), 
        .ap_return(ap_return)
    );

    initial begin
        // Initialize Inputs
        ap_rst = 1;
        ap_start = 0;
        x0y0_ap_vld = 0;
        x1y0_ap_vld = 0;
        x2y0_ap_vld = 0;
        x0y1_ap_vld = 0;
        x1y1_ap_vld = 0;
        x2y1_ap_vld = 0;
        x0y2_ap_vld = 0;
        x1y2_ap_vld = 0;
        x2y2_ap_vld = 0;
        x0y0 = 0;
        x1y0 = 0;
        x2y0 = 0;
        x0y1 = 0;
        x1y1 = 0;
        x2y1 = 0;
        x0y2 = 0;
        x1y2 = 0;
        x2y2 = 0;

        // Wait 100 ns for global reset to finish
        #10;
        
        // Add stimulus here
        @(posedge ap_clk); #1;
        ap_rst = 0;

        @(posedge ap_clk); #1;
        ap_start = 1;
        
        @(posedge ap_clk); #1;
        @(posedge ap_clk); #1;
        @(posedge ap_clk); #1;
        x0y0 = 1;
        x1y0 = 1;
        x2y0 = 1;
        x0y1 = 1;
        x1y1 = 2;
        x2y1 = 1;
        x0y2 = 1;
        x1y2 = 1;
        x2y2 = 1;
        x0y0_ap_vld = 1;
        x1y0_ap_vld = 1;
        x2y0_ap_vld = 1;
        x0y1_ap_vld = 1;
        x1y1_ap_vld = 1;
        x2y1_ap_vld = 1;
        x0y2_ap_vld = 1;
        x1y2_ap_vld = 1;
        x2y2_ap_vld = 1;

        @(posedge ap_clk); #1;
        x0y0 = 0;
        x1y0 = 1;
        x2y0 = 1;
        x0y1 = 1;
        x1y1 = 2;
        x2y1 = 1;
        x0y2 = 1;
        x1y2 = 1;
        x2y2 = 0;

        @(posedge ap_clk); #1;
        x0y0 = 0;
        x1y0 = 1;
        x2y0 = 0;
        x0y1 = 1;
        x1y1 = 2;
        x2y1 = 1;
        x0y2 = 0;
        x1y2 = 1;
        x2y2 = 0;

        @(posedge ap_clk); #1;
        @(posedge ap_clk); #1;
        @(posedge ap_clk); #1;
        @(posedge ap_clk); #1;
        @(posedge ap_clk); #1;
        @(posedge ap_clk); #1;
        @(posedge ap_clk); #1;
        $stop;
    end
      
endmodule


下に、Vivado HLSで生成された laplacian_filter.v のポートマップのみを示す。このVerilog HDLコードを生成したCソースコードは、”Vivado HLSでラプラシアン・フィルタ式のみをaxi lite slaveモジュールにする2”に貼ってある。

// ==============================================================
// RTL generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC
// Version: 2013.2
// Copyright (C) 2013 Xilinx Inc. All rights reserved.
// 
// ===========================================================

`timescale 1 ns / 1 ps 

(* CORE_GENERATION_INFO="laplacian_filter,hls_ip_2013_2,{HLS_INPUT_TYPE=c,HLS_INPUT_FLOAT=0,HLS_INPUT_FIXED=0,HLS_INPUT_PART=xc7z020clg484-1,HLS_INPUT_CLOCK=10.000000,HLS_INPUT_ARCH=pipeline,HLS_SYN_CLOCK=7.320000,HLS_SYN_LAT=2,HLS_SYN_TPT=1,HLS_SYN_MEM=0,HLS_SYN_DSP=0,HLS_SYN_FF=491,HLS_SYN_LUT=578}" *)

module laplacian_filter (
        ap_clk,
        ap_rst,
        ap_start,
        ap_done,
        ap_idle,
        ap_ready,
        x0y0_ap_vld,
        x1y0_ap_vld,
        x2y0_ap_vld,
        x0y1_ap_vld,
        x1y1_ap_vld,
        x2y1_ap_vld,
        x0y2_ap_vld,
        x1y2_ap_vld,
        x2y2_ap_vld,
        x0y0,
        x0y0_ap_ack,
        x1y0,
        x2y0,
        x0y1,
        x1y1,
        x2y1,
        x0y2,
        x1y2,
        x2y2,
        ap_return
);

  1. 2013年10月05日 19:35 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSで作ったラプラシアン・フィルタIPを使ってみる2

Vivado HLSで作ったラプラシアン・フィルタIPを使ってみる1”の続き。

前回、ビットストリームの生成まで出来たので、今回はSDKにハードウェアをエクスポートしてソフトウェアを作る。(今度買ったHQレンズ付きのMT9D111に対応できるようにSCLとSDAをプルアップしてインプリメントし直した)

・ISEでDesign の Hierarchyウインドウ から system_i を選択して、Processesウインドウの Export Hardware Design To SDK with Bitstream をクリックして、ハードウェアとビットストリームをSDKにエクスポートした。
HLS_lap_filter_12_131003.png

・SDKが立ち上がるが落ちてしまった。

・スタートメニューからSDKを上げた。
HLS_lap_filter_13_131003.png

・SDKの hw_platform フォルダのsystem.xml を見ると、laplacian_filter_top_0 が入っていない。
HLS_lap_filter_14_131003.png

・ISEのエクスポートの履歴を見ると hw フォルダにエクスポートしたようだ。

・D:\HDL\FndtnISEWork\Zynq-7000\ZedBoard\Zed_OOB_Design2_HLS\hw\xps_proj\SDK\SDK_Export\hw フォルダを見ると今日の更新日付になっている。
HLS_lap_filter_15_131003.png

・\SDK\SDK_Export\hw フォルダの内容を hw_platform フォルダにコピーすることにした。

・SDKを立ち上げるとエラーになってしまった。

・SDK\SDK_Exportフォルダを削除してから、もう一度、ISEでDesign の Hierarchyウインドウ から system_i を選択して、Processesウインドウの Export Hardware Design To SDK with Bitstream をクリックして、ハードウェアとビットストリームをSDKにエクスポートした。

・SDKが立ち上がった。xps_proj_hw_platform を生成したようだ。今度は、laplacian_filter_top_0 が入っていた。
HLS_lap_filter_16_131003.png

・hwフォルダとxps_proj_hw_platformフォルダの差分を見てみよう。WinMerge を立ちあげて差分を見てみた。
ps7_init.c、ps_init.h, ps_init.html, ps_init.tcl, system.xml だけが、xps_proj_hw_platformフォルダにコピーされているようだ。
HLS_lap_filter_17_131004.png

さて、ここから全部のCのプロジェクトを復旧させよう。

・大体、もとに戻った。
HLS_lap_filter_18_131004.png

今回は、device-tree はとりあえずはやめておこうと思う。ISE14.6のSDKにパッケージを入れてないので、選択できないのだ。アドレスを決め打ちすれば行けると思うので、今回はやめておくことにする。

・Boot.bin を作製する。
HLS_lap_filter_19_131004.png

・FSBL\bootimage に出来たu-boot.bin をBOOT.bin にリネームして、SDカードに書き込んだ。
HLS_lap_filter_20_131004.png

・Linuxが立ちあげて、カメラの画像が見えた。HQレンズ付きのMT9D111も無事にディスプレイに表示することができるようになった。
  1. 2013年10月03日 05:31 |
  2. Co-design
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSで作ったラプラシアン・フィルタIPを使ってみる1

"Vivado HLSでラプラシアン・フィルタ式のみをaxi lite slaveモジュールにする2"でラプラシアン・フィルタの式のみをEDKの pcore として出力したので、XPSのプロジェクトに入れてハードウェア化してLinuxのソフトウェアから使ってみようと思う。

Linux用のISEプロジェクトのバージョンは14.3 だったので、今回、最新のISE14.6 にアップグレードすることにした。
ISE14.6 のプロジェクトを下に示す。
HLS_lap_filter_1_131002.png

バージョン14.3から14.6に変換した現在のXPSを下に示す。
HLS_lap_filter_2_131002.png

・D:\HDL\FndtnISEWork\Zynq-7000\ZedBoard\Zed_OOB_Design2_HLS\hw\xps_proj\pcores に、Vivado HLSで作製した pcore のIP(laplacian_filter_top_v1_00_a) をコピーした。
HLS_lap_filter_3_131002.png

・XPSで、Project メニューからRescan User Repositories を実行した。

・laplacian_filter_top がProject Local PCores に出てきた。それを右クリックメニューからAdd IP を選択した。
HLS_lap_filter_4_131002.png

・ダイアログが出たので、Yes ボタンをクリックした。
HLS_lap_filter_5_131002.png

・XPS Core Config ダイアログが出た。そのまま、OKボタンをクリックした。
HLS_lap_filter_6_131002.png

・Instantiate adn Connect IP ダイアログで、laplacian_filter_top_0 を何処につなぐか設定する。デフォルトのprocessing_system_7_0 のままでOKボタンをクリックした。
HLS_lap_filter_7_131002.png

・XPSプロジェクトにlaplacian_filter_top_0 が入った。
HLS_lap_filter_8_131002.png

・Portsタブの様子。AXI4 Lite Slave バスは接続されていて大丈夫そうだった。
HLS_lap_filter_9_131002.png

・Addressタブをクリックしたら、アドレスがマップされていなかったので、64KBの領域を確保して、アドレスをマップした。
HLS_lap_filter_10_131002.png

・XPSは終了して、ISEに戻ってビットストリームを生成した。例によって論理合成に時間がかかったので、パソコンにやらせて寝てしまった。
・朝起きたらビットストリームの生成が終わっていた。
HLS_lap_filter_11_131003.png
  1. 2013年10月02日 05:30 |
  2. Co-design
  3. | トラックバック:0
  4. | コメント:0
»