2009年12月
PERIOD制約のDCMでの伝搬
その概要を下に引用する。
最もよくあるクロック回路では、入力クロックがDCM/PLL/MMCM に使用され、出力がデバイスの同期パスのクロックに
使用されています。この場合、DCM/PLL/MMCM への入力クロックにPERIOD 制約を定義することをお勧めします。
この入力クロックにPERIOD 制約を付けると、ザイリンクスツールは各DCM/PLL/MMCM 出力クロックに対して新しい
PERIOD 制約を自動的に作成し、出力クロックドメイン間のクロック関係を決定し、これらの同期ドメイン間のパスをす
べて解析します。
図も引用する。この図を見ると一目瞭然。
CLK1Xで動作するFFからCLK2Xで動作するFFのデータパスも、CLKINのPERIOD制約を与えておけば、解析されるはず。良かった。
さてそれでは実際の回路で検証してみることにする。
CMOSカメラからディスプレイ出力回路で、SRAMのWEは48MHzクロックで出力している。24MHzクロックのFFの出力を使用して48MHzのクロック動作のFFで受けている。24MHzクロックにPRIOD制約が掛けてあって、DCMのCLK2Xで48MHzを出力している。下がそのパスのセットアップ時間の解析結果。
上のピンクの四角がソースクロックとディスティネーション・クロック。cam_pclkが24MHzでclk48が48MHzクロックだ。下のClock Path Skewを見ると、1.789 - 5.266 = -3.477ns となっている。他の静的タイミングを見るとcam_pclk の遅延が5.266nsだった。多分、clk48のクロック遅延が1.789ns だと思う。これで、セットアップ時間にクロックスキューが換算されている事がわかった。
ESP企画の旧パージョンボードでのPCLK処理方法
拡大してみると、特に本の白い部分で黒いポチポチが見える。これはほんのちょっとVHDLソースを変えたりすると直ってしまう。詳しくは、”CMOSカメラから画像を入力してディスプレイへ出力15(できた!!!)”参照。これはどうして起こるか確かめてみることにした。
いろいろ原因を考えてみたが、SDRAMのReadとWriteもぶつかっているようでも無い。やはりクロックが臭いのかな?ということで、CMOSカメラからのPCLKは下の図のようにDCMを通しているが、PCLKを直接クロックとして使用することにした。それでもXSTが自動的にBUFGは入れてくれていると思うけど。。。
つまり上図でDCM1/1を通さないで直接、回路のマスタクロックとして使用した。そうしたら、おかしい現象は解消された。下のラプラシアンフィルタの図もなんか緑っぽく、ノイジーだったのが、ノイズが減った。(下図はノイジーな状態)
下に変更点を示す。DCM_module_24MHzのmclk出力からmclkをとらないで、外部入力のcam_pclkから直接mclk へ接続した。mclk が回路全体のクロックである。
DCM_module_24MHz_inst : DCM_module_24MHz port map(
clk48MHz_in => clk,
clk48_out => clk48,
cam_clk_out => cam_clk_node,
pclk_in => cam_pclk,
-- mclk_out => mclk,
mclk_out => open,
cam_clk_locked => cam_clk_locked,
mclk_locked => mclk_locked
);
reset <= not mclk_locked;
mclk <= cam_pclk;
DACクロックもやはり反転して、DACデータの真ん中でクロックを立ち上げた方が良いようだ。
-- DAC用クロックの生成
n_mclk <= not mclk;
ODDR2_for_dac_clk : ODDR2 generic map(
SRTYPE => "ASYNC"
) port map(
Q => dac_clk,
C0 => mclk,
C1 => n_mclk,
CE => '1',
D0 => '0',
D1 => '1',
R => reset,
S => '0'
);
D0 => '0', D1 => '1' で反対になっている。
これで、安心して仕事の回路がデバックできる。
(追記)
このボードはclk入力もpclk入力もGCLKにアサインされていないので、ISE11だと、下のような制約を入れる必要がある。どうにかならないものかな?GCLKにアサインされていれば。。。
NET "cam_pclk" CLOCK_DEDICATED_ROUTE = FALSE;
PIN "DCM_module_24MHz_inst/DCM_pclk.CLKIN" CLOCK_DEDICATED_ROUTE = FALSE;
(もう一度、追記)
新しいESP企画の画像ボードは、ちゃんとクロックがGCLK入力にアサインされていて、PCLKにDCMを使っても問題ないようです。
SCCBインターフェース回路の説明3(One_Transaction_SCCB.vhd)
entity One_Transaction_SCCB is
port(
clk : in std_logic; -- クロック
reset : in std_logic; -- リセット
SCCB_address : in std_logic_vector(7 downto 0); -- SCCBレジスタのアドレス
SCCB_data : in std_logic_vector(7 downto 0); -- SCCBレジスタのデータ
op_enable : in std_logic; -- 動作イネーブル(200KHz, 5usec間隔)
start_pulse : in std_logic; -- スタートパルス(1クロック幅)
end_pulse : out std_logic; -- エンドパルス(1クロック幅)
SCL : out std_logic;
SDA : out std_logic
);
end One_Transaction_SCCB;
start_pulseを入れて、end_pulseが出力されると渡されたアドレスのSCCBレジスタにデータを書き込む操作が終了となる。op_enableはfreqdiv.vhdから出力されたop_enaを接続する。SCLとSDAはCMOSカメラへ。
最初にメインのステートマシン。構造を下に示す。
VHDLコードを下に示す。
-- Main State Machine
process(clk) begin
if clk'event and clk='1' then
if reset='1' then
cs_main <= idle_main;
else
case cs_main is
when idle_main =>
if start_pulse='1' then -- スタートパルスが来たらスタート
cs_main <= start_state;
end if;
when start_state =>
if state_counter=17 and op_enable='1' then -- START ステートの最後で5us のイネーブルの時に遷移
cs_main <= ID_Address;
end if;
when ID_Address =>
if state_counter=17 and op_enable='1' then -- ID_Address ステートの最後で5us のイネーブルの時に遷移
cs_main <= SCCB_Reg_Addr;
end if;
when SCCB_Reg_Addr =>
if state_counter=17 and op_enable='1' then -- SCCB_Reg_Addr ステートの最後で5us のイネーブルの時に遷移
cs_main <= Write_Data;
end if;
when Write_Data =>
if state_counter=17 and op_enable='1' then -- Write_Data ステートの最後で5us のイネーブルの時に遷移
cs_main <= stop_state;
end if;
when stop_state =>
if state_counter=17 and op_enable='1' then -- stop_state ステートの最後で5us のイネーブルの時に遷移
cs_main <= idle_main;
end if;
end case;
end if;
end if;
end process;
state_counterが17でop_enaが1のときに遷移する。state_counter は0~17までカウントするカウンタで、op_enaが1の時にカウントアップする。
次にデータパスのブロック図を再度示す。
SDAはSCLに比べて、必ず遅れるようにFFを2段入れた。SDAの最後のトラーステートバッファのイネーブルは必ずFFの出力とした方が良い。それは、イネーブルが組み合わせ回路だと、どの時点でひげが出て、思わぬ時にONしてしまうかわからないためだ。
下にconstant値と18ビット、9ビットのシフトレジスタのVHDLコードを示す。
constant START_PATTERN_SCL : std_logic_vector := "111111111111111111";
constant IDA_SCCBR_WD_PATTERN_SCL : std_logic_vector := "010101010101010101";
constant STOP_PATTERN_SCL : std_logic_vector := "011111111111111111";
constant START_PATTERN_SDA : std_logic_vector := "111111110";
constant ID_ADDRESS_PATTERN_SDA : std_logic_vector := "010000100";
constant STOP_PATTERN_SDA : std_logic_vector := "011111111";
-- SLC 用18ビット・シフトレジスタ
process(clk) begin
if clk'event and clk='1' then
if reset='1' then
SCL_shift_reg <= (others => '1');
else
case cs_main is
when idle_main =>
if start_pulse='1' then -- スタートパルスが来たらスタート
SCL_shift_reg <= START_PATTERN_SCL;
end if;
when start_state =>
if op_enable='1' then
if state_counter=17 then -- START ステートの最後で5us のイネーブルの時に遷移
SCL_shift_reg <= IDA_SCCBR_WD_PATTERN_SCL;
else
SCL_shift_reg <= SCL_shift_reg(16 downto 0) & '1';
end if;
end if;
when ID_Address =>
if op_enable='1' then
if state_counter=17 then
SCL_shift_reg <= IDA_SCCBR_WD_PATTERN_SCL;
else
SCL_shift_reg <= SCL_shift_reg(16 downto 0) & '1';
end if;
end if;
when SCCB_Reg_Addr =>
if op_enable='1' then
if state_counter=17 then
SCL_shift_reg <= IDA_SCCBR_WD_PATTERN_SCL;
else
SCL_shift_reg <= SCL_shift_reg(16 downto 0) & '1';
end if;
end if;
when Write_Data =>
if op_enable='1' then
if state_counter=17 then
SCL_shift_reg <= STOP_PATTERN_SCL;
else
SCL_shift_reg <= SCL_shift_reg(16 downto 0) & '1';
end if;
end if;
when stop_state =>
if op_enable='1' then
SCL_shift_reg <= SCL_shift_reg(16 downto 0) & '1';
end if;
end case;
end if;
end if;
end process;
-- SDA 用9ビット・シフトレジスタ
process(clk) begin
if clk'event and clk='1' then
if reset='1' then
SDA_shift_reg <= (others => '1');
else
case cs_main is
when idle_main =>
if start_pulse='1' then -- スタートパルスが来たらスタート
SDA_shift_reg <= START_PATTERN_SDA;
end if;
when start_state =>
if op_enable='1' and state_counter(0)='1' then -- 2回に1回遷移
if state_counter=17 then -- START ステートの最後で5us のイネーブルの時に遷移
SDA_shift_reg <= ID_ADDRESS_PATTERN_SDA;
else
SDA_shift_reg <= SDA_shift_reg(7 downto 0) & '1';
end if;
end if;
when ID_Address =>
if op_enable='1' and state_counter(0)='1' then -- 2回に1回遷移
if state_counter=17 then
SDA_shift_reg <= SCCB_address & '1';
else
SDA_shift_reg <= SDA_shift_reg(7 downto 0) & '1';
end if;
end if;
when SCCB_Reg_Addr =>
if op_enable='1' and state_counter(0)='1' then -- 2回に1回遷移
if state_counter=17 then
SDA_shift_reg <= SCCB_data & '1';
else
SDA_shift_reg <= SDA_shift_reg(7 downto 0) & '1';
end if;
end if;
when Write_Data =>
if op_enable='1' and state_counter(0)='1' then -- 2回に1回遷移
if state_counter=17 then
SDA_shift_reg <= STOP_PATTERN_SDA;
else
SDA_shift_reg <= SDA_shift_reg(7 downto 0) & '1';
end if;
end if;
when stop_state =>
if op_enable='1' and state_counter(0)='1' then -- 2回に1回遷移
SDA_shift_reg <= SDA_shift_reg(7 downto 0) & '1';
end if;
end case;
end if;
end if;
end process;
これで大体説明できたと思う。これらのファイルを合わせて、テストベンチを書くと下のようなシミュレーション波形が得られる。
SCCBインターフェース回路の説明2(freqdiv.vhd、SCCB_reg_values_ROM.vhd)
-- Frequncy Divider
-- 200KHz clock
-- マスタークロックを200KHzのop_enaに分周します。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
entity FreqDiv is
generic(
divisor : integer := 125
);
port(
clk, reset : in std_logic;
op_ena : out std_logic
);
end FreqDiv;
architecture RTL of FreqDiv is
signal lcnt : std_logic_vector(15 downto 0);
begin
process(clk) begin -- Enable frequency is 200KHz
if clk'event and clk='1' then
if reset='1' then
lcnt <= (others => '0');
elsif lcnt = conv_std_logic_vector(divisor, 16)-1 then
lcnt <= (others => '0');
else
lcnt <= lcnt + 1;
end if;
end if;
end process;
process(clk) begin
if clk'event and clk='1' then
if lcnt = conv_std_logic_vector(divisor, 16)-1 then
op_ena <= '1';
else
op_ena <= '0';
end if;
end if;
end process;
end RTL;
次は、SCCB_reg_values_ROM.vhd だが、これは、”VHDLでのブロックRAMや分散RAMの初期化(16進数で書かれた外部データファイル)”で説明した大体そのままなのだが、RAMではなくROMになっている。下にVHDLコードを示す。
-- SCCB_reg_values_ROM.vhd
-- SCCBプロトコル・コントローラを実装してSCCBレジスタに書き込む値をセーブしてある ROM。書き込みデータは SCCB_reg_values.data にセーブしておく。
-- SCCB_reg_values.data のフォーマット"1280" 最初の2キャラクタが16進形式のアドレス、次の2キャラクタが16進形式のデータ。例は12番地に80を書く
-- SCCB_reg_values_ROM.data には必ず256行のデータを用意しておく。アドレスがFFの場合はレジスタ・アクセスはそこで終了する。
library IEEE, STD;
use IEEE.std_logic_1164.all;
use STD.textio.all;
use IEEE.std_logic_textio.all;
use IEEE.std_logic_unsigned.all;
use IEEE.std_logic_arith.all;
-- pragma translate_off
library UNISIM;
use UNISIM.VCOMPONENTS.ALL;
-- pragma translate_on
entity SCCB_reg_values_ROM is
port(
clk : in std_logic; -- Clock
address : in std_logic_vector(7 downto 0);
dout : out std_logic_vector(15 downto 0)
);
end SCCB_reg_values_ROM;
architecture RTL of SCCB_reg_values_ROM is
type RamType is array(0 to 255) of std_logic_vector(15 downto 0);
impure function InitRamFromFile (RamFileName : in string) return RamType is
FILE RamFile : text is in RamFileName;
variable RamFileLine : line;
variable RAM : RamType;
begin
for I in RamType'range loop
readline (RamFile, RamFileLine);
hread (RamFileLine, RAM(I));
end loop;
return RAM;
end function;
signal RAM : RamType := InitRamFromFile("SCCB_reg_values.data");
begin
process(clk) begin
if clk'event and clk='1' then
dout <= RAM(conv_integer(address));
end if;
end process;
end RTL;
SCCB_reg_values.dataの一部を下に示す。
ACDF
FF00
0000