2012年7月8日日曜日

FPGA FMトランスミッタ: プリエンファシス

プリエンファシスの構成

前回の記事を再掲します。

プリエンファシスのブロック図

ここで、fs=152kHzのときのパラメータは次の通りです。

  k: 0.75 (2進 0.11)
  b: 0.04541 (2進  0.00001011101)
  G: 20.875 (2進 10100.111)

ブロック図によると、処理の過程で k倍とか 1+b倍とかの乗算処理を多用しています。前回は、乗算器を節約するため乗算処理をシフト演算と加算に分解していました。今回は、先のローパスフィルタを参考にして、1個の乗算器を使い入力信号を適宜切り替えています。乗算器は前回と同じハードマクロをインスタンシエーションしていますのでレイテンシの3を考慮して、タイミング関係を作図したうえでコードを作成しました。※state512は1クロック位置がずれています。わかりやすさ優先であえて直していません

タイミング関係

特性実測値

周波数特性(入力ローパスフィルタ含む)

スペクトル(5kHz、プリエンファシスON)
※赤い波形はバッグクラウンドノイズ

スペクトル(5kHz、プリエンファシスOFF)

pre_emphコード

一見しただけでは、何をやっているかさっぱりわからないシロモノになってしまいました。タイミング関係図と合わせて見て下さい。また、計算のダイナミックレンジの取り方を配慮できていないので、ビット幅が不足しているところと過剰なところが混在していると思います。出力レジスタのサチュレーション処理はローパスフィルタのコードを使わせて頂いています。

