Skip to content

Instantly share code, notes, and snippets.

@yuichiro-shibata
Last active June 9, 2021 15:08
Show Gist options
  • Save yuichiro-shibata/80689033fae964ecfd68e99a0a2dc3e8 to your computer and use it in GitHub Desktop.
Save yuichiro-shibata/80689033fae964ecfd68e99a0a2dc3e8 to your computer and use it in GitHub Desktop.
ACRi ブログ「DA コンバータがなくてもできる FPGA ピアノ (5)」のコード
`default_nettype none
module autoplayer
#(
parameter real CLK_FREQ = 100e6, // クロック周波数 (Hz)
parameter real MM = 140 // メトロノームテンポ
)
(
input wire clk,
input wire ctrl_in, // 制御ボタン入力
output logic [47:0] en_out
);
localparam int N_COUNTS = $ceil(CLK_FREQ * 60.0 / MM / 4.0); // 16分音符を基準
localparam int N_TACTS = 880; // データの行数
logic is_playing = 1'b0; // 演奏状態レジスタ
logic ctrl_pressed; // 制御ボタン入力イベントフラグ
logic ctrl_in_prev = 1'b0; // 1クロック前の制御ボタン
logic [$clog2(N_COUNTS)-1:0] count = '0; // テンポ制御用カウンタ
logic [$clog2(N_TACTS)-1:0] addr; // アドレスカウンタ
// 制御ボタン入力の立ち上がりエッジ検出
always @(posedge clk) begin
ctrl_in_prev <= ctrl_in;
end
assign ctrl_pressed = ctrl_in & (!ctrl_in_prev);
// 演奏開始・停止の状態制御
always @(posedge clk) begin
if (!is_playing && ctrl_pressed)
is_playing <= 1'b1;
else if (is_playing && addr == (N_TACTS-1) && count == (N_COUNTS-1))
is_playing <= 1'b0;
else if (is_playing && ctrl_pressed)
is_playing <= 1'b0;
end
// 設定されたテンポで1拍をカウント
always @(posedge clk) begin
if (count == (N_COUNTS-1))
count <= '0;
else
count <= count + 1'b1;
end
// アドレスのインクリメント
always @(posedge clk) begin
if (!is_playing)
addr <= '0;
else if (count == (N_COUNTS-1)) begin
if (addr == (N_TACTS-1))
addr <= '0;
else
addr <= addr + 1'b1;
end
end
// ROM を BRAM として推論させファイルで初期化
(* rom_style = "block" *) logic [47:0] play_mem[N_TACTS];
initial $readmemh("eknm.mem", play_mem);
// ROM の読み出し
logic [47:0] play_mem_out;
always @(posedge clk) begin
play_mem_out <= play_mem[addr];
end
// 読み出したイネーブル情報を出力
assign en_out = is_playing? play_mem_out: '0;
endmodule
`default_nettype wire
`default_nettype none
module delta_sigma
#(
parameter int WIDTH = 16
)
(
input wire clk,
input wire [WIDTH-1:0] data_in,
output logic pulse_out
);
logic [(WIDTH+1)-1:0] sigma_reg = '1; // シグマレジスタ
always @(posedge clk) begin
sigma_reg <= sigma_reg + {pulse_out, data_in}; // デルタ加算とシグマ加算
end
assign pulse_out = ~sigma_reg[(WIDTH+1)-1]; // 2値化
endmodule
`default_nettype wire
001011081000
001011081000
001011081000
001011081000
000000000000
000000000000
000080080080
000080080080
001001001000
001001001000
001001001000
001001001000
000000000000
000000000000
000080080080
000080080080
001001001000
001001001000
000080080080
000080080080
001001001000
001001001000
010010010000
010010010000
080080080000
080080080000
080080080000
080080080000
000000000000
000000000000
000000000000
000000000000
020020020000
020020020000
020020020000
020020020000
000000000000
000000000000
004004004000
004004004000
020020020000
020020020000
020020020000
020020020000
000000000000
000000000000
004004004000
004004004000
020020020000
020020020000
004004004000
004004004000
000800800800
000800800800
004004004000
004004004000
000080080080
000080080080
000080080080
000080080080
000000000000
000000000000
000000000000
000000000000
001011011000
001000080000
000001011000
000000080000
001001011000
001000080000
001001011000
001000080000
001001011000
001000080000
010001011000
010000080000
004001011000
000000080000
001001011000
000000080000
004004021000
001000080000
000804021000
000000080000
000804021000
000800080000
000804021000
000800080000
000804021000
000804080000
004020021000
004000080000
020000821000
000000880000
000804021000
000000080000
004001011000
004000080000
001001011000
000000080000
001001011000
001000080000
001001011000
001000080000
001001011000
001000080000
010001011000
010000080000
004001011000
000000080000
001001011000
000000080000
004004021000
001000080000
000804021000
000000080000
000804021000
000800080000
000804021000
000800080000
000804021000
000804080000
004020021000
004000080000
020000821000
000000880000
000804021000
000000080000
001000091000
000000080000
001000081000
000000080000
001020084000
000800080000
000220084000
000800080000
001080090000
000000080000
001080090000
000000080000
010020080800
004020080000
001010080800
004004080000
010001081000
000000080000
010001081000
000000080000
080000884000
020000080000
010000884000
020000080000
080001090000
080001090000
080001090000
080001090000
000000000000
000000000000
000000000000
000000000000
000090000000
000090000000
000090000000
000090000000
000090000000
000090000000
000090000000
000090000000
000220000000
000220000000
000220000000
000220000000
000220000000
000220000000
000220000000
000220000000
000090000880
000024000880
000024000880
000000000880
000024000880
000024000880
000024000880
000000000880
000024001200
000011001200
000011001200
000000001200
000011001200
000011001200
000011001200
000000001200
000010220020
000004220020
000004220020
000000000000
000004220020
000004220020
000004220020
000000000000
000001024080
000001024080
000000824080
000000024000
000000204080
000000004080
000000804080
000000004080
000001080090
000001080090
000000000000
000000000000
000004880080
000004880080
000000000000
000000000000
000011081000
000011081000
000000081000
000000081000
000000000000
000000000000
000000000000
000000000000
000090000000
000090000000
000090000000
000090000000
000090000000
000090000000
000090000000
000090000000
000220000000
000220000000
000220000000
000220000000
000220000000
000220000000
000220000000
000220000000
000090800080
000090800080
000024800080
000000800080
000024800080
000000800080
000024800080
000000800080
000025000200
000025000200
000011000200
000001000200
000011000200
000001000200
000011000200
000001000200
000010220020
000010220020
000004220020
000000000000
000004220020
000000000020
000004220020
000000000000
000001024080
000001024080
000000824080
000000800000
000000224080
000000200080
000000824080
000000800000
000001011000
000001010000
000001011000
000001010000
000001011000
000001010000
000001011000
000001010000
000001011000
000000000000
000001011000
000001814000
000004021000
000004020000
000000805000
000000804000
000011001000
000011000000
000011001000
000011000000
000011001000
000011000000
000011001000
000011000000
000011001000
000011000000
000004801000
000011000000
000024001000
000024000000
000004801000
000004800000
000090001000
000090000000
000091001000
000090000000
000091001000
000090000000
000091001000
000090000000
000221001000
000220000000
000221001000
000220000000
000824001000
000820000000
000824001000
000820000000
001011001000
001010000000
001011001000
001010000000
004080800800
004080000000
004080800800
004080000000
010081001000
010080000000
010081001000
010080000000
041000200200
041000000000
041000200200
041000000000
080800080080
080804000000
080800080080
080804000000
080800080080
080804000000
004800080080
004804000000
041000200080
041004000000
041000200080
005004000000
041000200080
041004000000
041000200080
005004000000
080800080080
080804000000
080800080080
080804000000
080800080080
080804000000
004800080080
004804000000
041000200080
041004000000
041000200080
005004000000
041000200080
041004000000
041000200080
005004000000
080880800080
000000800080
080881000200
080881000200
080884000800
000004000800
080881000200
080881000200
080880800080
000000800080
080881000200
080881000200
080884000800
000004000800
080880800080
080880800080
080290001000
000010001000
080284004000
080284004000
080281010000
000001010000
080284004000
080284004000
080290001000
000010001000
080284004000
080284004000
080282010000
000002010000
080290002000
080290002000
040044004000
040200000000
004044004000
004200000000
080084004000
080800000000
004084004000
004800000000
040044004000
040200000000
004044004000
004200000000
080084004000
080800000000
004084004000
004800000000
040244004000
040240000000
000004004004
000000000000
000004004004
000000000000
000004004004
000000000000
000004004004
000004004004
000004004004
000004004004
000000000000
000000000000
000000000000
000000000000
004004000000
004004000000
004004000000
004004000000
004004000000
001001000000
000800800000
000200200000
000080080000
000080080000
000000000000
000000000000
010010800100
010010800100
000000000000
000000000000
001001010200
001001010200
000000000000
000000000000
000201200080
000201200080
000000000000
000000000000
004004200040
004004200040
000000000000
000000000000
000000040004
000000040004
000000000000
000000000000
000800800000
000800800000
000800800000
000800800000
000800a00008
000200200008
000080280008
000040240008
000010090010
000010090010
000000000000
000000000000
001001200001
001001200001
000000000000
000000000000
000800880004
000800880004
000800880004
000800880004
000800884000
000800884000
000800884000
000800884000
000200244000
000200244000
000200242000
000200242000
000000001000
000000001000
000000000200
000000000200
000004000080
000004000080
004004000000
000004000000
004004040200
000001040200
004000840000
000000240000
004000080800
000000080800
004000000000
000000000000
004010800100
000010800100
004000000000
000000000000
004001010200
000001010200
004000000000
000000000000
004000201080
000000201080
004000000000
000000000000
004004000240
000004000240
004004000200
000004000200
010000000004
000000000004
040001200000
000000000000
040001200010
040001200010
080000880000
080000880000
000000001000
000000001000
011000000000
000000000000
011000004000
011000004000
004800000000
004800000000
000000000004
000000000004
000041000000
000041000000
000080880080
000080000080
000080880080
000080000080
000001200000
000000000000
004001200000
000000000000
080004800000
080000000000
040004800000
040000000000
010000880000
010000000000
004000880000
004000000000
010000240004
010000000004
004000240004
004000000004
000000880000
000000000000
004000880000
000000000000
004001200000
000000000000
004001200000
000000000000
004000240000
000000000000
004000240000
000000000000
010000880080
010000000080
004000880080
004000000080
000001200000
000000000000
004001200000
000000000000
080004800000
080000000000
040004800000
040000000000
010000880000
010000000000
004000880000
004000000000
010000240004
010000000004
004000240004
004000000004
000000880000
000000000000
004000880000
000000000000
004001200000
000000000000
004001200000
000000000000
004000240000
000000000000
004000240000
000000000000
010000880000
010000800000
004000880080
004000880080
000000800800
000000800800
000000200200
000000200200
010810100100
010810100100
010810010010
010810010010
010810040040
004810040040
001810100100
000810100100
001200200200
001200200200
001201001000
001201001000
000000200200
000000200200
000000080080
000000080080
004204040040
004204040040
004204004004
004204004004
004204010010
001204010010
000a04040040
000204040040
000880080080
000800000000
000880000080
000800000080
001080000200
000000000200
004080000800
000000000800
011080001000
011000000000
044080001000
090000000000
044080001000
000000000000
011080001000
000000000000
011080004000
011080000000
004804004000
000000000000
000884004000
000000000000
004804004000
000000000000
004804000004
004800000000
001204000004
000000000000
000884000004
000000000000
000241000004
000000000000
000080800080
000080000080
000080880080
000080000080
000001200000
000000000000
004001200000
000000000000
080004800000
080000000000
040004800000
040000000000
010000880000
010000000000
004000880000
004000000000
040000240004
010000000004
004000240004
004000000004
000000880000
000000000000
004000880000
000000000000
004001200000
000000000000
004001200000
000000000000
004000240000
000000000000
004000240000
000000000000
040000880080
010000000080
004000880080
004000000080
000001200000
000000000000
004001200000
000000000000
080004800000
080000000000
040004800000
040000000000
010000880000
010000000000
004000880000
004000000000
040000240004
010000000004
004000240004
004000000004
000000880000
000000000000
004000880000
000000000000
004001200000
000000000000
004001200000
000000000000
004000240000
000000000000
004000240000
000000000000
040000880000
010000800000
004000880080
004000880080
000000800800
000000800800
000000200200
000000200200
010810100100
010810100100
010810010010
010810010010
010810040040
004810040040
001810100100
000810100100
001200200200
001200200200
001201001000
001201001000
000000200200
000000200200
000000080080
000000080080
004204040040
004204040040
004204004004
004204004004
004204010010
001204010010
000a04040040
000204040040
000880080080
000800000000
000880000080
000800000080
001080000200
000000000200
004080000800
000000000800
011080001000
011000000000
044080001000
090000000000
044080001000
000000000000
011080001000
000000000000
011080004000
011080000000
004804004000
000000000000
000884004000
000000000000
004804004000
000000000000
004804000004
004800000000
001204000004
000000000000
000884000004
000000000000
000241000004
000000000000
000080800080
000080000000
000004004004
000004004004
000010010010
000010010010
000040040040
000040040040
000080080080
000000000000
000080080080
000080000000
000800200200
000200000000
000080200200
000200000000
000800800800
000800800800
000040040040
000040040040
000080080080
000080080080
000200200200
000200200200
000800800800
000000000000
000800800800
000800000000
004001001000
001000000000
000801001000
001000000000
004004004000
000000000000
004004004000
004000000000
010008008000
008000000000
002008008000
008000000000
010010010000
010010010000
010010010000
010010010000
000000000000
000000000000
000000000000
000000000000
000011201000
000011201000
000011201000
000011201000
000011201000
000011201000
000201201000
000201201000
000081204000
000081204000
000041204000
000041204000
000011084000
000011084000
000005044000
000005044000
000080880080
000080880080
000000000000
000000000000
000804080080
000804080080
000000000000
000000000000
000080880080
000080880080
000000000000
000000000000
000000000000
000000000000
000000000000
000000000000
`default_nettype none
module piano
#(
parameter real CLK_FREQ = 100e6, // クロック周波数 (Hz)
parameter int PDM_WIDTH = 16, // PDM ビット幅
parameter int SINE_WIDTH = 25, // 正弦波ビット幅
parameter int SINE_K_WIDTH = 18, // 正弦波発生式の係数ビット幅
parameter real BASE_FREQ = 442.0, // 正弦波周波数
parameter real SINE_AMP = 0.98, // 正弦波振幅 ( < 1)
parameter int NKEYS = 48, // 鍵の数
parameter int NBUTTONS = 4 // 入力ボタンの数
)
(
input wire clk,
input wire [NBUTTONS-1:0] btn_in, // ボタン入力
input wire play_ctrl_in, // スタート・ストップボタン入力
output logic led_out,
output logic pulse_out
);
// ボタン入力に対応させる鍵盤の番号
localparam bit [$clog2(NKEYS)-1:0] BUTTON_NO[NBUTTONS] = '{
22, 21, 19, 17 // ファ,ミ,レ,ド
};
logic [NKEYS-1:0] en; // 正弦波生成のイネーブル
logic signed [SINE_WIDTH-1:0] sine[NKEYS]; // 正弦波の出力データ
logic [NKEYS-1:0] autoplayer_out; // 自動演奏によるイネーブル出力
// 正弦波生成
for (genvar i = 0; i < NKEYS; i++) begin
sine_wave_generator
#(
.CLK_FREQ(CLK_FREQ),
.WIDTH(SINE_WIDTH),
.K_WIDTH(SINE_K_WIDTH),
.TARGET_FREQ(n2freq(i - 26)), // 周波数の指定
.AMP(SINE_AMP)
)
sine_wave_generator_inst
(
.clk(clk),
.en_in(en[i]),
.dout(sine[i])
);
end
// 自動演奏
autoplayer
#(
.CLK_FREQ(CLK_FREQ),
.MM(140) // テンポの設定
)
autoplayer_inst
(
.clk(clk),
.ctrl_in(play_ctrl_in),
.en_out(autoplayer_out)
);
always_comb begin // 自動演奏によるイネーブルとボタン入力の論理和
en = autoplayer_out;
for (int i = 0; i < NBUTTONS; i++)
en |= btn_in[i] << BUTTON_NO[i];
end
// パイプライン型ツリー加算器
localparam int N_ARY = 4; // 木の基数
localparam int N_L1 = $ceil(real'(NKEYS) / N_ARY); // L1 のノード数
localparam int N_L2 = $ceil(real'(N_L1) / N_ARY); // L2 のノード数
logic signed [(SINE_WIDTH+2)-1:0] lv1_sum_reg[N_L1] = '{default: '0};
logic signed [(SINE_WIDTH+4)-1:0] lv2_sum_reg[N_L2] = '{default: '0};
logic signed [(SINE_WIDTH+6)-1:0] all_sum_reg = '0;
// L1 の加算
logic signed [(SINE_WIDTH+2)-1:0] lv1_sum[N_L1];
always_comb begin
for (int i = 0; i < NKEYS; i += N_ARY) begin
lv1_sum[i / N_ARY] = '0;
for (int j = 0; j < N_ARY && (i + j) < NKEYS; j++)
lv1_sum[i / N_ARY] += sine[i + j];
end
end
// L2 の加算
logic signed [(SINE_WIDTH+4)-1:0] lv2_sum[N_L2];
always_comb begin
for (int i = 0; i < N_L1; i += N_ARY) begin
lv2_sum[i / N_ARY] = '0;
for (int j = 0; j < N_ARY && (i + j) < N_L1; j++)
lv2_sum[i / N_ARY] += lv1_sum_reg[i + j];
end
end
// ツリー加算器のパイプライン
always @(posedge clk) begin
lv1_sum_reg <= lv1_sum;
lv2_sum_reg <= lv2_sum;
all_sum_reg <= lv2_sum_reg[0] + lv2_sum_reg[1] + lv2_sum_reg[2];
end
// デルタシグマ変調
logic [PDM_WIDTH-1:0] data_for_pdm = '0; // デルタシグマ変調の入力データ
always @(posedge clk) begin // オフセットバイナリへ変換
data_for_pdm <= {~all_sum_reg[(SINE_WIDTH+6)-1],
all_sum_reg[(SINE_WIDTH+6)-2-:(PDM_WIDTH-1)]};
end
delta_sigma
#(
.WIDTH(PDM_WIDTH)
)
delta_sigma_inst
(
.clk(clk),
.data_in(data_for_pdm),
.pulse_out(pulse_out)
);
assign led_out = pulse_out; // 確認用 LED 出力
// 基準音の距離から周波数を計算する関数
function real n2freq(int n);
return ((2.0 ** (n / 12.0)) * BASE_FREQ);
endfunction
endmodule
`default_nettype wire
`default_nettype none
module piano_artytop
(
input wire clk,
input wire [3:0] btn_in,
input wire ck_rst_in,
output logic led_out,
output logic pulse_out
);
localparam real CLK_FREQ = 100e6; // クロック周波数: 100MHz
localparam real STABLE_TIME = 1e-3; // 安定判定時間: 1 ms
logic [3:0] btn_sync;
logic play_ctrl_sync;
// シクロナイザつきデバウンサ
for (genvar i = 0; i < 4; i++) begin
sync_and_debounce
#(
.CLK_FREQ(CLK_FREQ),
.STABLE_TIME(STABLE_TIME)
)
sync_and_debounce_btn
(
.clk(clk),
.din(btn_in[i]),
.dout(btn_sync[i])
);
end
sync_and_debounce
#(
.CLK_FREQ(CLK_FREQ),
.STABLE_TIME(STABLE_TIME)
)
sync_and_debounce_btn
(
.clk(clk),
.din(~ck_rst_in), // アクティブ Low ボタンのため反転
.dout(play_ctrl_sync)
);
// ピアノモジュール
piano
#(
.CLK_FREQ(CLK_FREQ),
.PDM_WIDTH(16),
.SINE_WIDTH(25),
.SINE_K_WIDTH(18),
.BASE_FREQ(442.0),
.SINE_AMP(0.98),
.NKEYS(48),
.NBUTTONS(4)
)
piano_inst
(
.clk(clk),
.btn_in(btn_sync),
.play_ctrl_in(play_ctrl_sync),
.led_out(led_out),
.pulse_out(pulse_out)
);
endmodule
`default_nettype wire
## Clock signal
set_property -dict { PACKAGE_PIN E3 IOSTANDARD LVCMOS33 } [get_ports { clk }];
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports { clk }];
## LED
set_property -dict { PACKAGE_PIN H5 IOSTANDARD LVCMOS33 } [get_ports { led_out }];
## Buttons
set_property -dict { PACKAGE_PIN D9 IOSTANDARD LVCMOS33 } [get_ports { btn_in[0] }];
set_property -dict { PACKAGE_PIN C9 IOSTANDARD LVCMOS33 } [get_ports { btn_in[1] }];
set_property -dict { PACKAGE_PIN B9 IOSTANDARD LVCMOS33 } [get_ports { btn_in[2] }];
set_property -dict { PACKAGE_PIN B8 IOSTANDARD LVCMOS33 } [get_ports { btn_in[3] }];
set_property -dict { PACKAGE_PIN C2 IOSTANDARD LVCMOS33 } [get_ports { ck_rst_in }];
## Pmod Header JA
set_property -dict { PACKAGE_PIN G13 IOSTANDARD LVCMOS33 } [get_ports { pulse_out }];
`default_nettype none
`timescale 1ns/1ps
module sim_piano_artytop;
localparam real CLOCK_PERIOD_NS = 10;
localparam int N = 400000;
logic clk, ck_rst_in, pulse_out, led_out;
logic [3:0] btn_in;
piano_artytop piano_artytop_inst
(
.clk(clk),
.btn_in(btn_in),
.ck_rst_in(ck_rst_in),
.led_out(led_out),
.pulse_out(pulse_out)
);
initial begin
clk = 1'b0;
forever begin
#(CLOCK_PERIOD_NS / 2.0)
clk = 1'b1;
#(CLOCK_PERIOD_NS / 2.0)
clk = 1'b0;
end
end
initial begin
btn_in = 4'b0000;
ck_rst_in = 1'b1;
#(CLOCK_PERIOD_NS * N)
btn_in = 4'b0001;
#(CLOCK_PERIOD_NS * N)
btn_in = 4'b0011;
#(CLOCK_PERIOD_NS * N)
btn_in = 4'b0010;
#(CLOCK_PERIOD_NS * N)
btn_in = 4'b0000;
#(CLOCK_PERIOD_NS * N)
btn_in = 4'b1100;
#(CLOCK_PERIOD_NS * N)
btn_in = 4'b000;
#(CLOCK_PERIOD_NS * N)
$finish;
end
// ローパスフィルタ
localparam real R = 22.2e3; // 22.2kohm
localparam real C = 100.0e-12; // 100 pf
localparam real DELTA_T_NS = 1.0;
localparam real DELTA_T = DELTA_T_NS * 1.0e-9;
real v_in, v_out = 0.0;
always #DELTA_T_NS begin
v_in = pulse_out? 3.3: 0.0;
v_out = (R * C * v_out + DELTA_T * v_in) / (R * C + DELTA_T);
end
endmodule
`default_nettype wire
`default_nettype none
module sine_wave_generator
#(
parameter real CLK_FREQ = 100e6, // クロック周波数 (Hz)
parameter int WIDTH = 25, // データビット幅
parameter int K_WIDTH = 18, // 係数 k のビット幅
parameter real TARGET_FREQ = 100, // 生成波の周波数
parameter real AMP = 0.98 // 生成波の振幅 ( < 1)
)
(
input wire clk,
input wire en_in, // イネーブル入力
output logic [WIDTH-1:0] dout
);
localparam real PI = 3.14159265358979323846; // 円周率
localparam real K = 4.0 * PI * TARGET_FREQ / CLK_FREQ; // 係数 k
localparam real G0 = AMP * $cos(2.0 * PI * TARGET_FREQ / CLK_FREQ); // 初期値
localparam int K_Q = calc_shift_amount(K, K_WIDTH - 1); // k の小数部ビット幅
localparam bit signed [K_WIDTH-1:0] K_V = K * (2.0 ** K_Q); // 正規化された k
logic signed [WIDTH-1:0] freg = '0; // sin レジスタ
logic signed [WIDTH-1:0] greg = G0 * (2.0 ** (WIDTH-1)); // cos レジスタ
logic signed [WIDTH-1:0] kterm; // 係数 k の乗算結果
logic is_ringing = 1'b0; // 発振状態レジスタ
logic turn = 1'b0; // リープフロッグ状態レジスタ
logic zero_crossed; // ゼロクロス検出フラグ
logic previous_sign = 1'b0; // 1クロック前の出力値の符号
// 発振開始・停止の状態制御
always @(posedge clk) begin
if (!is_ringing && en_in)
is_ringing <= 1'b1;
else if (!en_in && zero_crossed)
is_ringing <= 1'b0;
end
// リープフロッグの状態制御
always @(posedge clk) begin
if (!is_ringing)
turn <= '0;
else
turn <= !turn;
end
// 乗算
assign kterm = ((K_WIDTH+WIDTH)'(K_V) * (!turn? greg: freg)) >>> K_Q;
// レジスタ更新
always @(posedge clk) begin
if (!is_ringing) begin
freg <= '0;
greg <= G0 * (2.0 ** (WIDTH-1));
end
else begin
if (!turn)
freg <= freg + kterm;
else
greg <= greg - kterm;
end
end
// 出力
assign dout = freg;
// 1クロック前の符号を保存しゼロクロスを検出
always @(posedge clk) begin
previous_sign <= dout[WIDTH-1];
end
assign zero_crossed = (dout[WIDTH-1] != previous_sign);
// 数値 x を width ビットで正規化するのに必要なシフト量を求める関数
function int calc_shift_amount(real x, int width);
longint t;
int q;
for (t = x, q = 0; !t[width-1] && q < 128; q++) // シフトの最大値を128に制限
t = x * (2.0 ** (q + 1));
return q;
endfunction
endmodule
`default_nettype wire
`default_nettype none
module sync_and_debounce
#(
parameter real CLK_FREQ = 100e6, // クロック周波数 (Hz)
parameter real STABLE_TIME = 1e-3 // 安定したと判定するまでの時間 (秒)
)
(
input wire clk,
input wire din,
output logic dout = 1'b0
);
// 2段のシフトレジスタによるシンクロナイザ
logic din_meta = 1'b0, din_sync = 1'b0;
always @(posedge clk) begin
din_meta <= din; // メタステーブルの危険あり
din_sync <= din_meta; // 同期化された入力信号
end
// デバウンス用カウンタのビット幅と最大値
localparam int COUNT_WIDTH = $clog2(int'(STABLE_TIME * CLK_FREQ));
localparam bit [COUNT_WIDTH-1:0] MAX_COUNT = STABLE_TIME * CLK_FREQ - 1;
// カウンタ
logic [COUNT_WIDTH-1:0] count = '0;
always @(posedge clk) begin
if (din_sync != dout) begin // 入力と出力が異なるならカウンタを進める
if (count == MAX_COUNT)
count <= '0;
else
count <= count + 1'b1;
end
else // 入力と出力が同じならカウンタをクリア
count <= '0;
end
// 出力レジスタ
always @(posedge clk) begin
// カウンタが最大値まで達したら入力の変化を出力に伝える
if (din_sync != dout && count == MAX_COUNT)
dout <= din_sync;
end
endmodule
`default_nettype wire
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment