Main Content

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

FPGA 用のデジタル ダウン コンバーターの実装

この例では、LTE などの無線通信用途のためにデジタル ダウン コンバーター (DDC) を設計して、HDL コードを生成する方法を示します。

はじめに

DDC は、無線周波数 (RF) または中間周波数 (IF) 信号をベースバンドに変換するためにデジタル通信受信機で幅広く使用されています。DDC は、低い周波数に信号をシフトしてサンプリング レートを削減することにより、以後の処理段階を容易にします。この例の DDC は、4 段階のフィルター チェーンを使用して、複雑な周波数変換を実行してからサンプル レートを変換します。この例では、最初に浮動小数点で DSP System Toolbox™ の関数を使用して DDC を設計します。次に、各段階を固定小数点に変換し、合成可能な HDL コードを生成する Simulink® モデルで使用します。この例ではこれら 2 つのテスト信号を使用して、DDC の動作の例示と検証を行います。

  • 32 MHz の IF 搬送波に変調された正弦波。

  • 32 MHz の IF 搬送波に変調された、帯域幅が 1.4 MHz の LTE ダウンリンク信号。

この例では、浮動小数点の DDC の出力における信号品質を、固定小数点の DDC の出力における信号品質と比較します。

最後に、FPGA 用フィルター チェーンの実装および合成結果を示します。

この例では、スティミュラスを生成する関数と DDC の出力を分析する関数が含まれているヘルパー クラスである DDCTestUtils を使用します。詳細については、DDCTestUtils.m ファイルを参照してください。

DDC の構成

DDC は、数値制御発振器 (NCO)、混合器および間引きフィルター チェーンから構成されます。フィルター チェーンは、カスケード接続積分器櫛形 (CIC) 間引き、CIC ゲイン修正、CIC 補正間引き (FIR)、ハーフバンド FIR 間引きおよび最終 FIR 間引きから構成されます。

このフィルター チェーンの全体的な応答は、同じ仕様の信号間引きフィルターの応答と同等です。しかし、フィルターを複数の間引き段に分割すると、より効率的な設計になり、使用するハードウェア リソースが少なくなります。

CIC 間引きは初期間引き係数が大きいので、以後のフィルターはより低いレートで動作できます。CIC 補正間引きは、サンプルの半分を間引きながら CIC の低下を補正することにより、スペクトル応答を向上させます。ハーフバンドは中間的な間引きであり、最終間引きは DDC の正確な Fpass および Fstop 特性を実装します。チェーンの終わり付近でサンプリング レートが低くなるほど、乗算器を共有することによりリソースの使用を最適化できます。

次の図は、DDC のブロック線図を示しています。

DDC への入力のサンプル レートは 122.88 Msps ですが、出力のサンプル レートは 1.92 Msps です。これらのレートより、全体的な間引き係数 64 が得られます。LTE 受信機は、セル サーチとマスター情報ブロック (MIB) リカバリの一般的なサンプリング レートとして 1.92 Msps を使用します。DDC フィルターはこの用途に合わせて設計されています。DDC は、122.88 MHz のクロック レートで動作するように最適化されています。

DDC 設計

この節では、MATLAB® の浮動小数点演算とフィルター設計関数を使用して DDC を設計する方法について説明します。

DDC のパラメーター

この例では、次のような入力サンプル レートおよび搬送周波数について、これらの仕様を満たす DDC フィルター特性を設計します。

FsIn = 122.88e6;    % Sampling rate of DDC input
FsOut = 1.92e6;     % Sampling rate of DDC output
Fc = 32e6;          % Carrier frequency
Fpass = 540e3;      % Passband frequency, equivalent to 36x15kHz LTE subcarriers
Fstop = 700e3;      % Stopband frequency
Ap = 0.1;           % Passband ripple
Ast = 60;           % Stopband attenuation

CIC 間引き

最初のフィルター段は CIC 間引きです。これは、大きい間引き係数を効率的に実装できるからです。CIC フィルターの応答は移動平均フィルターのカスケードに似ていますが、CIC フィルターでは乗算も除算も使用しません。この結果、CIC フィルターの DC ゲインは大きくなります。