module pre_emph(
  pe_l_i, pe_r_i, pe_l_o, pe_r_o,
  clk, en, reset, pe_thru
  );

  input [23:0] pe_l_i;
  input [23:0] pe_r_i;
  output [23:0] pe_l_o;
  output [23:0] pe_r_o;
  input clk;
  input en;
  input reset;
  input pe_thru;

  reg [8:0] state512;
  reg [35:0] input_reg;
  reg [17:0] mul_in1;
  reg [17:0] mul_in2;
  wire [35:0] mul_out;
  reg [35:0] reg0;
  reg [35:0] reg1;
  reg [17:0] reg2_l;
  reg [17:0] reg2_r;
  reg [17:0] reg2_l2;
  reg [17:0] reg2_r2;
  reg [35:0] delay_buff;
  reg [29:0] output_reg_l;
  reg [29:0] output_reg_r;
  reg [23:0] pe_l_o;
  reg [23:0] pe_r_o;

  // State counter
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        state512 <= 0;
      else if (state512 == 511)
        state512 <= 0;
      else
        state512 <= state512 + 1;
    end

  assign latch_out = (state512 == 17);

  // Pre Emphasis
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        begin
          input_reg <= 0;
          mul_in1 <= 0;
          mul_in2 <= 0;
          reg0 <= 0;
          reg1 <= 0;
          delay_buff <= 0;
          output_reg_l <= 0;
          output_reg_r <= 0;
        end

      else
        begin
          case (state512)
            0:  begin 
                input_reg <= {pe_l_i[23:6], pe_r_i[23:6]};  // Vin(L/R)
              end

            1:  begin 
                mul_in1 <= input_reg[35:18];  // Vin(L)
                mul_in2 <= 18'b01_1000_0000_0000_0000;  // k=0.75 (0.11)
              end

            2:  begin 
                mul_in1 <= input_reg[17:0];  // Vin(R)
                mul_in2 <= 18'b01_1000_0000_0000_0000;  // k=0.75 (0.11)
              end

            3:  begin 
                mul_in1 <= delay_buff[35:18];  // delay_buf(L)
                mul_in2 <= 18'b00_1000_0000_0000_0000;  // k=1-0.75=0.25 (0.01)
              end

            4:  begin 
                mul_in1 <= delay_buff[17:0];  // delay_buf(R)
                mul_in2 <= 18'b00_1000_0000_0000_0000;  // k=1-0.75=0.25 (0.01)
              end

            5:  begin 
                mul_in1 <= 0;
                mul_in2 <= 0;
                reg0[35:18] <= mul_out[34:17];
              end

            6:  begin 
                reg0[17:0] <= mul_out[34:17];
              end

            7:  begin 
                mul_in1 <= input_reg[35:18];  // Vin(L)
                mul_in2 <= 18'b01_0000_1011_1010_0000;  // 1+b=1.04541 (01.00001011101)
                reg1[35:18] <= mul_out[34:17];
              end

            8:  begin 
                mul_in1 <= input_reg[17:0];  // Vin(R)
                mul_in2 <= 18'b01_0000_1011_1010_0000;  // 1+b=1.04541 (01.00001011101)
                reg1[17:0] <= mul_out[34:17];
              end

            9:  begin 
                reg2_l <= reg0[35:18] + reg1[35:18];
                reg2_r <= reg0[17:0] + reg1[17:0];
              end

            10:begin 
                reg2_l2 <= ~reg2_l + 1;
                reg2_r2 <= ~reg2_r + 1;
              end

            11:begin 
                mul_in1 <= mul_out[34:17] + {reg2_l2[17], reg2_l2[17:1]};  // preemph(L)
                mul_in2 <= 18'b01_0100_1110_0000_0000;  // G=20.875 (010100.111)
                delay_buff <= {reg2_l, reg2_r};
              end

            12:begin 
                mul_in1 <= mul_out[34:17] + {reg2_r2[17], reg2_r2[17:1]};  // preemph(R)
                mul_in2 <= 18'b01_0100_1110_0000_0000;  // G=20.875 (010100.111)
              end

            15:begin 
                output_reg_l <= mul_out[34:5];  // Vout(L)
              end

            16:begin 
                output_reg_r <= mul_out[34:5];  // Vout(R)
              end

            default:  begin
                mul_in1 <= 0;
                mul_in2 <= 0;
              end

          endcase
        end
    end

  //  output data register, saturation
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        begin
          pe_l_o <= 0;
          pe_r_o <= 0;
        end
      else if (pe_thru)
        begin
          pe_l_o <= pe_l_i;
          pe_r_o <= pe_r_i;
        end
      else if (latch_out)
        begin
          // left channel
          if (~output_reg_l[29] && !(output_reg_l[28:23] == 6'b000000))
            pe_l_o <= 8388607;    // 24-bit positive max
          else if (output_reg_l[29] && !(output_reg_l[28:23] == 6'b111111))
            pe_l_o <= -8388608;    // 24-bit negative max
          else
            pe_l_o <= {output_reg_l[29], output_reg_l[22:0]};
          // right channel
          if (~output_reg_r[29] && !(output_reg_r[28:23] == 6'b000000))
            pe_r_o <= 8388607;    // 24-bit positive max
          else if (output_reg_r[29] && !(output_reg_r[28:23] == 6'b111111))
            pe_r_o <= -8388608;    // 24-bit negative max
          else
            pe_r_o <= {output_reg_r[29], output_reg_r[22:0]};
        end
    end

  mul_p U1 (
    .clk(clk),
    .sclr(reset),
    .a(mul_in1),
    .b(mul_in2),
    .p(mul_out)
    );

endmodule

速度の低下
プリエンファシスのモジュールの有無で最大動作周波数が下がってしまいました。できるだけ処理を細切れにしたつもりでしたが時間的に厳しいところもあるようです。

プリエンファシスあり

プリエンファシスなし

2012年7月2日月曜日

FPGA FMトランスミッタ: 15kHzローパスフィルタ その2

FIRフィルタIPコアを使わずにつくるローパスフィルタ
FIR Compilerを使わずに、乗算器やメモリを組み合わせたローパスフィルタです。見た目はシンプルながら、動作はまるでパズルのようです。ここでは、頂きもののverilogコードに直通機能(l15k_thru)を追加(改悪?)しました。JA2SVZ様、ご提供頂きましてありがとうございました。


全体構成
verilogを眺めるだけでは動作を理解できなかったので、ブロック図を書き起こしてみました。図の書き方は独自様式的なのでわかりにくいかもしれません。
入力信号はまずリングバッファに書き込まれます。今回は256タップなので過去256個分のデータを記録して行きます。入出力のサンプルレートは同一なので、1サンプルごとにリングバッファのデータとフィルタ係数について256回の積和演算を行います。この処理は、リソースの節約のため1個の乗算器multiplierでL/Rチャンネルを時分割で処理しています。



タイミング関係
Verilogを見れば一目瞭然なんでしょうが、私自身の理解が深まらないので波形も示します。

(1)ステートカウンタ
"state conters and control signals"で動作の基本となるクロック信号を作っています。
state512は、クロック信号 clk(512fs)毎にカウントアップして512回でゼロにリセットされるので、その周期は音声信号1サンプルの等しいです。
ring256は、state512がリセットされるたびにカウントアップし、256回目でゼロにリセットされます。ring256の周期は音声信号256サンプルに等しいです。
init_cycleは、ring256が初回に256カウントするときに0に変化します。
state512とかring256という命名は、わかりやすいですね!


(2)リングバッファの書き込み
L/Rの入力信号を束ねて36ビット幅としてリングバッファに書き込みます。アドレスはring256の値を使い、state512 = 5 の時に書き込み(write enable)します。


(3)リングバッファの読み出し
デジタルフィルタとしての処理を行うため、ring256で指定されたアドレスを先頭に256個のデータをを読み出し、それぞれに対してデジタルフィルタ係数との積和演算を行います。リングバッファの読み出しアドレスは (state512[8:1] + ring256) % 256 で表されます。


(4)積和演算
L/Rチャンネルを一つの乗算器で処理するため、その入出力をマルチプレクサで切り替えて時分割で処理します。入力信号と係数の乗算結果をアキュムレータで総和をとるため、まず acc_init = (state512 == 5) || (state512 == 6)クリアしたのち256回の総和の計算を行い、acc_out = (state512 == 5) で出力を確定させます。


ここで、タイミング関係が複雑だなあと感じたのがリングバッファ、係数ROM、乗算器レイテンシです。レイテンシはそれぞれ、リングバッファ2、係数ROM2、乗算器3です。データ処理の時間関係を図にしてみました。(合っているか心配ですが・・・)

2012.7.2追記
FIRフィルタの処理は動作速度のボトルネックになりがちとのことで、動作速度を確実に確保する手段としてレイテンシを導入(追加)しているそうです。動作速度を稼げる代償として制御がわかりにくくなっています。


データ処理の時間関係

(5)アキュムレータ出力のサチュレーション処理
"output data register"では、アキュムレータ出力の36ビットを24ビットに丸める・・・というか切り捨てる際にサチュレーション処理を行っています。サチュレーションとは、計算結果がオーバーフローして24ビットの範囲に収まらなかった場合は、24ビットの正負いずれかの最大値を出力させる処理です。


2012.7.2追記
入出力24ビットなのにアキュムレータが32ビットなのは、256個のデータの積和を行うため8ビットを足さないと情報の欠落が起こるためだそうです。
また、32ビットから24ビットへのまるめ処理とサチュレーションは無関係。フィルタでは、入力信号の波形によって出力にどのような信号が出てくるか完全に予測できないため、最上位2ビットでサチュレーションを行いフルスケールの50%以上の信号を50%にクリップさせているとのことです。



lpf15kコード

module lpf15k(
  l15k_l_i, l15k_r_i, l15k_l_o, l15k_r_o,
  clk, reset, l15k_thru
  );

  input clk;
  input reset;
  input l15k_thru;
  input [23:0] l15k_l_i;
  input [23:0] l15k_r_i;
  output [23:0] l15k_l_o;
  output [23:0] l15k_r_o;

  reg [8:0] state512;
  reg [7:0] ring256;
  reg init_cycle;
  wire acc_init;
  wire acc_out;
  wire sel_1 ;
  wire [7:0] COEF_ADDR;
  wire [17:0] COEF_DATA;
  wire [7:0] RING_RADDR;
  wire [35:0] RING_RDATA;
  wire [7:0] RING_WADDR;
  wire [35:0] RING_WDATA;
  wire [0:0] RING_WEN;
  wire [17:0] mul_in1;
  wire [17:0] mul_in2;
  wire [35:0] mul_out;
  wire [31:0] acc_in;
  reg [31:0] acc1;
  reg [31:0] acc2;
  reg [23:0] l15k_l_o;
  reg [23:0] l15k_r_o;

  ////   state counters and control signals   ////
  always @(posedge clk or posedge reset) begin
    if (reset) begin
      state512 <= 0;
      ring256 <= 0;
      init_cycle <= 1;
    end
    else if (state512 == 511) begin
      state512 <= 0;
      if (ring256 == 255) begin
        ring256 <= 0;
        init_cycle <= 0;
      end
      else
        ring256 <= ring256 + 1;
    end
    else
      state512 <= state512 + 1;
  end

  assign sel_1 = ~state512[0];
  assign acc_init = (state512 == 5) || (state512 == 6);
  assign acc_out = (state512 == 5) && (~init_cycle);
  assign RING_WEN = (state512 == 5);

  ////   ring-buffer RAM and coefficient ROM address generation   ////
  assign COEF_ADDR = state512[8:1];
  assign RING_WADDR = ring256;
  assign RING_RADDR = (state512[8:1] + ring256) % 256;
  
  ////   input data acquisition to ring-buffer RAM   ////
  assign RING_WDATA = {l15k_r_i[23:6], l15k_l_i[23:6]};

  ////   ring-buffer RAM and coefficient ROM read, input MUX and multiplier   ////
  assign mul_in1 = COEF_DATA;
  assign mul_in2 = (sel_1)? RING_RDATA[17:0] : RING_RDATA[35:18];
  assign acc_in = {{4{mul_out[34]}}, mul_out[34:7]};

  ////   accumulator   ////
  always @(posedge clk or posedge reset) begin
    if (reset) begin
      acc1 <= 0;
      acc2 <= 0;
    end
    else
      if (~sel_1)
        acc1 <= (acc_init)? acc_in : acc1 + acc_in;
      else
        acc2 <= (acc_init)? acc_in : acc2 + acc_in;
  end

  ////   output data register   ////
  always @(posedge clk or posedge reset) begin
    if (reset) begin
      l15k_l_o <= 0;
      l15k_r_o <= 0;
    end
    else if (l15k_thru) begin
      l15k_l_o <= l15k_l_i;
      l15k_r_o <= l15k_r_i;
    end
    else
      if (acc_out) begin
        // left channel
        if (acc1[31:30] == 2'b01)
          l15k_l_o <= 8388607;    // 24-bit positive max
        else if (acc1[31:30] == 2'b10)
          l15k_l_o <= -8388608;    // 24-bit negative max
        else
          l15k_l_o <= acc1[30:6];
        // right channel
        if (acc2[31:30] == 2'b01)
          l15k_r_o <= 8388607;    // 24-bit positive max
        else if (acc2[31:30] == 2'b10)
          l15k_r_o <= -8388608;    // 24-bit negative max
        else
          l15k_r_o <= acc2[30:6];
      end
  end

  ////   memory and multiplier instantiation (Xilinx)   //// 
  lpf15k_coef U1 (
    .clka(clk),
    .rsta(reset),
    .addra(COEF_ADDR),
    .douta(COEF_DATA)
    );

  lpf15k_rbuf U2 (
    .clka(clk),
    .wea(RING_WEN),
    .addra(RING_WADDR),
    .dina(RING_WDATA),
    .clkb(clk),
    .rstb(reset),
    .addrb(RING_RADDR),
    .doutb(RING_RDATA)
    );

  mul_p U3 (
    .clk(clk),
    .sclr(reset),
    .a(mul_in1),
    .b(mul_in2),
    .p(mul_out)
    );

endmodule


Block Memory Generator(ring-buffer RAM)の設定
リングバッファ用デュアルポートRAMです。読み出しのレイテンシは2クロックです。
Total Port B Read Latency (From Rising Edge of Read Clock): 2 Clock Cycles










Block Memory Generator(coefficient ROM)の設定

係数用ROMです。読み出しのレイテンシは2クロックです。
Total Port A Read Latency (From Rising Edge of Read Clock): 2 Clock Cycles










Multiplierの設定

乗算器です。演算のレイテンシは3クロックです。







2012年6月22日金曜日

FPGA FMトランスミッタ: 避けては通れぬ?2の補数(その2)

Verilog-2001から符号付き数値がサポートされましたが、Webの記事を見るとクセがあるようです。signedと宣言すれば安心、とは行かないところが中途半端な気がします。下記の例はISimを使って試してみた結果です。

(1)符号拡張
8ビットから15ビットへの変換例
左右のビット幅が異なってもVerilogではエラーになりません。例として良くないですが。

いずれもOK
input signed [7:0] a;
output signed [14:0] answer;
output [14:0] answer2, answer3, answer4;

wire [14:0] temp;

assign temp = a;
assign answer = a;
assign answer2 = a;

assign answer3 = temp;
assign answer4 = {{7{a[7]}}, a};

いずれもNG
input [7:0] a;
output signed [14:0] answer, answer2;
output [14:0] answer3;


wire signed [14:0] temp;

assign temp = a;
assign answer = a;
assign answer2 = temp;
assign answer3 = temp;


signed使わない例
input [7:0] a;
output [14:0] answer;

assign answer = {{7{a[7]}}, a};

(2)シフト演算
符号を保持してシフトする演算例
算術シフト演算子を使う場合
input signed [7:0] a;
output signed [14:0] answer;
output signed [14:0] answer2;

assign answer = a>>>2;
assign answer2 = a<<<2; // 右側は0詰めされる


自力で符号拡張する場合
input [7:0] a;
output [14:0] answer;

output [14:0] answer2;

assign answer = {{9{a[7]}}, a[7:2]};
assign answer2 = {{7{a[7]}}, a, 2'b0};

(3)正負の反転
input signed [7:0] a;
output signed [7:0] answer;

assign answer = - a;


(4)加算
input signed [7:0] a, b;
output [7:0] answer;

assign answer = a + b;


(5)減算
input signed [7:0] a, b;
output [7:0] answer;

assign answer = a - b;


(6)乗算
input signed [7:0] a, b;
output [14:0] answer;

assign answer = a * b;
ただし、負の最大値同士の乗算でビット溢れが出ます。これを避けるためには出力のビット幅を8+8=16ビットとします。


(7)signedとunsignedが混在する場合
Webの記事で、オペランドの両方がsignedでないとunsignedとして演算されてしまうと注意がありました。さらにこのページでは、シミュレータ目線で解説されています。
これらの記事を要約すると

・全てのオペランドがsignedでないとunsignedとして演算される
・ビットセレクトはunsigned (例) a[7:2]
・連接演算結果はunsigned
・比較演算結果は1ビットでunsigned
・複雑な式はsigned/unsignedが明示的になるように分解して記述した方がよい

unsignedをsignedに変換するには、1ビット符号拡張して符号を付ける。(MSBが1だった場合に負の数と誤認されてしまうため)
wire signed [8:0] temp;
assign temp = $signed({1'b0, a[7:0]});



参考資料

2012年6月19日火曜日

FPGA FMトランスミッタ: 避けては通れぬ?2の補数(その1)

音声や音楽波形など正負の極性をもつ情報をデジタル処理する場合には2の補数(two's complement)と呼ばれる形式がよく使われています。これ、計算しようとするたびに調べ直して、しかもその度に間違えてましたので自分用に整理してみました。

2の補数
教科書には、負の数値の表現方法として3種類が載っていました。
極性振幅表示:10進数を2進数に変換して、符号ビットを最上位ビットに付加
1の補数表示:極性振幅表示の符号ビットをのぞく全てのビットを反転
2の補数表示:1の補数表示の符号ビットをのぞく2進数に1を加える


Verilogで2の補数の計算
(1)正負の反転
全ビットを反転させて1を加える。
input [7:0] a;
output [7:0] answer;

assign answer = ~a + 1;


(2)加算
Webの解説記事から引用します。加算によって最上位ビットからの繰り上がりが発生した場合、その繰り上がりは無視する。
加算した結果も、2の補数によって表現されている。
加算した結果が2の補数で表現可能な範囲に収まらない場合、オーバーフローが発生する。(正しく計算されない)

え~と、繰り上がりを無視してそのまま加算してOKということです。8ビットの加算例: a + b
input [7:0] a, b;
output [7:0] answer;

assign answer = a + b;

なおビット幅が異なる数を加算するには、符号拡張を行い相互のビット幅を揃えます
また、加算結果がオーバーフローしたら当然ながら正しい結果が得られません。次の条件に該当していたらオーバーフローが発生していることになるそうです。

最上位ビットが0の数どうしを加算した結果、最上位ビットが1になった
(つまり、非負の数どうしの加算結果が負の数になった)
最上位ビットが1の数どうしを加算した結果、最上位ビットが0になった
(つまり、負の数どうしの加算結果が非負の数になった)

(3)減算
負の数の加算として計算します。(例) 3 - 1 → 3 + (-1)
8ビットの減算例: a - b
input [7:0] a, b;
output [7:0] answer;

assign answer = a + (~b + 1);

(4)乗算
絶対値で計算したうえで、最後に符号を付ける。
例: a * b
input [7:0] a, b;
output [14:0] answer;
wire [6:0] abs_a, abs_b;
wire [13:0] abs_mul;

assign abs_a = a[7] ? (~a[6:0]) +1 : a[6:0];
assign abs_b = b[7] ? (~b[6:0]) +1 : b[6:0];
assign abs_mul = abs_a * abs_b;
assign answer = (a[7] ^ b[7]) ? {1'b1, ~abs_mul + 1} : {1'b0, abs_mul};


参考資料
・辻井重男 久保田一(1993) わかりやすいディジタル信号処理 オーム社
基本情報技術者口座1の補数と2の補数で2進数のマイナスの数値を表現する
とあるソフトウェア開発者のブログ2の補数を理解する (2)

2012年6月16日土曜日

FPGA FMトランスミッタ: 原因は431の発振だった

コーデックのループバックテストの記事でみつかったナゾのノイズ31.6kHzの原因がわかりました。シャントレギュレータ431の発振です。こんなモノが発振するとは知りませんでした。ググると多くの記事が見つかり、ごく一般的な事象のようです。

昭和的電子工作日記 - LT431発振
Venetor Sound - 電気のみなもと(源)(4)

なんでも、出力に0.01uF~数uFのコンデンサを接続すると発振する場合があると、データシートにも明記してあります。

NJM431のデータシートより

バイアス電流は5mAなのでグラフによると約2uF以上であれば問題ないはずです。接続していたのは10uFでセーフのはずですが、積層セラミックだから2.5Vが加わって容量低下?しても2uFまで下がるとは思えません。ESRが低すぎてまずいというあれでしょうか。まあ余裕を持たせた設計をしましょうということなんでしょう。


今回原因となったコンデンサ 10uF

発振時の431の出力

無音時のコーデック アナログ出力(発振対策前)

無音時のコーデック アナログ出力(発振対策後)


2012年6月10日日曜日

FPGA FMトランスミッタ: 15kHzローパスフィルタ その1

FIR Compilerでつくるローパスフィルタ

バージョンが変わって、前回から設定画面が少し変わっています。
FIR Compilerでは、複数チャンネルを時分割で処理することができますが、うまく動かせなかったので左右2チャンネルを別々に処理してみました。(所要リソースが増えます)
※実際には、別途アップするFIR Compilerを使わない頂き物のコードを使うつもりです。



周波数特性


理論値と実測値を示します。実測値はCS4270コーデックのアナログ入出力端子間で測定しました。WaveSepectraの測定の都合上測定ダイナミックレンジが取れていません(実測値:阻止域)が、19kHzサイン波の比較では100dB程度の減衰量がありました。過剰なスペックかもしれませんね。

FIR Compilerの画面

周波数特性実測値(通過域)

周波数特性実測値(阻止域)

19kHzサイン波(ローパスフィルタ通さず)

19kHzサイン波(ローパスフィルタ挿入)


WaveSpectraで周波数特性測定の覚え書き

・WaveGeneのユーザ波形で周期スイープ信号を発生
・WaveSpectraでドライバ・録音デバイス・録音フォーマットをWaveGeneと一致させる
・WaveSpectraでFFTデータ数をWaveGeneと一致させる
・WaveSpectraで窓関数を なし(矩形) とする

96kHz以上のサンプルレートでは、波形が不安定で測定できない場合あり
(当方の環境ではダメでした)

WaveSpectraを用いた周波数特性の測定について
周期スイープを用いた周波数特性の測定について



FIR Compilerの設定








lpf15k コード

FIR Compiler出力の桁取りが良くわかっていません。実験的に設定を探りました。
なお、FIR Compilerが512クロック毎に出力データをアップデートしてくれるのでサンプリングパルス en は使っていません。


module lpf15k_cg(
l15k_l_i, l15k_r_i, l15k_l_o, l15k_r_o,
clk, en, reset, l15k_thru
);

input [23:0] l15k_l_i;
input [23:0] l15k_r_i;
output [23:0] l15k_l_o;
output [23:0] l15k_r_o;
input clk;
input en;
input reset;
input l15k_thru;

wire [43:0] dout_1, dout_2;

assign l15k_l_o = (l15k_thru) ? l15k_l_i : {dout_1[43], dout_1[35:13]};
assign l15k_r_o = (l15k_thru) ? l15k_r_i : {dout_2[43], dout_2[35:13]};

// FIR Compiler v5.0
lpf15k_coregen lpf15k_coregen (
.clk(clk),
// .rfd(ready_for_data),
// .rdy(ready),
.din_1(l15k_l_i[23:6]), // input [17 : 0] din_1
.din_2(l15k_r_i[23:6]), // input [17 : 0] din_2
.dout_1(dout_1), // output [43 : 0] dout_1
.dout_2(dout_2)); // output [43 : 0] dout_2

endmodule




フィルタ係数 COEファイル


・タップ数: 256
・入出力サンプルレート: 152kHz
・通過域: 0~15kHz
・阻止域: 19kHz~

この係数ファイルは頂き物です。(JA2SVZ様ありがとうございます)


radix=2;
coefdata=
000000000000000001,
000000000000000001,
000000000000000000,
111111111111111111,
111111111111111110,
111111111111111101,
111111111111111110,
000000000000000000,