Skip to content

Latest commit

 

History

History
455 lines (283 loc) · 20.1 KB

07_merge_sort_node_single.md

File metadata and controls

455 lines (283 loc) · 20.1 KB

VHDL で書くマージソーター(シングルワード マージソート ノード)

はじめに

別記事 「はじめに」 を参照してください。

この記事では、マージソートツリーの1要素のマージソートノードについて説明します。なお、この記事で説明するマージソートノードは、シングルワード版です。より高速なマルチワード版は次回(「マルチワード マージソート ノード」)で説明します。

マージソートノードとは

マージソートノードは2ワードの入力を受け付けて、「ワード比較器」 の比較結果をもとにどちらかのワードを出力します。「FPGAを使って基本的なアルゴリズムのソーティングを劇的に高速化(1)」 ではマージロジック回路として紹介されていますが、これと同じものです。

複数のマージソートノードを用いてマージソートツリーを構成します。例えば下図のように4-way マージソートツリーは3つのマージソートノードで構成されます。

Fig.1 マージソートノードと 4-way マージソートツリー

Fig.1 マージソートノードと 4-way マージソートツリー


マージソートノードのアーキテクチャ

入出力

マージソートノードは次図のような入出力ポートを持っています。

Fig.2 マージソートノードの入出力

Fig.2 マージソートノードの入出力


A_WORD、B_WORD、O_WORD は[ワードの定義]で指定されたワードのビット数×ジェネリック変数 WORDS で指定されるワード数のビット配列です。

A_INFO、B_INFO、O_INFO はワードではないその他の情報です。マージソートノード内で使用されることはありません。

データの入出力は AXI4-Stream と同じ 「VALID-then-READY」 プロトコルで行います。「VALID-then-READY」プロトコルは、VALID 信号と READY 信号によるハンドシェイクでデータの転送を行います。マージソートツリーおよびマージソートノードでは「VALID-then-READY」 により複数のワードをパイプライン的に処理します。

A_LAST、B_LAST、O_LAST は一連のワード列の最後のワードであることを示します。

状態遷移

マージソートノードは、内部で次のような状態を持ちます。

  • COMP_STATE はAとBからの入力ワードを比較する状態
  • A_SEL_STATEは A を選択している状態
  • B_SEL_STATEは B を選択している状態
  • A_FLUSH_STATEはB入力が終了してA入力のみ受け付ける状態
  • B_FLUSH_STATEはA入力が終了してB入力のみ受け付ける状態

COMP_STATE で Word_Compare によりA が選択されると一時的に A_SEL_STATE に遷移します。後段の準備が出来ていなかった場合は、A_SEL_STATE の状態を維持します。後段の準備が出来ていた場合でA_LAST='0' の時は、すぐにCOMP_STATE に戻ります。

COMP_STATE で Word_Compare によりB が選択されると一時的に B_SEL_STATE に遷移します。後段の準備が出来ていなかった場合は、B_SEL_STATE の状態を維持します。後段の準備が出来ていた場合でB_LAST='0'の時は、すぐにCOMP_STATE に戻ります。

Fig.3 マージソートノードの状態遷移(1)

Fig.3 マージソートノードの状態遷移(1)


A_SEL_STATE で A を選択している状態時、後段の準備が出来ていた場合でA_LAST='1' の時は B_FLUSH_STATE に遷移します。

B_FLUSH_STATE は B_LAST='1' が入力されるまで B からの入力を後段に出力し続けます。B_LAST='1' が入力されてBからの最後のワードが後段に出力されると COMP_STATE に戻ります。

Fig.4 マージソートノードの状態遷移(2)

Fig.4 マージソートノードの状態遷移(2)


B_SEL_STATE で B を選択している状態時、後段の準備が出来ていた場合でB_LAST='1' の時は A_FLUSH_STATE に遷移します。

