module wb_fpga_nes (
  input               clk,
  input               rst,

  //Add signals to control your device here

  //Wishbone Bus Signals
  input               i_wbs_we,
  input               i_wbs_cyc,
  input       [3:0]   i_wbs_sel,
  input       [31:0]  i_wbs_dat,
  input               i_wbs_stb,
  output  reg         o_wbs_ack,
  output  reg [31:0]  o_wbs_dat,
  input       [31:0]  i_wbs_adr,

  //master control signal for memory arbitration
  output              mem_o_we,
  output              mem_o_stb,
  output              mem_o_cyc,
  output      [3:0]   mem_o_sel,
  output      [31:0]  mem_o_adr,
  output      [31:0]  mem_o_dat,
  input       [31:0]  mem_i_dat,
  input               mem_i_ack,
  input               mem_i_int,

  //This interrupt can be controlled from this module or a submodule
  output  reg         o_wbs_int

  //Nes Interface

);
//Local Parameters
localparam      CONTROL            = 32'h00000000;
localparam      STATUS             = 32'h00000001;
localparam      USER_INPUT         = 32'h00000002;
localparam      HCI_OPCODE_COUNT   = 32'h00000003;
localparam      HCI_OPCODE_ADDR    = 32'h00000004;
localparam      HCI_OPCODE         = 32'h00000005;
localparam      HCI_OPCODE_DATA    = 32'h00000006;
localparam      DMA_STATUS         = 32'h00000007;
localparam      REG_MEM_0_BASE     = 32'h00000008;
localparam      REG_MEM_0_SIZE     = 32'h00000009;
localparam      REG_MEM_1_BASE     = 32'h0000000A;
localparam      REG_MEM_1_SIZE     = 32'h0000000B;
localparam      REG_IMAGE_WIDTH    = 32'h0000000C;
localparam      REG_IMAGE_HEIGHT   = 32'h0000000D;

//Control
localparam      CONTROL_HCI_RESET             = 0;
localparam      CONTROL_CONSOLE_RESET         = 1;
localparam      CONTROL_ENABLE_DMA            = 2;

//Status
localparam      STATUS_CLOCK_LOCKED           = 0;
localparam      STATUS_HCI_READY              = 1;
localparam      STATUS_HCI_NEW_STATUS         = 2;
localparam      STATUS_HCI_S_BOT              = 16;
localparam      STATUS_HCI_S_TOP              = STATUS_HCI_S_BOT + 15;

//Local Registers/Wires
//wire                  clk_locked;
//wire                  nes_clk;
wire          [31:0]  status;
reg           [31:0]  control;
wire          [3:0]   dma_status;

//DMA Transfer Registers
wire                w_enable_dma;

reg         [31:0]  r_memory_0_base;
reg         [31:0]  r_memory_0_size;
wire        [31:0]  w_memory_0_count;
reg                 r_memory_0_ready;
wire                w_memory_0_finished;
wire                w_memory_0_empty;

wire        [31:0]  w_default_mem_0_base;

reg         [31:0]  r_memory_1_base;
reg         [31:0]  r_memory_1_size;
wire        [31:0]  w_memory_1_count;
reg                 r_memory_1_ready;
wire                w_memory_1_finished;
wire                w_memory_1_empty;

wire        [31:0]  w_default_mem_1_base;

wire                w_write_finished;

wire                w_rfifo_ready;
wire                w_rfifo_activate;
wire                w_rfifo_strobe;
wire        [31:0]  w_rfifo_data;
wire        [23:0]  w_rfifo_size;
wire                w_captured;
wire                w_busy;
//NES Wires
wire                  w_console_reset;

//NES Controls
wire          [3:0]   w_mute_control;

reg           [7:0]   r_jp1_state;     //State of joypad 1
reg           [7:0]   r_jp2_state;     //State of joypad 2


//Audio
wire                  w_audio;

//Video Interface
wire          [9:0]   w_image_width;
wire          [9:0]   w_image_height;

wire                  w_hsync;
wire                  w_vsync;
wire          [2:0]   w_red;
wire          [2:0]   w_green;
wire          [1:0]   w_blue;

//HCI Interface
wire                  w_hci_reset;
reg           [7:0]   r_hci_opcode;
reg                   r_hci_opcode_strobe;

