//============================================================================
//
//  MiSTer hardware abstraction module
//  (c)2017,2018 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,
	inout         VGA_HS,  // VGA_HS is secondary SD card detect when VGA_EN = 1 (inactive)
	output		  VGA_VS,
	input         VGA_EN,  // active low

	/////////// 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,
	
	input         HDMI_TX_INT,

	//////////// 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,

	//////////// SDIO ///////////
	inout   [3:0] SDIO_DAT,
	inout         SDIO_CMD,
	output        SDIO_CLK,
	input         SDIO_CD,

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

	////////// MB SWITCH ////////
	input   [3:0] SW,

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


assign SDIO_DAT[2:1] = 2'bZZ;


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

reg [7:0] led_overtake = 0;
reg [7:0] led_state    = 0;

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;

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 = (led_overtake & led_state) | (~led_overtake & {3'b000, ~led_p, 1'b0, ~led_d, 1'b0, ~led_u});


//////////////////////////  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_got   = 0;
reg        cfg_set   = 0;
//wire [2:0] hdmi_res  = cfg[10:8];
wire       dvi_mode  = cfg[7];
wire       audio_96k = cfg[6];
wire       ypbpr_en  = cfg[5];
wire       csync     = cfg[3];
`ifndef LITE
wire vga_scaler= cfg[2];
`endif

reg        cfg_custom_t = 0;
reg  [5:0] cfg_custom_p1;
reg [31:0] cfg_custom_p2;

reg  [4:0] vol_att = 0;

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

	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];
			cnt <= 0;
		end
		else begin
			if(cmd == 1) begin
				cfg <= io_din;
				cfg_set <= 1;
			end
			if(cmd == 'h20) begin
				cfg_set <= 0;
				cnt <= cnt + 1'd1;
				if(cnt<8) begin
					if(!cnt) vip_newcfg <= ~cfg_ready;
					case(cnt)
						0: if(WIDTH  != io_din[11:0]) begin WIDTH  <= io_din[11:0]; vip_newcfg <= 1; end
						1: if(HFP    != io_din[11:0]) begin HFP    <= io_din[11:0]; vip_newcfg <= 1; end
						2: if(HS     != io_din[11:0]) begin HS     <= io_din[11:0]; vip_newcfg <= 1; end
						3: if(HBP    != io_din[11:0]) begin HBP    <= io_din[11:0]; vip_newcfg <= 1; end
						4: if(HEIGHT != io_din[11:0]) begin HEIGHT <= io_din[11:0]; vip_newcfg <= 1; end
						5: if(VFP    != io_din[11:0]) begin VFP    <= io_din[11:0]; vip_newcfg <= 1; end
						6: if(VS     != io_din[11:0]) begin VS     <= io_din[11:0]; vip_newcfg <= 1; end
						7: if(VBP    != io_din[11:0]) begin VBP    <= io_din[11:0]; vip_newcfg <= 1; end
					endcase
					if(cnt == 1) begin
						cfg_custom_p1 <= 0;
						cfg_custom_p2 <= 0;
						cfg_custom_t <= ~cfg_custom_t;
					end
				end
				else begin
					if(cnt[1:0]==0) cfg_custom_p1 <= io_din[5:0];
					if(cnt[1:0]==1) cfg_custom_p2[15:0]  <= io_din;
					if(cnt[1:0]==2) begin
						cfg_custom_p2[31:16] <= io_din;
						cfg_custom_t <= ~cfg_custom_t;
						cnt[1:0] <= 0;
					end
				end
			end
			if(cmd == 'h25) {led_overtake, led_state} <= io_din;
			if(cmd == 'h26) vol_att <= io_din[4:0];
			if(cmd == 'h27) VSET    <= io_din[11:0];
		end
	end
end

always @(posedge clk_sys) begin
	reg vsd, vsd2;
	if(~cfg_ready || ~cfg_set) cfg_got <= cfg_set;
	else begin
		vsd  <= HDMI_TX_VS;
		vsd2 <= vsd;
		if(~vsd2 & vsd) cfg_got <= cfg_set;
	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 clk_ctl;

///////////////////////// VIP version  ///////////////////////////////

wire iHdmiClk = ~HDMI_TX_CLK;			// Internal HDMI clock, inverted in relation to external clock

`ifndef LITE

wire reset;
vip vip
(
	//Reset/Clock
	.reset_reset_req(reset_req | ~cfg_ready),
	.reset_reset(reset),
	.reset_reset_vip(0),

	//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),

	//control
	.ctl_address(ctl_address),
	.ctl_write(ctl_write),
	.ctl_writedata(ctl_writedata),
	.ctl_waitrequest(ctl_waitrequest),
	.ctl_clock(clk_ctl),
	.ctl_reset(ctl_reset),

	//64-bit DDR3 RAM access
	.ramclk1_clk(ram_clk),
	.ram1_address(ram_address),
	.ram1_burstcount(ram_burstcount),
	.ram1_waitrequest(ram_waitrequest),
	.ram1_readdata(ram_readdata),
	.ram1_readdatavalid(ram_readdatavalid),
	.ram1_read(ram_read),
	.ram1_writedata(ram_writedata),
	.ram1_byteenable(ram_byteenable),
	.ram1_write(ram_write),

	//Spare 64-bit DDR3 RAM access
	//currently unused
	//can combine with ram1 to make a wider RAM bus (although will increase the latency)
	.ramclk2_clk(0),
	.ram2_address(0),
	.ram2_burstcount(0),
	.ram2_waitrequest(),
	.ram2_readdata(),
	.ram2_readdatavalid(),
	.ram2_read(0),
	.ram2_writedata(0),
	.ram2_byteenable(0),
	.ram2_write(0),

	//Video input
	.in_clk(clk_vid),
	.in_data({r_out, g_out, b_out}),
	.in_de(de),
	.in_v_sync(vs),
	.in_h_sync(hs),
	.in_ce(ce_pix),
	.in_f(0),

	//HDMI output
	.hdmi_clk(iHdmiClk),
	.hdmi_data(hdmi_data),
	.hdmi_de(hdmi_de),
	.hdmi_v_sync(HDMI_TX_VS),
	.hdmi_h_sync(HDMI_TX_HS)
);

wire  [8:0] ctl_address;
wire        ctl_write;
wire [31:0] ctl_writedata;
wire        ctl_waitrequest;
wire        ctl_reset;
wire  [7:0] ARX, ARY;

vip_config vip_config
(
	.clk(clk_ctl),
	.reset(ctl_reset),

	.ARX(ARX),
	.ARY(ARY),
	.CFG_SET(vip_newcfg & cfg_got),

	.WIDTH(WIDTH),
	.HFP(HFP),
	.HBP(HBP),
	.HS(HS),
	.HEIGHT(HEIGHT),
	.VFP(VFP),
	.VBP(VBP),
	.VS(VS),
	.VSET(VSET),

	.address(ctl_address),
	.write(ctl_write),
	.writedata(ctl_writedata),
	.waitrequest(ctl_waitrequest)
);
`endif


/////////////////////////  Lite version  ////////////////////////////////

`ifdef LITE

wire [11:0] x;
wire [11:0] y;

sync_vg #(.X_BITS(12), .Y_BITS(12)) sync_vg
(
	.clk(iHdmiClk),
	.reset(reset),
	.v_total(HEIGHT+VFP+VBP+VS),
	.v_fp(VFP),
	.v_bp(VBP),
	.v_sync(VS),
	.h_total(WIDTH+HFP+HBP+HS),
	.h_fp(HFP),
	.h_bp(HBP),
	.h_sync(HS),
	.hv_offset(0),
	.vde_out(vde),
	.hde_out(hde),
	.vs_out(vs_hdmi),
	.v_count_out(),
	.h_count_out(),
	.x_out(x),
	.y_out(y),
	.hs_out(hs_hdmi)
);

wire vde, hde;
wire vs_hdmi;
wire hs_hdmi;

/*

pattern_vg
#(
	.B(8), // Bits per channel
	.X_BITS(12),
	.Y_BITS(12),
	.FRACTIONAL_BITS(12) // Number of fractional bits for ramp pattern
)
pattern_vg
(
	.reset(reset),
	.clk_in(iHdmiClk),
	.x(x),
	.y(y),
	.vn_in(vs_hdmi),
	.hn_in(hs_hdmi),
	.dn_in(vde & hde),
	.r_in(0),
	.g_in(0),
	.b_in(0),
	.vn_out(HDMI_TX_VS),
	.hn_out(HDMI_TX_HS),
	.den_out(HDMI_TX_DE),
	.r_out(hdmi_data[23:16]),
	.g_out(hdmi_data[15:8]),
	.b_out(hdmi_data[7:0]),
	.total_active_pix(WIDTH),
	.total_active_lines(HEIGHT),
	.pattern(4),
	.ramp_step(20'h0333)
);
*/

wire reset;
sysmem_lite sysmem
(
	//Reset/Clock
	.reset_reset_req(reset_req),
	.reset_reset(reset),
	.ctl_clock(clk_ctl),

	//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),

	//64-bit DDR3 RAM access
	.ramclk1_clk(ram_clk),
	.ram1_address(ram_address),
	.ram1_burstcount(ram_burstcount),
	.ram1_waitrequest(ram_waitrequest),
	.ram1_readdata(ram_readdata),
	.ram1_readdatavalid(ram_readdatavalid),
	.ram1_read(ram_read),
	.ram1_writedata(ram_writedata),
	.ram1_byteenable(ram_byteenable),
	.ram1_write(ram_write),

	//Spare 64-bit DDR3 RAM access
	//currently unused
	//can combine with ram1 to make a wider RAM bus (although will increase the latency)
	.ramclk2_clk(0),
	.ram2_address(0),
	.ram2_burstcount(0),
	.ram2_waitrequest(),
	.ram2_readdata(),
	.ram2_readdatavalid(),
	.ram2_read(0),
	.ram2_writedata(0),
	.ram2_byteenable(0),
	.ram2_write(0),

	// HDMI frame buffer
	.vbuf_clk(clk_ctl),
	.vbuf_address(vbuf_address),
	.vbuf_burstcount(vbuf_burstcount),
	.vbuf_waitrequest(vbuf_waitrequest),
	.vbuf_writedata(vbuf_writedata),
	.vbuf_byteenable(vbuf_byteenable),
	.vbuf_write(vbuf_write),
	.vbuf_readdata(vbuf_readdata),
	.vbuf_readdatavalid(vbuf_readdatavalid),
	.vbuf_read(vbuf_read)
);

wire  [27:0] vbuf_address;
wire   [7:0] vbuf_burstcount;
wire         vbuf_waitrequest;
wire [127:0] vbuf_readdata;
wire         vbuf_readdatavalid;
wire         vbuf_read;
wire [127:0] vbuf_writedata;
wire  [15:0] vbuf_byteenable;
wire         vbuf_write;

assign HDMI_TX_VS = vs_hdmi;
assign HDMI_TX_HS = hs_hdmi;

hdmi_lite hdmi_lite
(
	.reset(reset),

	.clk_video(clk_vid),
	.ce_pixel(ce_pix),
	.video_vs(vs),
	.video_de(de),
	.video_d({r_out,g_out,b_out}),

	.clk_hdmi(HDMI_TX_CLK),
	.hdmi_hde(hde),
	.hdmi_vde(vde),
	.hdmi_d(hdmi_data),
	.hdmi_de(hdmi_de),

	.screen_w(WIDTH),
	.screen_h(HEIGHT),
	.quadbuf(1),
	.scale_x(0),
	.scale_y(0),
	.scale_auto(1),

	.clk_vbuf(clk_ctl),
	.vbuf_address(vbuf_address),
	.vbuf_burstcount(vbuf_burstcount),
	.vbuf_waitrequest(vbuf_waitrequest),
	.vbuf_writedata(vbuf_writedata),
	.vbuf_byteenable(vbuf_byteenable),
	.vbuf_write(vbuf_write),
	.vbuf_readdata(vbuf_readdata),
	.vbuf_readdatavalid(vbuf_readdatavalid),
	.vbuf_read(vbuf_read)
);

`endif


/////////////////////////  HDMI output  /////////////////////////////////

pll_hdmi pll_hdmi
(
	.refclk(FPGA_CLK1_50),
	.rst(reset_req),
	.reconfig_to_pll(reconfig_to_pll),
	.reconfig_from_pll(reconfig_from_pll),
	.outclk_0(HDMI_TX_CLK)
);

//1920x1080@60 PCLK=148.5MHz CEA
reg  [11:0] WIDTH  = 1920;
reg  [11:0] HFP    = 88;
reg  [11:0] HS     = 48;
reg  [11:0] HBP    = 148;
reg  [11:0] HEIGHT = 1080;
reg  [11:0] VFP    = 4;
reg  [11:0] VS     = 5;
reg  [11:0] VBP    = 36;
reg  [11:0] VSET   = 0;

wire [63:0] reconfig_to_pll;
wire [63:0] reconfig_from_pll;
wire        cfg_waitrequest;
reg         cfg_write;
reg   [5:0] cfg_address;
reg  [31:0] cfg_data;

pll_hdmi_cfg pll_hdmi_cfg
(
	.mgmt_clk(FPGA_CLK1_50),
	.mgmt_reset(reset_req),
	.mgmt_waitrequest(cfg_waitrequest),
	.mgmt_read(0),
	.mgmt_readdata(),
	.mgmt_write(cfg_write),
	.mgmt_address(cfg_address),
	.mgmt_writedata(cfg_data),
	.reconfig_to_pll(reconfig_to_pll),
	.reconfig_from_pll(reconfig_from_pll)
);

reg cfg_ready = 0;

always @(posedge FPGA_CLK1_50) begin
	reg gotd = 0, gotd2 = 0;
	reg custd = 0, custd2 = 0;
	reg old_wait = 0;

	gotd  <= cfg_got;
	gotd2 <= gotd;
	
	cfg_write <= 0;
	
	custd <= cfg_custom_t;
	custd2 <= custd;
	if(custd2 != custd & ~gotd) begin
		cfg_address <= cfg_custom_p1;
		cfg_data <= cfg_custom_p2;
		cfg_write <= 1;
	end

	if(~gotd2 & gotd) begin
		cfg_address <= 2;
		cfg_data <= 0;
		cfg_write <= 1;
	end

	old_wait <= cfg_waitrequest;
	if(old_wait & ~cfg_waitrequest & gotd) cfg_ready <= 1;
end

hdmi_config hdmi_config
(
	.iCLK(FPGA_CLK1_50),
	.iRST_N(cfg_ready & ~HDMI_TX_INT),

	.I2C_SCL(HDMI_I2C_SCL),
	.I2C_SDA(HDMI_I2C_SDA),

	.dvi_mode(dvi_mode),
	.audio_96k(audio_96k)
);

wire [23:0] hdmi_data;
wire        hdmi_de;

osd hdmi_osd
(
	.clk_sys(clk_sys),

	.io_osd(io_osd),
	.io_strobe(io_strobe),
	.io_din(io_din),

	.clk_video(iHdmiClk),
	.din(hdmi_data),
	.dout(HDMI_TX_D),
	.de_in(hdmi_de),
	.de_out(HDMI_TX_DE)
);

assign HDMI_MCLK = 0;
i2s i2s
(
	.reset(~cfg_ready),
	.clk_sys(FPGA_CLK3_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)
);


/////////////////////////  VGA output  //////////////////////////////////

wire [23:0] vga_q;
osd vga_osd
(
	.clk_sys(clk_sys),

	.io_osd(io_osd),
	.io_strobe(io_strobe),
	.io_din(io_din),

	.clk_video(clk_vid),
	.din(de ? {r_out, g_out, b_out} : 24'd0),
	.dout(vga_q),
	.de_in(de)
);

wire [23:0] vga_o;

vga_out vga_out
(
	.ypbpr_full(1),
	.ypbpr_en(ypbpr_en),
	.dout(vga_o),
`ifdef LITE
	.din(vga_q)
`else
	.din(vga_scaler ? {24{HDMI_TX_DE}} & HDMI_TX_D : vga_q)
`endif
);

`ifdef LITE
	wire vs1 = vs;
	wire hs1 = hs;
`else
	wire vs1 = vga_scaler ? HDMI_TX_VS : vs;
	wire hs1 = vga_scaler ? HDMI_TX_HS : hs;
`endif

assign VGA_VS = VGA_EN ? 1'bZ      : csync ?     1'b1     : ~vs1;
assign VGA_HS = VGA_EN ? 1'bZ      : csync ? ~(vs1 ^ hs1) : ~hs1;
assign VGA_R  = VGA_EN ? 6'bZZZZZZ : vga_o[23:18];
assign VGA_G  = VGA_EN ? 6'bZZZZZZ : vga_o[15:10];
assign VGA_B  = VGA_EN ? 6'bZZZZZZ : vga_o[7:2];


/////////////////////////  Audio output  ////////////////////////////////

wire al, ar, aspdif;

sigma_delta_dac #(15) dac_l
(
	.CLK(FPGA_CLK3_50),
	.RESET(reset),
	.DACin({audio_l[15] ^ audio_s, audio_l[14:0]}),
	.DACout(al)
);

sigma_delta_dac #(15) dac_r
(
	.CLK(FPGA_CLK3_50),
	.RESET(reset),
	.DACin({audio_r[15] ^ audio_s, audio_r[14:0]}),
	.DACout(ar)
);

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(aspdif)
);

assign AUDIO_SPDIF = SW[0] ? HDMI_LRCLK : aspdif;
assign AUDIO_R     = SW[0] ? HDMI_I2S   : ar;
assign AUDIO_L     = SW[0] ? HDMI_SCLK  : al;

reg [15:0] audio_l; 
reg [15:0] audio_r;

always @(posedge FPGA_CLK3_50) begin
	reg signed [15:0] al;
	reg signed [15:0] ar;

	case({audio_s,audio_mix})
		'b000: al <= audio_ls;
		'b001: al <= audio_ls - (audio_ls >> 3) + (audio_rs >> 3);
		'b010: al <= audio_ls - (audio_ls >> 2) + (audio_rs >> 2);
		'b011: al <= (audio_ls >> 1) + (audio_rs >> 1);
		'b100: al <= audio_ls;
		'b101: al <= audio_ls - (audio_ls >>> 3) + (audio_rs >>> 3);
		'b110: al <= audio_ls - (audio_ls >>> 2) + (audio_rs >>> 2);
		'b111: al <= (audio_ls >>> 1) + (audio_rs >>> 1);
	endcase

	case({audio_s,audio_mix})
		'b000: ar <= audio_rs;
		'b001: ar <= audio_rs - (audio_rs >> 3) + (audio_ls >> 3);
		'b010: ar <= audio_rs - (audio_rs >> 2) + (audio_ls >> 2);
		'b011: ar <= (audio_rs >> 1) + (audio_ls >> 1);
		'b100: ar <= audio_rs;
		'b101: ar <= audio_rs - (audio_rs >>> 3) + (audio_ls >>> 3);
		'b110: ar <= audio_rs - (audio_rs >>> 2) + (audio_ls >>> 2);
		'b111: ar <= (audio_rs >>> 1) + (audio_ls >>> 1);
	endcase
	
	if(vol_att[4]) begin
		audio_l <= 0;
		audio_r <= 0;
	end
	else
	if(audio_s) begin
		audio_l <= al >>> vol_att[3:0];
		audio_r <= ar >>> vol_att[3:0];
	end
	else
	begin
		audio_l <= al >> vol_att[3:0];
		audio_r <= ar >> vol_att[3:0];
	end
end

///////////////////  User module connection ////////////////////////////

wire signed [15:0] audio_ls, audio_rs;
wire        audio_s;
wire  [1:0] audio_mix;
wire  [7:0] r_out, g_out, b_out;
wire        vs, hs, de;
wire        clk_sys, clk_vid, ce_pix;

wire        ram_clk;
wire [28:0] ram_address;
wire [7:0]  ram_burstcount;
wire        ram_waitrequest;
wire [63:0] ram_readdata;
wire        ram_readdatavalid;
wire        ram_read;
wire [63:0] ram_writedata;
wire [7:0]  ram_byteenable;
wire        ram_write;

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

wire vs_emu, hs_emu;
sync_fix sync_v(FPGA_CLK3_50, vs_emu, vs);
sync_fix sync_h(FPGA_CLK3_50, hs_emu, hs);

emu emu
(
	.CLK_50M(FPGA_CLK3_50),
	.RESET(reset),
	.HPS_BUS({HDMI_TX_VS, clk_ctl, clk_vid, ce_pix, de, hs, vs, 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_emu),
	.VGA_VS(vs_emu),
	.VGA_DE(de),

	.LED_USER(led_user),
	.LED_POWER(led_power),
	.LED_DISK(led_disk),

`ifndef LITE
	.VIDEO_ARX(ARX),
	.VIDEO_ARY(ARY),
`endif

	.AUDIO_L(audio_ls),
	.AUDIO_R(audio_rs),
	.AUDIO_S(audio_s),
	.AUDIO_MIX(audio_mix),
	.TAPE_IN(0),

	// SCK  -> CLK
	// MOSI -> CMD
	// MISO <- DAT0
	//    Z -> DAT1
	//    Z -> DAT2
	// CS   -> DAT3
	.SD_SCK(SDIO_CLK),
	.SD_MOSI(SDIO_CMD),
	.SD_MISO(SDIO_DAT[0]),
	.SD_CS(SDIO_DAT[3]),
	.SD_CD(VGA_EN ? VGA_HS : SDIO_CD),

	.DDRAM_CLK(ram_clk),
	.DDRAM_ADDR(ram_address),
	.DDRAM_BURSTCNT(ram_burstcount),
	.DDRAM_BUSY(ram_waitrequest),
	.DDRAM_DOUT(ram_readdata),
	.DDRAM_DOUT_READY(ram_readdatavalid),
	.DDRAM_RD(ram_read),
	.DDRAM_DIN(ram_writedata),
	.DDRAM_BE(ram_byteenable),
	.DDRAM_WE(ram_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)
);

endmodule

module sync_fix
(
	input clk,
	
	input sync_in,
	output sync_out
);

assign sync_out = sync_in ^ pol;

reg pol;
always @(posedge clk) begin
	integer pos = 0, neg = 0, cnt = 0;
	reg s1,s2;

	s1 <= sync_in;
	s2 <= s1;

	if(~s2 & s1) neg <= cnt;
	if(s2 & ~s1) pos <= cnt;

	cnt <= cnt + 1;
	if(s2 != s1) cnt <= 0;

	pol <= pos > neg;
end

endmodule