A_FLUSH_STATE は A_LAST='1' が入力されるまで A からの入力を後段に出力し続けます。A_LAST='1' が入力されてAからの最後のワードが後段に出力されると COMP_STATE に戻ります。

Fig.5 マージソートノードの状態遷移(3)

Fig.5 マージソートノードの状態遷移(3)


マージソートノードの VHDL 記述

Entity

「ワードの定義」で説明したパラメータを ジェネリック変数 WORD_PARAM に設定します。

同時に処理するワード数を ジェネリック変数WORDS に設定します。この記事ではシングルワード版のみの説明なので、WORDS=1 の場合のみ紹介します。

ジェネリック変数 SORT_ORDER は昇順か降順かを指定します。

library ieee;
use     ieee.std_logic_1164.all;
library Merge_Sorter;
use     Merge_Sorter.Word;
entity  Merge_Sorter_Node is
    generic (
        WORD_PARAM  :  Word.Param_Type := Word.Default_Param;
        WORDS       :  integer :=  1;
        INFO_BITS   :  integer :=  1;
        SORT_ORDER  :  integer :=  0
    );
    port (
        CLK         :  in  std_logic;
        RST         :  in  std_logic;
        CLR         :  in  std_logic;
        A_WORD      :  in  std_logic_vector(WORDS*WORD_PARAM.BITS-1 downto 0);
        A_INFO      :  in  std_logic_vector(            INFO_BITS-1 downto 0) := (others => '0');
        A_LAST      :  in  std_logic;
        A_VALID     :  in  std_logic;
        A_READY     :  out std_logic;
        B_WORD      :  in  std_logic_vector(WORDS*WORD_PARAM.BITS-1 downto 0);
        B_INFO      :  in  std_logic_vector(            INFO_BITS-1 downto 0) := (others => '0');
        B_LAST      :  in  std_logic;
        B_VALID     :  in  std_logic;
        B_READY     :  out std_logic;
        O_WORD      :  out std_logic_vector(WORDS*WORD_PARAM.BITS-1 downto 0);
        O_INFO      :  out std_logic_vector(            INFO_BITS-1 downto 0);
        O_LAST      :  out std_logic;
        O_VALID     :  out std_logic;
        O_READY     :  in  std_logic
    );
end Merge_Sorter_Node;

Architecture

マージソートノードでは以下のライブラリパッケージを使用します。

library ieee;
use     ieee.std_logic_1164.all;
library Merge_Sorter;
use     Merge_Sorter.Word;
use     Merge_Sorter.Sorting_Network;
use     Merge_Sorter.OddEven_MergeSort_Network;
use     Merge_Sorter.Core_Components.Word_Compare;
use     Merge_Sorter.Core_Components.Word_Queue;
use     Merge_Sorter.Core_Components.Sorting_Network_Core;
library PipeWork;
use     PipeWork.Components.REDUCER;

内部で使用する信号とステートマシンの定義です。

architecture RTL of Merge_Sorter_Node is
    signal    i_word            :  std_logic_vector(WORDS*WORD_PARAM.BITS-1 downto 0);
    signal    i_info            :  std_logic_vector(            INFO_BITS-1 downto 0);
    signal    i_sel_a           :  std_logic;
    signal    i_last            :  std_logic;
    signal    i_valid           :  std_logic;
    signal    i_ready           :  std_logic;
    type      STATE_TYPE        is (IDLE_STATE , COMP_STATE   ,
                                    A_SEL_STATE, A_FLUSH_STATE, 
                                    B_SEL_STATE, B_FLUSH_STATE
                                );
    signal    curr_state        :  STATE_TYPE;
    signal    next_state        :  STATE_TYPE;
    signal    temp_state        :  STATE_TYPE;
    signal    comp_valid        :  std_logic;
    signal    comp_ready        :  std_logic;
    signal    comp_sel_a        :  std_logic;
    signal    comp_sel_b        :  std_logic;
begin