wire          [15:0]  w_hci_opcode_status;
reg           [15:0]  r_hci_opcode_status;
wire                  w_hci_opcode_ack;
reg                   r_hci_opcode_new_status;

reg           [15:0]  r_hci_address;
reg           [31:0]  r_hci_count;

reg                   r_hci_din_strobe;
wire                  w_hci_sm_ready;
reg           [7:0]   r_hci_din;

wire                  w_hci_dout_strobe;
reg                   r_hci_host_ready;
wire          [7:0]   w_hci_dout;

wire                  w_flush_memory;
wire                  w_memory_ready;


//Submodules

image_to_ppfifo proc (
  .clk                  (clk                      ),
  .rst                  (rst                      ),
  .i_enable             (w_enable_dma             ),

  .i_hsync              (w_hsync                  ),
  .i_vsync              (w_vsync                  ),
  .i_red                (w_red                    ),
  .i_green              (w_green                  ),
  .i_blue               (w_blue                   ),

  .o_frame_finished     (w_flush_memory           ),

  .o_rfifo_ready        (w_rfifo_ready            ),
  .i_rfifo_activate     (w_rfifo_activate         ),
  .i_rfifo_strobe       (w_rfifo_strobe           ),
  .o_rfifo_data         (w_rfifo_data             ),
  .o_rfifo_size         (w_rfifo_size             )
);

wb_ppfifo_2_mem p2m(

  .clk                  (clk                      ),
  .rst                  (rst  || !w_console_reset ),
  //.debug                (debug                    ),

  //Control
  .i_enable             (w_enable_dma             ),
  .i_flush              (w_flush_memory           ),

  .i_memory_0_base      (r_memory_0_base          ),
  .i_memory_0_size      (r_memory_0_size          ),
  .o_memory_0_count     (w_memory_0_count         ),
  .i_memory_0_ready     (r_memory_0_ready         ),
  .o_memory_0_finished  (w_memory_0_finished      ),
  .o_memory_0_empty     (w_memory_0_empty         ),

  .o_default_mem_0_base (w_default_mem_0_base     ),

  .i_memory_1_base      (r_memory_1_base          ),
  .i_memory_1_size      (r_memory_1_size          ),
  .o_memory_1_count     (w_memory_1_count         ),
  .i_memory_1_ready     (r_memory_1_ready         ),
  .o_memory_1_finished  (w_memory_1_finished      ),
  .o_memory_1_empty     (w_memory_1_empty         ),

  .o_default_mem_1_base (w_default_mem_1_base     ),

  .o_write_finished     (w_write_finished         ),

  //master control signal for memory arbitration
  .o_mem_we             (mem_o_we                 ),
  .o_mem_stb            (mem_o_stb                ),
  .o_mem_cyc            (mem_o_cyc                ),
  .o_mem_sel            (mem_o_sel                ),
  .o_mem_adr            (mem_o_adr                ),
  .o_mem_dat            (mem_o_dat                ),
  .i_mem_dat            (mem_i_dat                ),
  .i_mem_ack            (mem_i_ack                ),
  .i_mem_int            (mem_i_int                ),

  //Ping Pong FIFO Interface (Read)
  .i_ppfifo_rdy         (w_rfifo_ready            ),
  .o_ppfifo_act         (w_rfifo_activate         ),
  .i_ppfifo_size        (w_rfifo_size             ),
  .o_ppfifo_stb         (w_rfifo_strobe           ),
  .i_ppfifo_data        (w_rfifo_data             )
);


/*
nes_clkgen clkgen (
	.clk                 (clk                 ),
	.rst                 (rst                 ),

	.locked              (clk_locked          ),
	//.phy_out_clk         (phy_out_clk         ),
	.out_clk             (nes_clk             )
);
*/

