▲開発環境

・Windows10

・Ollama cloud

・deepseek-v4.1-flash

・VS Code + Copilot agent

・Quartus Prime Lite + Questa

▲ハードウェア

・max10(Altera)

・特別版No.3のボード + 自作Pmod拡張ボード

・PMOD-TFTLCD v1.1 (muse lab)

・PMOD-microphone v1.0 (muse lab)

スピーカから220Hzおよび440Hzのサイン波を流して動作確認

●プロンプト

▲初期プロンプト

FPGAデザインを作る

# Application
マイクを使って音声情報を取得し,その音声波形をLCDに表示する
Quartus Primerのプロジェクトとして実装する

# Hardware
## FPGA
10M08SCE144C8G

## LCD
PMOD-TFTLCD v1.1(muse lab)
Resolusion:320x240
https://github.com/wuxx/icesugar/blob/master/schematic/pmod-tftlcd-v1.1.pdf

## Microphone
PMOD-Microphone v1.0(muse lab)
https://github.com/wuxx/icesugar/blob/master/schematic/pmod-microphone-v1.0.pdf

# Software
## Development Evironment
Quartus Prime
## HDL
SystemVerilog

# Physical constraint
Pmod 1 | Microphone
46 | NC
44 | NC
41 | NC
38 | NC
45 | WS
43 | NC
39 | SD
47 | SCK


Pmod 2 |  LCD
80 | NC
79 | NC
77 | RS
76 | NC
81 | CS
78 | MOSI
74 | NC
75 | CLK
広げる

▲追加プロンプト

アプリケーションは正しく動いているようです.次の点を改良してください.
・ノイズが多いので移動平均を取る
・振幅後入力値のゲインを下げる

●生成されたRTLコード

top.sv

// ============================================================
// top.sv
//   マイクで取り込んだ音声波形を LCD に表示する
//
//   [PMOD 1] PMOD-Microphone v1.0 (I2S MEMS マイク)
//     FPGA が I2S マスタとして SCK / WS を生成し SD を受信
//
//   [PMOD 2] PMOD-TFTLCD v1.1 (ILI9341, 320x240, SPI)
//     音声波形を 1 サンプル 1 列で描画
//
//   構成:
//     i2s_rx     : I2S 受信      -> sample (16bit 符号付き)
//     moving_avg : 移動平均      -> ノイズ低減
//     audio_buf  : 波形用 RAM    -> 4096 サンプルのリングバッファ
//     lcd_wave   : ILI9341 制御 + 波形描画
//
//   ボタン (btn_rst) を押すとリセット
// ============================================================