cicParams.DecimationFactor = 8;
cicParams.DifferentialDelay = 1;
cicParams.NumSections = 3;
cicParams.FsOut = FsIn/cicParams.DecimationFactor;

cicFilt = dsp.CICDecimator(cicParams.DecimationFactor, ...
    cicParams.DifferentialDelay,cicParams.NumSections)
cicGain = gain(cicFilt)
cicFilt = 

  dsp.CICDecimator with properties:

      DecimationFactor: 8
     DifferentialDelay: 1
           NumSections: 3
    FixedPointDataType: 'Full precision'


cicGain =

   512

CIC のゲインは 2 のべき乗なので、シフト演算を使用してハードウェア実装でゲイン ファクターを容易に修正できます。解析用に、この例では MATLAB のゲイン修正を 1 タップの dsp.FIRFilter System object™ によって表しています。

cicGainCorr = dsp.FIRFilter('Numerator',1/cicGain)
cicGainCorr = 

  dsp.FIRFilter with properties:

            Structure: 'Direct form'
      NumeratorSource: 'Property'
            Numerator: 0.0020
    InitialConditions: 0

  Use get to show all properties

fvtool を使用して、ゲイン修正がある場合とない場合の CIC フィルターの振幅応答を表示します。解析用に、CIC フィルターとゲイン修正フィルターを dsp.FilterCascade System object に結合します。CIC フィルターは内部的に固定小数点演算を使用するので、fvtool は量子化された応答と量子化されていない応答の両方をプロットします。

ddcPlots.cicDecim = fvtool(...
    cicFilt, ...
    dsp.FilterCascade(cicFilt,cicGainCorr), ...
    'Fs',[FsIn,FsIn]);
legend(ddcPlots.cicDecim, ...
    'CIC No Correction', ...
    'CIC With Gain Correction');

CIC 低下補正フィルター

CIC フィルターの振幅応答は通過帯域領域で大幅に "低下" するので、この例では FIR ベースの低下補正フィルターを使用して通過帯域応答をフラット化します。低下補償器は、CIC 間引きと同じプロパティをもちます。このフィルターはサンプル数を半分にする間引きを実装するので、フィルターに帯域制限特性も指定する必要があります。関数 design を使用して、指定された特性があるフィルターの System object を取得します。

compParams.R = 2;                                % CIC compensation decimation factor
compParams.Fpass = Fstop;                        % CIC compensation passband frequency
compParams.FsOut = cicParams.FsOut/compParams.R; % New sampling rate
compParams.Fstop = compParams.FsOut - Fstop;     % CIC compensation stopband frequency
compParams.Ap = Ap;                              % Same passband ripple as overall filter
compParams.Ast = Ast;                            % Same stopband attenuation as overall filter

compSpec = fdesign.decimator(compParams.R,'ciccomp', ...
    cicParams.DifferentialDelay, ...
    cicParams.NumSections, ...
    cicParams.DecimationFactor, ...
    'Fp,Fst,Ap,Ast', ...
    compParams.Fpass,compParams.Fstop,compParams.Ap,compParams.Ast, ...
    cicParams.FsOut);
compFilt = design(compSpec,'SystemObject',true)
compFilt = 

  dsp.FIRDecimator with properties:

   Main
    DecimationFactor: 2
     NumeratorSource: 'Property'
           Numerator: [-0.0398 -0.0126 0.2901 0.5258 0.2901 -0.0126 -0.0398]
           Structure: 'Direct form'

  Use get to show all properties

CIC フィルター (ゲイン修正あり) の応答と低下補正の応答を結合してプロットします。

ddcPlots.cicComp = fvtool(...
    dsp.FilterCascade(cicFilt,cicGainCorr,compFilt), ...
    'Fs',FsIn,'Legend','off');

ハーフバンド間引き

ハーフバンド フィルターは、サンプルの半分を効率的に間引きます。ハーフバンド フィルターは、その係数のほぼ半分がゼロに等しく、それらの乗算器はハードウェア実装から除外されるので効率的です。

hbParams.FsOut = compParams.FsOut/2;
hbParams.TransitionWidth = hbParams.FsOut - 2*Fstop;
hbParams.StopbandAttenuation = Ast;

