module prog_melody_gen (
  input [7:0] io_in,
  output [7:0] io_out
);
    reg [9:0] div_tmr = 0;
    reg tick;
    reg state;
    reg [7:0] curr_tone;

    reg [5:0] tone_seq;
    wire [3:0] rom_rdata;

    wire clock = io_in[0];
    wire reload = io_in[1];
    wire restart = io_in[2];

    wire pgm_data = io_in[3];
    wire pgm_strobe = io_in[4];

    assign io_out[7:1] = 1'b0;

    always @(posedge clock, posedge restart) begin
        if (restart) begin
            div_tmr <= 0;
            tone_seq <= 0;
            curr_tone <= 0;
            tick <= 1'b0;
            state <= 1'b0;
        end else begin
            {tick, div_tmr} <= div_tmr + 1'b1;
            if (tick) begin
                if (!state) begin
                    tone_seq <= tone_seq + 1'b1;
                    if (rom_rdata == 15)
                        curr_tone <= 0; // silence
                    else
                        curr_tone <= 12 + rom_rdata; // note
                end else begin
                    curr_tone <= 0; // gap between notes
                end
                state <= ~state;
            end
        end
    end

    reg [7:0] mel_gen = 0;
    reg mel_out;
    always @(posedge clock) begin
        if (mel_gen >= curr_tone)
            mel_gen <= 0;
        else 
            mel_gen <= mel_gen + 1'b1;
        mel_out <= mel_gen > (curr_tone / 2);
    end

    assign io_out[0] = mel_out;

    localparam C = 4'd11, CS = 4'd10, D = 4'd9, E = 4'd7, F = 4'd6, FS = 4'd5, G = 4'd4, GS = 4'd3, A = 4'd2, AS = 4'd1, B = 4'd0, S = 4'd15;
    localparam [4*64:0] JINGLE_BELS = {
        E, E, E, S, E, E, E, S,
        E, G, C, D, E, S, F, F,
        F, F, F, E, E, E, E, E,
        D, D, E, D, S, G, S, E,
        E, E, S, E, E, E, S, E,
        G, C, D, E, S, F, F, F,
        F, F, E, E, E, E, F, F,
        E, D, C, S, S, S, S, S
    };

    wire [3:0] tone_rom[0:63];

    // program shift register
    reg [10:0] write_sr;
    always @(posedge clock)
        write_sr <= {pgm_data, write_sr[10:1]};

    wire [5:0] pgm_word_sel = write_sr[10:5];
    wire [3:0] pgm_write_data = write_sr[3:0];

    // the tone RAM
    generate
        genvar ii;
        genvar jj;
        for (ii = 0; ii < 64; ii = ii + 1'b1) begin : words
            wire word_we;
            sky130_fd_sc_hd__and2_1 word_we_i ( // make sure this is really glitch free
                .A(pgm_word_sel == ii),
                .B(pgm_strobe),
                .X(word_we)
            );
            for (jj = 0; jj < 4; jj = jj + 1'b1) begin : bits
                localparam pgm_bit = JINGLE_BELS[(63 - ii) * 4 + jj];
                wire lat_o;
                sky130_fd_sc_hd__dlrtp_1 rfbit_i (
                    .GATE(word_we),
                    .RESET_B(reload),
                    .D(pgm_write_data[jj]),
                    .Q(lat_o)
                );
                assign tone_rom[ii][jj] = lat_o ^ pgm_bit;
            end
        end
    endgenerate

    assign rom_rdata = tone_rom[tone_seq];

endmodule