module axi_video_resizer #(
  parameter           INVERT_AXI_RESET    = 1,
  parameter           INVERT_AXIS_RESET   = 1,
  parameter           BUFFER_SIZE         = 9,
  parameter           ADDR_WIDTH          = 4,
  parameter           DATA_WIDTH          = 32,
  parameter           STROBE_WIDTH        = (DATA_WIDTH / 8),
  parameter           AXIS_WIDTH          = 24,
  parameter           VIDEO_IN_WIDTH      = 1280,
  parameter           VIDEO_IN_HEIGHT     = 720,
  parameter           VIDEO_IN_SIZE       = VIDEO_IN_WIDTH * VIDEO_IN_HEIGHT,
  parameter           VIDEO_OUT_WIDTH     = 1280,
  parameter           VIDEO_OUT_HEIGHT    = 720,
  parameter           VIDEO_OUT_SIZE      = VIDEO_OUT_WIDTH * VIDEO_OUT_HEIGHT,
  parameter           VIDEO_START_X       = 0,
  parameter           VIDEO_START_Y       = 0
)(
  input                               clk,
  input                               rst,

  //Write Address Channel
  input                               i_awvalid,
  input       [ADDR_WIDTH - 1: 0]     i_awaddr,
  output                              o_awready,

  //Write Data Channel
  input                               i_wvalid,
  output                              o_wready,
  input       [STROBE_WIDTH - 1:0]    i_wstrb,
  input       [DATA_WIDTH - 1: 0]     i_wdata,

  //Write Response Channel
  output                              o_bvalid,
  input                               i_bready,
  output      [1:0]                   o_bresp,

  //Read Address Channel
  input                               i_arvalid,
  output                              o_arready,
  input       [ADDR_WIDTH - 1: 0]     i_araddr,

  //Read Data Channel
  output                              o_rvalid,
  input                               i_rready,
  output      [1:0]                   o_rresp,
  output      [DATA_WIDTH - 1: 0]     o_rdata,

  //AXI Stream
  input                               i_axis_clk,
  input                               i_axis_rst,

  //AXI Video Stream Input
  input       [AXIS_WIDTH - 1:0]      i_axis_video_in_data,
  output                              o_axis_video_in_ready,
  input                               i_axis_video_in_last,
  input                               i_axis_video_in_valid,

  //AXI Video Stream Output
  output       [AXIS_WIDTH - 1:0]     o_axis_video_out_data,
  input                               i_axis_video_out_ready,
  output                              o_axis_video_out_last,
  output                              o_axis_video_out_valid

);
//local parameters


//Address Map
localparam                      REG_CONTROL           = 0;
localparam                      REG_STATUS            = 1;
localparam                      REG_VERSION           = 2;

localparam                      REG_VIDEO_IN_SIZE     = 4;
localparam                      REG_VIDEO_IN_WIDTH    = 5;
localparam                      REG_VIDEO_IN_HEIGHT   = 6;

localparam                      REG_VIDEO_OUT_SIZE    = 8;
localparam                      REG_VIDEO_OUT_WIDTH   = 9;
localparam                      REG_VIDEO_OUT_HEIGHT  = 10;

localparam                      REG_VIDEO_IN_START_X  = 12;
localparam                      REG_VIDEO_IN_START_Y  = 13;
localparam                      REG_IN_FILL_PIXEL     = 14;

localparam                      REG_LAST_REG = REG_IN_FILL_PIXEL;

//registes/wires

//Simple User Interface
wire  [ADDR_WIDTH - 1: 0]       w_reg_address;
reg                             r_reg_invalid_addr;

wire                            w_reg_in_rdy;
reg                             r_reg_in_ack_stb;
wire  [DATA_WIDTH - 1: 0]       w_reg_in_data;

wire                            w_reg_out_req;
reg                             r_reg_out_rdy_stb;
reg   [DATA_WIDTH - 1: 0]       r_reg_out_data;


reg                             r_enable;
reg                             r_reset;
reg   [31:0]                    r_video_in_size;
reg   [31:0]                    r_video_in_width;
reg   [31:0]                    r_video_in_height;

reg   [31:0]                    r_video_out_width;
reg   [31:0]                    r_video_out_height;
reg   [31:0]                    r_video_out_size;

reg   [31:0]                    r_video_in_start_x;
reg   [31:0]                    r_video_in_start_y;
reg   [AXIS_WIDTH - 1:0]        r_fill_pixel_value;

wire                            w_axi_rst;
wire                            w_axis_rst;


wire  [1:0]                     w_fifo_in_rdy;
wire  [1:0]                     w_fifo_in_act;
wire                            w_fifo_in_stb;
wire  [23:0]                    w_fifo_in_size;
wire  [AXIS_WIDTH - 1:0]        w_fifo_in_data;

wire                            w_fifo_out_rdy;
wire                            w_fifo_out_act;
wire                            w_fifo_out_stb;
wire  [23:0]                    w_fifo_out_size;
wire  [AXIS_WIDTH - 1:0]        w_fifo_out_data;