hbSpec = fdesign.decimator(2,'halfband',...
    'Tw,Ast', ...
    hbParams.TransitionWidth, ...
    hbParams.StopbandAttenuation, ...
    compParams.FsOut);
hbFilt = design(hbSpec,'SystemObject',true)
hbFilt = 

  dsp.FIRDecimator with properties:

   Main
    DecimationFactor: 2
     NumeratorSource: 'Property'
           Numerator: [0.0089 0 -0.0565 0 0.2977 0.5000 ... ] (1x11 double)
           Structure: 'Direct form'

  Use get to show all properties

ハーフバンド フィルターの出力までの DDC の応答をプロットします。

ddcPlots.halfbandFIR = fvtool(...
    dsp.FilterCascade(cicFilt,cicGainCorr,compFilt,hbFilt), ...
    'Fs',FsIn,'Legend','off');

最終 FIR 間引き

最終 FIR は、DDC の通過帯域と阻止帯域の詳細な特性を実装します。このフィルターには先の FIR フィルターより多くの係数がありますが、低いサンプリング レートで動作するので、リソース共有を利用できてハードウェア実装が効率的になります。

固定小数点による量子化後も DDC が仕様を満たすように、阻止帯域の減衰量に 3 dB のヘッドルームを追加します。この値は、fvtool を使用して経験的に求めたものです。

finalSpec = fdesign.decimator(2,'lowpass', ...
    'Fp,Fst,Ap,Ast',Fpass,Fstop,Ap,Ast+3,hbParams.FsOut);
finalFilt = design(finalSpec,'equiripple','SystemObject',true)
finalFilt = 

  dsp.FIRDecimator with properties:

   Main
    DecimationFactor: 2
     NumeratorSource: 'Property'
           Numerator: [9.3365e-04 0.0013 9.3466e-04 ... ] (1x70 double)
           Structure: 'Direct form'

  Use get to show all properties

DDC の全体的な振幅応答を可視化します。

ddcFilterChain           = dsp.FilterCascade(cicFilt,cicGainCorr,compFilt,hbFilt,finalFilt);
ddcPlots.overallResponse = fvtool(ddcFilterChain,'Fs',FsIn,'Legend','off');

固定小数点への変換

浮動小数点 DDC フィルター チェーンの周波数応答が、仕様を満たすようになりました。次に、各フィルター段を量子化して固定小数点型を使用し、これらを解析してフィルター チェーンが引き続き仕様を満たすことを確認します。

フィルターの量子化

この例では、仕様を十分に満たす 16 ビットの係数を使用します。18 ビット未満の係数を使用すると、FPGA の実装に必要な DSP ブロックの数が最小限になります。DDC フィルター チェーンへの入力は 16 ビット データであり、15 ビットが小数ビットです。フィルターの出力は 18 ビット値です。これにより、中間信号のヘッドルームと精度が向上します。

CIC 間引きについて固定小数点データ型オプションとして 'Minimum section word lengths' を選択すると、出力の語長および他の CIC パラメーターに基づいて内部的な語長が自動的に最適化されます。

cicFilt.FixedPointDataType = 'Minimum section word lengths';
cicFilt.OutputWordLength = 18;

FIR ベースの System object とゲイン修正の固定小数点プロパティを設定します。この System object は RoundingMethod プロパティと OverflowAction プロパティの既定値 (それぞれ 'Floor''Wrap') を使用します。

% CIC Gain Correction
cicGainCorr.FullPrecisionOverride = false;
cicGainCorr.CoefficientsDataType = 'Custom';
cicGainCorr.CustomCoefficientsDataType = numerictype(fi(cicGainCorr.Numerator,1,16));
cicGainCorr.OutputDataType = 'Custom';
cicGainCorr.CustomOutputDataType = numerictype(1,18,16);

% CIC Droop Compensation
compFilt.FullPrecisionOverride = false;
compFilt.CoefficientsDataType = 'Custom';
compFilt.CustomCoefficientsDataType = numerictype([],16,15);
compFilt.ProductDataType = 'Full precision';
compFilt.AccumulatorDataType = 'Full precision';
compFilt.OutputDataType = 'Custom';
compFilt.CustomOutputDataType = numerictype([],18,16);