nes_top nes(
//  .clk          (nes_clk             ),
  .clk                  (clk                 ),
  .rst                  (rst                 ),
  //.RXD                 (rxd                 ),
  //.TXD                 (txd                 ),
  .i_console_reset      (w_console_reset     ),
  .i_mute_control       (w_mute_control      ),

  .i_jp1_state          (r_jp1_state         ),
  .i_jp2_state          (r_jp2_state         ),

  .o_audio              (w_audio             ),

  .o_image_width        (w_image_width       ),
  .o_image_height       (w_image_height      ),
  .o_hsync              (w_hsync             ),
  .o_vsync              (w_vsync             ),
  .o_red                (w_red               ),
  .o_green              (w_green             ),
  .o_blue               (w_blue              ),

  //HCI Interface
  .i_hci_reset          (w_hci_reset         ),
  .i_hci_opcode         (r_hci_opcode        ),
  .i_hci_opcode_strobe  (r_hci_opcode_strobe ),

  .o_hci_opcode_status  (w_hci_opcode_status ),
  .o_hci_opcode_ack     (w_hci_opcode_ack    ),

  .i_hci_address        (r_hci_address       ),
  .i_hci_count          (r_hci_count         ),

  .i_hci_data_strobe    (r_hci_din_strobe    ),
  .o_hci_sm_ready       (w_hci_sm_ready      ),
  .i_hci_data           (r_hci_din           ),

  .o_hci_data_strobe    (w_hci_dout_strobe   ),
  .i_hci_host_ready     (r_hci_host_ready    ),
  .o_hci_data           (w_hci_dout          )
);

//Asynchronous Logic
//assign  status[STATUS_CLOCK_LOCKED]               = clk_locked;
assign  status[STATUS_CLOCK_LOCKED]               = 1'b0;
assign  status[STATUS_HCI_READY]                  = w_hci_sm_ready;
assign  status[STATUS_HCI_NEW_STATUS]             = r_hci_opcode_new_status;
assign  status[STATUS_HCI_S_TOP:STATUS_HCI_S_BOT] = r_hci_opcode_status;
assign  status[15:3]                              = 0;

assign  dma_status[3:0]                           = {
                                                      (r_memory_1_size == 0),
                                                      (r_memory_0_size == 0),
                                                       w_memory_1_finished,
                                                       w_memory_0_finished
                                                    };
assign w_memory_ready                             = (!w_memory_0_empty) || (!w_memory_1_empty);


assign  w_hci_reset                               = control[CONTROL_HCI_RESET];
assign  w_console_reset                           = control[CONTROL_CONSOLE_RESET];
assign  w_enable_dma                              = control[CONTROL_ENABLE_DMA];
assign  w_mute_control                            = 4'h0;

//Synchronous Logic
always @ (posedge clk) begin
  if (rst || !w_enable_dma || o_wbs_int) begin
    o_wbs_int           <= 0;
  end
  else begin
    if (w_memory_0_finished || w_memory_1_finished) begin
      o_wbs_int         <= 1;
    end
    else if (!w_memory_0_finished && !w_memory_1_finished) begin
      o_wbs_int         <= 0;
    end
  end
end

