EECS151 / fpga_labs_fa20 / lab5 / src / music_streamer.v
music_streamer.v
Raw
module music_streamer (
    input clk,
    input rst,
    input tempo_up,
    input tempo_down,
    input play_pause,
    input reverse,
    output [2:0] leds,
    output [23:0] tone
);
    wire [9:0] last_address;
    wire       reset, valid_change;

    reg [23:0] delay;
    reg [9:0]  address;
    reg [23:0] clk_counter;
    reg [1:0]  next_state, curr_state;
    
    // Tempo control
    localparam [23:0] DELTA_TEMPO  = 500_000; // 
    localparam [23:0] BASE_DELAY   = 5_000_000; // 1/25 sec/tone
    localparam [23:0] MAX_DELAY    = ~0 - DELTA_TEMPO; // prevent overflow

    // FSM states
    localparam  [1:0] REGULAR_PLAY = 2'd0,
                      REVERSE_PLAY = 2'd1,
                      PAUSED       = 2'd2,
                      S3           = 2'd3; // unused

    wire [23:0] rom_out;
    assign tone = (curr_state == PAUSED) ? 24'd0 : rom_out;
    
    rom memory (.address(address),
                .data(rom_out),
                .last_address(last_address)
               );
   
    // Ensures changing tempo doesn't cause problems
    assign valid_change = (delay > DELTA_TEMPO) && (delay < MAX_DELAY);

    // Control notes being played
    always @(posedge clk) begin  
        if (rst) begin
            address <= 0;
            clk_counter <= 0;
        end else if (curr_state == PAUSED) begin
            address <= address;
            clk_counter <= clk_counter; 
        end else if (clk_counter == delay && curr_state == REGULAR_PLAY) begin
            address <= (address + 1) % (last_address + 1);
            clk_counter <= 0; 
        end else if (clk_counter == delay && curr_state == REVERSE_PLAY) begin
            address <= (address - 1) % (last_address + 1);
            clk_counter <= 0;
        end else
            clk_counter <= clk_counter + 1;
    end

    // Control tempo
    always @(posedge clk) begin
        if (rst) 
            delay <= BASE_DELAY;
        else if (tempo_up && valid_change) 
            delay <= delay - DELTA_TEMPO;
        else if (tempo_down && valid_change) 
            delay <= delay + DELTA_TEMPO;
    end

    // ----------------------------------------------------
    // FSM Control Logic 
    // ----------------------------------------------------
    
    assign leds[0] = (curr_state == REGULAR_PLAY);
    assign leds[1] = (curr_state == PAUSED);
    assign leds[2] = (curr_state == REVERSE_PLAY); 

    // FSM state transition logic
    always @(*) begin 
        next_state = curr_state;
        case (curr_state)
            REGULAR_PLAY : begin 
                if (play_pause) next_state   = PAUSED;
                else if (reverse) next_state = REVERSE_PLAY;
            end
            PAUSED : 
                if (play_pause) next_state   = REGULAR_PLAY;
            REVERSE_PLAY : begin 
                if (play_pause) next_state   = PAUSED;
                else if (reverse) next_state = REGULAR_PLAY;
            end
            S3 : next_state = REGULAR_PLAY; 
        endcase 
    end

    // FSM transition 
    always @(posedge clk) begin
        if (rst) curr_state <= REGULAR_PLAY; // initial
        else curr_state <= next_state;   
    end

endmodule