Skip to content

Instantly share code, notes, and snippets.

@yuichiro-shibata
Created April 14, 2021 19:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yuichiro-shibata/86bf9fe63d5ef16992cf8467c437c593 to your computer and use it in GitHub Desktop.
Save yuichiro-shibata/86bf9fe63d5ef16992cf8467c437c593 to your computer and use it in GitHub Desktop.
ACRi ブログ「DA コンバータがなくてもできる FPGA ピアノ (1)」のコード
`default_nettype none
module pulse_sound
#(
parameter real CLK_FREQ = 100e6, // クロック周波数 (Hz)
parameter int COUNT_WIDTH = 32 // カウンタビット幅
)
(
input wire clk,
input wire [3:0] btn_in,
output logic led_out,
output logic pulse_out
);
// カウンタ増分値の設定: 500 Hz, 1 KHz, 2 KHz, 4 KHz
localparam bit [COUNT_WIDTH-1:0] INCS[4] = '{
freq2inc(500), freq2inc(1000), freq2inc(2000), freq2inc(4000)
};
logic [COUNT_WIDTH-1:0] count = '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
assign pulse_out = count[COUNT_WIDTH-1];
assign led_out = count[COUNT_WIDTH-1];
// 周波数からカウンタの増分値を計算する関数
function bit [COUNT_WIDTH-1:0] freq2inc(real freq);
return ((2.0 ** COUNT_WIDTH) / CLK_FREQ * freq);
endfunction
endmodule
`default_nettype wire
`default_nettype none
module pulse_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
logic [3:0] btn_sync; // 同期化されたボタン入力
for (genvar i = 0; i < 4; i++) begin
// シクロナイザつきデバウンサ
sync_and_debounce
#(
.CLK_FREQ(CLK_FREQ),
.STABLE_TIME(1e-3) // 安定判定時間: 1 ms
)
sync_and_debounce_inst
(
.clk(clk),
.din(btn_in[i]),
.dout(btn_sync[i])
);
end
// パルス波発生回路
pulse_sound
#(
.CLK_FREQ(CLK_FREQ),
.COUNT_WIDTH(32)
)
pulse_sound_inst
(
.clk(clk),
.btn_in(btn_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] }];
## Pmod Header JA
set_property -dict { PACKAGE_PIN G13 IOSTANDARD LVCMOS33 } [get_ports { pulse_out }];
`default_nettype none
`timescale 1ns/1ps
module sim_pulse_sound_artytop;
localparam real CLOCK_PERIOD_NS = 10;
localparam int N = 500000;
logic clk, pulse_out, led_out;
logic [3:0] btn_in;
pulse_sound_artytop pulse_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'b0000;
#(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;
#(CLOCK_PERIOD_NS * 200)
btn_in = 4'b0001;
#(CLOCK_PERIOD_NS * N)
$finish;
end
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