目次
前
次
PLLC(Phase Locked Loop Controller)
CPLD/FPGAを利用して、PLL(Phase Locked Loop)ICをテストする
コントローラを作成します。
PLL_ICを利用すると、周波数逓倍が簡単に実現できます。
周波数逓倍機能を利用して、1kHzから1kHz刻みで200kHzまで
発振させてみます。
PLL_ICには、CMOSシリーズの4046を使います。
4046内部には、位相比較器(Phase Comparator)、
電圧制御発振器(VoltageControlled Oscillator)が含まれています。
基本となる使い方は、下図です。
位相比較器(以下PCと略記)には、基準となるクロックと外部分周器から
の出力クロックを接続します。
電圧制御発振器(以下VCOと略記)の入力には、PCからの出力をフィルタ
を介して、接続します。また、VCOの出力には、外部分周器を接続します。
外部分周器の出力クロックをPCの入力に接続します。
ブロック図作成
動作仕様から、ブロック図を作成します。
4046のPCに、基準クロックと分周器の出力クロックを
与えます。
Clock Generatorで、基準クロックの1kHzを生成します。
CPLDに4MHzのクロックを入力し、4000分周すると1kHzと
なります。
VCOの出力クロックを、分周する回路をClock Dividerで
実現します。
ブロック図で動作を決めたならば、必要な信号を考えます。
- Clock Generatorの出力信号 MCLK
- Clock Dividerの入力信号 SCLK
- Clock Dividerの出力信号 DCLK
- 分周比の指定信号 DCNT
- Clock Dividerの出力制御 OE
entity定義
ブロック図で外部とやりとりする信号が決まれば、entityを定義します。
entity pllx is
Port (
-- system
nRESET : in std_logic;
CLOCK : in std_logic;
-- Divide
DCNT : in std_logic_vector(10 downto 0);
MCLK : out std_logic;
SCLK : in std_logic;
DCLK : out std_logic;
OE : in std_logic--;
);
end pllx;
Clock Dividerの分周比は、1kHz〜200kHzの逓倍なので
8ビットあれば充分ですが、11ビットとして最大2048kHz
まで逓倍できるようにします。
4046のVCOでは、2048kHzまで発振できませんが、74HC4046
を利用すれば、実現可能です。
Clock Generator
システムクロックを4MHzとすると、4000分周すれば
PCに与える1kHzを生成できます。
4000を超えない、2のべき乗の最大値は4096です。
4096を生成するには、バイナリカウンタのビット数を12
とすればよいので、内部にカウンタ用レジスタを用意します。
signal iMCNT : std_logic_vector(11 downto 0) ;
クロック出力のために、1ビットのレジスタを用意し
このレジスタにカウント値により、1か0を設定します。
signal iMCLK : std_logic ;
システムクロックを入力し、バイナリカウンタをインクリメント
します。バイナリカウンタの値により、出力値を決めると分周が
できるので、次のように定義します。
-- master clock
MCLK <= iMCLK ;
-- master clock
process( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iMCNT <= (others => '0') ;
elsif rising_edge( CLOCK ) then
iMCNT <= iMCNT + '1' ;
if ( conv_integer( iMCNT ) = 4000 ) then
iMCNT <= (others => '0') ;
end if ;
end if;
end process;
iMCLK <= '1' when ( conv_integer( iMCNT ) = 0 ) else '0' ;
Clock Divider定義
VCOの出力クロックを入力し、内部カウンタを+1します。
DIPスイッチで設定された値と比較し、一致したならば1を
異なれば0を出力する仕様とします。
DIPスイッチで設定するビット数は、11ビットとしたので
内部カウンタは、11ビットとします。
signal iCNT : std_logic_vector(10 downto 0) ;
クロック出力のために、1ビットのレジスタを用意し
このレジスタにカウント値により、1か0を設定します。
signal iDCLK : std_logic ;
内部カウンタの値とDIPスイッチで指定した値が一致
したとき、カウンタをゼロクリアするのを忘れずに
記述します。
-- divide clock
process( nRESET , SCLK )
begin
if ( nRESET = '0' ) then
iCNT <= (others => '0') ;
elsif rising_edge( SCLK ) then
iCNT <= iCNT + '1' ;
if ( iCNT = DCNT ) then
iCNT <= (others => '0') ;
end if ;
end if;
end process;
iDCLK <= '1' when ( conv_integer(iCNT) = 0 ) else '0' ;
分周クロック出力を制御するピンを用意したので
そのためのコードを記述します。
-- divide clock
DCLK <= iDCLK when ( OE = '1' ) else '0' ;
全ソースコード
ブロック図から各ブロックを定義したので、まとめます。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity pllx is
Port (
-- system
nRESET : in std_logic;
CLOCK : in std_logic;
-- Divide
DCNT : in std_logic_vector(10 downto 0);
MCLK : out std_logic;
SCLK : in std_logic;
DCLK : out std_logic;
OE : in std_logic--;
);
end pllx;
architecture Behavioral of pllx is
signal iMCLK : std_logic ;
signal iMCNT : std_logic_vector(11 downto 0) ;
signal iDCLK : std_logic ;
signal iCNT : std_logic_vector(10 downto 0) ;
begin
-- master clock
MCLK <= iMCLK ;
-- divide clock
DCLK <= iDCLK when ( OE = '1' ) else '0' ;
-- master clock
process( nRESET , CLOCK )
begin
if ( nRESET = '0' ) then
iMCNT <= (others => '0') ;
elsif rising_edge( CLOCK ) then
iMCNT <= iMCNT + '1' ;
if ( conv_integer( iMCNT ) = 4000 ) then
iMCNT <= (others => '0') ;
end if ;
end if;
end process;
iMCLK <= '1' when ( conv_integer( iMCNT ) = 0 ) else '0' ;
-- divide clock
process( nRESET , SCLK )
begin
if ( nRESET = '0' ) then
iCNT <= (others => '0') ;
elsif rising_edge( SCLK ) then
iCNT <= iCNT + '1' ;
if ( iCNT = DCNT ) then
iCNT <= (others => '0') ;
end if ;
end if;
end process;
iDCLK <= '1' when ( conv_integer(iCNT) = 0 ) else '0' ;
-- divide clock
DCLK <= iDCLK when ( OE = '1' ) else '0' ;
end Behavioral;
CPLD選定
手元にあるCPLDボードには、XC9572が実装されています。
XC9572に2ブロックが入るかを試してみると、収納できました。
同じピン配置で同一サイズのXC9536では、マクロセルの割当が
できず、収納できませんでした。
他にあるCool RunnerII(256マクロセル品)では、マクロセル数が
XC9572より多いので、楽に入りました。
マクロセル数が、72以上のCPLDを利用すればよいでしょう。
別のソースコード
CPLD、FPGAは複数のクロック入力を持っているので
基準クロックとVCOの分周クロックを別に設定する
構成を考えました。
VCOからフィードバックしてくるクロックを
最大9999分周できるようにしました。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity plltst is
generic (
TOPX : integer := 13 ;
RMAX : integer := 5888 -- ; 11.776MHz => 1kHz
);
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ;
-- VCO
CLKX : in std_logic ;
-- divider
DIVX : in std_logic_vector(13 downto 0) ;
-- controlo out
ACLK : out std_logic ;
BCLK : out std_logic ;
MCLK : out std_logic -- ;
);
end plltst;
architecture Behavioral of plltst is
-- component clock generator
component clkgenx is
generic (
TOPX : integer ;
RMAX : integer --;
);
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ;
-- output
CLKOUT : out std_logic -- ;
);
end component ;
-- clock
signal iMCLK : std_logic ;
-- divider
signal iDIVX : std_logic_vector(13 downto 0) ;
signal iCNTVX : std_logic_vector(13 downto 0) ;
signal iBCLK : std_logic ;
begin
-- component clock generator (11.776MHz/5888 = 2kHz)
XCLKX : clkgenx generic map (TOPX,RMAX) port map (nRESET,CLOCK,iMCLK);
-- input
iDIVX <= DIVX ;
-- output
MCLK <= not iMCLK ;
ACLK <= iMCLK ;
BCLK <= iBCLK ;
-- counter
process (nRESET,CLKX)
begin
if ( nRESET = '0' ) then
iCNTVX <= (others => '0') ;
iBCLK <= '0' ;
elsif rising_edge(CLKX) then
if ( iCNTVX = iDIVX ) then
iCNTVX <= (others => '0') ;
iBCLK <= '1' ;
else
iCNTVX <= iCNTVX + '1' ;
iBCLK <= '0' ;
end if ;
end if ;
end process ;
end Behavioral;
目次
前
次