% Halfband
hbFilt.FullPrecisionOverride = false;
hbFilt.CoefficientsDataType = 'Custom';
hbFilt.CustomCoefficientsDataType = numerictype([],16,15);
hbFilt.ProductDataType = 'Full precision';
hbFilt.AccumulatorDataType = 'Full precision';
hbFilt.OutputDataType = 'Custom';
hbFilt.CustomOutputDataType = numerictype([],18,16);

% FIR
finalFilt.FullPrecisionOverride = false;
finalFilt.CoefficientsDataType = 'Custom';
finalFilt.CustomCoefficientsDataType = numerictype([],16,15);
finalFilt.ProductDataType = 'Full precision';
finalFilt.AccumulatorDataType = 'Full precision';
finalFilt.OutputDataType = 'Custom';
finalFilt.CustomOutputDataType = numerictype([],18,16);

固定小数点の解析

fvtool で量子化の効果を調べます。フィルターは、個別でもカスケード接続した状態でも解析できます。fvtool は、量子化された応答と量子化されていない応答 (リファレンス) を重ねて表示します。たとえば、次の図は最終 FIR フィルター段を量子化した効果を示しています。

ddcPlots.quantizedFIR = fvtool(finalFilt, ...
    'Fs',hbParams.FsOut,'arithmetic','fixed');

ddcFilterChain カスケード オブジェクトを再定義して、各フィルターの固定小数点プロパティを含めます。次に、fvtool を使用してフィルター チェーン全体を解析し、量子化された DDC が引き続き仕様を満たすことを確認します。

ddcFilterChain = dsp.FilterCascade(cicFilt, ...
    cicGainCorr,compFilt,hbFilt,finalFilt);
ddcPlots.quantizedDDCResponse = fvtool(ddcFilterChain, ...
    'Fs',FsIn,'Arithmetic','fixed');

legend(ddcPlots.quantizedDDCResponse, ...
    'DDC filter chain');

HDL 用に最適化された Simulink モデル

設計フローの次のステップでは、HDL コード生成をサポートするブロックを使用して、Simulink で DDC を実装します。

モデル コンフィギュレーション

このモデルでは、MATLAB ワークスペースの変数を使用してブロックを設定します。この例で前に定義した、同じフィルター チェーンの変数を使用します。次に、NCO の特性と入力信号を定義します。この例では、これらの特性を使用して NCO ブロックを設定します。

必要な周波数分解能を指定し、目的の分解能を達成するために必要なアキュムレータのビット数を計算します。必要なスプリアス フリー ダイナミック レンジを設定してから、量子化されたアキュムレータのビット数を定義します。NCO は量子化されたアキュムレータの出力を使用して、正弦ルックアップ テーブルのアドレスを指定します。さらに、指定された搬送周波数を生成するために NCO が使用する位相増分を計算します。NCO はこれらのアキュムレータのビットに位相ディザーを適用します。これらのビットは量子化時に除去されます。

nco.Fd = 1;
nco.AccWL =  nextpow2(FsIn/nco.Fd)+1;
SFDR  = 84;
nco.QuantAccWL = ceil((SFDR-12)/6);
nco.PhaseInc = round((-Fc*2^nco.AccWL)/FsIn);
nco.NumDitherBits = nco.AccWL-nco.QuantAccWL;

DDC への入力は変数 ddcIn に由来します。ここでは、モデルでデータ型を計算できるようにするため、ddcIn にダミー値を割り当てます。テスト時に、ddcIn は入力データをモデルに提供します。

ddcIn = 0;

サンプルベースの信号は、FrameSize を 1 に設定して作成し、受信したときに個別のサンプルを出力します。入力サンプリング周波数が高くなる場合または電力が低下する場合を考慮して、この設計でフレームベースの処理も実現できるようにする場合は、FrameSize を適宜変更する必要があります。この場合は、FrameSize が 4 のケースを示しています。

FrameSize = 4;

モデルの構造

