Pulse Repeater
室内飛行機基板を新たに作ったわけですが、そこに内蔵されているCPLDのロジックを設計してみました。この記事ではその中の肝と思われる、複数パルスを順番に整列させる部分の話です。パルスをなぜ整列したいのかというのは、前述のリンク先ならびに『ラジコン受信機とマイコンの接続』という記事で察してください。
どのようにその機能を実現したかというと、入力されたパルスの時間長さを計測しておいて、外部から『パルスください』信号がきたときに同じ長さのパルスを出力する下位機能を、カスケード接続することによって実現しました。この下位機能をPulse Repeaterと名づけました。
Pulse Repeaterの解説図やVHDLコードは続きをどうぞ。
※2007/1/21 これだとうまく動かないことがわかり修正しました。コードではwire_in_risenという項目が増えています。
Pulse Repeaterはステートマシンとして実装しました。状態遷移図は上のようになっています。
また各状態に応じた信号は、上のタイミング図に示すとおりです。wire_inがパルス入力、syncが『パルスください』信号、wire_outがパルス出力です。timerは時間を計測するアップダウンカウンタです。
VHDLであらわすとこのようになります。
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
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;
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にしようと思います。
コメント
コメントする
- 匿名でのコメントは受け付けておりません。
- お名前(ハンドル名可)とメールアドレスは必ず入力してください。
- メールアドレスを表示されたくないときはURLも必ず記入してください。
- コメント欄でHTMLタグは使用できません。
- コメント本文に日本語(全角文字)がある程度多く含まれている必要があります。
- コメント欄内のURLと思われる文字列は自動的にリンクに変換されます。
- 投稿ボタンを押してエラーがでなければ、投稿は成功しています。反映されるまでには少し時間がかかります。