module top (
    // --- クロック / リセット ---
    input  logic       clk,        // PIN_88 : 50 MHz
    input  logic       btn_rst,    // PIN_17 : 押下 = Low

    // --- PMOD 1 : Microphone ---
    output logic       mic_sck,    // PIN_47 : SCK
    output logic       mic_ws,     // PIN_45 : WS
    input  logic       mic_sd,     // PIN_39 : SD

    // --- PMOD 2 : LCD ---
    output logic       lcd_cs,     // PIN_81 : CS
    output logic       lcd_dc,     // PIN_77 : RS
    output logic       lcd_mosi,   // PIN_78 : MOSI
    output logic       lcd_sck,    // PIN_75 : CLK

    // --- LED ---
    output logic       led,        // PIN_85 : フレーム描画ごとにトグル
    output logic       led0,       // PIN_123: マイク入力検出
    output logic       led1,       // PIN_122: サンプル受信ごとにトグル
    output logic       led2,       // PIN_121: 未使用 (0)
    output logic       led3        // PIN_120: LCD 初期化完了
);

    // ========================================================
    // パラメータ
    // ========================================================
    localparam int CLK_HZ  = 50_000_000;   // 入力クロック
    localparam int SCK_HZ  = 1_000_000;    // I2S ビットクロック (Fs = 15.625 kHz)
    localparam int BUF_AW  = 12;           // 波形 RAM アドレス幅 (4096 サンプル)

    localparam int AVG_N         = 8;      // 移動平均のサンプル数
    localparam int AMP_GAIN_SHIFT = 0;     // 波形表示ゲイン (1 -> 2倍)

    // ========================================================
    // 電源投入時リセット + ボタン 2 段同期化
    //   rst_n : Low 有効の非同期リセット
    // ========================================================
    logic [15:0] por_cnt;
    logic        btn_r1, btn_r2;
    logic        rst_n;

    always_ff @(posedge clk) begin
        if (por_cnt != 16'hFFFF) por_cnt <= por_cnt + 16'd1;
    end

    always_ff @(posedge clk) begin
        btn_r1 <= btn_rst;
        btn_r2 <= btn_r1;
    end

    assign rst_n = (por_cnt == 16'hFFFF) & btn_r2;

    // ========================================================
    // I2S マイク受信
    // ========================================================
    logic signed [15:0] sample;
    logic               sample_valid;

    i2s_rx #(
        .CLK_HZ (CLK_HZ),
        .SCK_HZ (SCK_HZ)
    ) u_i2s_rx (
        .clk          (clk),
        .rst_n        (rst_n),
        .mic_sck      (mic_sck),
        .mic_ws       (mic_ws),
        .mic_sd       (mic_sd),
        .sample       (sample),
        .sample_valid (sample_valid)
    );

    // ========================================================
    // 移動平均フィルタ (ノイズ低減)
    //   AVG_N サンプル分の平均をとることで、波形に乗る
    //   高周波ノイズを抑える。
    // ========================================================
    logic signed [15:0] avg_sample;
    logic               avg_valid;

    moving_avg #(
        .N  (AVG_N),
        .DW (16)
    ) u_moving_avg (
        .clk       (clk),
        .rst_n     (rst_n),
        .valid_in  (sample_valid),
        .data_in   (sample),
        .data_out  (avg_sample),
        .valid_out (avg_valid)
    );

    // ========================================================
    // 波形用リングバッファ
    // ========================================================
    logic [BUF_AW-1:0] rd_addr;
    logic [BUF_AW-1:0] wr_ptr;
    logic signed [15:0] rdata;

    audio_buf #(
        .AW (BUF_AW),
        .DW (16)
    ) u_audio_buf (
        .clk      (clk),
        .we       (avg_valid),
        .wdata    (avg_sample),
        .wr_ptr   (wr_ptr),
        .rd_addr  (rd_addr),
        .rdata    (rdata)
    );

    // ========================================================
    // LCD コントローラ + 波形描画
    // ========================================================
    logic init_done;
    logic frame_tick;

    lcd_wave #(
        .CLK_HZ         (CLK_HZ),
        .BUF_AW         (BUF_AW),
        .LCD_W          (320),
        .LCD_H          (240),
        .AMP_GAIN_SHIFT (AMP_GAIN_SHIFT),
        .FRAME_WAIT     (2_500_000)   // 50 ms
    ) u_lcd_wave (
        .clk        (clk),
        .rst_n      (rst_n),
        .lcd_cs     (lcd_cs),
        .lcd_dc     (lcd_dc),
        .lcd_mosi   (lcd_mosi),
        .lcd_sck    (lcd_sck),
        .rd_addr    (rd_addr),
        .rdata      (rdata),
        .wr_ptr     (wr_ptr),
        .init_done  (init_done),
        .frame_tick (frame_tick)
    );

    // ========================================================
    // LED インジケータ
    // ========================================================
    logic led_frame;
    logic led_sample;
    logic [22:0] mic_act_cnt;

    // フレーム描画ごとにトグル
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n)       led_frame <= 1'b0;
        else if (frame_tick) led_frame <= ~led_frame;
    end

    // サンプル受信ごとにトグル
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n)             led_sample <= 1'b0;
        else if (sample_valid)  led_sample <= ~led_sample;
    end

    // マイク入力がある間点灯 (約 0.17 秒間保持)
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n)            mic_act_cnt <= '0;
        else if (sample_valid) mic_act_cnt <= '0;
        else if (mic_act_cnt != 23'h7FFFFF) mic_act_cnt <= mic_act_cnt + 23'd1;
    end

    assign led  = led_frame;
    assign led0 = (mic_act_cnt != 23'h7FFFFF);
    assign led1 = led_sample;
    assign led2 = 1'b0;
    assign led3 = init_done;

endmodule
広げる

moving_avg.sv

// ============================================================
// moving_avg.sv
//   移動平均フィルタ (ノイズ低減用)
//
//   N サンプルの単純移動平均を出力する。
//   ランニングサム方式なので乗算器は不要 (加算 2 回のみ)。
//
//   Fs = 15.625 kHz, N = 8 の場合の -3dB 遮断周波数は約 1.1 kHz。
//   耳障りな高周波ノイズを落としつつ、音声波形の形は保たれる。
//
//   平均は「サンプル列を N 個ずつ矩形窓で平均」する FIR フィルタと
//   等価。乗算が不要で FPGA では最も安価な平滑化手段。
// ============================================================

module moving_avg #(
    parameter int N  = 8,    // 平均するサンプル数 (2 の冪)
    parameter int DW = 16    // データ幅
) (
    input  logic                 clk,
    input  logic                 rst_n,      // 非同期リセット (Low 有効)

    input  logic                 valid_in,   // 入力有効 (1 クロックパルス)
    input  logic signed [DW-1:0] data_in,

    output logic signed [DW-1:0] data_out,   // 移動平均値
    output logic                 valid_out   // 1 クロック遅れて出力
);

    localparam int AW = $clog2(N);        // 履歴バッファのアドレス幅
    // 和のビット幅を 1bit 余分に取る。
    // これにより acc - hist[wp] の中間結果が 19bit からはみ出しても
    // 切り捨てが起きない (Verilog は全オペランドの最大幅で演算するため)。
    localparam int SW = DW + AW + 1;

    logic signed [DW-1:0] hist [0:N-1];   // 過去 N サンプルの履歴
    logic [AW-1:0]        wp;             // 履歴の書き込み位置
    logic signed [SW-1:0] acc;            // 現在の窓の合計値
    logic [AW:0]          fill;           // 有効なサンプル数 (0 .. N)

    // 窓が埋まったかどうかの判定用 (幅を明示して切捨て警告を避ける)
    localparam logic [AW:0] N_CNT = N[AW:0];

    // 次の合計
    //   窓がまだ埋まっていない間は hist[wp] を引かない
    //   (hist は 1 度も書かれていない位置を参照するため)
    //   窓が埋まった後はランニングサムで最古の値を引く
    wire signed [SW-1:0] acc_next = (fill == N_CNT)
                                 ? (acc - hist[wp] + data_in)
                                 : (acc + data_in);

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            // hist をリセットするループを置くと合成時にラッチを
            // 推論するツールがあるため、acc を 0 に戻し
            // fill カウンタで窓が埋まるまで hist を参照しない方式にする
            wp        <= '0;
            acc       <= '0;
            fill      <= '0;
            data_out  <= '0;
            valid_out <= 1'b0;
        end else begin
            valid_out <= valid_in;

            if (valid_in) begin
                hist[wp] <= data_in;
                wp       <= wp + 1'b1;
                acc      <= acc_next;
                if (fill != N_CNT)
                    fill <= fill + 1'b1;
                // 合計 / N : 符号付きなので算術シフト相当のビット選択
                // acc_next[SW-2:AW] は符号ビットを含む DW bit になる
                data_out <= acc_next[SW-2:AW];
            end
        end
    end

endmodule
広げる

lcd_wave.sv

// ============================================================
// lcd_wave.sv
//   ILI9341 SPI LCD コントローラ + 波形描画
//   PMOD-TFTLCD v1.1 (320x240, RGB565)
//
//   画面構成 (横向き: MADCTL = 0x28)
//     背景   : 黒
//     グリッド: 32px ごとの暗い線
//     中央線  : グレー (振幅 0 のライン)
//     波形    : 緑 (1 サンプル = 1 列)
//
//   描画方式:
//     フレーム開始時に書き込みポインタ (wr_ptr) をラッチし、
//     その 320 サンプル前から現在までのデータを
//     左→右に 1 サンプル 1 列で描画する。
//
//   SPI : Mode 0, SCK = CLK_HZ / 4 (= 12.5 MHz @ 50 MHz)
// ============================================================

module lcd_wave #(
    parameter int CLK_HZ = 50_000_000,
    parameter int BUF_AW = 12,           // 波形 RAM アドレス幅
    parameter int LCD_W  = 320,
    parameter int LCD_H  = 240,
    parameter int AMP_GAIN_SHIFT = 2,    // 振幅ゲイン (左シフト量)
    parameter int FRAME_WAIT = 2_500_000, // フレーム間隔 (50MHz -> 50ms)
    parameter int POWERON_WAIT = 7_500_000, // 電源ON後待機 (50MHz -> 150ms)
    parameter int SWRST_WAIT   = 250_000,   // SWRESET 後待機 (5ms)
    parameter int SLPOUT_WAIT  = 6_000_000  // SLPOUT 後待機 (120ms)
) (
    input  logic               clk,
    input  logic               rst_n,       // 非同期リセット (Low 有効)

    // --- PMOD-TFTLCD ---
    output logic               lcd_cs,      // CS   (Low 有効)
    output logic               lcd_dc,      // RS   (1=Data, 0=Command)
    output logic               lcd_mosi,    // MOSI
    output logic               lcd_sck,     // SCK

    // --- 波形 RAM 読み出し ---
    output logic [BUF_AW-1:0]  rd_addr,
    input  logic signed [15:0] rdata,
    input  logic [BUF_AW-1:0]  wr_ptr,      // RAM 書き込みポインタ

    // --- ステータス ---
    output logic               init_done,
    output logic               frame_tick    // 1 フレーム描画完了パルス
);

    // ========================================================
    // 定数
    // ========================================================
    // SPI バイト送信器は 1 ビットあたり 4 クロック使うため
    // SPI クロック = CLK_HZ / 4 = 12.5 MHz となる。
    localparam int TOTAL_PIX = LCD_W * LCD_H;      // 76800
    localparam int CNT_W     = $clog2(TOTAL_PIX);  // 17

    localparam int Y_CENTER = LCD_H / 2;           // 120
    localparam int Y_OFFSET = Y_CENTER - 1;        // 119 (振幅 0 の行)
    localparam int Y_MAXROW = LCD_H - 1;           // 239

    // 定数 (比較用は int のままにしておくとビット幅の切捨て警告が出ない)
    localparam int LAST_PIX = TOTAL_PIX - 1;
    localparam int LAST_COL = LCD_W - 1;
    localparam int LAST_ROW = LCD_H - 1;

    // サイズ付き定数 (演算に使うものだけ幅を明示する)
    localparam logic [BUF_AW-1:0]  OFF_PREV     = LCD_W;
    localparam logic [23:0]        POWERON_CNT  = POWERON_WAIT;
    localparam logic [23:0]        FRAME_WAIT_C = FRAME_WAIT;
    localparam logic [23:0]        SWRST_CNT    = SWRST_WAIT;
    localparam logic [23:0]        SLPOUT_CNT   = SLPOUT_WAIT;
    localparam logic [8:0]         Y_OFFSET_C   = Y_OFFSET;
    localparam logic [7:0]         Y_MAXROW_C   = Y_MAXROW;

    localparam logic [15:0] COL_BG   = 16'h0000;   // 黒
    localparam logic [15:0] COL_GRID = 16'h1082;   // 暗い緑 (グリッド)
    localparam logic [15:0] COL_AXIS = 16'h4208;   // グレー (中央線)
    localparam logic [15:0] COL_WAVE = 16'h07E0;   // 緑 (波形)

    // ========================================================
    // 初期化シーケンス ROM
    //   bit[8] = 0 : CMD (DC = 0)
    //   bit[8] = 1 : DAT (DC = 1)
    // ========================================================
    localparam [6:0] ROM_DEPTH = 7'd64;

    localparam [8:0]
        R00=9'h001, R01=9'h011,                        // SWRESET, SLPOUT
        R02=9'h03A, R03=9'h155,                        // COLMOD  = 0x55
        R04=9'h036, R05=9'h128,                        // MADCTL  = 0x28 (横)
        R06=9'h0B1, R07=9'h100, R08=9'h118,            // FRMCTR1
        R09=9'h0C0, R10=9'h123,                        // PWCTR1
        R11=9'h0C1, R12=9'h110,                        // PWCTR2
        R13=9'h0C5, R14=9'h13E, R15=9'h128,            // VMCTR1
        R16=9'h0C7, R17=9'h186,                        // VMCTR2
        R18=9'h026, R19=9'h101,                        // GAMMASET = 1
        R20=9'h0E0,                                    // GMCTRP1
        R21=9'h10F, R22=9'h131, R23=9'h12B, R24=9'h10C,
        R25=9'h10E, R26=9'h108, R27=9'h14E, R28=9'h1F1,
        R29=9'h137, R30=9'h107, R31=9'h110, R32=9'h103,
        R33=9'h10E, R34=9'h109, R35=9'h100,
        R36=9'h0E1,                                    // GMCTRN1
        R37=9'h100, R38=9'h10E, R39=9'h114, R40=9'h103,
        R41=9'h111, R42=9'h107, R43=9'h131, R44=9'h1C1,
        R45=9'h148, R46=9'h108, R47=9'h10F, R48=9'h10C,
        R49=9'h131, R50=9'h136, R51=9'h10F,
        R52=9'h02A, R53=9'h100, R54=9'h100,            // CASET (0..319)
        R55=9'h101, R56=9'h13F,
        R57=9'h02B, R58=9'h100, R59=9'h100,            // PASET (0..239)
        R60=9'h100, R61=9'h0EF,
        R62=9'h029,                                    // DISPON
        R63=9'h02C;                                    // Memory Write

    function automatic [8:0] rom_read(input [6:0] addr);
        case (addr)
            7'd0:  rom_read = R00;  7'd1:  rom_read = R01;
            7'd2:  rom_read = R02;  7'd3:  rom_read = R03;
            7'd4:  rom_read = R04;  7'd5:  rom_read = R05;
            7'd6:  rom_read = R06;  7'd7:  rom_read = R07;
            7'd8:  rom_read = R08;  7'd9:  rom_read = R09;
            7'd10: rom_read = R10;  7'd11: rom_read = R11;
            7'd12: rom_read = R12;  7'd13: rom_read = R13;
            7'd14: rom_read = R14;  7'd15: rom_read = R15;
            7'd16: rom_read = R16;  7'd17: rom_read = R17;
            7'd18: rom_read = R18;  7'd19: rom_read = R19;
            7'd20: rom_read = R20;  7'd21: rom_read = R21;
            7'd22: rom_read = R22;  7'd23: rom_read = R23;
            7'd24: rom_read = R24;  7'd25: rom_read = R25;
            7'd26: rom_read = R26;  7'd27: rom_read = R27;
            7'd28: rom_read = R28;  7'd29: rom_read = R29;
            7'd30: rom_read = R30;  7'd31: rom_read = R31;
            7'd32: rom_read = R32;  7'd33: rom_read = R33;
            7'd34: rom_read = R34;  7'd35: rom_read = R35;
            7'd36: rom_read = R36;  7'd37: rom_read = R37;
            7'd38: rom_read = R38;  7'd39: rom_read = R39;
            7'd40: rom_read = R40;  7'd41: rom_read = R41;
            7'd42: rom_read = R42;  7'd43: rom_read = R43;
            7'd44: rom_read = R44;  7'd45: rom_read = R45;
            7'd46: rom_read = R46;  7'd47: rom_read = R47;
            7'd48: rom_read = R48;  7'd49: rom_read = R49;
            7'd50: rom_read = R50;  7'd51: rom_read = R51;
            7'd52: rom_read = R52;  7'd53: rom_read = R53;
            7'd54: rom_read = R54;  7'd55: rom_read = R55;
            7'd56: rom_read = R56;  7'd57: rom_read = R57;
            7'd58: rom_read = R58;  7'd59: rom_read = R59;
            7'd60: rom_read = R60;  7'd61: rom_read = R61;
            7'd62: rom_read = R62;  7'd63: rom_read = R63;
            default: rom_read = 9'h000;
        endcase
    endfunction

    // ========================================================
    // 電源 ON 後 150ms 待機
    // ========================================================
    logic [23:0] poweron_cnt;
    logic        init_start;

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            poweron_cnt <= '0;
            init_start  <= 1'b0;
        end else if (poweron_cnt == POWERON_CNT) begin
            init_start <= 1'b1;
        end else begin
            poweron_cnt <= poweron_cnt + 24'd1;
        end
    end

    // ========================================================
    // SPI バイト送信器 (Mode 0, MSB ファースト)
    //   tx_cnt[1:0] : 0=MSB 出力, 1=SCK 立上, 2=保持, 3=SCK 立下
    //   tx_cnt[5:2] : ビット番号 0..7
    // ========================================================
    logic [7:0] tx_byte;
    logic       tx_dc_reg;
    logic       tx_start;
    logic       tx_busy;
    logic [5:0] tx_cnt;

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            lcd_cs   <= 1'b1;
            lcd_sck  <= 1'b0;
            lcd_mosi <= 1'b0;
            lcd_dc   <= 1'b0;
            tx_busy  <= 1'b0;
            tx_cnt   <= '0;
        end else if (tx_start && !tx_busy) begin
            tx_busy  <= 1'b1;
            tx_cnt   <= '0;
            lcd_cs   <= 1'b0;
            lcd_dc   <= tx_dc_reg;
            lcd_sck  <= 1'b0;
            lcd_mosi <= tx_byte[7];
        end else if (tx_busy) begin
            tx_cnt <= tx_cnt + 6'd1;
            case (tx_cnt[1:0])
                2'b00: lcd_mosi <= tx_byte[7 - tx_cnt[5:2]];
                2'b01: lcd_sck  <= 1'b1;
                2'b10: ; // 半周期保持
                2'b11: begin
                    lcd_sck <= 1'b0;
                    if (tx_cnt[5:2] == 4'd7) begin
                        tx_busy <= 1'b0;
                        lcd_cs  <= 1'b1;
                    end
                end
            endcase
        end
    end

    // ========================================================
    // ステートマシン
    // ========================================================
    // ステートマシン (typedef enum は古いシミュレータで扱いにくいため
    // localparam の整数で表現する)
    localparam [3:0]
        S_WAIT       = 4'd0,
        S_INIT_TX    = 4'd1,
        S_INIT_WAIT  = 4'd2,
        S_SWRST_DLY  = 4'd3,
        S_SLPOUT_DLY = 4'd4,
        S_FRAME_START= 4'd5,
        S_PIX_CALC   = 4'd6,
        S_PIX_RD     = 4'd7,
        S_PIX_HI_TX  = 4'd8,
        S_PIX_HI_W   = 4'd9,
        S_PIX_LO_TX  = 4'd10,
        S_PIX_LO_W   = 4'd11,
        S_FRAME_END  = 4'd12,
        S_NEXT_FRAME = 4'd13;

    logic [3:0]  state;
    logic [6:0]  init_idx;
    logic [23:0] dly_cnt;
    logic [23:0] frm_cnt;
    logic [CNT_W-1:0] pix_cnt;
    logic [8:0]  col_cnt;
    logic [7:0]  row_cnt;
    logic [15:0] cur_color;
    logic [BUF_AW-1:0] rd_base;

    // 読み出したサンプルから波形の Y 座標を求める
    //   16bit 符号付き -> ゲイン (左シフト) を掛け、振幅 1 ビット = 1 px
    //   として中央 (Y_OFFSET) からの変位に変換する
    wire signed [31:0] samp_off = $signed(rdata) <<< AMP_GAIN_SHIFT;
    wire signed [31:0] y_raw    = $signed({23'd0, Y_OFFSET_C}) - samp_off;
    wire        [7:0]  y_wave   = (y_raw < 32'sd0)                        ? 8'd0 :
                                  (y_raw > $signed({24'd0, Y_MAXROW_C}))  ? Y_MAXROW_C :
                                                                           y_raw[7:0];

    // 波形は 1 サンプル = 1 列で描画する。
    // 隣接列とのつながりを良くするため WAVE_THICK px の太さで描く。
    localparam int WAVE_THICK = 3;    // 奇数 (1 = 1px 幅)
    localparam int WAVE_HALF  = WAVE_THICK / 2;
    localparam logic [7:0] WAVE_HALF_C = WAVE_HALF;
    wire [7:0] y_lo = (y_wave < WAVE_HALF_C) ? 8'd0
                                             : (y_wave - WAVE_HALF_C);
    wire [7:0] y_hi = ((y_wave + WAVE_HALF_C) > Y_MAXROW_C) ? Y_MAXROW_C
                                                            : (y_wave + WAVE_HALF_C);

    // 現在の列を描画するサンプル位置
    //   rd_base は「最新サンプル + 1」の位置なので、1 サンプル戻してから
    //   LCD_W サンプル前を読み出す (最新の LCD_W サンプルを左→右に描画)
    //   col_cnt は 9bit 固定なので BUF_AW 幅へゼロ拡張する
    wire [BUF_AW-1:0] col_off;
    generate
        if (BUF_AW <= 9) begin : g_col_off
            assign col_off = col_cnt[BUF_AW-1:0];
        end else begin : g_col_off
            assign col_off = {{(BUF_AW-9){1'b0}}, col_cnt};
        end
    endgenerate

    wire [BUF_AW-1:0] rd_idx = rd_base - 1'b1 - OFF_PREV + col_off;

    assign rd_addr = rd_idx;

    // ピクセル色 (S_PIX_RD で rdata が有効)
    function automatic [15:0] pixel_color(
        input [8:0] x,
        input [7:0] y,
        input [7:0] wy_lo,
        input [7:0] wy_hi
    );
        if ((y >= wy_lo) && (y <= wy_hi))         pixel_color = COL_WAVE;
        else if (y == Y_OFFSET_C[7:0])            pixel_color = COL_AXIS;
        else if (x[4:0] == 5'd0)                  pixel_color = COL_GRID;
        else if (y[4:0] == 5'd0)                  pixel_color = COL_GRID;
        else                                      pixel_color = COL_BG;    endfunction

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state      <= S_WAIT;
            init_idx   <= '0;
            dly_cnt    <= '0;
            frm_cnt    <= '0;
            pix_cnt    <= '0;
            col_cnt    <= '0;
            row_cnt    <= '0;
            cur_color  <= '0;
            tx_start   <= 1'b0;
            tx_byte    <= '0;
            tx_dc_reg  <= 1'b0;
            rd_base    <= '0;
            init_done  <= 1'b0;
            frame_tick <= 1'b0;
        end else begin
            tx_start   <= 1'b0;
            frame_tick <= 1'b0;

            case (state)

                // ---- 電源 ON 待機 ----
                S_WAIT: begin
                    if (init_start) begin
                        init_idx <= '0;
                        state    <= S_INIT_TX;
                    end
                end

                // ---- 初期化コマンド/データ送信 ----
                S_INIT_TX: begin
                    if (init_idx < ROM_DEPTH) begin
                        if (!tx_busy && !tx_start) begin
                            {tx_dc_reg, tx_byte} <= rom_read(init_idx);
                            tx_start <= 1'b1;
                            state    <= S_INIT_WAIT;
                        end
                    end else begin
                        init_done <= 1'b1;
                        state     <= S_FRAME_START;
                    end
                end

                S_INIT_WAIT: begin
                    if (!tx_busy && !tx_start) begin
                        case (tx_byte)
                            8'h01: begin dly_cnt <= '0; state <= S_SWRST_DLY;  end
                            8'h11: begin dly_cnt <= '0; state <= S_SLPOUT_DLY; end
                            default: begin
                                init_idx <= init_idx + 7'd1;
                                state    <= S_INIT_TX;
                            end
                        endcase
                    end
                end

                // SWRESET 後 5ms
                S_SWRST_DLY: begin
                    if (dly_cnt == SWRST_CNT) begin
                        init_idx <= init_idx + 7'd1;
                        state    <= S_INIT_TX;
                    end else begin
                        dly_cnt <= dly_cnt + 24'd1;
                    end
                end

                // SLPOUT 後 120ms
                S_SLPOUT_DLY: begin
                    if (dly_cnt == SLPOUT_CNT) begin
                        init_idx <= init_idx + 7'd1;
                        state    <= S_INIT_TX;
                    end else begin
                        dly_cnt <= dly_cnt + 24'd1;
                    end
                end

                // ---- フレーム開始 ----
                // 書き込みポインタをラッチして描画範囲を固定する
                S_FRAME_START: begin
                    rd_base <= wr_ptr;
                    pix_cnt <= '0;
                    col_cnt <= '0;
                    row_cnt <= '0;
                    state   <= S_PIX_CALC;
                end

                // ---- 読み出しアドレス設定 ----
                S_PIX_CALC: begin
                    state <= S_PIX_RD;
                end

                // ---- 波形 RAM 読み出し待ち (1 サイクル) ----
                S_PIX_RD: begin
                    cur_color <= pixel_color(col_cnt, row_cnt, y_lo, y_hi);
                    state     <= S_PIX_HI_TX;
                end

                // ---- 上位バイト送信 ----
                S_PIX_HI_TX: begin
                    if (!tx_busy && !tx_start) begin
                        tx_byte   <= cur_color[15:8];
                        tx_dc_reg <= 1'b1;
                        tx_start  <= 1'b1;
                        state     <= S_PIX_HI_W;
                    end
                end

                S_PIX_HI_W: begin
                    if (!tx_busy && !tx_start) state <= S_PIX_LO_TX;
                end

                // ---- 下位バイト送信 ----
                S_PIX_LO_TX: begin
                    if (!tx_busy && !tx_start) begin
                        tx_byte   <= cur_color[7:0];
                        tx_dc_reg <= 1'b1;
                        tx_start  <= 1'b1;
                        state     <= S_PIX_LO_W;
                    end
                end

                // ---- カウンタ更新 ----
                S_PIX_LO_W: begin
                    if (!tx_busy && !tx_start) begin
                        if (col_cnt == LAST_COL) begin
                            col_cnt <= '0;
                            row_cnt <= row_cnt + 8'd1;
                        end else begin
                            col_cnt <= col_cnt + 9'd1;
                        end

                        if (pix_cnt == LAST_PIX) begin
                            frame_tick <= 1'b1;
                            frm_cnt    <= '0;
                            state      <= S_FRAME_END;
                        end else begin
                            pix_cnt <= pix_cnt + 1'b1;
                            state   <= S_PIX_CALC;
                        end
                    end
                end

                // ---- フレーム間待機 ----
                S_FRAME_END: begin
                    if (frm_cnt == FRAME_WAIT_C)
                        state <= S_NEXT_FRAME;
                    else
                        frm_cnt <= frm_cnt + 24'd1;
                end

                // ---- Memory Write 再発行 -> 次フレーム開始 ----
                // ILI9341 は 0x2C を送るとアドレスポインタが先頭に戻る
                S_NEXT_FRAME: begin
                    if (!tx_busy && !tx_start) begin
                        tx_byte   <= 8'h2C;
                        tx_dc_reg <= 1'b0;
                        tx_start  <= 1'b1;
                        state     <= S_FRAME_START;
                    end
                end

                default: state <= S_WAIT;
            endcase
        end
    end

endmodule
広げる

i2s_rx.sv

// ============================================================
// i2s_rx.sv
//   PMOD-Microphone v1.0 (I2S MEMS マイク) 受信回路
//
//   FPGA がマスタとなり SCK (ビットクロック) と
//   WS (LRCLK / ワードセレクト) を生成し、
//   SD から I2S フォーマットの音声データを取り込む。
//
//   フォーマット : Philips I2S
//     - WS は SCK の立ち下がりエッジで変化
//     - データは MSB ファースト
//     - WS 変化の 1 SCK 後に MSB が出力される
//     - 受信側は SCK の立ち上がりエッジでサンプリング
//     - 1 フレーム = 64 SCK (32bit x 2ch)
//
//   サンプリング周波数 Fs = SCK_HZ / 64
//     SCK_HZ = 1 MHz  ->  Fs = 15.625 kHz
//
//   出力 sample : 24bit データの上位 16bit (符号付き, 2 の補数)
// ============================================================

module i2s_rx #(
    parameter int CLK_HZ = 50_000_000,   // 入力クロック周波数
    parameter int SCK_HZ = 1_000_000     // ビットクロック周波数
) (
    input  logic               clk,
    input  logic               rst_n,        // 非同期リセット (Low 有効)

    // --- PMOD-Microphone ---
    output logic               mic_sck,      // ビットクロック (FPGA -> MIC)
    output logic               mic_ws,       // ワードセレクト (FPGA -> MIC)
    input  logic               mic_sd,       // シリアルデータ (MIC -> FPGA)

    // --- 取り込んだ音声データ ---
    output logic signed [15:0] sample,       // 24bit の上位 16bit
    output logic               sample_valid  // 1 サンプル確定時の 1 クロックパルス
);

    // --------------------------------------------------------
    // ビットクロック生成
    //   div_cnt : 0 .. DIV-1
    //   SCK : div_cnt < HALF -> Low, それ以外 -> High
    // --------------------------------------------------------
    localparam int DIV  = (CLK_HZ + SCK_HZ / 2) / SCK_HZ;  // 50
    localparam int HALF = DIV / 2;                         // 25

    logic [15:0] div_cnt;
    logic [5:0]  bit_idx;      // 0 .. 63 (フレーム内ビット位置)
    logic        ws_r;
    logic [23:0] shift;        // 受信シフトレジスタ
    logic signed [15:0] sample_r;
    logic        valid_r;

    wire       div_wrap  = (div_cnt == 16'(DIV - 1));
    wire       rise_tick = (div_cnt == 16'(HALF - 1));   // SCK 立ち上がり
    wire       fall_tick = div_wrap;                     // SCK 立ち下がり

    wire [4:0]  bit_pos    = bit_idx[4:0];               // チャネル内 0..31
    wire        in_data    = (bit_pos >= 5'd1) && (bit_pos <= 5'd24);
    wire        is_left    = (ws_r == 1'b0);
    wire [23:0] shift_next = {shift[22:0], mic_sd};
    wire [5:0]  bit_next   = (bit_idx == 6'd63) ? 6'd0 : (bit_idx + 6'd1);

    assign mic_sck = (div_cnt >= 16'(HALF));
    assign mic_ws  = ws_r;

    assign sample       = sample_r;
    assign sample_valid = valid_r;

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            div_cnt  <= '0;
            bit_idx  <= '0;
            ws_r     <= 1'b0;
            shift    <= '0;
            sample_r <= '0;
            valid_r  <= 1'b0;
        end else begin
            div_cnt <= div_wrap ? '0 : (div_cnt + 16'd1);
            valid_r <= 1'b0;

            // --- SCK 立ち下がり: ビット位置と WS を更新 ---
            //     左ch = bit_idx 0..31, 右ch = bit_idx 32..63
            if (fall_tick) begin
                bit_idx <= bit_next;
                ws_r    <= (bit_next >= 6'd32);
            end

            // --- SCK 立ち上がり: データを取り込む ---
            //     bit_pos = 0 は I2S の 1 ビット遅延なので取り込まない
            if (rise_tick && in_data) begin
                shift <= shift_next;

                // 左チャネル (= モノラルマイクのデータ) を採用
                if ((bit_pos == 5'd24) && is_left) begin
                    sample_r <= shift_next[23:8];   // 24bit の上位 16bit
                    valid_r  <= 1'b1;
                end
            end
        end
    end

endmodule
広げる

audio_buf.sv

// ============================================================
// audio_buf.sv
//   音声波形表示用 シングルクロック デュアルポート RAM
//
//   書き込み : i2s_rx が取り込んだサンプル (Fs = 15.625 kHz)
//   読み出し : lcd_wave の画面描画 (ランダムアクセス)
//
//   深さ 4096 サンプル (≈ 262 ms)
//     LCD 1 フレームの描画には約 110 ms かかる。
//     フレーム開始時に書き込みポインタをスナップショットして
//     描画中の領域が上書きされないようにするため、
//     深さは「1 フレーム中に進むサンプル数 (約 1720) +
//     表示サンプル数 (240)」より十分大きくとる。
//     4096 - 240 = 3856 サンプル (≈ 246 ms) > 110 ms
// ============================================================

module audio_buf #(
    parameter int AW = 12,   // アドレス幅 (2^12 = 4096)
    parameter int DW = 16    // データ幅
) (
    input  logic                 clk,

    // --- 書き込みポート ---
    input  logic                 we,
    input  logic signed [DW-1:0] wdata,
    output logic [AW-1:0]        wr_ptr,

    // --- 読み出しポート ---
    input  logic [AW-1:0]        rd_addr,
    output logic signed [DW-1:0] rdata
);

    localparam int DEPTH = 1 << AW;

    logic signed [DW-1:0] mem [DEPTH];
    logic [AW-1:0]        wp;

    always_ff @(posedge clk) begin
        if (we) begin
            mem[wp] <= wdata;
            wp      <= wp + 1'b1;
        end
    end

    always_ff @(posedge clk) begin
        rdata <= mem[rd_addr];
    end

    assign wr_ptr = wp;

endmodule
広げる

コメントを残す