Created
May 26, 2021 05:02
-
-
Save yuichiro-shibata/fcd7788334376e2b22b619cc1439d4b8 to your computer and use it in GitHub Desktop.
ACRi ブログ「DA コンバータがなくてもできる FPGA ピアノ (4)」のコード
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`default_nettype none | |
`timescale 1ns/1ps | |
module sim_sine_sound_artytop; | |
localparam real CLOCK_PERIOD_NS = 10; | |
localparam int N = 1000000; | |
logic clk, pulse_out, led_out; | |
logic [3:0] btn_in, sw_in; | |
sine_sound_artytop sine_sound_artytop_inst | |
( | |
.clk(clk), | |
.btn_in(btn_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'b0001; | |
#(CLOCK_PERIOD_NS * 2 * N) | |
btn_in = 4'b0000; | |
#(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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`default_nettype none | |
module sine_sound | |
#( | |
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 SINE_FREQ = 440, // 正弦波周波数 | |
parameter real SINE_AMP = 0.98 // 正弦波振幅 ( < 1) | |
) | |
( | |
input wire clk, | |
input wire [3:0] btn_in, // ボタン入力 | |
output logic led_out, | |
output logic pulse_out | |
); | |
logic [SINE_WIDTH-1:0] sin; | |
logic [PDM_WIDTH-1:0] data_for_pdm = '0; | |
// 正弦波生成 | |
sine_wave_generator | |
#( | |
.CLK_FREQ(CLK_FREQ), | |
.WIDTH(SINE_WIDTH), | |
.K_WIDTH(SINE_K_WIDTH), | |
.TARGET_FREQ(SINE_FREQ), | |
.AMP(SINE_AMP) | |
) | |
sine_wave_generator_inst | |
( | |
.clk(clk), | |
.dout(sin) | |
); | |
always @(posedge clk) begin | |
if (btn_in[0]) // ボタン押下なら正弦波をオフセットバイナリへ変換 | |
data_for_pdm <= {~sin[SINE_WIDTH-1], sin[(SINE_WIDTH-2)-:(PDM_WIDTH-1)]}; | |
else // 固定値 (MSB だけ1) | |
data_for_pdm <= 1'b1 << (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 出力 | |
endmodule | |
`default_nettype wire |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`default_nettype none | |
module sine_sound_artytop | |
( | |
input wire clk, | |
input wire [3:0] btn_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; | |
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 | |
// 正弦波発生とデルタシグマ変調 | |
sine_sound | |
#( | |
.CLK_FREQ(CLK_FREQ), | |
.PDM_WIDTH(16), | |
.SINE_WIDTH(25), | |
.SINE_K_WIDTH(18), | |
.SINE_FREQ(440), | |
.SINE_AMP(0.98) | |
) | |
sine_sound_inst | |
( | |
.clk(clk), | |
.btn_in(btn_sync), | |
.led_out(led_out), | |
.pulse_out(pulse_out) | |
); | |
endmodule | |
`default_nettype wire |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## 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] }]; | |
## Pmod Header JA | |
set_property -dict { PACKAGE_PIN G13 IOSTANDARD LVCMOS33 } [get_ports { pulse_out }]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`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, | |
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 turn = 1'b0; // 状態レジスタ | |
// クロックごとに状態を交代 | |
always @(posedge clk) begin | |
turn <= !turn; | |
end | |
// 乗算 | |
assign kterm = ((K_WIDTH+WIDTH)'(K_V) * (!turn? greg: freg)) >>> K_Q; | |
// レジスタ更新 | |
always @(posedge clk) begin | |
if (!turn) | |
freg <= freg + kterm; | |
else | |
greg <= greg - kterm; | |
end | |
// 出力 | |
assign dout = freg; | |
// 数値 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`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