目次

DDS(Digital Direct synthesis)

 DDS(Digital Direct synthesis)は、波形ROMとカウンタで  デジタル回路でアナログ波形を出力する回路方式です。  カウンタとROMで、カウンタの加算値を可変に  すると、出力波形の周波数を可変にします。  カウンタに与えるクロック周波数は固定です。  カウンタは、位相アキュムレータと呼ばれ  加算値をアドレスとしてROMに与えます。  ROMの内容を変えると、任意の波形を出力できます。  ROMのデータは、VHDLでは次のように定義します。 subtype DTYPE is std_logic_vector(7 downto 0) ; type SINTABLE is array(0 to 63) of DTYPE ; constant SINDATA : SINTABLE := ( X"7F",X"82",X"85",X"88",X"8B",X"8E",X"91",X"94", -- 0 X"97",X"9B",X"9E",X"A1",X"A4",X"A7",X"AA",X"AD", -- 1 X"AF",X"B2",X"B5",X"B8",X"BB",X"BE",X"C0",X"C3", -- 2 X"C6",X"C8",X"CB",X"CD",X"D0",X"D2",X"D4",X"D7", -- 3 X"D9",X"DB",X"DD",X"DF",X"E1",X"E3",X"E5",X"E7", -- 4 X"E9",X"EB",X"EC",X"EE",X"EF",X"F1",X"F2",X"F4", -- 5 X"F5",X"F6",X"F7",X"F8",X"F9",X"FA",X"FB",X"FB", -- 6 X"FC",X"FD",X"FD",X"FE",X"FE",X"FE",X"FE",X"FE" -- 7 ) ;  正弦波のような対称性をもった波形では  0度〜90度の範囲のデータを持っていれば  カウンタ値により、出力値を加工すれば  チップ内部のROMサイズを減らせます。  1波形が0度〜360度として、90度までの  データを持っている場合、次のように変形  します。 iDDSOUT <= SINDATA(127-iCNT) when ( iCNT > 63 and iCNT < 128 ) else -- pi/ 2 => pi ((not SINDATA(iCNT-128))-X"01") when ( iCNT > 127 and iCNT < 192 ) else -- pi => 3pi / 2 ((not SINDATA(255-iCNT))-X"01") when ( iCNT > 191 ) else -- 3pi/2 => 2pi SINDATA(iCNT) ;  正弦波では、次の赤領域の値を使います。  R-2RラダーD/Aコンバータは、次のように構成します。  位相アキュムレータは16ビットとして、外部から  与える加算値は、10ビットとします。  16ビットの上位8ビットを、ROMのアドレスと  します。  仕様を決めると、位相アキュムレータの  VHDLコードは簡単です。 process (nRESET,iPCLK) begin if ( nRESET = '0' ) then iACCX <= (others => '0') ; elsif rising_edge(iPCLK) then iACCX <= iACCX + ("000000" & iACCD) ; end if ; end process ; iCNT <= conv_integer(iACCX(15 downto 8)) ;  まとめると、全体は以下となります。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity dds0 is generic ( TOPX : integer := 3 ; RMAX : integer := 7 --; ); port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- fdiv ACCD : in std_logic_vector(9 downto 0) ; -- dds out LED : out std_logic ; DDSOUT : out std_logic_vector(7 downto 0) -- ; ) ; end dds0; architecture Behavioral of dds0 is -- component 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 iPCLK : std_logic ; -- fdiv signal iACCX : std_logic_vector(15 downto 0); signal iACCD : std_logic_vector( 9 downto 0); signal iDDSOUT : std_logic_vector(7 downto 0) ; signal iCNT : integer range 0 to 255 ; -- ROM subtype DTYPE is std_logic_vector(7 downto 0) ; type SINTABLE is array(0 to 63) of DTYPE ; constant SINDATA : SINTABLE := ( X"7F",X"82",X"85",X"88",X"8B",X"8E",X"91",X"94", -- 0 X"97",X"9B",X"9E",X"A1",X"A4",X"A7",X"AA",X"AD", -- 1 X"AF",X"B2",X"B5",X"B8",X"BB",X"BE",X"C0",X"C3", -- 2 X"C6",X"C8",X"CB",X"CD",X"D0",X"D2",X"D4",X"D7", -- 3 X"D9",X"DB",X"DD",X"DF",X"E1",X"E3",X"E5",X"E7", -- 4 X"E9",X"EB",X"EC",X"EE",X"EF",X"F1",X"F2",X"F4", -- 5 X"F5",X"F6",X"F7",X"F8",X"F9",X"FA",X"FB",X"FB", -- 6 X"FC",X"FD",X"FD",X"FE",X"FE",X"FE",X"FE",X"FE" -- 7 ) ; begin -- clock generator CLKX : clkgenx generic map (TOPX,RMAX) port map (nRESET,CLOCK,iPCLK); -- input iACCD <= ACCD ; LED <= not iPCLK ; -- output DDSOUT <= iDDSOUT ; -- internal iDDSOUT <= SINDATA(127-iCNT) when ( iCNT > 63 and iCNT < 128 ) else -- pi/ 2 => pi ((not SINDATA(iCNT-128))-X"01") when ( iCNT > 127 and iCNT < 192 ) else -- pi => 3pi / 2 ((not SINDATA(255-iCNT))-X"01") when ( iCNT > 191 ) else -- 3pi/2 => 2pi SINDATA(iCNT) ; -- phase accumulator process (nRESET,iPCLK) begin if ( nRESET = '0' ) then iACCX <= (others => '0') ; elsif rising_edge(iPCLK) then iACCX <= iACCX + ("000000" & iACCD) ; end if ; end process ; iCNT <= conv_integer(iACCX(15 downto 8)) ; end Behavioral;  CPLDのCoolRunnerII(XC2C256)に入れる場合  ピンアサインは、以下のようにしました。 # system NET "CLOCK" LOC = "P38" ; NET "nRESET" LOC = "P143" ; NET "LED" LOC = "P92" ; # output group A (J4) NET "ACCD<0>" LOC = "P142" ; NET "ACCD<1>" LOC = "P140" ; NET "ACCD<2>" LOC = "P139" ; NET "ACCD<3>" LOC = "P138" ; NET "ACCD<4>" LOC = "P137" ; NET "ACCD<5>" LOC = "P136" ; NET "ACCD<6>" LOC = "P135" ; NET "ACCD<7>" LOC = "P134" ; # output group B (J4) NET "ACCD<8>" LOC = "P133" ; NET "ACCD<9>" LOC = "P132" ; # output group C (J4) NET "DDSOUT<0>" LOC = "P124" ; NET "DDSOUT<1>" LOC = "P121" ; NET "DDSOUT<2>" LOC = "P120" ; NET "DDSOUT<3>" LOC = "P119" ; NET "DDSOUT<4>" LOC = "P118" ; NET "DDSOUT<5>" LOC = "P117" ; NET "DDSOUT<6>" LOC = "P116" ; NET "DDSOUT<7>" LOC = "P115" ;  正弦波波形のROMデータを生成するには、Tcl/Tkの  スクリプトを利用しました。 # generate data set pi [expr atan(1)*4] set tmp 0 for {set i 0} {$i < 128} {incr i} { set tmp [expr 128*sin($i*$pi/128)] set sdat($i) [expr int($tmp)] } # send code set outbuf "" for {set i 0} {$i < 256} {incr i} { if { $i > 127 } { set j [expr 255 - $i] set tmp [expr 128 - $sdat($j)] } else { set j $i set tmp [expr $sdat($j)+127] } set outs [format "X\"%02X\"" $tmp] set outbuf "$outbuf$outs," if { [expr $i % 8] == 7 } { puts $outbuf set outbuf "" } }  このスクリプトが生成するデータを、I/Oリダイレクトで  テキストファイルに格納すると、次のようになります。 X"7F",X"82",X"85",X"88",X"8B",X"8E",X"91",X"94", X"97",X"9B",X"9E",X"A1",X"A4",X"A7",X"AA",X"AD", X"AF",X"B2",X"B5",X"B8",X"BB",X"BE",X"C0",X"C3", X"C6",X"C8",X"CB",X"CD",X"D0",X"D2",X"D4",X"D7", X"D9",X"DB",X"DD",X"DF",X"E1",X"E3",X"E5",X"E7", X"E9",X"EB",X"EC",X"EE",X"EF",X"F1",X"F2",X"F4", X"F5",X"F6",X"F7",X"F8",X"F9",X"FA",X"FB",X"FB", X"FC",X"FD",X"FD",X"FE",X"FE",X"FE",X"FE",X"FE", X"FF",X"FE",X"FE",X"FE",X"FE",X"FE",X"FD",X"FD", X"FC",X"FB",X"FB",X"FA",X"F9",X"F8",X"F7",X"F6", X"F5",X"F4",X"F2",X"F1",X"EF",X"EE",X"EC",X"EB", X"E9",X"E7",X"E5",X"E3",X"E1",X"DF",X"DD",X"DB", X"D9",X"D7",X"D4",X"D2",X"D0",X"CD",X"CB",X"C8", X"C6",X"C3",X"C0",X"BE",X"BB",X"B8",X"B5",X"B2", X"AF",X"AD",X"AA",X"A7",X"A4",X"A1",X"9E",X"9B", X"97",X"94",X"91",X"8E",X"8B",X"88",X"85",X"82", X"7D",X"7A",X"77",X"74",X"71",X"6E",X"6B",X"68", X"64",X"61",X"5E",X"5B",X"58",X"55",X"52",X"50", X"4D",X"4A",X"47",X"44",X"41",X"3F",X"3C",X"39", X"37",X"34",X"32",X"2F",X"2D",X"2B",X"28",X"26", X"24",X"22",X"20",X"1E",X"1C",X"1A",X"18",X"16", X"14",X"13",X"11",X"10",X"0E",X"0D",X"0B",X"0A", X"09",X"08",X"07",X"06",X"05",X"04",X"04",X"03", X"02",X"02",X"01",X"01",X"01",X"01",X"01",X"00", X"01",X"01",X"01",X"01",X"01",X"02",X"02",X"03", X"04",X"04",X"05",X"06",X"07",X"08",X"09",X"0A", X"0B",X"0D",X"0E",X"10",X"11",X"13",X"14",X"16", X"18",X"1A",X"1C",X"1E",X"20",X"22",X"24",X"26", X"28",X"2B",X"2D",X"2F",X"32",X"34",X"37",X"39", X"3C",X"3F",X"41",X"44",X"47",X"4A",X"4D",X"50", X"52",X"55",X"58",X"5B",X"5E",X"61",X"64",X"68", X"6B",X"6E",X"71",X"74",X"77",X"7A",X"7D",X"80",  テキストデータになっているので、VHDLコードの  中にCopy and Paste後、必要となる記述を付加し  ROMデータにしています。
NCO(Neumerial Controlled Oscillator)  DDSを構成するときに、NCOの処理を理解していると  実装が楽になります。  手持ちのXC9572と8kバイトEEROMを利用したNCOを  作成してみます。  ブロック図は、DDSと同じです。  ROMのアドレス出力と位相アキュムレータの加算値入力を  持つ仕様とします。  位相アキュムレータのサイズは、16ビットとして  加算値は最大12ビットとします。  位相アキュムレータの加算値は、クロックに同期で  入力します。 process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iDI <= (others => '0') ; elsif rising_edge( CLOCK ) then iDI <= DI ; end if ; end process ;  位相アキュムレータの処理は、単純にひとつ前の状態値に  加算値を加えるだけにします。 process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iSUM <= (others => '0') ; elsif rising_edge( CLOCK ) then iSUM <= iSUM + ("0000" & iDI) ; end if ; end process ;  位相アキュムレータの上位8ビットをROMのアドレスと  するので、切り出して出力します。 ROMADR <= iSUM(15 downto 8) ;  必要な内容を揃えたのでVHDLコードにまとめます。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity nco is port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- data input DI : in std_logic_vector(11 downto 0) ; -- output ROMADR : out std_logic_vector(7 downto 0) --; ); end nco ; architecture behavioral of nco is signal iSUM : std_logic_vector(15 downto 0) ; signal iDI : std_logic_vector(11 downto 0) ; begin -- output ROMADR <= iSUM(15 downto 8) ; -- input process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iDI <= (others => '0') ; elsif rising_edge( CLOCK ) then iDI <= DI ; end if ; end process ; -- counter process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iSUM <= (others => '0') ; elsif rising_edge( CLOCK ) then iSUM <= iSUM + ("0000" & iDI) ; end if ; end process ; end behavioral;  ピンアサインは、以下としました。 # system NET "nRESET" LOC = "P39" ; NET "CLOCK" LOC = "P5" ; # ROM address NET "ROMADR<0>" LOC = "P1" ; NET "ROMADR<1>" LOC = "P2" ; NET "ROMADR<2>" LOC = "P3" ; NET "ROMADR<3>" LOC = "P4" ; NET "ROMADR<4>" LOC = "P6" ; NET "ROMADR<5>" LOC = "P7" ; NET "ROMADR<6>" LOC = "P8" ; NET "ROMADR<7>" LOC = "P9" ; # offset NET "DI<0>" LOC = "P11" ; NET "DI<1>" LOC = "P12" ; NET "DI<2>" LOC = "P13" ; NET "DI<3>" LOC = "P14" ; NET "DI<4>" LOC = "P18" ; NET "DI<5>" LOC = "P19" ; NET "DI<6>" LOC = "P20" ; NET "DI<7>" LOC = "P22" ; NET "DI<8>" LOC = "P24" ; NET "DI<9>" LOC = "P25" ; NET "DI<10>" LOC = "P26" ; NET "DI<11>" LOC = "P27" ;  XC9572のマクロセル消費量は、41でした。  最大72マクロセルで、7割近くの消費量  なので、ROMデータを複数持てるFPGAでは  3あるいは4程度のNCOを入れられると  考えられます。


目次 inserted by FC2 system