FPGAの設定用レジスタについて

FPGAで回路を組んだ場合、基本的には回路に対して何らかの設定を行う必要があります。マイコンやその他ICでも同様ですね。そのような場合”レジスタ”を使用するのですが、FPGAを触り始めたばかりの頃に「ここは設定用にレジスタを用意して…」と言われてピンとこなかった思い出があるので、一度まとめておきます。

“レジスタ”の言葉の意味

Wikipediaで調べると、「状態を保持する装置」と説明されていました(Wikipedia)。”装置”というところがポイントで、フリップフロップ自体もレジスタと呼べますし、複数のフリップフロップを組み合わせて何らかの意味を持たせ、特別な名前を持たせる場合もあります。CPUにはアキュムレータやデータレジスタ、アドレスレジスタと色んなレジスタがありますね。

自分は初め「レジスタってフリップフロップと何が違うの??」と思って混乱した覚えがあります。それ自体は間違っていないのですが、”レジスタ”=”特別な意味を持つ状態保持装置”という意味を持つ場合があって、それが一般的に使用されるということです。

FPGAで使用するような回路設定用レジスタは正にそのような場合に該当します。

メモリマップドレジスタ

回路設定用のレジスタを用意するような場合は、”メモリマップドレジスタ”を使用します。メモリマップドレジスタとは、アドレスとレジスタを対応付けて、メモリのように特定のレジスタへアクセス出来るような仕組みです。

具体例

適当に例を考えてみました。下表のように、アドレスとレジスタの対応表をレジスタマップと呼びます。

アドレス レジスタ 概要
00h Version Register バージョン値
01h GPIO Register 汎用入出力

各レジスタの仕様は次のように決めました。レジスタの中でもビット毎に異なる意味を持つ場合もあります。R/WはRead/Writeの略で、Rだけの場合はRead Only、R/Wはどちらも可能なレジスタです。

  • Version Register
レジスタ ビット R/W 詳細 初期値
Version 7:0 R バージョン値を表示
例:10h = バージョン1.0
10h
  • GPIO Register
レジスタ ビット R/W 詳細 初期値
GPO 7:4 R/W 汎用出力
0: 0出力, 1: 1出力
0h
GPI 3:0 R 汎用入力
0: 0入力, 1: 1入力

ソースコード

