書き込みには,MAX10-JBを利用した.USB Blaster互換のJTAGモジュールとして使える.

 特別版No.3のMAX10基板を,Pmodポートを拡張するベースボードに接続した.

 音を出すモジュールとして,PMOD-AUDIO V1.2(muse lab)を使用した.信号1本で制御できる.

 単純な矩形波で音を鳴らすデザインをベースに,いい感じに音色を変えるように指示した.

●Claudeの回答

くろうどモン

単純な矩形波駆動からウェーブテーブル音源方式に作り直しました。

変更点

波形ROM — 1〜8倍音を減衰させながら合成した256サンプルの単周期波形(打弦楽器的なスペクトルの近似)をcase文ベースのROMとして実装
DDS位相アキュムレータ — 16bit、約48kHz(AUDIO_DIV=1042)のサンプルレートで波形ROMを読み出し
アタック/ディケイ・エンベロープ — 発音開始で約0.7msの高速アタック→フルスケール到達後は約0.8ms毎にenv -= env >> 6の指数減衰。ピアノやハンマー打弦楽器特有の「叩いた瞬間が最大音量、あとは自然に減衰していく」性質を再現しています
8bit PWM DAC — 波形サンプル×エンベロープの積をPWMデューティに変換し、CLK/256(約195kHz)のキャリアで出力(PMOD-AUDIO側のRCフィルタでさらに平滑化されます)

●生成された音源部分(およびシーケンサ)