次の図に、DDC の Simulink モデルの最上位レベルを示します。このモデルでは、Signal From Workspace ブロックを使用して変数 ddcIn を MATLAB ワークスペースからインポートし、入力信号を 16 ビット値に変換して DDC に適用します。HDL コードは、HDL_DDC サブシステムから生成できます。

modelName = 'DDCforLTEHDL';
open_system(modelName);
set_param(modelName,'SimulationCommand','Update');
set_param(modelName,'Open','on');

HDL_DDC サブシステムが DDC フィルターを実装します。まず、NCO ブロックが搬送周波数で複素フェーザを生成します。この信号は混合器に送られ、フェーザが入力信号と乗算されます。次に、混合器の出力がフィルター チェーンに渡され、1.92 Msps に間引かれます。

set_param([modelName '/HDL_DDC'],'Open','on');

NCO ブロックのパラメーター

このモデルの NCO ブロックは、nco 構造体で定義されているパラメーターを使用して設定されます。次の図に、NCO ブロックのパラメーター ダイアログのタブを両方とも示します。

CIC 間引きとゲイン修正

最初のフィルター段階は CIC Decimator ブロックで実装された CIC 間引きです。ブロック パラメーターは cicParams 構造体値に設定されます。ゲイン修正を実装するため、モデルは [Gain correction] パラメーターを選択します。次の図は CIC Decimator ブロックのブロック パラメーターを示します。

モデルでは、対応する System object のプロパティを使用してフィルターを設定します。CIC 補正、ハーフバンド間引き、および最終間引きフィルターは、それぞれクロック レートの 1/8、1/16 および 1/32 より低いサンプル レートで動作します。モデルでは、"有効な" 入力信号を使用してこれらのサンプル レートを実装し、各レートでどのサンプルが有効かを示します。フィルター チェーンの信号はすべて同じ Simulink サンプル時間です。

CIC 補正、ハーフバンド間引き、および最終間引きフィルターは、それぞれ FIR Decimator で実装されます。[Minimum number of cycles between valid input samples] パラメーターを設定することで、入力サンプル間の無効なサイクル数を使用できます。たとえば、CIC 補正間引きの各入力間の間隔は 8 で、これは間引き係数と同じです。そのため、CIC 補正間引きの [Minimum number of cycles between valid input samples]2 サイクルと等しい ceil(cicParams.DecimationFactor/FrameSize) に設定されます。次の図は、CIC Compensation Decimation ブロックのブロック パラメーターを示します。

FIR Decimator ブロックは、指定したクロック サイクルの数を超える時間で乗算器を完全に再利用します。FrameSize4 の場合、複雑な入力データのある CIC 補正間引きフィルターは 4 個の乗算器を使用することになります。ハーフバンド間引きでは 4 個の乗算器を使用し、最終間引きでは 12 個の乗算器を使用します。FrameSize1 の場合は、CIC 補正間引きおよびハーフバンド間引きの入力間隔がそのフィルター長より大きくなるため、これらの 2 つの間引きでは 2 個の乗算器のみが必要です。その場合、最終間引きには 4 個の乗算器が必要です。

搬送波への正弦波の変調によるテストと検証

DDC をテストするため、40 kHz の正弦波を搬送周波数に変調し、変調後の正弦波に DDC を通過させます。そして、生成されたトーンのスプリアス フリー ダイナミック レンジ (SFDR) と NCO の出力の SFDR を測定します。NCO の SFDR と固定小数点の DDC 出力をプロットします。

% Initialize random seed before executing any simulations.
rng(0);

% Generate a 40 kHz test tone, modulated onto the carrier.
ddcIn = DDCTestUtils.GenerateTestTone(40e3,Fc);

% Demodulate the test signal with the floating-point DDC.
ddcOut = DDCTestUtils.DownConvert(ddcIn,FsIn,Fc,ddcFilterChain);
release(ddcFilterChain);

% Demodulate the test signal by running the Simulink model.
out = sim(modelName);

% Measure the SFDR of the NCO, floating-point DDC outputs, and fixed-point
% DDC outputs.
results.sfdrNCO = sfdr(real(out.ncoOut),FsIn);
results.sfdrFloatDDC = sfdr(real(ddcOut),FsOut);
results.sfdrFixedDDC = sfdr(real(out.ddcFixedOut),FsOut);

