module get_noise_matrix(input logic   clk,
    input logic  reset,
    input logic  start,
    input logic [3:0] length, // n <= 8
    input logic [3:0] width,  // n <= 8

    output logic signed [31:0] data_out,
    output logic done);

    // Notice that a random matrix generated has a maximium size of 8 by 8
    // To get larger random matices, concatenation needed on the software side
	
    logic gen_enable;
    logic read_enable;
    logic [3:0] gen_index;
    logic [3:0] read_index;
    logic [3:0] gen_row_num;
    logic [3:0] spitting_row_num;


    // seeds below can be modified
    logic [3:0] seed_0 = 4'd1;
    logic [3:0] seed_1 = 4'd2;
    logic [3:0] seed_2 = 4'd3;
    logic [3:0] seed_3 = 4'd4;
    logic [3:0] seed_4 = 4'd5;
    logic [3:0] seed_5 = 4'd6;
    logic [3:0] seed_6 = 4'd7;
    logic [3:0] seed_7 = 4'd8;
    // seed above can be modified

    logic signed [3:0] lfsr_out_0;
    logic signed [3:0] lfsr_out_1;
    logic signed [3:0] lfsr_out_2;
    logic signed [3:0] lfsr_out_3;
    logic signed [3:0] lfsr_out_4;
    logic signed [3:0] lfsr_out_5;
    logic signed [3:0] lfsr_out_6;
    logic signed [3:0] lfsr_out_7;
    
    // generate multiple LFSR instances to create a Gaussian random variable at each cycle
    // 8 4-bit LFSR
    lfsr4 lfsr_0( .clk(clk), .resetn(reset), .seed(seed_0), .lfsr_out(lfsr_out_0) );
    lfsr4 lfsr_1( .clk(clk), .resetn(reset), .seed(seed_1), .lfsr_out(lfsr_out_1) );
    lfsr4 lfsr_2( .clk(clk), .resetn(reset), .seed(seed_2), .lfsr_out(lfsr_out_2) );
    lfsr4 lfsr_3( .clk(clk), .resetn(reset), .seed(seed_3), .lfsr_out(lfsr_out_3) );
    lfsr4 lfsr_4( .clk(clk), .resetn(reset), .seed(seed_4), .lfsr_out(lfsr_out_4) );
    lfsr4 lfsr_5( .clk(clk), .resetn(reset), .seed(seed_5), .lfsr_out(lfsr_out_5) );
    lfsr4 lfsr_6( .clk(clk), .resetn(reset), .seed(seed_6), .lfsr_out(lfsr_out_6) );
    lfsr4 lfsr_7( .clk(clk), .resetn(reset), .seed(seed_7), .lfsr_out(lfsr_out_7) );
    
    // initialize a DMEM to store input vector.
    // maximum number of 8 rows are supported.
    logic signed [31:0] output_mem0 [7:0]; // n <= 8
    logic signed [31:0] output_mem1 [7:0];
    logic signed [31:0] output_mem2 [7:0];
    logic signed [31:0] output_mem3 [7:0];
    logic signed [31:0] output_mem4 [7:0];
    logic signed [31:0] output_mem5 [7:0];
    logic signed [31:0] output_mem6 [7:0];
    logic signed [31:0] output_mem7 [7:0];	
    
    integer i;
    always_ff @(posedge clk) begin
        if (reset) begin
	        for(i = 0; i <= 7; i = i+1)begin
                output_mem0[i] <= 0;
                output_mem1[i] <= 0;
                output_mem2[i] <= 0;
                output_mem3[i] <= 0;
                output_mem4[i] <= 0;
                output_mem5[i] <= 0;
                output_mem6[i] <= 0;
                output_mem7[i] <= 0;
            end
            gen_enable <= 0;
            read_enable <= 0;
            gen_index <= 0;
            gen_row_num <= 0;

        end else if (start || gen_enable) begin
            if (gen_index < width) begin
                gen_enable <= 1;

                case (gen_row_num)
                    4'b0000: output_mem0[gen_index[2:0]][3:0] <= lfsr_out_0;

                    4'b0001: output_mem1[gen_index[2:0]][3:0] <= lfsr_out_1;

                    4'b0010: output_mem2[gen_index[2:0]][3:0] <= lfsr_out_2;

                    4'b0011: output_mem3[gen_index[2:0]][3:0] <= lfsr_out_3;

                    4'b0100: output_mem4[gen_index[2:0]][3:0] <= lfsr_out_4;

                    4'b0101: output_mem5[gen_index[2:0]][3:0] <= lfsr_out_5;

                    4'b0110: output_mem6[gen_index[2:0]][3:0] <= lfsr_out_6;

                    4'b0111: output_mem7[gen_index[2:0]][3:0] <= lfsr_out_7;

                    default: output_mem0[gen_index[2:0]][3:0] <= lfsr_out_0;
                endcase

                gen_index <= gen_index + 1;

                if (gen_index + 1 == width) begin
                    gen_index <= 0;
                    gen_row_num <= gen_row_num + 1;

                    if (gen_row_num + 1 == length) begin
                        gen_index <= 0;
                        // once finished, set read_enable to true
                        read_enable <= 1;
                        // set computational flag to false
                        gen_enable <= 0;
                    end
                end
            end
        end  else if (done) begin
            read_enable <= 0;
        end
    end
    
    // read element in each row every clock cycle
    always_ff @(posedge clk) begin
        if (read_enable) begin
            if (read_index < length) begin

                // spitting out one element each cycle with appropriate index
                case (spitting_row_num)
                    4'b0000: data_out <= output_mem0[read_index[2:0]];

                    4'b0001: data_out <= output_mem1[read_index[2:0]];

                    4'b0010: data_out <= output_mem2[read_index[2:0]];

                    4'b0011: data_out <= output_mem3[read_index[2:0]];

                    4'b0100: data_out <= output_mem4[read_index[2:0]];

                    4'b0101: data_out <= output_mem5[read_index[2:0]];

                    4'b0110: data_out <= output_mem6[read_index[2:0]];
                    
                    4'b0111: data_out <= output_mem7[read_index[2:0]];

                    default: data_out <= output_mem0[read_index[2:0]];
                endcase
                read_index <= read_index + 1;
                if (read_index + 1 == width) begin
                    read_index <= 0;
                    spitting_row_num <= spitting_row_num + 1;
                    if (spitting_row_num + 1 == length) begin
                        read_index <= 0;
                        spitting_row_num <= 0;
                        done <= 1;
                    end
                end
            end
        end else begin
            done <= 0;
        end
    end
    
endmodule