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,