December 16, 2006

Pulse Repeater

室内飛行機基板を新たに作ったわけですが、そこに内蔵されているCPLDのロジックを設計してみました。この記事ではその中の肝と思われる、複数パルスを順番に整列させる部分の話です。パルスをなぜ整列したいのかというのは、前述のリンク先ならびに『ラジコン受信機とマイコンの接続』という記事で察してください。

どのようにその機能を実現したかというと、入力されたパルスの時間長さを計測しておいて、外部から『パルスください』信号がきたときに同じ長さのパルスを出力する下位機能を、カスケード接続することによって実現しました。この下位機能をPulse Repeaterと名づけました。

Pulse Repeaterの解説図やVHDLコードは続きをどうぞ。

※2007/1/21 これだとうまく動かないことがわかり修正しました。コードではwire_in_risenという項目が増えています。

pulse_repeater_states.gif

Pulse Repeaterはステートマシンとして実装しました。状態遷移図は上のようになっています。

pulse_repeater_timing.gif

また各状態に応じた信号は、上のタイミング図に示すとおりです。wire_inがパルス入力、syncが『パルスください』信号、wire_outがパルス出力です。timerは時間を計測するアップダウンカウンタです。

VHDLであらわすとこのようになります。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity PulseRepeater is
    generic (
            BIT_WIDTH : positive := 8
        );
    port (
            clk : in std_logic;
            clear : in std_logic;
            tick : in std_logic;
            sync : in std_logic;
            wire_in : in std_logic;
            wire_out : out std_logic
        );
end PulseRepeater;

architecture Behavioral of PulseRepeater is
    signal timer : std_logic_vector(BIT_WIDTH - 1 downto 0)
        := conv_std_logic_vector(0, BIT_WIDTH);
    type state_type is (READY, LOADING, LOADED, LOADING_RUNNING, RUNNING);
    signal current_state : state_type := READY;
    signal next_state : state_type;
    signal sync_fallen : boolean;
    signal wire_in_risen : boolean;
begin
    wire_out <= '1'
            when (current_state = LOADING_RUNNING or current_state = RUNNING)
        else '0';
    
    process(clk, clear) begin
        if clear = '1' then
            current_state <= READY;
        elsif clk'event and clk = '1' then
            current_state <= next_state;
        end if;
    end process;
    
    process(tick) begin
        if tick'event and tick = '1' then
            case current_state is
                when LOADING => timer <= timer + 1;
                when RUNNING => timer <= timer - 1;
                when READY => timer <= conv_std_logic_vector(0, BIT_WIDTH);
                when others => timer <= timer;
            end case;
        end if;
    end process;

    process(current_state, sync) begin
        if not (current_state = LOADING or current_state = LOADED) then
            sync_fallen <= false;
        elsif sync'event and sync = '0' then
            sync_fallen <= true;
        end if;
    end process;

    process(current_state, sync_fallen, wire_in_risen, timer, wire_in) begin
        case current_state is
            when READY =>
                if wire_in_risen then
                    next_state <= LOADING;
                else
                    next_state <= READY;
                end if;
            when LOADING =>
                if sync_fallen then
                    next_state <= LOADING_RUNNING;
                elsif wire_in = '0' then
                    next_state <= LOADED;
                else
                    next_state <= LOADING;
                end if;
            when LOADED =>
                if sync_fallen then
                    next_state <= RUNNING;
                else
                    next_state <= LOADED;
                end if;
            when LOADING_RUNNING =>
                if wire_in = '0' then
                    next_state <= RUNNING;
                else
                    next_state <= LOADING_RUNNING;
                end if;
            when RUNNING =>
                if timer = 0 then
                    next_state <= READY;
                else
                    next_state <= RUNNING;
                end if;
        end case;
    end process;

    process(current_state, wire_in) begin
        if not (current_state = READY) then
            wire_in_risen <= false;
        elsif wire_in'event and wire_in = '1' then
            wire_in_risen <= true;
        end if;
    end process;
end Behavioral;

補足として、tickはカウンタをアップダウンさせるタイミングを指示するクロックです。tickとclkは同じでも構いませんが、今回はロジック数の少ないCPLDにいれる関係でパルス時間を計測するtimerレジスタのビット数を減らしたかったので、計測間隔を荒くする必要がありました。そのためのtickです。
syncは立下り検出ですが、これをステートマシンのエンコーダの中で書くとエラーになる(実装上の問題でeventアトリビュートは最上位if階層でしか使えない、たとえ文法的に正しくても)ので、ステートエンコーダとは別のprocessで受けています。

最後におまけ話として、実はこれではある状態で正しく動作しません。パルスを出力している際にパルスの入力があった場合、それを計測することができません。タイマーをバッファすることができればこの問題は解消するのですが、その機能を取り込むにはCPLDは回路規模が非常に小さすぎました。
CPLDはやはり使う状況をよく考える必要があることを改めて思い知らされました。基板設計の時点で特に何も考えずにグルーロジックとして載せる場合は、これからはFPGAにしようと思います。

21:48 fenrir が投稿 : 固定リンク | | このエントリーを含むはてなブックマーク | この記事をdel.icio.usでブックマーク | トラックバック
このエントリーのトラックバックURL: https://fenrir.naruoka.org/mt/mt-tb.cgi/536
コメント
コメントする









名前、アドレスを登録しますか?
(次回以降コメント入力が楽になります)
  • 匿名でのコメントは受け付けておりません。
  • 名前(ハンドル名可)とメールアドレスは必ず入力してください。
  • メールアドレスを表示されたくないときはURLも必ず記入してください。
  • コメント欄でHTMLタグは使用できません。
  • コメント本文に日本語(全角文字)がある程度多く含まれている必要があります。
  • コメント欄内のURLと思われる文字列は自動的にリンクに変換されます。