Created
May 12, 2021 07:13
-
-
Save yuichiro-shibata/1adf92737e6cbb7364cc437927bc08f6 to your computer and use it in GitHub Desktop.
ACRi ブログ「DA コンバータがなくてもできる FPGA ピアノ (3)」のコード
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 | |
module pdm_sound | |
#( | |
parameter real CLK_FREQ = 100e6, // クロック周波数 (Hz) | |
parameter int COUNT_WIDTH = 32, // カウンタビット幅 | |
parameter int PDM_WIDTH = 16 // PDM ビット幅 | |
) | |
( | |
input wire clk, | |
input wire [3:0] btn_in, // ボタン入力 | |
input wire [3:0] sw_in, // スライドスイッチ入力 | |
output logic led_out, | |
output logic pulse_out | |
); | |
localparam real BASE_FREQ = 442.0; // 基準音の周波数 | |
localparam bit [COUNT_WIDTH-1:0] INCS[4] = '{ // カウンタの増分値 | |
n2inc(-4), n2inc(-5), n2inc(-7), n2inc(-9) // ファ,ミ,レ,ド | |
}; | |
localparam [COUNT_WIDTH-1:0] DUTY = 1'b1 << (COUNT_WIDTH-1); // デューティ50% | |
logic [COUNT_WIDTH-1:0] count = '0; // カウンタ | |
logic [PDM_WIDTH-1:0] data_for_pdm = '0; // デルタシグマ変調の入力データ | |
always @(posedge clk) begin | |
if (btn_in == '0) | |
count <= '0; // どのボタンも押されていなければカウンタをクリア | |
else begin | |
for (int i = 0; i < 4; i++) begin | |
if (btn_in[i]) begin | |
count <= count + INCS[i]; // 押されたボタンに対応する増分値を加える | |
break; | |
end | |
end | |
end | |
end | |
always @(posedge clk) begin | |
if (btn_in != '0 && count < DUTY) // PWM 発生用の比較器 | |
data_for_pdm[(PDM_WIDTH-1)-:4] <= sw_in; | |
else | |
data_for_pdm <= '0; | |
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 bit [COUNT_WIDTH-1:0] n2inc(int n); | |
return ((2.0 ** (COUNT_WIDTH + n / 12.0)) * BASE_FREQ / CLK_FREQ); | |
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 pdm_sound_artytop | |
( | |
input wire clk, | |
input wire [3:0] btn_in, | |
input wire [3:0] sw_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 [3:0] sw_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]) | |
); | |
sync_and_debounce | |
#( | |
.CLK_FREQ(CLK_FREQ), | |
.STABLE_TIME(STABLE_TIME) | |
) | |
sync_and_debounce_sw | |
( | |
.clk(clk), | |
.din(sw_in[i]), | |
.dout(sw_sync[i]) | |
); | |
end | |
// PDM によるパルス波発生の回路 | |
pdm_sound | |
#( | |
.CLK_FREQ(CLK_FREQ), | |
.COUNT_WIDTH(32), | |
.PDM_WIDTH(16) | |
) | |
pdm_sound_inst | |
( | |
.clk(clk), | |
.btn_in(btn_sync), | |
.sw_in(sw_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] }]; | |
## Switches | |
set_property -dict { PACKAGE_PIN A8 IOSTANDARD LVCMOS33 } [get_ports { sw_in[0] }]; #IO_L12N_T1_MRCC_16 Sch=sw[0] | |
set_property -dict { PACKAGE_PIN C11 IOSTANDARD LVCMOS33 } [get_ports { sw_in[1] }]; #IO_L13P_T2_MRCC_16 Sch=sw[1] | |
set_property -dict { PACKAGE_PIN C10 IOSTANDARD LVCMOS33 } [get_ports { sw_in[2] }]; #IO_L13N_T2_MRCC_16 Sch=sw[2] | |
set_property -dict { PACKAGE_PIN A10 IOSTANDARD LVCMOS33 } [get_ports { sw_in[3] }]; #IO_L14P_T2_SRCC_16 Sch=sw[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 | |
`timescale 1ns/1ps | |
module sim_pdm_sound_artytop; | |
localparam real CLOCK_PERIOD_NS = 10; | |
localparam int N = 1300000; | |
logic clk, pulse_out, led_out; | |
logic [3:0] btn_in, sw_in; | |
pdm_sound_artytop pdm_sound_artytop_inst | |
( | |
.clk(clk), | |
.btn_in(btn_in), | |
.sw_in(sw_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; | |
sw_in = 4'b1000; | |
#(CLOCK_PERIOD_NS * N / 2) | |
btn_in = 4'b0001; | |
#(CLOCK_PERIOD_NS * N) | |
btn_in = 4'b0000; | |
sw_in = 4'b0100; | |
#(CLOCK_PERIOD_NS * N / 2) | |
btn_in = 4'b0001; | |
#(CLOCK_PERIOD_NS * N) | |
btn_in = 4'b0000; | |
sw_in = 4'b0010; | |
#(CLOCK_PERIOD_NS * N / 2) | |
btn_in = 4'b0010; | |
#(CLOCK_PERIOD_NS * N) | |
btn_in = 4'b0000; | |
sw_in = 4'b0001; | |
#(CLOCK_PERIOD_NS * N / 2) | |
btn_in = 4'b1000; | |
#(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 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