// CSEE 4840 Project: Choose and Run One of the Three Key-Switching Operations
// Avalon memory-mapped peripheral that generates accelerate key_switchiing operations
//
// Spring 2022
//
// By: Lanxiang Hu
// Uni: lh3116

module key_switching (input logic     clk,          // 50MHz clock
	      input logic 	      reset,
		input logic signed [31:0]      writedata,
		input logic 	      write,
		input logic		      chipselect,
		input logic [3:0]       address,

            input logic             all_loaded,

            output logic signed [31:0]     DATA_OUT, 

            output logic [3:0]      OUTPUT_LENGTH, // at most 8
            output logic [7:0]      OUTPUT_WIDTH,  // at most 256
            output logic            DONE);

      logic [3:0] operation;
      logic start_0;
      logic start_1;
      logic start_2;
      logic start_3;
      logic [3:0]  width; // n <= 8
      logic [3:0]  length; // n <= 8
      logic [31:0] in;     // field for c_i and S_ij
      logic [7:0]  ell;
      logic [7:0]  output_length_0; // at most 256, same as vector 'width'
      logic [3:0]  output_length_1;
      logic [7:0]  output_width_1;  // at most 256
      logic signed [31:0] data_out_0;
      logic signed [31:0] data_out_1;
      logic signed [31:0] data_out_2;
      logic signed [31:0] data_out_3;
      logic done_0;
      logic done_1;
      logic done_2;
      logic done_3;

      const logic [3:0] load_op_type = 4'h0;
      const logic [3:0] load_width = 4'h1;
      const logic [3:0] load_length = 4'h2;
      const logic [3:0] load_ell = 4'h3;
      const logic [3:0] load_input = 4'h4;

      const logic [3:0] bit_repr_vector = 4'h0;
      const logic [3:0] bit_repr_matrix = 4'h1;
      const logic [3:0] get_random_matrix = 4'h2;
      const logic [3:0] get_noise_matrix = 4'h3;

      // instantiate the three modules
      bit_repr_vector bit_repr_vector0(.clk(clk),
                        .reset(reset),
                        .start(start_0),
                        .width(width),
                        .c_i(in),
                        .ell(ell),

                        .output_length(output_length_0),
                        .data_out(data_out_0),    // prev memory or S_star_ij
                        .done(done_0));

      bit_repr_matrix bit_repr_matrix0(.clk(clk),
                        .reset(reset),
                        .start(start_1),
                        .width(width), // n <= 8
                        .length(length), // n <= 8
                        .S_ij(in),
                        .ell(ell),

                        .output_length(output_length_1),      // at most 8
                        .output_width(output_width_1),        // at most 256
                        .data_out(data_out_1),
                        .done(done_1));
      
      get_random_matrix get_random_matrix0(.clk(clk),
                        .reset(reset),
                        .start(start_2),
                        .length(length), // n <= 8
                        .width(width),  // n <= 8

                        .data_out(data_out_2),
                        .done(done_2));
      
      get_noise_matrix get_noise_matrix0(.clk(clk),
                        .reset(reset),
                        .start(start_3),
                        .length(length), // n <= 8
                        .width(width),  // n <= 8

                        .data_out(data_out_3),
                        .done(done_3));

      always_ff @(posedge clk) begin
            if (reset) begin
                  operation <= 0;
                  width <= 0;
                  length <= 0;
                  in <= 0;   
                  ell <= 0;
                  done_0 <= 0;
                  done_1 <= 0;
                  done_2 <= 0;
                  done_3 <= 0;

            end else if (chipselect && write) begin
                  case (address)
                        load_op_type : operation <= writedata[3:0];
                        load_width : width <= writedata[3:0]; 
                        load_length : length <= writedata[3:0];
                        load_ell : ell <= writedata[7:0];
                        load_input : in <= writedata;
                  endcase
            end
      end

      logic [3:0]  counter_0;
      logic [6:0]  counter_1;
      logic [6:0]  counter_2;
      logic [6:0]  counter_3;
      // workflow: load operation type, width, length, ell, inputs sequentially
      // after been properly loaded, "all_loaded" turns true
      always_ff @(posedge clk) begin
            // computation stage
            if (all_loaded) begin
                  case (operation)
                        bit_repr_vector : begin 
                              if (counter_0 < width) begin
                                    start_0 <= 1;
                                    counter_0 <= counter_0 + 1;
                              end else begin
                                    start_0 <= 0;
                              end
                              DONE <= done_0;
                              DATA_OUT <= data_out_0;
                              OUTPUT_WIDTH <= output_length_0;
                        end
                        bit_repr_matrix : begin
                              if (counter_1 < width * length) begin
                                    start_1 <= 1;
                                    counter_1 <= counter_1 + 1;
                              end else begin
                                    start_1 <= 0;
                              end
                              DONE <= done_1;
                              DATA_OUT <= data_out_1;
                              OUTPUT_LENGTH <= output_length_1;
                              OUTPUT_WIDTH <= output_width_1;
                        end
                        get_random_matrix : begin
                              if (counter_2 < width * length) begin
                                    start_2 <= 1;
                                    counter_2 <= counter_2 + 1;
                              end else begin
                                    start_2 <= 0;
                              end
                              DONE <= done_2;
                              DATA_OUT <= data_out_2;
                        end
                        get_random_matrix : begin
                              if (counter_3 < width * length) begin
                                    start_3 <= 1;
                                    counter_3 <= counter_3 + 1;
                              end else begin
                                    start_3 <= 0;
                              end
                              DONE <= done_3;
                              DATA_OUT <= data_out_3;
                        end
                  endcase
            end else begin
                  counter_0 <= 0;
                  counter_1 <= 0;
                  counter_2 <= 0;
                  counter_3 <= 0;
                  
                  start_0 <= 0;
                  start_1 <= 0;
                  start_2 <= 0;
                  start_3 <= 0;
            end
      end

   
endmodule

