`timescale 1ns/1ps
module multiply_mem_uart(
	input				i_clk,
	input				i_nrst,
   input          i_rx,
   output         o_tx
);

   parameter   SAMPLE            = 1250,           // BAUD = 9600 with 12MHz clk
               DATA_WIDTH        = 128,
               HALF_DATA_WIDTH   = DATA_WIDTH/2,
               ADDR_WIDTH        = 8,
               SM_READ           = 2'b00,
               SM_MUL            = 2'b01,
               SM_WRITE          = 2'b10;

   wire  [DATA_WIDTH-1:0]  mul_to_mem,
                           mem_to_mul;
   
   reg   [ADDR_WIDTH-1:0]  addr;

   reg   [1:0]             state;
   
   wire                    i_read_valid,
                           o_read_accept,
                           i_mul_read,
                           o_mul_accept,
                           i_write_valid,
                           o_write_accept;

   assign   i_read_valid   = (state == SM_READ);
   assign   i_mul_valid    = (state == SM_MUL);
   assign   i_write_valid  = (state == SM_WRITE);

	always@(posedge i_clk or negedge i_nrst) begin
		if(!i_nrst) begin
	      addr     <= 'b0;	
         state    <= SM_READ;
		end else begin
	      case(state)
            SM_READ:    if(o_read_accept)
                           state <= SM_MUL;
            SM_MUL:     if(o_mul_accept)
                           state <= SM_WRITE;
            SM_WRITE:   if(o_write_accept) begin
                           state <= SM_READ;
                           addr  <= addr + 'b1;
                        end
         endcase
      end
	end

   mem_uart #(
      .DATA_WIDTH       (DATA_WIDTH                               ),
      .ADDR_WIDTH       (ADDR_WIDTH                               ),
      .SAMPLE           (SAMPLE                                   )
   ) mem_uart(
		.i_clk	         (i_clk                                    ),
		.i_nrst           (i_nrst                                   ),
      .i_data           (mul_to_mem                               ),
      .i_addr           (addr                                     ),
      .o_data           (mem_to_mul                               ),
      .i_read_valid     (i_read_valid                             ),
      .o_read_accept    (o_read_accept                            ),
      .i_write_valid    (i_write_valid                            ),
      .o_write_accept   (o_write_accept                           ),
      .i_uart_rx        (i_rx                                     ),
      .o_uart_tx        (o_tx                                     )
	);

   shift_and_add_multiplier #(
      .DATA_WIDTH_A     (HALF_DATA_WIDTH                          ),
      .DATA_WIDTH_B     (HALF_DATA_WIDTH                          )
   ) shift_and_add_multiplier(
      .i_clk            (i_clk                                    ),
      .i_nrst           (i_nrst                                   ),
      .i_a              (mem_to_mul[HALF_DATA_WIDTH-1:0]          ),
      .i_b              (mem_to_mul[DATA_WIDTH-1:HALF_DATA_WIDTH] ),
      .o_c              (mul_to_mem                               ),
      .i_valid          (i_mul_valid                              ),
      .o_accept         (o_mul_accept                             )
	);
  
endmodule