A_WORD と B_WORD の比較を Word_Comapre(「ワード比較器」)で行います。Word_Compare への有効信号(comp_valid信号)は現在のステートが COMP_STATE かつ、A入力側のVALID信号(A_VALID信号) が '1' かつ B入力側のVALID信号(B_VALID信号) が '1' の時にアサートします。

    COMP: Word_Compare                                         --
        generic map(                                           --
            WORD_PARAM  => WORD_PARAM                        , -- 
            SORT_ORDER  => SORT_ORDER                          -- 
        )                                                      -- 
        port map (                                             --
            CLK         => CLK                               , -- In  :
            RST         => RST                               , -- In  :
            CLR         => CLR                               , -- In  :
            A_WORD      => A_WORD(WORD_PARAM.BITS-1 downto 0), -- In  :
            B_WORD      => B_WORD(WORD_PARAM.BITS-1 downto 0), -- In  :
            VALID       => comp_valid                        , -- In  :
            READY       => comp_ready                        , -- Out :
            SEL_A       => comp_sel_a                        , -- Out :
            SEL_B       => comp_sel_b                          -- Out :
        );                                                     -- 
    comp_valid <= '1' when (curr_state = COMP_STATE and A_VALID = '1' and B_VALID = '1') else '0';

現在の状態(curr_state 信号) が COMP_STATE の時のみ、Word_Compare の結果を一時的な状態(temp_state) に反映します。現在の状態(curr_state 信号) が COMP_STATE 以外の時は一時的な状態(temp_state) は現在の状態(curr_state信号) を反映します。

    process (curr_state, comp_ready, comp_sel_b) begin
        case curr_state is
            when COMP_STATE =>
                if   (comp_ready = '1') then
                    if (comp_sel_b = '1') then
                        temp_state <= B_SEL_STATE;
                    else
                        temp_state <= A_SEL_STATE;
                    end if;
                else
                        temp_state <= COMP_STATE;
                end if;
            when others =>
                        temp_state <= curr_state;
        end case;
    end process;

一時的な状態(temp_state) から次に遷移する状態(next_state) を決めます。後段の準備が出来ていない時(i_ready='0'の時)は、次に遷移する状態(next_state)は一時的な状態(temp_state)から変わりません。

後段の準備が出来ている時(i_ready='1'の時)は、A_LAST信号またはB_LAST信号の状態によって次に遷移する状態(next_state)が変わります。

    process (temp_state, A_VALID, A_LAST, B_VALID, B_LAST, i_ready) begin
        case temp_state is
            when A_SEL_STATE =>
                if    (i_ready = '1' and A_VALID = '1' and A_LAST = '1') then
                    next_state <= B_FLUSH_STATE;
                elsif (i_ready = '1' and A_VALID = '1' and A_LAST = '0') then
                    next_state <= COMP_STATE;
                else
                    next_state <= A_SEL_STATE;
                end if;
            when B_SEL_STATE =>
                if    (i_ready = '1' and B_VALID = '1' and B_LAST = '1') then
                    next_state <= A_FLUSH_STATE;
                elsif (i_ready = '1' and B_VALID = '1' and B_LAST = '0') then
                    next_state <= COMP_STATE;
                else
                    next_state <= B_SEL_STATE;
                end if;
            when A_FLUSH_STATE =>
                if    (i_ready = '1' and A_VALID = '1' and A_LAST = '1') then
                    next_state <= COMP_STATE;
                else
                    next_state <= A_FLUSH_STATE;
                end if;
            when B_FLUSH_STATE =>
                if    (i_ready = '1' and B_VALID = '1' and B_LAST = '1') then
                    next_state <= COMP_STATE;
                else
                    next_state <= B_FLUSH_STATE;
                end if;
            when COMP_STATE  =>
                    next_state <= COMP_STATE;
            when others =>
                    next_state <= COMP_STATE;
        end case;
    end process;

