Last active
May 4, 2021 02:16
-
-
Save yuichiro-shibata/e699fcc9caddbfd38a4c614f8672bcb6 to your computer and use it in GitHub Desktop.
ACRi ブログ「DA コンバータがなくてもできる FPGA ピアノ (2)」のコード
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 pwm_sound | |
#( | |
parameter real CLK_FREQ = 100e6, // クロック周波数 (Hz) | |
parameter int COUNT_WIDTH = 32 // カウンタビット幅 | |
) | |
( | |
input wire clk, | |
input wire [3:0] btn_in, // ボタン入力 | |
input wire [3:0] sw_in, // スライドスイッチ入力 | |
output logic led_out, | |
output logic pulse_out = 1'b0 | |
); | |
localparam real BASE_FREQ = 442.0; // 基準音の周波数 | |
localparam bit [COUNT_WIDTH-1:0] INCS[4] = '{ // カウンタの増分値 | |
n2inc(-4), n2inc(-5), n2inc(-7), n2inc(-9) // ファ,ミ,レ,ド | |
}; | |
logic [COUNT_WIDTH-1:0] count = '0; // カウンタ | |
logic [COUNT_WIDTH-1:0] duty_reg = '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) // どのボタンも押されていないときだけ | |
duty_reg[(COUNT_WIDTH-1)-:4] <= sw_in; // 上位4ビットを更新 | |
end | |
always @(posedge clk) begin | |
if (btn_in != '0 && count < duty_reg) // PWM 発生用の比較器 | |
pulse_out <= 1'b1; | |
else | |
pulse_out <= 1'b0; | |
end | |
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 pwm_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 | |
// PWM回路 | |
pwm_sound | |
#( | |
.CLK_FREQ(CLK_FREQ), | |
.COUNT_WIDTH(32) | |
) | |
pwm_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_pwm_sound_artytop; | |
localparam real CLOCK_PERIOD_NS = 10; | |
localparam int N = 2000000; | |
logic clk, pulse_out, led_out; | |
logic [3:0] btn_in, sw_in; | |
pwm_sound_artytop pwm_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'b000; | |
sw_in = 4'b1000; | |
#(CLOCK_PERIOD_NS * N) | |
btn_in = 4'b0001; | |
#(CLOCK_PERIOD_NS * N) | |
btn_in = 4'b0010; | |
#(CLOCK_PERIOD_NS * N) | |
btn_in = 4'b0100; | |
#(CLOCK_PERIOD_NS * N) | |
btn_in = 4'b1000; | |
#(CLOCK_PERIOD_NS * N) | |
btn_in = 4'b0000; | |
#(CLOCK_PERIOD_NS * N) | |
btn_in = 4'b0001; | |
#(CLOCK_PERIOD_NS * 100) | |
btn_in = 4'b0000; | |
sw_in = 4'b0100; | |
#(CLOCK_PERIOD_NS * 200) | |
btn_in = 4'b0001; | |
#(CLOCK_PERIOD_NS * N) | |
sw_in = 4'b0000; | |
$finish; | |
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