//Convert AXI Slave signals to a simple register/address strobe
axi_lite_slave #(
  .ADDR_WIDTH         (ADDR_WIDTH           ),
  .DATA_WIDTH         (DATA_WIDTH           )

) axi_lite_reg_interface (
  .clk                (clk                  ),
  .rst                (w_axi_rst            ),


  .i_awvalid          (i_awvalid            ),
  .i_awaddr           (i_awaddr             ),
  .o_awready          (o_awready            ),

  .i_wvalid           (i_wvalid             ),
  .o_wready           (o_wready             ),
  .i_wstrb            (i_wstrb              ),
  .i_wdata            (i_wdata              ),

  .o_bvalid           (o_bvalid             ),
  .i_bready           (i_bready             ),
  .o_bresp            (o_bresp              ),

  .i_arvalid          (i_arvalid            ),
  .o_arready          (o_arready            ),
  .i_araddr           (i_araddr             ),

  .o_rvalid           (o_rvalid             ),
  .i_rready           (i_rready             ),
  .o_rresp            (o_rresp              ),
  .o_rdata            (o_rdata              ),


  .o_reg_address      (w_reg_address        ),
  .i_reg_invalid_addr (r_reg_invalid_addr   ),

  .o_reg_in_rdy       (w_reg_in_rdy         ),
  .i_reg_in_ack_stb   (r_reg_in_ack_stb     ),
  .o_reg_in_data      (w_reg_in_data        ),

  .o_reg_out_req      (w_reg_out_req        ),
  .i_reg_out_rdy_stb  (r_reg_out_rdy_stb    ),
  .i_reg_out_data     (r_reg_out_data       )
);

//Take in an AXI video stream and output the data into a PPFIFO
adapter_axi_stream_2_ppfifo #(
  .DATA_WIDTH         (AXIS_WIDTH           )
) as2p (
  .rst                (w_axis_rst           ),

  //AXI Stream Input
  .i_axi_clk          (i_axis_clk           ),
  .o_axi_ready        (o_axis_video_in_ready),
  .i_axi_data         (i_axis_video_in_data ),
  .i_axi_last         (i_axis_video_in_last ),
  .i_axi_valid        (i_axis_video_in_valid),

  //Ping Pong FIFO Write Controller
  .o_ppfifo_clk       (                     ),
  .i_ppfifo_rdy       (w_fifo_in_rdy        ),
  .o_ppfifo_act       (w_fifo_in_act        ),
  .i_ppfifo_size      (w_fifo_in_size       ),
  .o_ppfifo_stb       (w_fifo_in_stb        ),
  .o_ppfifo_data      (w_fifo_in_data       )
);


adapter_ppfifo_2_axi_stream #(
  .DATA_WIDTH         (AXIS_WIDTH             )
) p2as (
  .rst                (w_axis_rst             ),

  .i_total_out_size   (r_video_out_size[23:0] ),

  //Ping Poing FIFO Read Interface
  .i_ppfifo_clk       (i_axis_clk             ),
  .i_ppfifo_rdy       (w_fifo_out_rdy         ),
  .o_ppfifo_act       (w_fifo_out_act         ),
  .i_ppfifo_size      (w_fifo_out_size        ),
  .i_ppfifo_data      (w_fifo_out_data        ),
  .o_ppfifo_stb       (w_fifo_out_stb         ),

  //AXI Stream Output
  .o_axi_clk          (                       ),
  .i_axi_ready        (i_axis_video_out_ready ),
  .o_axi_data         (o_axis_video_out_data  ),
//  .o_axi_keep         (  ),
  .o_axi_last         (o_axis_video_out_last  ),
  .o_axi_valid        (o_axis_video_out_valid )
);


video_resizer #(
  .AXIS_WIDTH        (AXIS_WIDTH           ),
  .PPFIFO_WIDTH       (BUFFER_SIZE          )
) vr (
  .clk                (i_axis_clk           ),
  .rst                (w_axis_rst           ),

  //Configuration Ports
  .i_enable           (r_enable             ),
  .i_reset            (r_reset              ),
  .i_video_in_size    (r_video_in_size      ),
  .i_video_in_width   (r_video_in_width     ),
  .i_video_in_height  (r_video_in_height    ),

  .i_video_out_size   (r_video_out_size     ),
  .i_video_out_width  (r_video_out_width    ),
  .i_video_out_height (r_video_out_height   ),

  .i_video_in_start_x (r_video_in_start_x   ),
  .i_video_in_start_y (r_video_in_start_y   ),
  .i_fill_pixel_value (r_fill_pixel_value   ),

  //FIFO in Signals
  .o_fifo_in_rdy      (w_fifo_in_rdy        ),
  .i_fifo_in_act      (w_fifo_in_act        ),
  .i_fifo_in_stb      (w_fifo_in_stb        ),
  .o_fifo_in_size     (w_fifo_in_size       ),
  .i_fifo_in_data     (w_fifo_in_data       ),

  .o_fifo_out_rdy     (w_fifo_out_rdy       ),
  .i_fifo_out_act     (w_fifo_out_act       ),
  .i_fifo_out_stb     (w_fifo_out_stb       ),
  .o_fifo_out_size    (w_fifo_out_size      ),
  .o_fifo_out_data    (w_fifo_out_data      )

);




