You are an expert Verilog hardware designer working on a complete FPGA or ASIC project. You are given the full source code of a Verilog project. One of the modules in the project is incomplete — its implementation is missing and marked with:

// >>> Module Implementation Begin

// <<< Module Implementation End

Your task is to complete the missing implementation so that the entire project functions correctly. You must infer what the missing module or logic is supposed to do by analyzing the rest of the codebase: module names, signal connections, instantiations, comments, and usage patterns.

Follow these rules:

1. Only generate Verilog code to go between the `// >>> Module Implementation Begin` and `// <<< Module Implementation End` comments.
2. Use Verilog-2001 syntax. Ensure the code is synthesizable.
3. Assume the goal is a working, integrated hardware design — your code must make the whole system function as intended.
4. Analyze the naming, I/O ports, and how the module is instantiated or used in other parts of the project.
5. Implement correct behavior based on design context. Do not guess randomly — reason it out.
6. Avoid vendor-specific features unless explicitly required.
7. Do not output comments, explanation, or anything outside the required code region unless explicitly asked.

Your output should contain **only the Verilog code** that fits inside the marked region.

/*
 * Copyright (c) 2024 Gabriel Galeote-Checa
 * SPDX-License-Identifier: Apache-2.0
 */

module tt_um_top_layer (
    input  wire [7:0] ui_in,    // Dedicated inputs 
    output wire [7:0] uo_out,   // Dedicated outputs
    input  wire [7:0] uio_in,   // IOs: input path
    output wire [7:0] uio_out,  // Output path
    output wire [7:0] uio_oe,   // IOs: Enable path (active high: 0=input, 1=output)
    input  wire       ena,      // Always 1,this is to enable the project 
    input  wire       clk,      // Clock
    input  wire       rst_n     // Reset of the project (active-low)
);

    // Parameters
    parameter integer NUM_UNITS  = 2;
    parameter integer DATA_WIDTH = 16;

    // Reset / control
    wire rst = ~rst_n;

    // Channel select input from ui_in[1:0] (only 2 bits are available on UI)
    wire [1:0] selected_unit = ui_in[1:0];

    // Mark unused UI bits as used to avoid lint warnings (exclude bit[2] byte_valid)
    wire _unused_ui = &{ ui_in[7:3] };

    // Byte-valid strobe on ui_in[2]
    wire byte_valid = ui_in[2];

    // 2-byte sample assembler (MSB then LSB), synchronous to clk
    reg [DATA_WIDTH-1:0] sample_sr   = {DATA_WIDTH{1'b0}};
    reg                  byte_idx    = 1'b0;   // toggles 0 → 1 → 0
    reg                  sample_wr_en= 1'b0;

    always @(posedge clk) begin
        if (rst) begin
            byte_idx     <= 1'b0;
            sample_sr    <= {DATA_WIDTH{1'b0}};
            sample_wr_en <= 1'b0;
        end
        else begin
            sample_wr_en <= 1'b0;

            if (byte_valid) begin
                if (byte_idx == 1'b0) begin
                    // First byte → MSB
                    sample_sr[15:8] <= uio_in;
                    byte_idx        <= 1'b1;
                end
                else begin
                    // Second byte → LSB
                    sample_sr[7:0]  <= uio_in;
                    byte_idx        <= 1'b0;
                    sample_wr_en    <= 1'b1;  // complete 16-bit sample
                end
            end
        end
    end

    // ------------------------------------------------------------------------
    // Processing system instance (flat buses only)
    // ------------------------------------------------------------------------
    wire                     sample_valid_unused;           // unused output
    wire [NUM_UNITS-1:0]     spike_bus;                     // 1 bit per unit
    wire [2*NUM_UNITS-1:0]   event_bus;                     // 2 bits per unit

    processing_system #(
        .NUM_UNITS  (NUM_UNITS),
        .DATA_WIDTH (DATA_WIDTH)
    ) u_processing (
        .clk                   (clk),
        .rst                   (rst),
        .sample_in             (sample_sr),
        .write_sample_in       (sample_wr_en),
        .spike_detection_array (spike_bus),
        .event_out_array       (event_bus),
        .sample_valid          (sample_valid_unused)
    );

    // Output mux: select event/spike data from the selected unit

    assign uo_out = {
        5'b00000,
        event_bus[2*selected_unit +: 2],
        spike_bus[selected_unit]
    };

    // ------------------------------------------------------------------------
    // Unused IOs
    // ------------------------------------------------------------------------
    assign uio_out = 8'h00;
    assign uio_oe  = 8'h00;
    wire _unused_ena = &{ ena };

endmodule


/*
 * Copyright (c) 2024 Gabriel Galeote-Checa
 * SPDX-License-Identifier: Apache-2.0
 */
 module processing_system #(
        parameter integer NUM_UNITS  = 2,
        parameter integer DATA_WIDTH = 16
    )(
        input  wire                     clk,
        input  wire                     rst,

        input  wire [DATA_WIDTH-1:0]    sample_in,        
        input  wire                     write_sample_in,  
        output wire [NUM_UNITS-1:0]     spike_detection_array,
        output wire [2*NUM_UNITS-1:0]   event_out_array,
        output wire                     sample_valid   
    );

    localparam integer RAM_ADDR_W = $clog2(NUM_UNITS);

    wire ram_full_pulse;                
    wire [DATA_WIDTH-1:0] ram_dout;

    RAM16 #(.ADDR_WIDTH(RAM_ADDR_W)) u_ram16 (
        .CLK  (clk),
        .RST  (rst),
        .READ (read_en),
        .WRITE(write_sample_in),
        .FULL (ram_full_pulse),
        .A    (rd_addr),
        .Di   (sample_in),
        .Do   (ram_dout)
    );

    // ------------------------------------------------------------
    // 2. Read-out FSM
    // ------------------------------------------------------------
    reg                       read_en = 1'b0;
    reg [RAM_ADDR_W-1:0]      rd_addr = 0;
    reg [DATA_WIDTH-1:0]      proc_word_buf [0:NUM_UNITS-1];
    reg [31:0]                read_count;
    reg                       sample_valid_int = 1'b0;   

    assign sample_valid = sample_valid_int;

    always @(posedge clk) begin
        if (rst) begin
            read_en        <= 0;
            rd_addr        <= 0;
            read_count     <= 0;
            sample_valid_int <= 0;
        end else begin
            sample_valid_int <= 0;

            if (ram_full_pulse && !read_en) begin
                read_en        <= 1;
                rd_addr        <= 0;
                read_count     <= 0;
            end else if (read_en) begin

                if (read_count <= NUM_UNITS) begin
                    proc_word_buf[read_count - 1] <= ram_dout;
                end

                rd_addr    <= rd_addr + 1;
                read_count <= read_count + 1;

                if (read_count == NUM_UNITS) begin
                    read_en <= 0;
                    sample_valid_int <= 1;
                end
            end
        end
    end

    wire [NUM_UNITS-1:0]   spike_det_int;
    wire [2*NUM_UNITS-1:0] event_out_int;

    genvar gi;
    for (gi = 0; gi < NUM_UNITS; gi = gi + 1) begin : G_PU

        processing_unit u_proc (
            .clk                (clk),
            .rst                (rst),
            .data_in            (proc_word_buf[gi]),
            .threshold_in       (16'd200),
            .class_a_thresh_in  (8'd10),
            .class_b_thresh_in  (8'd3),
            .timeout_period_in  (16'd1000),
            .spike_detection    (spike_det_int[gi]),
            .event_out          (event_out_int[2*gi +: 2])
        );

    end

    assign spike_detection_array = spike_det_int;
    assign event_out_array       = event_out_int;

endmodule


/*
 * Copyright (c) 2024 Gabriel Galeote-Checa
 * SPDX-License-Identifier: Apache-2.0
 */
module processing_unit (
    input  wire        clk,
    input  wire        rst,
    input  wire [15:0] data_in,
    input  wire [15:0] threshold_in,
    input  wire [7:0]  class_a_thresh_in,
    input  wire [7:0]  class_b_thresh_in,
    input  wire [15:0] timeout_period_in,
    output wire        spike_detection,
    output wire [1:0]  event_out
);
    wire spike_detected_internal;

    aso spike_detector_instance (
        .clk(clk),
        .rst(rst),
        .data_in(data_in),
        .threshold_in(threshold_in),
        .spike_detected(spike_detected_internal)
    );

    classifier classifier_instance (
        .clk(clk),
        .reset(rst),
        .current_detection(spike_detected_internal),
        .event_out(event_out),
        .class_a_thresh_in(class_a_thresh_in),
        .class_b_thresh_in(class_b_thresh_in),
        .timeout_period_in(timeout_period_in)
    );

    assign spike_detection = spike_detected_internal;

endmodule


/*
 * Copyright (c) 2024 Gabriel Galeote-Checa
 * SPDX-License-Identifier: Apache-2.0
 */
 module ado (
    input wire clk,
    input wire rst,
    input wire [15:0] data_in,
    input wire [15:0] threshold_in,
    output reg spike_detected
);

    // State encoding
    localparam TRAINING  = 1'b0;
    localparam OPERATION = 1'b1;

    reg state;

    // Constants
    localparam integer SAMPLE_RATE_HZ     = 2000;
    localparam integer REFRACTORY_SAMPLES = SAMPLE_RATE_HZ / 8;   
    reg signed [15:0] x1, x2, x3, x4;
    reg signed [15:0] ado;
    reg signed [15:0] threshold;

    // Refractory logic
    reg [31:0] refractory_counter = 0;
    reg in_refractory = 0;

    // Absolute value (2's complement)
    function signed [15:0] abs_val;
        input signed [15:0] val;
        begin
            abs_val = (val < 0) ? -val : val;
        end
    endfunction

    always @(posedge clk or posedge rst) begin
        if (rst) begin
            x1 <= 16'sd0;
            x2 <= 16'sd0;
            x3 <= 16'sd0;
            x4 <= 16'sd0;
            ado <= 16'sd0;
            threshold <= 16'sd500;
            state <= TRAINING;
            spike_detected <= 1'b0;
            refractory_counter <= 0;
            in_refractory <= 0;
        end else begin
            // Shift input samples
            x1 <= x2;
            x2 <= x3;
            x3 <= x4;
            x4 <= $signed(data_in);

            spike_detected <= 1'b0;

            // Refractory logic
            if (in_refractory) begin
                if (refractory_counter >= REFRACTORY_SAMPLES) begin
                    in_refractory <= 0;
                    refractory_counter <= 0;
                end else begin
                    refractory_counter <= refractory_counter + 1;
                end
            end

            // FSM
            case (state)
                TRAINING: begin
                    threshold <= 16'sd500;
                    state <= OPERATION;
                end

                OPERATION: begin
                    threshold <= $signed(threshold_in);
                    ado <= abs_val(x4 - x1);

                    if ((ado > threshold) && !in_refractory) begin
                        spike_detected <= 1'b1;
                        in_refractory <= 1;
                        refractory_counter <= 0;
                    end
                end
            endcase
        end
    end

endmodule


/*
 * Copyright (c) 2024 Gabriel Galeote-Checa
 * SPDX-License-Identifier: Apache-2.0
 */
module aso (
    input  wire        clk,
    input  wire        rst,
    input  wire [15:0] data_in,
    input  wire [15:0] threshold_in,
    output reg         spike_detected
);
// >>> Module Implementation Begin

// <<< Module Implementation End
endmodule




/*
 * Copyright (c) 2024 Gabriel Galeote-Checa
 * SPDX-License-Identifier: Apache-2.0
 */
 module neo (
    input wire clk,
    input wire rst,
    input wire [15:0] data_in,
    input wire [15:0] threshold_in,
    output reg spike_detected
);

// State encoding
localparam TRAINING  = 1'b0;
localparam OPERATION = 1'b1;

reg state;

// Internal signals
reg signed [15:0] x1, x2, x3;
reg signed [15:0] neo;
reg signed [15:0] threshold;

reg signed [31:0] mult_x2_x2;
reg signed [31:0] mult_x3_x1;
reg signed [31:0] diff_result;

// Absolute value function
function signed [31:0] abs_val;
    input signed [31:0] val;
    begin
        abs_val = (val < 0) ? -val : val;
    end
endfunction

always @(posedge clk or posedge rst) begin
    if (rst) begin
        x1 <= 16'sd0;
        x2 <= 16'sd0;
        x3 <= 16'sd0;
        neo <= 16'sd0;
        threshold <= 16'sd10000;
        state <= TRAINING;
        spike_detected <= 1'b0;
    end else begin
        // Shift input samples
        x1 <= x2;
        x2 <= x3;
        x3 <= $signed(data_in);

        case (state)
            TRAINING: begin
                threshold <= 16'sd10000;
                state <= OPERATION;
            end

            OPERATION: begin
                threshold <= $signed(threshold_in);

                // Calculate NEO = |x2^2 - x3 * x1|
                mult_x2_x2 <= x2 * x2;
                mult_x3_x1 <= x3 * x1;
                diff_result <= abs_val(mult_x2_x2 - mult_x3_x1);

                // Resize diff_result to 16-bit neo (clipping if needed)
                neo <= diff_result[15:0];  // Simple truncation; saturation logic can be added

                // Spike detection
                spike_detected <= (neo > threshold) ? 1'b1 : 1'b0;
            end
        endcase
    end
end

endmodule


/*
 * Copyright (c) 2024 Gabriel Galeote-Checa
 * SPDX-License-Identifier: Apache-2.0
 */
module ed (
    input wire clk,
    input wire rst,
    input wire [15:0] data_in,
    input wire [15:0] threshold_in,
    output reg spike_detected
);

// Delay constant
localparam integer k = 2;

// State encoding
localparam TRAINING  = 1'b0;
localparam OPERATION = 1'b1;

reg state;

// Delay buffer for input samples
reg signed [15:0] input_buffer [0:k];

// Internal signals
reg signed [31:0] squared_diff;
reg signed [15:0] threshold;

integer i;

always @(posedge clk or posedge rst) begin
    if (rst) begin
        for (i = 0; i <= k; i = i + 1)
            input_buffer[i] <= 16'sd0;

        squared_diff <= 32'sd0;
        threshold <= 16'sd10000;  // Default threshold
        state <= TRAINING;
        spike_detected <= 1'b0;
    end else begin
        // Shift register operation
        for (i = k; i > 0; i = i - 1)
            input_buffer[i] <= input_buffer[i - 1];

        input_buffer[0] <= $signed(data_in);

        case (state)
            TRAINING: begin
                threshold <= 16'sd10000;
                state <= OPERATION;
            end

            OPERATION: begin
                threshold <= $signed(threshold_in);

                // Compute squared difference
                squared_diff <= (input_buffer[0] - input_buffer[k]) * 
                                (input_buffer[0] - input_buffer[k]);

                // Compare against threshold (promoted to 32 bits)
                spike_detected <= (squared_diff > {{16{threshold[15]}}, threshold}) ? 1'b1 : 1'b0;
            end
        endcase
    end
end

endmodule


/*
 * Copyright (c) 2024 Gabriel Galeote-Checa
 * SPDX-License-Identifier: Apache-2.0
 */
 module RAM16 #(
    parameter ADDR_WIDTH = 3
)(
    input                   CLK,
    input                   RST,        // synchronous reset (active-high)
    input                   READ,       // read enable
    input                   WRITE,      // write strobe
    output reg              FULL,       // 1-cycle pulse when buffer fills
    input  [ADDR_WIDTH-1:0] A,          // read address
    input  [15:0]           Di,         // data in
    output reg [15:0]       Do          // data out
);

    // ------------------------------------------------------------
    localparam DEPTH = 1 << ADDR_WIDTH;

    reg [15:0] RAM [0:DEPTH-1];
    reg [ADDR_WIDTH-1:0] wr_addr;

    integer j;

    // ------------------------------------------------------------
    always @(posedge CLK) begin
        //--------------------- reset ------------------------------------
        if (RST) begin
            // $display("[RAM] DEBUG: @%0t RST=1, clearing RAM", $time);
            for (j = 0; j < DEPTH; j = j + 1)
                RAM[j] <= 16'h0000;
            wr_addr <= 0;
            FULL    <= 1'b0;
            Do      <= 16'h0000;
        end
        //--------------------- normal operation -------------------------
        else begin
            // ---------- write side -------------------------------------
            FULL <= 1'b0;                       // default: de-assert

            if (WRITE) begin
                RAM[wr_addr] <= Di;            // store incoming sample
                // $display("[RAM] DEBUG: @%0t WRITE=1, wr_addr=%0d, Di=0x%04h", $time, wr_addr, Di);

                if (wr_addr == DEPTH-1) begin   // last location just written
                    FULL    <= 1'b1;            // one-cycle flag
                    wr_addr <= 0;               // wrap for next frame
                end else begin
                    wr_addr <= wr_addr + 1'b1;  // advance pointer
                end
            end

            // ---------- read side --------------------------------------
            if (READ)
                Do <= RAM[A];
            else
                Do <= 16'h0000;
        end
    end
endmodule


/*
 * Copyright (c) 2024 Gabriel Galeote-Checa
 * SPDX-License-Identifier: Apache-2.0
 */

module classifier (
    input  wire        clk,
    input  wire        reset,
    input  wire        current_detection,
    output reg  [1:0]  event_out,
    input  wire [7:0]  class_a_thresh_in,
    input  wire [7:0]  class_b_thresh_in,
    input  wire [15:0] timeout_period_in
);

    localparam [1:0] EVENT_C = 2'b00;
    localparam [1:0] EVENT_B = 2'b01;
    localparam [1:0] EVENT_A = 2'b10;

    localparam SAMPLE_RATE                   = 2000;
    localparam MAX_EXCITABILITY              = 100;
    localparam SATURATION_EXCITABILITY       = 10;
    localparam ICTAL_REFRACTORY_PERIOD       = 10 * SAMPLE_RATE;
    localparam DECAY_STEP_PERIOD             = 8 * SAMPLE_RATE;
    localparam COUNTER_CONFIRMATION_A_THRESH = 4;

    reg  [1:0] current_event;
    reg  [1:0] previous_event;

    reg  [31:0] class_a_threshold;
    reg  [31:0] class_b_threshold;
    reg  [31:0] timeout_period;

    reg  [31:0] excitability;
    reg  [31:0] sample_count;
    reg  [31:0] last_peak_sample_count;
    reg  [31:0] last_event_sample_count;
    reg  [31:0] counter_confirmation_a;
    reg  [31:0] counter_confirmation_b;
    reg  [31:0] last_a_section_end;
    reg  [31:0] last_b_section_end;
    reg  [31:0] k;

    always @(posedge clk) begin
        if (reset) begin
            current_event          <= EVENT_C;
            previous_event         <= EVENT_C;
            class_a_threshold      <= 5;
            class_b_threshold      <= 1;
            timeout_period         <= 5 * SAMPLE_RATE;
            excitability           <= 0;
            sample_count           <= 0;
            last_peak_sample_count <= 0;
            last_event_sample_count <= 0;
            counter_confirmation_a <= 0;
            counter_confirmation_b <= 0;
            last_a_section_end     <= 0;
            event_out              <= EVENT_C;
        end else begin
            class_a_threshold <= {24'b0, class_a_thresh_in};
            class_b_threshold <= {24'b0, class_b_thresh_in};
            timeout_period    <= {16'b0, timeout_period_in};


            sample_count <= sample_count + 1;

            if (current_detection) begin
                excitability <= excitability + MAX_EXCITABILITY;
                if (excitability > (SATURATION_EXCITABILITY * MAX_EXCITABILITY))
                    excitability <= SATURATION_EXCITABILITY * MAX_EXCITABILITY;

                last_event_sample_count <= sample_count;
                last_peak_sample_count  <= sample_count;
            end else begin
                k <= sample_count - last_peak_sample_count;
                if (k >= DECAY_STEP_PERIOD)
                    excitability <= 0;
            end

            // Fix 1: Timeout fallback to class C only if excitability is low
            if ((sample_count - last_event_sample_count) > timeout_period &&
                excitability < (class_b_threshold * MAX_EXCITABILITY)) begin
                current_event <= EVENT_C;
            end

            // Classification logic
            if (excitability >= (class_a_threshold * MAX_EXCITABILITY)) begin
                counter_confirmation_a <= counter_confirmation_a + 1;
                if (counter_confirmation_a > COUNTER_CONFIRMATION_A_THRESH) begin
                    if (current_event != EVENT_A) begin
                        previous_event <= current_event;
                    end
                    current_event <= EVENT_A;
                end
            end else if (excitability >= (class_b_threshold * MAX_EXCITABILITY)) begin
                if ((current_event != EVENT_B) &&
                    ((sample_count - last_a_section_end) > ICTAL_REFRACTORY_PERIOD)) begin
                    previous_event <= current_event;
                    current_event  <= EVENT_B;
                end else begin
                    counter_confirmation_b <= counter_confirmation_b + 1;
                end
            end else begin
                if ((current_event == EVENT_A) &&
                    ((sample_count - last_a_section_end) > ICTAL_REFRACTORY_PERIOD)) begin
                    if (excitability > (class_b_threshold * MAX_EXCITABILITY))
                        current_event <= EVENT_B;
                    else
                        current_event <= EVENT_C;
                end else begin
                    // Fix 2: Only reset counters if excitability is truly low
                    if (previous_event != EVENT_C) begin
                        if (excitability < (class_b_threshold * MAX_EXCITABILITY)) begin
                            counter_confirmation_a <= 0;
                            counter_confirmation_b <= 0;
                        end

                        if (current_event == EVENT_B)
                            last_b_section_end <= sample_count;
                        else if (current_event == EVENT_A)
                            last_a_section_end <= sample_count;

                        previous_event <= EVENT_C;
                    end

                    if (excitability < (class_b_threshold * MAX_EXCITABILITY))
                        current_event <= EVENT_C;
                end
            end

            event_out <= current_event;
        end
    end
endmodule


Reference solution:
rtl/generated.v: 
    localparam TRAINING  = 1'b0;
    localparam OPERATION = 1'b1;
    reg state;
    localparam integer SAMPLE_RATE_HZ     = 2000;
    localparam integer REFRACTORY_SAMPLES = SAMPLE_RATE_HZ / 4;  // 500 samples
    reg  signed [15:0] x1, x2, x3, x4;
    reg  signed [15:0] aso;
    reg  signed [15:0] threshold;

    reg        in_refractory    = 1'b0;
    reg [31:0] refractory_cnt   = 32'd0;  

    function signed [15:0] abs_val;
        input signed [15:0] val;
        begin
            abs_val = (val < 0) ? -val : val;
        end
    endfunction

    always @(posedge clk) begin
        if (rst) begin
            x1 <= 16'sd0;
            x2 <= 16'sd0;
            x3 <= 16'sd0;
            x4 <= 16'sd0;
            aso        <= 16'sd0;
            threshold  <= 16'sd500;
            state <= TRAINING;
            spike_detected <= 1'b0;
            in_refractory  <= 1'b0;
            refractory_cnt <= 32'd0;
        end else begin
            x1 <= x2;
            x2 <= x3;
            x3 <= x4;
            x4 <= $signed(data_in);
            spike_detected <= 1'b0;
            if (in_refractory) begin
                if (refractory_cnt >= REFRACTORY_SAMPLES) begin
                    in_refractory  <= 1'b0;
                    refractory_cnt <= 32'd0;
                end
                else begin
                    refractory_cnt <= refractory_cnt + 1;
                end
            end

            case (state)
                TRAINING: begin
                    threshold <= 16'sd500;   
                    state     <= OPERATION;
                end

                OPERATION: begin
                    threshold <= $signed(threshold_in);
                    aso       <= abs_val(x4 - x1);

                    if ((aso > threshold) && !in_refractory) begin
                        spike_detected <= 1'b1;
                        in_refractory  <= 1'b1; 
                        refractory_cnt <= 32'd0;
                    end
                end
            endcase
        end
    end