// 省略
module jingle_player #(
    parameter int CLK_HZ    = 50_000_000,
    parameter int AUDIO_DIV = 1042 // audio sample tick every AUDIO_DIV clk cycles (~48kHz)
) (
    input  logic clk,
    input  logic rst,
    output logic audio
);

    // 省略
    
    localparam int         NUM_STEPS = 6;
    localparam logic [2:0] REST_STEP = 3'd5;

    logic [2:0]  step;
    logic [2:0]  next_step;
    logic [15:0] phase_inc;
    logic [26:0] duration_cyc;

    assign next_step = (step == NUM_STEPS - 1) ? 3'd0 : step + 3'd1;

    always_comb begin
        unique case (step)
            3'd0: begin phase_inc = 16'd505; duration_cyc = 27'd10_000_000;  end // F#4, 200ms
            3'd1: begin phase_inc = 16'd379; duration_cyc = 27'd10_000_000;  end // C#4, 200ms
            3'd2: begin phase_inc = 16'd505; duration_cyc = 27'd10_000_000;  end // F#4, 200ms
            3'd3: begin phase_inc = 16'd379; duration_cyc = 27'd10_000_000;  end // C#4, 200ms
            3'd4: begin phase_inc = 16'd567; duration_cyc = 27'd30_000_000;  end // G#4, 600ms
            default: begin phase_inc = 16'd567; duration_cyc = 27'd130_000_000; end // ring-out, 2.6s
        endcase
    end

    // -----------------------
    // Step/duration sequencer
    // -----------------------
    logic [26:0] dur_cnt;
    logic        step_done;
    logic        note_on_pulse; // fires only when the *upcoming* step is a real note

    assign step_done     = (dur_cnt == duration_cyc - 27'd1);
    assign note_on_pulse = step_done && (next_step != REST_STEP);

    always_ff @(posedge clk) begin
        if (rst) begin
            dur_cnt <= '0;
            step    <= '0;
        end else if (step_done) begin
            dur_cnt <= '0;
            step    <= next_step;
        end else begin
            dur_cnt <= dur_cnt + 27'd1;
        end
    end

    // ------------------------------------------
    // Audio sample-rate tick (~48kHz strobe, free running)
    // ------------------------------------------
    logic [10:0] smp_cnt;
    logic        smp_en;

    assign smp_en = (smp_cnt == AUDIO_DIV - 1);

    always_ff @(posedge clk) begin
        if (rst)          smp_cnt <= '0;
        else if (smp_en)  smp_cnt <= '0;
        else              smp_cnt <= smp_cnt + 11'd1;
    end

    // 省略
    
    logic [15:0] phase;

    always_ff @(posedge clk) begin
        if (rst) begin
            phase <= '0;
        end else if (note_on_pulse) begin
            phase <= '0;
        end else if (smp_en) begin
            phase <= phase + phase_inc;
        end
    end

    // ---------------------------------------------------------------
    // Attack/decay envelope. Fast linear attack, then a slow "leaky"
    // exponential decay (~2.5s time constant) for a long bell-like ring.
    // Only retriggers (fresh attack) on a real note-on; during the rest
    // step it simply continues decaying from wherever it left off.
    // ---------------------------------------------------------------
    localparam logic       ENV_ATTACK  = 1'b0;
    localparam logic       ENV_DECAY   = 1'b1;
    localparam logic [7:0] ATTACK_STEP = 8'd8;  // envelope units added per sample (~0.7ms to full scale)
    localparam int         DECAY_TICK  = 48;    // samples between each decay update (~1ms @48kHz)
    localparam int         DECAY_SHIFT = 9;     // decay rate: env -= env >> DECAY_SHIFT (~2.5s to silence)

    logic       env_state;
    logic [7:0] env;
    logic [5:0] decay_cnt;

    always_ff @(posedge clk) begin
        if (rst) begin
            env       <= 8'd0;
            env_state <= ENV_ATTACK;
            decay_cnt <= '0;
        end else if (note_on_pulse) begin
            env       <= 8'd0;
            env_state <= ENV_ATTACK;
            decay_cnt <= '0;
        end else if (smp_en) begin
            if (env_state == ENV_ATTACK) begin
                if (env >= 8'd255 - ATTACK_STEP) begin
                    env       <= 8'd255;
                    env_state <= ENV_DECAY;
                end else begin
                    env <= env + ATTACK_STEP;
                end
            end else begin // ENV_DECAY
                if (decay_cnt == DECAY_TICK - 1) begin
                    decay_cnt <= '0;
                    env       <= env - (env >> DECAY_SHIFT);
                end else begin
                    decay_cnt <= decay_cnt + 6'd1;
                end
            end
        end
    end

    // --------------------------------------------------
    // Bell/chime-like single-cycle waveform ROM (256 entries, signed
    // --------------------------------------------------
    function automatic logic signed [7:0] wave_rom(input logic [7:0] idx);
        case (idx)
            8'd0: wave_rom = 8'sd0;
            8'd1: wave_rom = 8'sd20;
            8'd2: wave_rom = 8'sd40;
            8'd3: wave_rom = 8'sd58;
            8'd4: wave_rom = 8'sd75;
            8'd5: wave_rom = 8'sd90;
            8'd6: wave_rom = 8'sd103;
            8'd7: wave_rom = 8'sd113;
            8'd8: wave_rom = 8'sd120;
            8'd9: wave_rom = 8'sd125;
            8'd10: wave_rom = 8'sd127;
            8'd11: wave_rom = 8'sd127;
            8'd12: wave_rom = 8'sd125;
            8'd13: wave_rom = 8'sd122;
            8'd14: wave_rom = 8'sd118;
            8'd15: wave_rom = 8'sd113;
            8'd16: wave_rom = 8'sd108;
            8'd17: wave_rom = 8'sd103;
            8'd18: wave_rom = 8'sd98;
            8'd19: wave_rom = 8'sd95;
            8'd20: wave_rom = 8'sd92;
            8'd21: wave_rom = 8'sd90;
            8'd22: wave_rom = 8'sd89;
            8'd23: wave_rom = 8'sd89;
            8'd24: wave_rom = 8'sd89;
            8'd25: wave_rom = 8'sd90;
            8'd26: wave_rom = 8'sd92;
            8'd27: wave_rom = 8'sd94;
            8'd28: wave_rom = 8'sd95;
            8'd29: wave_rom = 8'sd97;
            8'd30: wave_rom = 8'sd98;
            8'd31: wave_rom = 8'sd99;
            8'd32: wave_rom = 8'sd99;
            8'd33: wave_rom = 8'sd99;
            8'd34: wave_rom = 8'sd99;
            8'd35: wave_rom = 8'sd99;
            8'd36: wave_rom = 8'sd98;
            8'd37: wave_rom = 8'sd98;
            8'd38: wave_rom = 8'sd98;
            8'd39: wave_rom = 8'sd98;
            8'd40: wave_rom = 8'sd98;
            8'd41: wave_rom = 8'sd99;
            8'd42: wave_rom = 8'sd100;
            8'd43: wave_rom = 8'sd102;
            8'd44: wave_rom = 8'sd104;
            8'd45: wave_rom = 8'sd107;
            8'd46: wave_rom = 8'sd109;
            8'd47: wave_rom = 8'sd112;
            8'd48: wave_rom = 8'sd114;
            8'd49: wave_rom = 8'sd116;
            8'd50: wave_rom = 8'sd118;
            8'd51: wave_rom = 8'sd119;
            8'd52: wave_rom = 8'sd119;
            8'd53: wave_rom = 8'sd119;
            8'd54: wave_rom = 8'sd118;
            8'd55: wave_rom = 8'sd116;
            8'd56: wave_rom = 8'sd114;
            8'd57: wave_rom = 8'sd111;
            8'd58: wave_rom = 8'sd108;
            8'd59: wave_rom = 8'sd104;
            8'd60: wave_rom = 8'sd101;
            8'd61: wave_rom = 8'sd97;
            8'd62: wave_rom = 8'sd94;
            8'd63: wave_rom = 8'sd90;
            8'd64: wave_rom = 8'sd87;
            8'd65: wave_rom = 8'sd85;
            8'd66: wave_rom = 8'sd82;
            8'd67: wave_rom = 8'sd79;
            8'd68: wave_rom = 8'sd77;
            8'd69: wave_rom = 8'sd74;
            8'd70: wave_rom = 8'sd72;
            8'd71: wave_rom = 8'sd69;
            8'd72: wave_rom = 8'sd66;
            8'd73: wave_rom = 8'sd62;
            8'd74: wave_rom = 8'sd58;
            8'd75: wave_rom = 8'sd54;
            8'd76: wave_rom = 8'sd49;
            8'd77: wave_rom = 8'sd43;
            8'd78: wave_rom = 8'sd38;
            8'd79: wave_rom = 8'sd32;
            8'd80: wave_rom = 8'sd27;
            8'd81: wave_rom = 8'sd21;
            8'd82: wave_rom = 8'sd16;
            8'd83: wave_rom = 8'sd11;
            8'd84: wave_rom = 8'sd6;
            8'd85: wave_rom = 8'sd2;
            8'd86: wave_rom = -8'sd2;
            8'd87: wave_rom = -8'sd5;
            8'd88: wave_rom = -8'sd7;
            8'd89: wave_rom = -8'sd9;
            8'd90: wave_rom = -8'sd11;
            8'd91: wave_rom = -8'sd12;
            8'd92: wave_rom = -8'sd13;
            8'd93: wave_rom = -8'sd14;
            8'd94: wave_rom = -8'sd15;
            8'd95: wave_rom = -8'sd16;
            8'd96: wave_rom = -8'sd17;
            8'd97: wave_rom = -8'sd18;
            8'd98: wave_rom = -8'sd19;
            8'd99: wave_rom = -8'sd20;
            8'd100: wave_rom = -8'sd21;
            8'd101: wave_rom = -8'sd22;
            8'd102: wave_rom = -8'sd22;
            8'd103: wave_rom = -8'sd22;
            8'd104: wave_rom = -8'sd21;
            8'd105: wave_rom = -8'sd19;
            8'd106: wave_rom = -8'sd17;
            8'd107: wave_rom = -8'sd13;
            8'd108: wave_rom = -8'sd8;
            8'd109: wave_rom = -8'sd3;
            8'd110: wave_rom = 8'sd3;
            8'd111: wave_rom = 8'sd10;
            8'd112: wave_rom = 8'sd17;
            8'd113: wave_rom = 8'sd24;
            8'd114: wave_rom = 8'sd31;
            8'd115: wave_rom = 8'sd38;
            8'd116: wave_rom = 8'sd44;
            8'd117: wave_rom = 8'sd49;
            8'd118: wave_rom = 8'sd52;
            8'd119: wave_rom = 8'sd54;
            8'd120: wave_rom = 8'sd54;
            8'd121: wave_rom = 8'sd52;
            8'd122: wave_rom = 8'sd49;
            8'd123: wave_rom = 8'sd44;
            8'd124: wave_rom = 8'sd37;
            8'd125: wave_rom = 8'sd29;
            8'd126: wave_rom = 8'sd20;
            8'd127: wave_rom = 8'sd10;
            8'd128: wave_rom = 8'sd0;
            8'd129: wave_rom = -8'sd10;
            8'd130: wave_rom = -8'sd20;
            8'd131: wave_rom = -8'sd29;
            8'd132: wave_rom = -8'sd37;
            8'd133: wave_rom = -8'sd44;
            8'd134: wave_rom = -8'sd49;
            8'd135: wave_rom = -8'sd52;
            8'd136: wave_rom = -8'sd54;
            8'd137: wave_rom = -8'sd54;
            8'd138: wave_rom = -8'sd52;
            8'd139: wave_rom = -8'sd49;
            8'd140: wave_rom = -8'sd44;
            8'd141: wave_rom = -8'sd38;
            8'd142: wave_rom = -8'sd31;
            8'd143: wave_rom = -8'sd24;
            8'd144: wave_rom = -8'sd17;
            8'd145: wave_rom = -8'sd10;
            8'd146: wave_rom = -8'sd3;
            8'd147: wave_rom = 8'sd3;
            8'd148: wave_rom = 8'sd8;
            8'd149: wave_rom = 8'sd13;
            8'd150: wave_rom = 8'sd17;
            8'd151: wave_rom = 8'sd19;
            8'd152: wave_rom = 8'sd21;
            8'd153: wave_rom = 8'sd22;
            8'd154: wave_rom = 8'sd22;
            8'd155: wave_rom = 8'sd22;
            8'd156: wave_rom = 8'sd21;
            8'd157: wave_rom = 8'sd20;
            8'd158: wave_rom = 8'sd19;
            8'd159: wave_rom = 8'sd18;
            8'd160: wave_rom = 8'sd17;
            8'd161: wave_rom = 8'sd16;
            8'd162: wave_rom = 8'sd15;
            8'd163: wave_rom = 8'sd14;
            8'd164: wave_rom = 8'sd13;
            8'd165: wave_rom = 8'sd12;
            8'd166: wave_rom = 8'sd11;
            8'd167: wave_rom = 8'sd9;
            8'd168: wave_rom = 8'sd7;
            8'd169: wave_rom = 8'sd5;
            8'd170: wave_rom = 8'sd2;
            8'd171: wave_rom = -8'sd2;
            8'd172: wave_rom = -8'sd6;
            8'd173: wave_rom = -8'sd11;
            8'd174: wave_rom = -8'sd16;
            8'd175: wave_rom = -8'sd21;
            8'd176: wave_rom = -8'sd27;
            8'd177: wave_rom = -8'sd32;
            8'd178: wave_rom = -8'sd38;
            8'd179: wave_rom = -8'sd43;
            8'd180: wave_rom = -8'sd49;
            8'd181: wave_rom = -8'sd54;
            8'd182: wave_rom = -8'sd58;
            8'd183: wave_rom = -8'sd62;
            8'd184: wave_rom = -8'sd66;
            8'd185: wave_rom = -8'sd69;
            8'd186: wave_rom = -8'sd72;
            8'd187: wave_rom = -8'sd74;
            8'd188: wave_rom = -8'sd77;
            8'd189: wave_rom = -8'sd79;
            8'd190: wave_rom = -8'sd82;
            8'd191: wave_rom = -8'sd85;
            8'd192: wave_rom = -8'sd87;
            8'd193: wave_rom = -8'sd90;
            8'd194: wave_rom = -8'sd94;
            8'd195: wave_rom = -8'sd97;
            8'd196: wave_rom = -8'sd101;
            8'd197: wave_rom = -8'sd104;
            8'd198: wave_rom = -8'sd108;
            8'd199: wave_rom = -8'sd111;
            8'd200: wave_rom = -8'sd114;
            8'd201: wave_rom = -8'sd116;
            8'd202: wave_rom = -8'sd118;
            8'd203: wave_rom = -8'sd119;
            8'd204: wave_rom = -8'sd119;
            8'd205: wave_rom = -8'sd119;
            8'd206: wave_rom = -8'sd118;
            8'd207: wave_rom = -8'sd116;
            8'd208: wave_rom = -8'sd114;
            8'd209: wave_rom = -8'sd112;
            8'd210: wave_rom = -8'sd109;
            8'd211: wave_rom = -8'sd107;
            8'd212: wave_rom = -8'sd104;
            8'd213: wave_rom = -8'sd102;
            8'd214: wave_rom = -8'sd100;
            8'd215: wave_rom = -8'sd99;
            8'd216: wave_rom = -8'sd98;
            8'd217: wave_rom = -8'sd98;
            8'd218: wave_rom = -8'sd98;
            8'd219: wave_rom = -8'sd98;
            8'd220: wave_rom = -8'sd98;
            8'd221: wave_rom = -8'sd99;
            8'd222: wave_rom = -8'sd99;
            8'd223: wave_rom = -8'sd99;
            8'd224: wave_rom = -8'sd99;
            8'd225: wave_rom = -8'sd99;
            8'd226: wave_rom = -8'sd98;
            8'd227: wave_rom = -8'sd97;
            8'd228: wave_rom = -8'sd95;
            8'd229: wave_rom = -8'sd94;
            8'd230: wave_rom = -8'sd92;
            8'd231: wave_rom = -8'sd90;
            8'd232: wave_rom = -8'sd89;
            8'd233: wave_rom = -8'sd89;
            8'd234: wave_rom = -8'sd89;
            8'd235: wave_rom = -8'sd90;
            8'd236: wave_rom = -8'sd92;
            8'd237: wave_rom = -8'sd95;
            8'd238: wave_rom = -8'sd98;
            8'd239: wave_rom = -8'sd103;
            8'd240: wave_rom = -8'sd108;
            8'd241: wave_rom = -8'sd113;
            8'd242: wave_rom = -8'sd118;
            8'd243: wave_rom = -8'sd122;
            8'd244: wave_rom = -8'sd125;
            8'd245: wave_rom = -8'sd127;
            8'd246: wave_rom = -8'sd127;
            8'd247: wave_rom = -8'sd125;
            8'd248: wave_rom = -8'sd120;
            8'd249: wave_rom = -8'sd113;
            8'd250: wave_rom = -8'sd103;
            8'd251: wave_rom = -8'sd90;
            8'd252: wave_rom = -8'sd75;
            8'd253: wave_rom = -8'sd58;
            8'd254: wave_rom = -8'sd40;
            8'd255: wave_rom = -8'sd20;
        endcase
    endfunction

    logic signed [7:0] wave_sample;
    assign wave_sample = wave_rom(phase[15:8]);

    // ---------------------------------------------------------------
    // wave_sample (signed) x env (unsigned volume) -> dry sample, then
    // through the reverb (3 comb filters + 1 allpass, BRAM delay lines)
    // ---------------------------------------------------------------
    logic signed [16:0] product;
    logic signed [13:0] dry_sample;

    assign product    = wave_sample * $signed({1'b0, env});
    assign dry_sample = product >>> 2;

    logic                     rvb_en;
    logic signed [13:0]       rvb_out;
    logic signed [13:0]       rvb_hold;

    reverb #(.DATA_W(14)) u_reverb (
        .clk        (clk),
        .rst        (rst),
        .smp_en     (smp_en),
        .dry_sample (dry_sample),
        .out_en     (rvb_en),
        .out_sample (rvb_out)
    );

    // Hold the latest reverb output steady between audio ticks -- the
    // reverb's internal pipeline (a few clk cycles) is far shorter than
    // the ~1000+ clk audio sample period, so this never sees stale data.
    always_ff @(posedge clk) begin
        if (rst)          rvb_hold <= '0;
        else if (rvb_en)  rvb_hold <= rvb_out;
    end

    logic signed [9:0] duty_sum;
    logic [7:0]        pwm_duty;

    assign duty_sum = 10'sd128 + (rvb_hold >>> 6);

    always_comb begin
        if (duty_sum < 10'sd0)        pwm_duty = 8'd0;
        else if (duty_sum > 10'sd255) pwm_duty = 8'd255;
        else                          pwm_duty = duty_sum[7:0];
    end

    // ------------------------------------------------
    // 8-bit PWM DAC, free-running at CLK_HZ/256 (~195kHz carrier)
    // ------------------------------------------------
    logic [7:0] pwm_cnt;

    always_ff @(posedge clk) begin
        if (rst) pwm_cnt <= '0;
        else     pwm_cnt <= pwm_cnt + 8'd1;
    end

    assign audio = (pwm_cnt < pwm_duty);

endmodule
広げる

●とりあえず鳴った

コメントを残す