決定した仕様を実際にVHDLで記述してみました。レジスタへのアクセスI/Fはこれまた適当です。

  • レジスタブロック
  • library ieee;
    use ieee.std_logic_1164.all;
    use ieee.std_logic_unsigned.all;
    
    entity memory_maped_register is
        port    (
            srstn_i     : in    std_logic;
            clk_i       : in    std_logic;
    
            addr_i      : in    std_logic_vector (7 downto 0);  -- レジスタアドレス
            wr_en_i     : in    std_logic;                      -- ライトイネーブル
            wr_data_i   : in    std_logic_vector (7 downto 0);  -- ライトデータ
            rd_en_i     : in    std_logic;                      -- リードイネーブル
            rd_data_o   : out   std_logic_vector (7 downto 0);  -- リードデータ
    
            gpi_i       : in    std_logic_vector (3 downto 0);  -- 汎用入力信号
            gpo_o       : out   std_logic_vector (3 downto 0)   -- 汎用出力信号
        );
    end memory_maped_register;
    
    architecture rtl of memory_maped_register is
    
    constant version    :   std_logic_vector (7 downto 0) := x"10";
    
    signal gpo      :   std_logic_vector (3 downto 0);
    signal rd_data  :   std_logic_vector (7 downto 0);
    
    begin
    
    -- レジスタライト
    -- 汎用出力レジスタ
    process (clk_i) begin
        if (rising_edge(clk_i)) then
            if (srstn_i = '0') then
                gpo <= (others => '0');
            else
                if ((wr_en_i = '1') and (addr_i = x"01")) then
                    gpo <= wr_data_i(7 downto 4);
                end if;
            end if;
        end if;
    end process;
    
    -- レジスタリード
    process (clk_i) begin
        if (rising_edge(clk_i)) then
            if (srstn_i = '0') then
                rd_data <= (others => '0');
            else
                if (rd_en_i = '1') then
                    case addr_i is
                        when x"00"  =>  rd_data <= version;         -- バージョンレジスタ
                        when x"01"  =>  rd_data <= gpo & gpi_i;     -- 汎用入出力レジスタ
                        when others =>  rd_data <= (others => '0');
                    end case;
                else
                    rd_data <= (others => '0');
                end if;
            end if;
        end if;
    end process;
    
    rd_data_o   <= rd_data;
    gpo_o       <= gpo;
    
    end rtl;
  • テストベンチ
  • library ieee;
    use ieee.std_logic_1164.all;
    use ieee.std_logic_unsigned.all;
    
    entity testbench is
    end testbench;
    
    architecture rtl of testbench is
    
    component memory_maped_register is
        port    (
            srstn_i     : in    std_logic;
            clk_i       : in    std_logic;
    
            addr_i      : in    std_logic_vector (7 downto 0);
            wr_en_i     : in    std_logic;
            wr_data_i   : in    std_logic_vector (7 downto 0);
            rd_en_i     : in    std_logic;
            rd_data_o   : out   std_logic_vector (7 downto 0);
    
            gpi_i       : in    std_logic_vector (3 downto 0);
            gpo_o       : out   std_logic_vector (3 downto 0)
        );
    end component;
    
    signal srstn_i      :   std_logic;
    signal clk_i        :   std_logic;
    signal addr_i       :   std_logic_vector (7 downto 0);
    signal wr_en_i      :   std_logic;
    signal wr_data_i    :   std_logic_vector (7 downto 0);
    signal rd_en_i      :   std_logic;
    signal rd_data_o    :   std_logic_vector (7 downto 0);
    signal gpi_i        :   std_logic_vector (3 downto 0);
    signal gpo_o        :   std_logic_vector (3 downto 0);
    
    begin
    
    process begin
        srstn_i <= '0';
        wait for 100 ns;
        srstn_i <= '1';
        wait;
    end process;
    
    process begin
        clk_i   <= '0';
        wait for 5 ns;
        clk_i   <= '1';
        wait for 5 ns;
    end process;
    
    process begin
        addr_i      <= (others => '0');
        wr_en_i     <= '0';
        wr_data_i   <= (others => '0');
        rd_en_i     <= '0';
        gpi_i       <= x"5";
        wait until (srstn_i = '1');
        wait for 100 ns;
    
        -- 汎用入力レジスタライト
        wait until (rising_edge(clk_i));
        rd_en_i <= '0';
        addr_i  <= x"01";
        wr_en_i <= '1';
        wr_data_i   <= x"a0";
        -- バージョンレジスタリード
        wait until (rising_edge(clk_i));
        wr_en_i <= '0';
        addr_i  <= x"00";
        rd_en_i <= '1';
        -- 汎用入出力レジスタリード
        wait until (rising_edge(clk_i));
        addr_i  <= x"01";
        rd_en_i <= '1';
        wait until (rising_edge(clk_i));
        rd_en_i <= '0';
        wait;
    end process;
    
    u_mmr   :   memory_maped_register
        port map    (
            srstn_i     => srstn_i,     --: in    std_logic;
            clk_i       => clk_i,       --: in    std_logic;
    
            addr_i      => addr_i,      --: in    std_logic_vector (7 downto 0);
            wr_en_i     => wr_en_i,     --: in    std_logic;
            wr_data_i   => wr_data_i,   --: in    std_logic_vector (7 downto 0);
            rd_en_i     => rd_en_i,     --: in    std_logic;
            rd_data_o   => rd_data_o,   --: out   std_logic_vector (7 downto 0);
    
            gpi_i       => gpi_i,       --: in    std_logic_vector (3 downto 0);
            gpo_o       => gpo_o        --: out   std_logic_vector (3 downto 0)
        );
    
    end rtl;

    シミュレーション

    まず汎用出力レジスタライトのシミュレーション結果です。アドレスを"01h"に、ライトデータを"A0h"に設定して、ライトイネーブルをアサート。内部レジスタに値が設定され、汎用出力信号へ反映されます。
    メモリマップドレジスタ ライト

    レジスタリード例です。対応アドレスを設定してリードイネーブルをアサート。レジスタ値がリードデータバスから読み出されます。
    メモリマップドレジスタ リード

    シェアする

    • このエントリーをはてなブックマークに追加

    フォローする