//submodules
//asynchronous logic

assign  w_axi_rst                         = (INVERT_AXI_RESET)   ? ~rst         : rst;
assign  w_axis_rst                        = (INVERT_AXIS_RESET)  ? ~i_axis_rst  : i_axis_rst;


//synchronous logic
always @ (posedge clk) begin
  //De-assert Strobes
  r_reg_in_ack_stb                        <=  0;
  r_reg_out_rdy_stb                       <=  0;
  r_reg_invalid_addr                      <=  0;

  r_reset                                 <=  0;

  if (w_axi_rst) begin
    r_reg_out_data                        <=  0;
    r_enable                              <=  0;

    r_video_in_width                      <=  VIDEO_IN_WIDTH;
    r_video_in_height                     <=  VIDEO_IN_HEIGHT;
    r_video_in_size                       <=  VIDEO_IN_SIZE;

    r_video_out_width                     <=  VIDEO_OUT_WIDTH;
    r_video_out_height                    <=  VIDEO_OUT_HEIGHT;
    r_video_out_size                      <=  VIDEO_OUT_SIZE;

    r_video_in_start_x                    <=  VIDEO_START_X;
    r_video_in_start_y                    <=  VIDEO_START_Y;

    r_fill_pixel_value                    <=  `DEFAULT_FILL_PIXEL;
  end
  else begin

    if (w_reg_in_rdy) begin
      //From master
      case (w_reg_address)
        REG_CONTROL: begin
          r_enable                        <= w_reg_in_data[`BIT_CONTROL_ENABLE];
          r_reset                         <= w_reg_in_data[`BIT_CONTROL_RESET];
        end
        REG_VIDEO_IN_SIZE: begin
          r_video_in_size                 <= w_reg_in_data;
        end
        REG_VIDEO_IN_WIDTH: begin
          r_video_in_width                <= w_reg_in_data;
        end
        REG_VIDEO_IN_HEIGHT: begin
          r_video_in_height               <= w_reg_in_data;
        end
        REG_VIDEO_OUT_SIZE: begin
          r_video_out_size                <= w_reg_in_data;
        end
        REG_VIDEO_OUT_WIDTH: begin
          r_video_out_width               <= w_reg_in_data;
        end
        REG_VIDEO_OUT_HEIGHT: begin
          r_video_out_height              <= w_reg_in_data;
        end
        REG_VIDEO_IN_START_X: begin
          r_video_in_start_x              <= w_reg_in_data;
        end
        REG_VIDEO_IN_START_Y: begin
          r_video_in_start_y              <= w_reg_in_data;
        end
        REG_IN_FILL_PIXEL: begin
          r_fill_pixel_value              <= w_reg_in_data[AXIS_WIDTH - 1: 0];
        end
        default: begin
        end
      endcase
      if (w_reg_address > REG_LAST_REG) begin
        r_reg_invalid_addr                <= 1;
      end
      r_reg_in_ack_stb                    <= 1;
    end
    else if (w_reg_out_req) begin
      //To master
      case (w_reg_address)
        REG_CONTROL: begin
          r_reg_out_data[`BIT_CONTROL_ENABLE] <=  r_enable;
          r_reg_out_data[`BIT_CONTROL_RESET]  <=  r_reset;
        end
        REG_STATUS: begin
          r_reg_out_data                  <= 32'h0;
          r_reg_out_data[0]               <= r_enable;
        end
        REG_VERSION: begin
          r_reg_out_data                  <= 32'h00;
          r_reg_out_data[`MAJOR_RANGE]    <= `MAJOR_VERSION;
          r_reg_out_data[`MINOR_RANGE]    <= `MINOR_VERSION;
          r_reg_out_data[`REVISION_RANGE] <= `REVISION;
        end
        REG_VIDEO_IN_SIZE: begin
          r_reg_out_data                  <= r_video_in_size;
        end
        REG_VIDEO_IN_WIDTH: begin
          r_reg_out_data                  <= r_video_in_width;
        end
        REG_VIDEO_IN_HEIGHT: begin
          r_reg_out_data                  <= r_video_in_height;
        end
        REG_VIDEO_OUT_SIZE: begin
          r_reg_out_data                  <= r_video_out_size;
        end
        REG_VIDEO_OUT_WIDTH: begin
          r_reg_out_data                  <= r_video_out_width;
        end
        REG_VIDEO_OUT_HEIGHT: begin
          r_reg_out_data                  <= r_video_out_height;
        end
        REG_VIDEO_IN_START_X: begin
          r_reg_out_data                  <= r_video_in_start_x;
        end
        REG_VIDEO_IN_START_Y: begin
          r_reg_out_data                  <= r_video_in_start_y;
        end
        REG_IN_FILL_PIXEL: begin
          r_reg_out_data                  <= r_fill_pixel_value;
        end
        default: begin
          r_reg_out_data                  <= 32'h00;
        end
      endcase
      if (w_reg_address > REG_LAST_REG) begin
        r_reg_invalid_addr                <= 1;
      end
      r_reg_out_rdy_stb                   <= 1;
    end
  end
end



endmodule