module Paella
	(
		input	wire			CLK_50MHz,
		output	wire			LED,

		inout	wire	[3:0]	TRG,
		inout	wire			I2C_SDA,
		inout	wire			I2C_SCL,
		inout	wire	[4:0]	CON_A,
		input	wire	[16:0]	CON_B,
		input	wire	[12:0]	CON_C,
		input	wire			CON_BCLK,
		input	wire	[1:0]	CON_CCLK,

		input	wire			ADC_DCO,
		input	wire			ADC_FCO,
		input	wire	[2:0]	ADC_D,

		output	wire			USB_SLRD, 
		output	wire			USB_SLWR,
		input	wire			USB_IFCLK,
		input	wire			USB_FLAGA, // EMPTY flag for EP6
		input	wire			USB_FLAGB, // FULL flag for EP8
		input	wire			USB_FLAGC,
		inout	wire			USB_PA0,
		inout	wire			USB_PA1,
		output	wire			USB_PA2,
		inout	wire			USB_PA3,
		output	wire			USB_PA4,
		output	wire			USB_PA5,
		output	wire			USB_PA6,
		inout	wire			USB_PA7,
		inout	wire	[7:0]	USB_PB,

		output	wire			RAM_CLK,
		output	wire			RAM_CE1,
		output	wire			RAM_WE,
		output	wire	[19:0]	RAM_ADDR,
		inout	wire			RAM_DQAP,
		inout	wire	[7:0]	RAM_DQA,
		inout	wire			RAM_DQBP,
		inout	wire	[7:0]	RAM_DQB
	);

	localparam	N		=	3;

	//	Turn output ports off
/*
	assign	RAM_CLK		=	1'b0;
	assign	RAM_CE1		=	1'b0;
	assign	RAM_WE		=	1'b0;
	assign	RAM_ADDR	=	20'h00000;
*/
	assign	RAM_CLK = sys_clock;
	assign	RAM_CE1 = 1'b0;

	//	Turn inout ports to tri-state
	assign	TRG			=	4'bz;
	assign	CON_A		=	5'bz;
	assign	USB_PA0		=	1'bz;
	assign	USB_PA1		=	1'bz;
	assign	USB_PA3		=	1'bz;
	assign	USB_PA7		=	1'bz;
//	assign	RAM_DQAP	=	1'bz;
//	assign	RAM_DQA		=	8'bz;
//	assign	RAM_DQBP	=	1'bz;
//	assign	RAM_DQB		=	8'bz;

	assign	USB_PA2		=	~usb_rden;
	assign	USB_PA5		=	1'b1;
	assign	USB_PA6		=	~usb_pktend;

	wire			usb_wrreq, usb_rdreq, usb_rden, usb_pktend;
	wire			usb_tx_wrreq, usb_rx_rdreq;
	wire			usb_tx_full, usb_rx_empty;
	wire	[7:0]	usb_tx_data, usb_rx_data;

	assign	USB_SLRD = ~usb_rdreq;
	assign	USB_SLWR = ~usb_wrreq;

	usb_fifo usb_unit
	(
		.usb_clock(USB_IFCLK),
		.usb_data(USB_PB),
		.usb_full(~USB_FLAGB),
		.usb_empty(~USB_FLAGA),
		.usb_wrreq(usb_wrreq),
		.usb_rdreq(usb_rdreq),
		.usb_rden(usb_rden),
		.usb_pktend(usb_pktend),
		.usb_addr(USB_PA4),

		.clock(sys_clock),

		.tx_full(usb_tx_full),
		.tx_wrreq(usb_tx_wrreq),
		.tx_data(usb_tx_data),

		.rx_empty(usb_rx_empty),
		.rx_rdreq(usb_rx_rdreq),
		.rx_q(usb_rx_data)
	);