disp('SFDR Measurements');
disp(['   Floating-point DDC SFDR: ',num2str(results.sfdrFloatDDC) ' dB']);
disp(['   Fixed-point NCO SFDR: ',num2str(results.sfdrNCO) ' dB']);
disp(['   Optimized fixed-point DDC SFDR: ',num2str(results.sfdrFixedDDC) ' dB']);
fprintf(newline);

% Plot the SFDR of the NCO and fixed-point DDC outputs.
ddcPlots.ncoOutSDFR = figure;
sfdr(real(out.ncoOut),FsIn);

ddcPlots.OptddcOutSFDR = figure;
sfdr(real(out.ddcFixedOut),FsOut);
SFDR Measurements
   Floating-point DDC SFDR: 291.4184 dB
   Fixed-point NCO SFDR: 83.0306 dB
   Optimized fixed-point DDC SFDR: 110.386 dB

LTE 信号テスト

LTE テスト信号を使用して、DDC のより厳密なテストを実行できます。LTE Toolbox™ の関数を使用して、規格準拠の LTE 波形を生成します。次に、この波形を DDC モデルでダウンコンバートします。LTE Toolbox の関数を使用して、生成された信号のエラー ベクトル振幅 (EVM) を測定します。

rng(0);
% Execute this test only if you have the LTE Toolbox product.
if license('test','LTE_Toolbox')

    % Generate a modulated LTE test signal by using the LTE Toolbox functions.
    [ddcIn,sigInfo] = DDCTestUtils.GenerateLTETestSignal(Fc);

    % Downconvert the signal with the floating-point DDC.
    ddcOut = DDCTestUtils.DownConvert(ddcIn,FsIn,Fc,ddcFilterChain);
    release(ddcFilterChain);

    % Downconvert the signal with the Simulink model, then measure and plot the
    % EVM of the floating-point and fixed-point results. Pad the input with zeros
    % to represent propagation latency and return the complete result.
    ddcIn = [ddcIn;zeros(2480*FrameSize,1)];
    out = sim(modelName);

    results.evmFloat = DDCTestUtils.MeasureEVM(sigInfo,ddcOut);
    results.evmFixed = DDCTestUtils.MeasureEVM(sigInfo,out.ddcFixedOut(1:length(ddcOut)));

    disp('LTE Error Vector Magnitude (EVM) Measurements');
    disp(['   Floating-point DDC RMS EVM: '  num2str(results.evmFloat.RMS*100,3) '%']);
    disp(['   Floating-point DDC Peak EVM: ' num2str(results.evmFloat.Peak*100,3) '%']);
    disp(['   Fixed-point DDC RMS EVM: '     num2str(results.evmFixed.RMS*100,3) '%']);
    disp(['   Fixed-point DDC Peak EVM: '    num2str(results.evmFixed.Peak*100,3) '%']);
    fprintf(newline);


end
LTE Error Vector Magnitude (EVM) Measurements
   Floating-point DDC RMS EVM: 0.633%
   Floating-point DDC Peak EVM: 2.44%
   Fixed-point DDC RMS EVM: 0.731%
   Fixed-point DDC Peak EVM: 2.69%

HDL コードの生成と FPGA の実装

この例の HDL コードを生成するには、HDL Coder™ 製品が必要です。makehdl コマンドと makehdltb コマンドを使用して、HDL_DDC サブシステムの HDL コードと HDL テスト ベンチを生成します。この DDC は、Xilinx® Zynq®-7000 ZC706 評価ボードで合成されています。次の表に、配置配線後のリソース使用状況の結果を示します。この設計は、331 MHz のクロック周波数でタイミングを満たしました。

T = table(...
    categorical({'LUT'; 'LUTRAM'; 'FF'; 'BRAM'; 'DSP'}),...
    categorical({'4341'; '383'; '8248'; '2.0'; '36'}),...
    'VariableNames',{'Resource','Usage'})
T =

  5x2 table

    Resource    Usage
    ________    _____

     LUT        4341 
     LUTRAM     383  
     FF         8248 
     BRAM       2.0  
     DSP        36