module Octopus
	(
		input	wire			CLK_50MHz,
		output	wire			LED,

		input	wire	[16:0]	CON_A,

		input	wire	[1:0]	CON_BCLK,
		input	wire	[10:0]	CON_B,
		inout	wire			I2C_SDA,
		inout	wire			I2C_SCL,

//		input	wire			ADC_DCO,
		input	wire			ADC_FCO,
		input	wire	[7: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
		output	wire			USB_PA2,
		output	wire			USB_PA4,
		output	wire			USB_PA5,
		output	wire			USB_PA6,
		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		=	16;
	localparam	M		=	4;

	assign	RAM_CLK = sys_clock;
	assign	RAM_CE1 = 1'b0;

	assign	USB_PA2		=	~usb_rden;
	assign	USB_PA4		=	usb_addr[0];
	assign	USB_PA5		=	usb_addr[1];
	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;
	wire	[1:0]	usb_addr;

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

		.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)
	);
		
	wire	[N*M*12-1:0]	int_mux_data;

	wire	[11:0]	osc_mux_data [M-1:0];
	wire	[11:0]	trg_mux_data;
	wire			trg_flag;

	wire			ana_good [N-1:0];
	wire	[11:0]	ana_data [N-1:0];

	wire			sys_clock, sys_frame;

	wire			adc_fast_clock, adc_slow_clock;

    wire	[N*12-1:0]	int_data;

    wire	[11:0]	sys_data [N-1:0];
	wire	[11:0]	nowhere;

	wire 	[23:0]	uwt_d1 [N-1:0];
	wire 	[23:0]	uwt_a1 [N-1:0];
	wire 	[23:0]	uwt_peak1 [N-1:0];
	wire 	[23:0]	uwt_d2 [N-1:0];
	wire 	[23:0]	uwt_a2 [N-1:0];
	wire 	[23:0]	uwt_peak2 [N-1:0];
	wire 	[23:0]	uwt_d3 [N-1:0];
	wire 	[23:0]	uwt_a3 [N-1:0];
	wire 	[23:0]	uwt_peak3 [N-1:0];

	wire 	[1:0]	uwt_flag1 [N-1:0];
	wire 	[1:0]	uwt_flag2 [N-1:0];
	wire 	[1:0]	uwt_flag3 [N-1:0];
	
	wire			i2c_reset;


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

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

	adc_pll adc_pll_unit (
		.inclk0(ADC_FCO),
		.c0(adc_fast_clock),
		.c1(adc_slow_clock));

	adc_lvds #(
		.size(N/2),
		.width(24)) adc_lvds_unit (
		.clock(sys_clock),
		.lvds_dco(adc_fast_clock),
		.lvds_fco(adc_slow_clock),
		.lvds_d(ADC_D),
		.adc_frame(sys_frame),
		.adc_data(int_data));

	wire	[15:0]	cfg_bits [31:0];
	wire	[511:0]	int_cfg_bits;

	wire	[31:0]	cfg_mux_selector;

	wire 			cfg_reset;

	wire 	[19-1:0]	bus_ssel;
	wire			bus_wren;
	wire	[31:0]	bus_addr;
	wire	[15:0]	bus_mosi;
	wire 	[15:0]	bus_miso [19-2:0];
	wire 	[19-1:0]	bus_busy;

	wire 	[15:0]	mrg_bus_miso;
	wire 			mrg_bus_busy;

	wire 	[(19-1)*16-1:0]	int_bus_miso;

	genvar j;

	generate
		for (j = 0; j < 32; 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 < N; j = j + 1)
		begin : MUX_DATA
			assign int_mux_data[j*4*12+4*12-1:j*4*12] = {
				{ana_good[j], 11'd0},
				ana_data[j],
				uwt_a2[j][17:6],
				sys_data[j]};
		end
	endgenerate

	assign cfg_mux_selector = {cfg_bits[5], cfg_bits[4]};

	lpm_mux #(
		.lpm_size(N*4),
		.lpm_type("LPM_MUX"),
		.lpm_width(12),
		.lpm_widths(6)) trg_mux_unit (
		.sel(cfg_bits[6][5:0]),
		.data(int_mux_data),
		.result(trg_mux_data));

	generate
		for (j = 0; j < M; j = j + 1)
		begin : OSC_CHAIN
		
			lpm_mux #(
				.lpm_size(N*4),
				.lpm_type("LPM_MUX"),
				.lpm_width(12),
				.lpm_widths(6)) osc_mux_unit (
				.sel(cfg_mux_selector[j*8+5:j*8]),
				.data(int_mux_data),
				.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[7][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[7][12]),
		.trg_flag(trg_flag),
		.osc_data({16'd0, 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]));

	generate
		for (j = 0; j < N; j = j + 1)
		begin : MCA_CHAIN

			assign sys_data[j] = (cfg_bits[3][j]) ? (int_data[j*12+11:j*12] ^ 12'hfff) : (int_data[j*12+11:j*12]);

			uwt_bior31 #(.L(1)) uwt_1_unit (
				.clock(sys_clock),
				.frame(sys_frame),
				.reset(1'b0),
				.x({20'h00000, sys_data[j]}),
				.d(uwt_d1[j]),
				.a(uwt_a1[j]),
				.peak(uwt_peak1[j]),
				.flag(uwt_flag1[j]));
		
			uwt_bior31 #(.L(2)) uwt_2_unit (
				.clock(sys_clock),
				.frame(sys_frame),
				.reset(1'b0),
				.x(uwt_a1[j]),
				.d(uwt_d2[j]),
				.a(uwt_a2[j]),
				.peak(uwt_peak2[j]),
				.flag(uwt_flag2[j]));
		
			uwt_bior31 #(.L(3)) uwt_3_unit (
				.clock(sys_clock),
				.frame(sys_frame),
				.reset(1'b0),
				.x(uwt_a2[j]),
				.d(uwt_d3[j]),
				.a(uwt_a3[j]),
				.peak(uwt_peak3[j]),
				.flag(uwt_flag3[j]));
	
			analyser analyser_unit (
				.clock(sys_clock),
				.frame(sys_frame),
				.reset(cfg_bits[1][j]),
				.cfg_data(cfg_bits[8+j]),
				.uwt_flag(uwt_flag3[j]),
				.uwt_data(uwt_peak3[j][11:0]),
				.ana_good(ana_good[j]),
				.ana_data(ana_data[j]));

			histogram histogram_unit (
				.clock(sys_clock),
				.frame(sys_frame),
				.reset(cfg_bits[2][j]),
				.hst_good(ana_good[j]),
				.hst_data(ana_data[j][11:3]),
				.bus_ssel(bus_ssel[2+j]),
				.bus_wren(bus_wren),
				.bus_addr(bus_addr[9:0]),
				.bus_mosi(bus_mosi),
				.bus_miso(bus_miso[2+j]),
				.bus_busy(bus_busy[2+j]));

		end
	endgenerate

	i2c_fifo i2c_unit(
		.clock(sys_clock),
		.reset(i2c_reset),

		.i2c_sda(I2C_SDA),
		.i2c_scl(I2C_SCL),
		
		.bus_ssel(bus_ssel[18]),
		.bus_wren(bus_wren),
		.bus_mosi(bus_mosi),
		.bus_busy(bus_busy[18]));

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

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

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

	lpm_decode #(
		.lpm_decodes(19),
		.lpm_type("LPM_DECODE"),
		.lpm_width(5)) lpm_decode_unit (
		.data(bus_addr[28:24]),
		.eq(bus_ssel),
		.aclr(),
		.clken(),
		.clock(),
		.enable());

	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