/*
	reg		[31:0]	led_counter;
	always @(posedge CLK_50MHz)
	begin
		led_counter = led_counter + 32'd1;
	end
	assign LED = led_counter[28];
*/	
	wire	[11:0]	osc_mux_data [4:0];

	wire	[11:0]	trg_mux_data;
	wire			trg_flag;

	wire	[4*12-1:0]	int_mux_data [N-1:0];

	wire	[1:0]	amp_flag [3*N-1:0];
	wire	[12:0]	amp_data [3*N-1:0];

	wire	[1:0]	amp_mux_flag [2:0];
	wire	[11:0]	amp_mux_data [2:0];

	wire			cnt_good [3:0];
	wire	[15:0]	cnt_bits_wire;

	wire			sys_clock, sys_frame;

    wire	[11:0]	adc_data [N-1:0];
    wire	[11:0]	sys_data [N-1:0];
	wire	[11:0]	tst_data;

    wire	[1:0]	cmp_data;
    wire	[1:0]	del_data;

	wire	[19:0]	cic_data [N-1:0];

	wire	[11:0]	dec_data [N-1:0];
	wire	[12:0]	clp_data [N-1:0];
	wire	[11:0]	tmp_data;


	wire			i2c_reset;

	sys_pll sys_pll_unit(
		.inclk0(CLK_50MHz),
		.c0(sys_clock));

	test test_unit(
		.clk(ADC_FCO),
		.data(tst_data));

	adc_lvds #(
		.size(3),
		.width(12)) adc_lvds_unit (
		.clock(sys_clock),
		.lvds_dco(ADC_DCO),
		.lvds_fco(ADC_FCO),
		.lvds_d(ADC_D),
		.trig(TRG[1:0]),
		.adc_frame(sys_frame),
		.adc_data({cmp_data, adc_data[2], adc_data[1], adc_data[0]}));

	wire	[15:0]		cfg_bits [63:0];
	wire	[1023:0]	int_cfg_bits;

	wire	[39:0]	cfg_mux_selector;

	wire 			cfg_reset;

	wire 	[11:0]	bus_ssel;
	wire			bus_wren;
	wire	[31:0]	bus_addr;
	wire	[15:0]	bus_mosi;
	wire 	[15:0]	bus_miso [10:0];
	wire 	[11:0]	bus_busy;

	wire 	[15:0]	mrg_bus_miso;
	wire 			mrg_bus_busy;

	wire 	[11*16-1:0]	int_bus_miso;

	genvar j;

	generate
		for (j = 0; j < 64; j = j + 1)
		begin : CONFIGURATION_OUTPUT
			assign cfg_bits[j] = int_cfg_bits[j*16+15:j*16];
		end
	endgenerate

	configuration configuration_unit (
		.clock(sys_clock),
		.reset(cfg_reset),
		.bus_ssel(bus_ssel[0]),
		.bus_wren(bus_wren),
		.bus_addr(bus_addr[4:0]),
		.bus_mosi(bus_mosi),
		.bus_miso(bus_miso[0]),
		.bus_busy(bus_busy[0]),
		.cfg_bits(int_cfg_bits));

	generate
		for (j = 0; j < 3; j = j + 1)
		begin : MUX_DATA
			assign int_mux_data[j] = {
				{4'd0, amp_flag[0+j][0], 7'd0},
				amp_data[0+j][11:0],
				clp_data[j][11:0],
				sys_data[j]};
		end
	endgenerate

	assign cfg_mux_selector = {cfg_bits[4][7:0], cfg_bits[3], cfg_bits[2]};

	lpm_mux #(
		.lpm_size(4*3),
		.lpm_type("LPM_MUX"),
		.lpm_width(12),
		.lpm_widths(4)) trg_mux_unit (
		.sel(cfg_bits[4][11:8]),
		.data({int_mux_data[2], int_mux_data[1], int_mux_data[0]}),
		.result(trg_mux_data));

	lpm_mux #(
		.lpm_size(3),
		.lpm_type("LPM_MUX"),
		.lpm_width(14),
		.lpm_widths(2)) amp_mux_unit_1 (
		.sel(cfg_bits[6][1:0]),
		.data({
			{amp_flag[2], amp_data[2][11:0]},
			{amp_flag[1], amp_data[1][11:0]},
			{amp_flag[0], amp_data[0][11:0]}}),
		.result({amp_mux_flag[0], amp_mux_data[0]}));

	lpm_mux #(
		.lpm_size(3),
		.lpm_type("LPM_MUX"),
		.lpm_width(14),
		.lpm_widths(2)) amp_mux_unit_2 (
		.sel(cfg_bits[6][5:4]),
		.data({
			{amp_flag[5], amp_data[5][11:0]},
			{amp_flag[4], amp_data[4][11:0]},
			{amp_flag[3], amp_data[3][11:0]}}),
		.result({amp_mux_flag[1], amp_mux_data[1]}));

	lpm_mux #(
		.lpm_size(3),
		.lpm_type("LPM_MUX"),
		.lpm_width(14),
		.lpm_widths(2)) amp_mux_unit_3 (
		.sel(cfg_bits[6][9:8]),
		.data({
			{amp_flag[8], amp_data[8][11:0]},
			{amp_flag[7], amp_data[7][11:0]},
			{amp_flag[6], amp_data[6][11:0]}}),
		.result({amp_mux_flag[2], amp_mux_data[2]}));

	generate
		for (j = 0; j < 5; j = j + 1)
		begin : OSC_CHAIN
		
			lpm_mux #(
				.lpm_size(4*3),
				.lpm_type("LPM_MUX"),
				.lpm_width(12),
				.lpm_widths(4)) osc_mux_unit (
				.sel(cfg_mux_selector[j*8+3:j*8]),
				.data({int_mux_data[2], int_mux_data[1], int_mux_data[0]}),
				.result(osc_mux_data[j]));
		
		end
	endgenerate

	trigger trigger_unit (
		.clock(sys_clock),
		.frame(sys_frame),
		.reset(cfg_bits[0][0]),
		.cfg_data(cfg_bits[5][11:0]),
		.trg_data(trg_mux_data),
		.trg_flag(trg_flag));
	
	oscilloscope oscilloscope_unit (
		.clock(sys_clock),
		.frame(sys_frame),
		.reset(cfg_bits[0][1]),
		.cfg_data(cfg_bits[5][12]),
		.trg_flag(trg_flag),
		.osc_data({2'd0, cmp_data, osc_mux_data[4], osc_mux_data[3], osc_mux_data[2], osc_mux_data[1], osc_mux_data[0]}),
		.ram_wren(RAM_WE),
		.ram_addr(RAM_ADDR),
		.ram_data({RAM_DQA, RAM_DQAP, RAM_DQB, RAM_DQBP}),
		.bus_ssel(bus_ssel[1]),
		.bus_wren(bus_wren),
		.bus_addr(bus_addr[19:0]),
		.bus_mosi(bus_mosi),
		.bus_miso(bus_miso[1]),
		.bus_busy(bus_busy[1]));

	filter #(.size(3), .width(12)) filter_unit (
		.clock(sys_clock),
		.frame(sys_frame),
		.reset(1'b0),
		.inp_data({sys_data[2], sys_data[1], sys_data[0]}),
		.out_data({cic_data[2], cic_data[1], cic_data[0]}));
		

/*        
	clip #(.shift(19), .width(19), .widthr(12)) clip_unit (
		.clock(sys_clock),
		.frame(sys_frame),
		.reset(1'b0),
		.del_data({cfg_bits[39][5:0], cfg_bits[37][5:0], cfg_bits[35+8][5:0], cfg_bits[33][5:0]}),
		.amp_data({6'd6, 6'd6, 6'd6, 6'd6}),
		.tau_data({cfg_bits[38], cfg_bits[36], cfg_bits[34], cfg_bits[32]}),
		.inp_data({
			19'd0, cic_data[2][18:0],
			cic_data[1][18:0], cic_data[0][18:0]}),
		.out_data({
			tmp_data, clp_data[2], 
			clp_data[1], clp_data[0]}));
*/
	generate
		for (j = 0; j < 3; j = j + 1)
		begin : MCA_CHAIN
			
			shift #(.shift(11), .width(19), .widthr(13)) shift_unit (
				.clock(sys_clock),
				.frame(sys_frame),
				.reset(1'b0),
				.amp_data(6'd21),
				.inp_data(cic_data[j][18:0]),
				.out_data(clp_data[j]));
				
			assign sys_data[j] = (cfg_bits[1][4*j]) ? (adc_data[j] ^ 12'hfff) : (adc_data[j]);

			amplitude #(.width(13)) amplitude_unit_1 (
				.clock(sys_clock),
				.frame(sys_frame),
				.reset(1'b0),
				.min_data({1'b0, cfg_bits[7][11:0]}),
				.max_data(13'd4095),
//				.cfg_data(cfg_bits[6+2*j][11:0]),
				.inp_data(clp_data[j]),
				.out_flag(amp_flag[0+j]),
				.out_data(amp_data[0+j]));

			amplitude #(.width(13)) amplitude_unit_2 (
				.clock(sys_clock),
				.frame(sys_frame),
				.reset(1'b0),
				.min_data({1'b0, cfg_bits[8][11:0]}),
				.max_data(13'd4095),
//				.cfg_data(cfg_bits[7+2*j][11:0]),
				.inp_data(clp_data[j]),
				.out_flag(amp_flag[3+j]),
				.out_data(amp_data[3+j]));

			amplitude #(.width(13)) amplitude_unit_3 (
				.clock(sys_clock),
				.frame(sys_frame),
				.reset(1'b0),
				.min_data({1'b0, cfg_bits[9][11:0]}),
				.max_data(13'd4095),
//				.cfg_data(cfg_bits[7+2*j][11:0]),
				.inp_data(clp_data[j]),
				.out_flag(amp_flag[6+j]),
				.out_data(amp_data[6+j]));

		end
	endgenerate

	histogram32 histogram32_unit_1 (
		.clock(sys_clock),
		.frame(sys_frame),
		.reset(cfg_bits[0][5]),
		.hst_good((amp_mux_flag[0][0]) & (cnt_good[0]) & (cfg_bits[13][1])),
		.hst_data(amp_mux_data[0]),
		.bus_ssel(bus_ssel[2]),
		.bus_wren(bus_wren),
		.bus_addr(bus_addr[12:0]),
		.bus_mosi(bus_mosi),
		.bus_miso(bus_miso[2]),
		.bus_busy(bus_busy[2]));

	counter hst_counter_unit_1 (
		.clock(sys_clock),
		.frame((sys_frame) & (~amp_mux_flag[0][1])),
//		.frame(sys_frame),
		.reset(cfg_bits[0][8]),
		.setup(cfg_bits[13][0]),
		.count(cfg_bits[13][1]),
		.bus_ssel(bus_ssel[5]),
		.bus_wren(bus_wren),
		.bus_addr(bus_addr[1:0]),
		.bus_mosi(bus_mosi),
		.bus_miso(bus_miso[5]),
		.bus_busy(bus_busy[5]),
		.cnt_good(cnt_good[0]));

	histogram32 histogram32_unit_2 (
		.clock(sys_clock),
		.frame(sys_frame),
		.reset(cfg_bits[0][6]),
		.hst_good((amp_mux_flag[1][0]) & (cnt_good[1]) & (cfg_bits[14][1])),
		.hst_data(amp_mux_data[1]),
		.bus_ssel(bus_ssel[3]),
		.bus_wren(bus_wren),
		.bus_addr(bus_addr[12:0]),
		.bus_mosi(bus_mosi),
		.bus_miso(bus_miso[3]),
		.bus_busy(bus_busy[3]));

	counter hst_counter_unit_2 (
		.clock(sys_clock),
		.frame((sys_frame) & (~amp_mux_flag[1][1])),
//		.frame(sys_frame),
		.reset(cfg_bits[0][8]),
		.setup(cfg_bits[14][0]),
		.count(cfg_bits[14][1]),
		.bus_ssel(bus_ssel[6]),
		.bus_wren(bus_wren),
		.bus_addr(bus_addr[1:0]),
		.bus_mosi(bus_mosi),
		.bus_miso(bus_miso[6]),
		.bus_busy(bus_busy[6]),
		.cnt_good(cnt_good[1]));

	histogram16 histogram16_unit (
		.clock(sys_clock),
		.frame(sys_frame),
		.reset(cfg_bits[0][11]),
		.hst_good((cnt_good[3]) & (~cnt_good[2])),
		.hst_data(cnt_bits_wire < 16'd9999 ? cnt_bits_wire[13:0] : 14'd9999),
		.bus_ssel(bus_ssel[8]),
		.bus_wren(bus_wren),
		.bus_addr(bus_addr[13:0]),
		.bus_mosi(bus_mosi),
		.bus_miso(bus_miso[8]),
		.bus_busy(bus_busy[8]));

	counter rmt_counter_1 (
		.clock(sys_clock),
		.frame((sys_frame) & (~amp_mux_flag[2][1])),
//		.frame(sys_frame),
		.reset(cfg_bits[0][12]),
		.setup((sys_frame) & (~cnt_good[2])),
		.count((cnt_good[3]) & (cfg_bits[16][1])),
		.bus_ssel(bus_ssel[9]),
		.bus_wren(bus_wren),
		.bus_addr(bus_addr[1:0]),
		.bus_mosi(bus_mosi),
		.bus_miso(bus_miso[9]),
		.bus_busy(bus_busy[9]),
		.cnt_good(cnt_good[2]));

	counter rmt_counter_2 (
		.clock(sys_clock),
		.frame((sys_frame) & (~cnt_good[2])),
		.reset(cfg_bits[0][13]),
		.setup(cfg_bits[16][0]),
		.count(cfg_bits[16][1]),
		.bus_ssel(bus_ssel[10]),
		.bus_wren(bus_wren),
		.bus_addr(bus_addr[1:0]),
		.bus_mosi(bus_mosi),
		.bus_miso(bus_miso[10]),
		.bus_busy(bus_busy[10]),
		.cnt_good(cnt_good[3]));

	lpm_counter	#(
		.lpm_direction("UP"),
		.lpm_port_updown("PORT_UNUSED"),
		.lpm_type("LPM_COUNTER"),
		.lpm_width(16)) lpm_counter_component (
		.sclr(((sys_frame) & (cnt_good[3]) & (~cnt_good[2])) | (cfg_bits[0][11])),
		.clock(sys_clock),
		.cnt_en((sys_frame) & (amp_mux_flag[2][0]) & (cnt_good[2]) & (cnt_good[3]) & (cfg_bits[16][1])),
		.q(cnt_bits_wire));

	i2c_fifo i2c_unit(
		.clock(sys_clock),
		.reset(i2c_reset),
/*
		normal connection
		.i2c_sda(I2C_SDA),
		.i2c_scl(I2C_SCL),

		following is a cross wire connection for EPT
*/
		.i2c_sda(I2C_SCL),
		.i2c_scl(I2C_SDA),
		
		.bus_ssel(bus_ssel[11]),
		.bus_wren(bus_wren),
		.bus_mosi(bus_mosi),
		.bus_busy(bus_busy[11]));

	generate
		for (j = 0; j < 11; j = j + 1)
		begin : BUS_OUTPUT
			assign int_bus_miso[j*16+15:j*16] = bus_miso[j];
		end
	endgenerate

	lpm_mux #(
		.lpm_size(11),
		.lpm_type("LPM_MUX"),
		.lpm_width(16),
		.lpm_widths(4)) bus_miso_mux_unit (
		.sel(bus_addr[31:28]),
		.data(int_bus_miso),
		.result(mrg_bus_miso));

	lpm_mux #(
		.lpm_size(12),
		.lpm_type("LPM_MUX"),
		.lpm_width(1),
		.lpm_widths(4)) bus_busy_mux_unit (
		.sel(bus_addr[31:28]),
		.data(bus_busy),
		.result(mrg_bus_busy));

	lpm_decode #(
		.lpm_decodes(12),
		.lpm_type("LPM_DECODE"),
		.lpm_width(4)) lpm_decode_unit (
		.data(bus_addr[31:28]),
		.eq(bus_ssel));


	control control_unit (
		.clock(sys_clock),
		.rx_empty(usb_rx_empty),
		.tx_full(usb_tx_full),
		.rx_data(usb_rx_data),
		.rx_rdreq(usb_rx_rdreq),
		.tx_wrreq(usb_tx_wrreq),
		.tx_data(usb_tx_data),
		.bus_wren(bus_wren),
		.bus_addr(bus_addr),
		.bus_mosi(bus_mosi),
		.bus_miso(mrg_bus_miso),
		.bus_busy(mrg_bus_busy),
		.led(LED));

/*
	altserial_flash_loader #(
		.enable_shared_access("OFF"),
		.enhanced_mode(1),
		.intended_device_family("Cyclone III")) sfl_unit (
		.noe(1'b0),
		.asmi_access_granted(),
		.asmi_access_request(),
		.data0out(),
		.dclkin(),
		.scein(),
		.sdoin());
*/

endmodule