always @ (posedge clk) begin
  if (rst) begin
    o_wbs_dat           <= 32'h0;
    o_wbs_ack           <= 0;

    //HCI
    r_hci_address       <=  0;
    r_hci_count         <=  0;

    r_hci_opcode_status <=  0;
    r_hci_opcode        <=  0;
    r_hci_opcode_strobe <=  0;

    r_hci_din_strobe    <=  0;
    r_hci_din           <=  0;

    //Control
    control             <=  0;

    r_jp1_state         <=  0;
    r_jp2_state         <=  0;

    //DMA Controller
    //Default base, user can change this from the API
    r_memory_0_base     <=  w_default_mem_0_base;
    r_memory_1_base     <=  w_default_mem_1_base;

    //Nothing in the memory initially
    r_memory_0_size     <=  0;
    r_memory_1_size     <=  0;

    r_memory_0_ready    <=  0;
    r_memory_1_ready    <=  0;


  end
  else begin
    r_hci_opcode_strobe <=  0;
    r_hci_din_strobe    <=  0;
    r_hci_host_ready    <=  0;

    r_memory_0_ready    <=  0;
    r_memory_1_ready    <=  0;



    //when the master acks our ack, then put our ack down
    if (o_wbs_ack && ~i_wbs_stb)begin
      o_wbs_ack <= 0;
      if (i_wbs_adr == STATUS) begin
        r_hci_opcode_new_status <= 0;
      end
    end

    if (i_wbs_stb && i_wbs_cyc) begin
      //master is requesting somethign
      if (!o_wbs_ack) begin
        if (i_wbs_we) begin
          //write request
          case (i_wbs_adr)
            CONTROL: begin
              control                 <=  i_wbs_dat;
              o_wbs_ack               <= 1;
            end
            //STATUS
            USER_INPUT: begin
              //Need to make the 32-bit values to joypad locations
              r_jp1_state             <=  i_wbs_dat[7:0];
              r_jp2_state             <=  i_wbs_dat[15:8];
              o_wbs_ack               <= 1;
            end
            HCI_OPCODE_COUNT: begin
              r_hci_count             <= i_wbs_dat;
              o_wbs_ack               <= 1;
            end
            HCI_OPCODE_ADDR: begin
              r_hci_address           <= i_wbs_dat;
              o_wbs_ack               <= 1;
            end
            HCI_OPCODE: begin
              r_hci_opcode            <= i_wbs_dat;
              r_hci_opcode_strobe     <= 1;
              o_wbs_ack               <= 1;
            end
            HCI_OPCODE_DATA: begin
              r_hci_din               <=  i_wbs_dat[7:0];
              if (w_hci_sm_ready) begin
                r_hci_din_strobe      <= 1;
                o_wbs_ack             <= 1;
              end
            end
            REG_MEM_0_BASE: begin
              r_memory_0_base         <=  i_wbs_dat;
              o_wbs_ack               <= 1;
            end
            REG_MEM_0_SIZE: begin
              r_memory_0_size         <=  i_wbs_dat;
              if (i_wbs_dat > 0) begin
                r_memory_0_ready      <=  1;
              end
              o_wbs_ack               <= 1;
            end
            REG_MEM_1_BASE: begin
              r_memory_1_base         <=  i_wbs_dat;
              o_wbs_ack               <= 1;
            end
            REG_MEM_1_SIZE: begin
              r_memory_1_size         <=  i_wbs_dat;
              if (i_wbs_dat > 0) begin
                r_memory_1_ready      <=  1;
              end
              o_wbs_ack               <= 1;
            end
            default: begin
              o_wbs_ack               <= 1;
            end
          endcase
        end
        else begin
          //read request
          case (i_wbs_adr)
            CONTROL: begin
              o_wbs_dat               <=  control;
              o_wbs_ack               <= 1;
            end
            STATUS: begin
              o_wbs_dat               <= status;
              o_wbs_ack               <= 1;
            end
            //USER_INPUT
            HCI_OPCODE_COUNT: begin
              o_wbs_dat               <= r_hci_count;
              o_wbs_ack               <= 1;
            end

            //Host Controller Interface
            HCI_OPCODE_ADDR: begin
              o_wbs_dat               <= r_hci_address;
              o_wbs_ack               <= 1;
            end
            HCI_OPCODE: begin
              o_wbs_dat               <= r_hci_opcode;
              o_wbs_ack               <= 1;
            end
            HCI_OPCODE_DATA: begin
              r_hci_host_ready        <=  1;
              if (w_hci_dout_strobe) begin
                o_wbs_dat             <= {24'h000000, w_hci_dout};
                o_wbs_ack             <= 1;
              end
            end

            //DMA Registers
            DMA_STATUS: begin
              o_wbs_dat             <=  {28'h0000000, dma_status};
              if (w_memory_0_finished) begin
                $display ("Reset size 0");
                r_memory_0_size     <=  0;
              end
              if (w_memory_1_finished) begin
                $display ("Reset size 1");
                r_memory_1_size     <=  0;
              end
              o_wbs_ack             <= 1;
            end
            REG_MEM_0_BASE: begin
              o_wbs_dat <=  r_memory_0_base;
              o_wbs_ack             <= 1;
            end
            REG_MEM_0_SIZE: begin
              o_wbs_dat <=  w_memory_0_count;
              o_wbs_ack             <= 1;
            end
            REG_MEM_1_BASE: begin
              o_wbs_dat <=  r_memory_1_base;
              o_wbs_ack             <= 1;
            end
            REG_MEM_1_SIZE: begin
              o_wbs_dat <=  w_memory_1_count;
              o_wbs_ack             <= 1;
            end
            REG_IMAGE_WIDTH: begin
              o_wbs_dat <=  w_image_width;
              o_wbs_ack             <= 1;
            end
            REG_IMAGE_HEIGHT: begin
              o_wbs_dat <=  w_image_height;
              o_wbs_ack             <= 1;
            end

            default: begin
              o_wbs_ack             <= 1;
            end
          endcase
        end
      end //!o_wbs_ack
    end
    if (w_hci_opcode_ack) begin
      r_hci_opcode_status <= w_hci_opcode_status;
    end
  end
end

endmodule