//============================================================================
//
//  DE10-nano HAL top module
//  (c)2017 Sorgelig
//
//  This program is free software; you can redistribute it and/or modify it
//  under the terms of the GNU General Public License as published by the Free
//  Software Foundation; either version 2 of the License, or (at your option)
//  any later version.
//
//  This program is distributed in the hope that it will be useful, but WITHOUT
//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
//  more details.
//
//  You should have received a copy of the GNU General Public License along
//  with this program; if not, write to the Free Software Foundation, Inc.,
//  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
//============================================================================

module sys_top
(
	/////////// CLOCK //////////
	input         FPGA_CLK1_50,
	input         FPGA_CLK2_50,
	input         FPGA_CLK3_50,

	//////////// VGA ///////////
	output  [5:0] VGA_R,
	output  [5:0] VGA_G,
	output  [5:0] VGA_B,
	output	      VGA_HS,
	output	      VGA_VS,
	input         VGA_EN,
    
	/////////// AUDIO //////////
	output	      AUDIO_L,
	output	      AUDIO_R,
	output	      AUDIO_SPDIF,
    
	//////////// HDMI //////////
	output        HDMI_I2C_SCL,
	inout         HDMI_I2C_SDA,

	output        HDMI_MCLK,
	output        HDMI_SCLK,
	output        HDMI_LRCLK,
	output        HDMI_I2S,

	output        HDMI_TX_CLK,
	output        HDMI_TX_DE,
	output [23:0] HDMI_TX_D,
	output        HDMI_TX_HS,
	output        HDMI_TX_VS,

	//////////// SDR ///////////
	output [12:0] SDRAM_A,
	inout  [15:0] SDRAM_DQ,
	output        SDRAM_DQML,
	output        SDRAM_DQMH,
	output        SDRAM_nWE,
	output        SDRAM_nCAS,
	output        SDRAM_nRAS,
	output        SDRAM_nCS,
	output  [1:0] SDRAM_BA,
	output        SDRAM_CLK,
	output        SDRAM_CKE,

	//////////// I/O ///////////
	output        LED_USER,
	output        LED_HDD,
	output        LED_POWER,
	input         BTN_USER,
	input         BTN_OSD,
	input         BTN_RESET,

	////////// MB KEY ///////////
	input   [1:0] KEY,

	////////// MB LED ///////////
	output  [7:0] LED
);