クロックで次に遷移する状態(next_state)を現在の状態(curr_state)に反映します。

    process (CLK, RST) begin
        if (RST = '1') then
                curr_state <= IDLE_STATE;
        elsif (CLK'event and CLK = '1') then
            if (CLR = '1') then
                curr_state <= IDLE_STATE;
            else
                curr_state <= next_state;
            end if;
        end if;
    end process;

A入力側への応答(A_READY信号)は、 temp_state が A_SEL_STATE(比較結果がSEL_Aの時) または A_FLUSH_STATE(B入力が終了してA入力のみ受け付ける状態) に後段からのREADY信号(i_ready信号) の値を反映します。

同様にB入力側への応答(B_READY信号)は、 temp_state が B_SEL_STATE(比較結果がSEL_Bの時) または B_FLUSH_STATE(A入力が終了してB入力のみ受け付ける状態) に後段からのREADY信号(i_ready信号) の値を反映します。

    A_READY <= '1' when (temp_state = A_SEL_STATE   and i_ready = '1') or
                        (temp_state = A_FLUSH_STATE and i_ready = '1') else '0';
    B_READY <= '1' when (temp_state = B_SEL_STATE   and i_ready = '1') or
                        (temp_state = B_FLUSH_STATE and i_ready = '1') else '0';

後段への VALID 信号(i_valid 信号) は temp_state が A_SEL_STATE(比較結果がSEL_Aの時) または A_FLUSH_STATE(B入力が終了してA入力のみ受け付ける状態) の時は、A入力側からのVALID信号(A_VALID信号) の値を反映します。

または、temp_state が B_SEL_STATE(比較結果がSEL_Bの時) または B_FLUSH_STATE(A入力が終了してB入力のみ受け付ける状態) の時は、B入力側からのVALID信号(B_VALID信号) の値を反映します。

    i_valid <= '1' when (temp_state = A_SEL_STATE   and A_VALID = '1') or
                        (temp_state = A_FLUSH_STATE and A_VALID = '1') or
                        (temp_state = B_SEL_STATE   and B_VALID = '1') or
                        (temp_state = B_FLUSH_STATE and B_VALID = '1') else '0';

後段へのLAST信号(i_last信号) は、temp_state が A_FLUSH_STATE(B入力が終了してA入力のみ受け付ける状態)の時に A入力側のLAST信号(A_LAST信号) がアサートされた時、またはtemp_state が B_FLUSH_STATE(A入力が終了してB入力のみ受け付ける状態)の時に B入力側のLAST信号(B_LAST信号) がアサートされた時にアサートします。

    i_last  <= '1' when (temp_state = A_FLUSH_STATE and A_LAST  = '1') or
                        (temp_state = B_FLUSH_STATE and B_LAST  = '1') else '0';

後段への選択信号(i_sel_a信号)、ワード(i_word信号)、その他の情報(i_info) は次のように出力します。

    i_sel_a <= '1' when (temp_state = A_SEL_STATE  ) or
                        (temp_state = A_FLUSH_STATE) else '0';
    i_word  <= B_WORD when (temp_state = B_SEL_STATE  ) or
                           (temp_state = B_FLUSH_STATE) else A_WORD;
    i_info  <= B_INFO when (temp_state = B_SEL_STATE  ) or
                           (temp_state = B_FLUSH_STATE) else A_INFO;

シングルワード(WORDS=1)の場合は、後段への出力(i_word信号、i_info信号、i_last信号、i_valid信号) はそのままマージソートノードの後段への出力(O_WORD信号、O_INFO信号、O_LAST信号、O_VALID信号) となります。

    SINGLE_WORD: if (WORDS = 1) generate
        O_WORD  <= i_word;
        O_INFO  <= i_info;
        O_LAST  <= i_last;
        O_VALID <= i_valid;
        i_ready <= O_READY;
    end generate;

マルチワード(WORDS>1)の場合は、次回のマルチワード マージソート ノードで説明します。

    MULTI_WORD: if (WORDS > 1) generate
		-- 次回説明 --
    end generate;
end RTL;

参照