//////////////////////////  LEDs  ///////////////////////////////////////

   wire      led_p =  led_power[1] ? ~led_power[0] : 1'b0;
   wire      led_d =  led_disk[1]  ? ~led_disk[0]  : ~(led_disk[0] | gp_out[29]);
   wire      led_u = ~led_user;
   wire      TESTLEDS;
   
   
   assign LED_POWER = led_p ? 1'bZ : 1'b0;
   assign LED_HDD   = led_d ? 1'bZ : 1'b0;
   assign LED_USER  = led_u ? 1'bZ : 1'b0;

   //LEDs on main board
   //assign LED = {TESTLEDS , ~led_p, ~led_d, 1'b0, ~led_u};
   assign LED = {FPGA_CLK1_50, HDMI_SCLK, HDMI_LRCLK, HDMI_I2S, 
		 1'b0, 1'b0, 1'b0, 1'b1};
   
   //////////////////////////  Buttons  ///////////////////////////////////
   reg 	      btn_user, btn_osd;
   
   always @(posedge FPGA_CLK2_50) begin
      integer div;
      reg [7:0] deb_user;
      reg [7:0] deb_osd;

      div <= div + 1'b1;
      if(div > 100000) div <= 0;
      
      if(!div) begin
	 deb_user <= {deb_user[6:0], ~(BTN_USER & KEY[1])};
	 if(&deb_user) btn_user <= 1;
	 if(!deb_user) btn_user <= 0;

	 deb_osd <= {deb_osd[6:0], ~(BTN_OSD & KEY[0])};
	 if(&deb_osd) btn_osd <= 1;
	 if(!deb_osd) btn_osd <= 0;
      end
   end

   reg btn_reset = 1;
   always @(posedge FPGA_CLK2_50) btn_reset <= BTN_RESET;


   /////////////////////////  HPS I/O  /////////////////////////////////////

   // gp_in[31] = 0 - quick flag that FPGA is initialized (HPS reads 1 when FPGA is not in user mode)
   //                 used to avoid lockups while JTAG loading
   wire [31:0] gp_in = {1'b0, btn_user, btn_osd, 9'd0, io_ver, io_ack, io_wide, io_dout};
   wire [31:0] gp_out;

   wire [1:0]  io_ver    = 1; // 0 - standard MiST I/O (for quick porting of complex MiST cores). 1 - optimized HPS I/O. 2,3 - reserved for future.
   wire        io_wait;
   wire        io_wide;
   wire [15:0] io_dout;                  
   wire [15:0] io_din    = gp_outr[15:0];
   wire        io_clk    = gp_outr[17];
   wire        io_fpga   = gp_outr[18];
   wire        io_osd    = gp_outr[19];
   wire        io_uio    = gp_outr[20];
   //wire        io_sdd    = gp_outr[21]; // used only in ST core

   reg 	       io_ack;
   reg 	       rack;
   wire        io_strobe = ~rack & io_clk;

   always @(posedge clk_sys) begin
      if(~io_wait | io_strobe) begin
	 rack <= io_clk;
	 io_ack <= rack;
      end
   end

   reg [31:0] gp_outr;
   always @(posedge clk_sys) begin
      reg [31:0] gp_outd;
      gp_outr <= gp_outd;
      gp_outd <= gp_out;
   end

   wire [7:0] 	 core_type  = 'hA4; // A4 - generic core.

   // HPS will not communicate to core if magic is different
   wire [31:0] 	 core_magic = {24'h5CA623, core_type};

   cyclonev_hps_interface_mpu_general_purpose h2f_gp
     (
      .gp_in({~gp_out[31] ? core_magic : gp_in}),
      .gp_out(gp_out)
      );


   reg [15:0] 	 cfg;

   reg       cfg_ready = 0;
   wire      audio_96k = cfg[6];
   wire      ypbpr_en  = cfg[5];
   wire      csync     = cfg[3];

   always@(posedge clk_sys) begin
      reg  [7:0] cmd;
      reg        has_cmd;
      reg        old_strobe;

      old_strobe <= io_strobe;

      if(~io_uio)
        has_cmd <= 0;
      else
        if(~old_strobe & io_strobe) begin
          if(!has_cmd) begin
            has_cmd <= 1;
            cmd <= io_din[7:0];
          end
        else
          if(cmd == 1) begin
            cfg <= io_din;
            cfg_ready <= 1;
          end
        end
      end

   ///////////////////////////  RESET  ///////////////////////////////////

   reg reset_req = 0;
   always @(posedge FPGA_CLK2_50) begin
      reg [1:0] resetd, resetd2;
      reg       old_reset;

      //latch the reset
      old_reset <= reset;
      if(~old_reset & reset) reset_req <= 1;

      //special combination to set/clear the reset
      //preventing of accidental reset control
      if(resetd==1) reset_req <= 1;
      if(resetd==2 && resetd2==0) reset_req <= 0;

      resetd  <= gp_out[31:30];
      resetd2 <= resetd;
   end
   
   wire 	vde, hde;
   wire 	vs_hdmi;
   wire 	hs_hdmi;
   wire 	field;
   wire 	reset;
   
   wire 	ram0_clk;
   wire [29:0] 	ram0_address;
   wire [7:0] 	ram0_burstcount;
   wire 	ram0_waitrequest;
   wire [31:0] 	ram0_readdata;
   wire 	ram0_readdatavalid;
   wire 	ram0_read;
   wire [31:0] 	ram0_writedata;
   wire [3:0] 	ram0_byteenable;
   wire 	ram0_write;

   wire 	ram1_clk;
   wire [29:0] 	ram1_address;
   wire [7:0] 	ram1_burstcount;
   wire 	ram1_waitrequest;
   wire [31:0] 	ram1_readdata;
   wire 	ram1_readdatavalid;
   wire 	ram1_read;
   wire [31:0] 	ram1_writedata;
   wire [3:0] 	ram1_byteenable;
   wire 	ram1_write;

   wire 	ram2_clk;
   wire [29:0] 	ram2_address;
   wire [7:0] 	ram2_burstcount;
   wire 	ram2_waitrequest;
   wire [31:0] 	ram2_readdata;
   wire 	ram2_readdatavalid;
   wire 	ram2_read;
   wire [31:0] 	ram2_writedata;
   wire [3:0] 	ram2_byteenable;
   wire 	ram2_write;
   
   wire user0_clk;
   wire user1_clk;
   
   
   
sysmem sysmem
   (
   .user0_clk (user0_clk),
   .user1_clk (user1_clk),
   
   
   //Reset/Clock
   .reset_reset_req(reset_req),
   .reset_reset(reset),

   //DE10-nano has no reset signal on GPIO, so core has to emulate cold reset button.
   .reset_cold_req(~btn_reset),
   .reset_warm_req(0),

   .ramclk0_clk(ram0_clk),
   .ram0_address(ram0_address),
   .ram0_burstcount(ram0_burstcount),
   .ram0_waitrequest(ram0_waitrequest),
   .ram0_readdata(ram0_readdata),
   .ram0_readdatavalid(ram0_readdatavalid),
   .ram0_read(ram0_read),
   .ram0_writedata(ram0_writedata),
   .ram0_byteenable(ram0_byteenable),
   .ram0_write(ram0_write),

   .ramclk1_clk(ram1_clk),
   .ram1_address(ram1_address),
   .ram1_burstcount(ram1_burstcount),
   .ram1_waitrequest(ram1_waitrequest),
   .ram1_readdata(ram1_readdata),
   .ram1_readdatavalid(ram1_readdatavalid),
   .ram1_read(ram1_read),
   .ram1_writedata(ram1_writedata),
   .ram1_byteenable(ram1_byteenable),
   .ram1_write(ram1_write),

   .ramclk2_clk(ram2_clk),
   .ram2_address(ram2_address),
   .ram2_burstcount(ram2_burstcount),
   .ram2_waitrequest(ram2_waitrequest),
   .ram2_readdata(ram2_readdata),
   .ram2_readdatavalid(ram2_readdatavalid),
   .ram2_read(ram2_read),
   .ram2_writedata(ram2_writedata),
   .ram2_byteenable(ram2_byteenable),
   .ram2_write(ram2_write)	
);
   
   /////////////////////////  HDMI output  /////////////////////////////////
   
   reg hdmi_ready=1'b0;
   
   always @(posedge FPGA_CLK1_50) begin
      reg [11:0] count  = 0;
      if (count==1024) begin
	 hdmi_ready<=1'b1;
      end else begin
	 count<=count+1;
	 hdmi_ready<=1'b0;
      end
   end
   
   hdmi_config hdmi_config
     (
      .iCLK(FPGA_CLK1_50),
      .iRST_N(cfg_ready),
      .I2C_SCL(HDMI_I2C_SCL),
      .I2C_SDA(HDMI_I2C_SDA),

      .audio_48k(~audio_96k),
      .iRES(4), // 720p
      .iAR(1)   // Aspect Ratio
      );
   
   wire [23:0] 	hdmi_data;
   osd hdmi_osd
     (
      .clk_sys(clk_sys),

      .io_osd(io_osd),
      .io_strobe(io_strobe),
      .io_din(io_din[7:0]),

      .clk_video(HDMI_TX_CLK),
      .din(hdmi_data),
      .dout(HDMI_TX_D),
      .de(HDMI_TX_DE)
      );
   
   assign HDMI_MCLK = 0;
   i2s i2s
     (
      .reset(~hdmi_ready), //cfg_ready),
      .clk_sys(FPGA_CLK1_50),
      .half_rate(~audio_96k),
      
      .sclk (HDMI_SCLK),
      .lrclk(HDMI_LRCLK),
      .sdata(HDMI_I2S),
      
      //Could inverse the MSB but it will shift 0 level to -MAX level
      .left_chan (audio_l >> !audio_s),
      .right_chan(audio_r >> !audio_s)
      );
   
   /////////////////////////  Audio output  ////////////////////////////////
       
   sigma_delta_dac #(15) dac_l
     (
      .CLK(FPGA_CLK3_50),
      .RESET(0), //reset),
      .DACin({audio_l[15] ^ audio_s, audio_l[14:0]}),
      .DACout(AUDIO_L)
      );
   
   sigma_delta_dac #(15) dac_r
     (
      .CLK(FPGA_CLK3_50),
      .RESET(0), //reset),
      .DACin({audio_r[15] ^ audio_s, audio_r[14:0]}),
      .DACout(AUDIO_R)
      );
   
   spdif toslink
     (
      .clk_i(FPGA_CLK3_50),

      .rst_i(reset),
      .half_rate(0),

      .audio_l(audio_l >> !audio_s),
      .audio_r(audio_r >> !audio_s),

      .spdif_o(AUDIO_SPDIF)
      );
   
   ///////////////////  User module connection ////////////////////////////

   wire [15:0] 	audio_l, audio_r;
   wire 	audio_s;
   wire [7:0] 	r_out, g_out, b_out;
   wire 	vs, hs, de;
   wire 	clk_sys, clk_vid, ce_pix;

   wire 	led_user;
   wire [1:0] 	led_power;
   wire [1:0] 	led_disk;

   wire [7:0] 	ARX, ARY;
   
   wire [15:0]  spot_x,spot_y;
   wire [7:0]   spot_z;
   wire         spot_en,spot_clk;
   wire         ds_x,ds_y,ds_z;

   assign HDMI_TX_CLK=clk_vid;
   assign HDMI_TX_DE=de;
   assign HDMI_TX_HS=hs;
   assign HDMI_TX_VS=vs;
   assign hdmi_data = {r_out , g_out , b_out};

   assign VGA_R={ds_x,ds_x,ds_x,ds_x,ds_x,ds_x}; //r_out[7:2];
   assign VGA_G={ds_y,ds_y,ds_y,ds_y,ds_y,ds_y}; //g_out[7:2];
   assign VGA_B={ds_z,ds_z,ds_z,ds_z,ds_z,ds_z}; //b_out[7:2];
   assign VGA_HS=hs;
   assign VGA_VS=vs;
   
emu emu
     (
      .CLK_50M(FPGA_CLK3_50),
      .CLK_50M2(FPGA_CLK1_50),
      .RESET(reset),
      .HPS_BUS({io_wait, clk_sys, io_fpga, io_uio, io_strobe, io_wide, io_din, io_dout}),

      .CLK_VIDEO(clk_vid),
      .CE_PIXEL(ce_pix),

      .VGA_R(r_out),
      .VGA_G(g_out),
      .VGA_B(b_out),
      .VGA_HS(hs),
      .VGA_VS(vs),
      .VGA_DE(de),

      .SPOT_X(spot_x),
      .SPOT_Y(spot_y),
      .SPOT_Z(spot_z),
      .SPOT_EN(spot_en),
      .SPOT_CLK(spot_clk),
      
      .LED_USER(led_user),
      .LED_POWER(led_power),
      .LED_DISK(led_disk),

      .TESTLEDS(TESTLEDS),

      .VIDEO_ARX(ARX),
      .VIDEO_ARY(ARY),
      
      .AUDIO_L(audio_l),
      .AUDIO_R(audio_r),
      .AUDIO_S(audio_s),
      .TAPE_IN(0),

      .ddram1_clk(ram1_clk),
      .ddram1_address(ram1_address),
      .ddram1_burstcount(ram1_burstcount),
      .ddram1_waitrequest(ram1_waitrequest),
      .ddram1_readdata(ram1_readdata),
      .ddram1_readdatavalid(ram1_readdatavalid),
      .ddram1_read(ram1_read),
      .ddram1_writedata(ram1_writedata),
      .ddram1_byteenable(ram1_byteenable),
      .ddram1_write(ram1_write),

      .ddram2_clk(ram2_clk),
      .ddram2_address(ram2_address),
      .ddram2_burstcount(ram2_burstcount),
      .ddram2_waitrequest(ram2_waitrequest),
      .ddram2_readdata(ram2_readdata),
      .ddram2_readdatavalid(ram2_readdatavalid),
      .ddram2_read(ram2_read),
      .ddram2_writedata(ram2_writedata),
      .ddram2_byteenable(ram2_byteenable),
      .ddram2_write(ram2_write),

      .ddram3_clk(ram0_clk),
      .ddram3_address(ram0_address),
      .ddram3_burstcount(ram0_burstcount),
      .ddram3_waitrequest(ram0_waitrequest),
      .ddram3_readdata(ram0_readdata),
      .ddram3_readdatavalid(ram0_readdatavalid),
      .ddram3_read(ram0_read),
      .ddram3_writedata(ram0_writedata),
      .ddram3_byteenable(ram0_byteenable),
      .ddram3_write(ram0_write),
      
      .SDRAM_DQ(SDRAM_DQ),
      .SDRAM_A(SDRAM_A),
      .SDRAM_DQML(SDRAM_DQML),
      .SDRAM_DQMH(SDRAM_DQMH),
      .SDRAM_BA(SDRAM_BA),
      .SDRAM_nCS(SDRAM_nCS),
      .SDRAM_nWE(SDRAM_nWE),
      .SDRAM_nRAS(SDRAM_nRAS),
      .SDRAM_nCAS(SDRAM_nCAS),
      .SDRAM_CLK(SDRAM_CLK),
      .SDRAM_CKE(SDRAM_CKE)
      );

   sigma_delta_dac #(15) dac_x
     (
      .CLK(spot_clk),
      .RESET(reset),
      .DACin(spot_x),
      .DACout(ds_x)
      );

   sigma_delta_dac #(15) dac_y
     (
      .CLK(spot_clk),
      .RESET(reset),
      .DACin(spot_y),
      .DACout(ds_y)
      );
      
  sigma_delta_dac #(7) dac_z
     (
      .CLK(spot_clk),
      .RESET(reset),
      .DACin(spot_z),
      .DACout(ds_z)
      );
 
endmodule
