/*
 * top.v
 *
 * vim: ts=4 sw=4
 *
 * Top-level for the icE1usb production boards
 *
 * Copyright (C) 2019-2020  Sylvain Munaut <tnt@246tNt.com>
 * SPDX-License-Identifier: CERN-OHL-S-2.0
 */

`default_nettype none

// `define WITH_SINGLE_CHANNEL
`define WITH_SB_I2C
// `define WITH_CUSTOM_I2C

module top (
	// E1 PHY
	input  wire e1A_rx_hi_p,
//	input  wire e1A_rx_hi_n,
	input  wire e1A_rx_lo_p,
//	input  wire e1A_rx_lo_n,
	output wire e1A_tx_hi,
	output wire e1A_tx_lo,

	input  wire e1B_rx_hi_p,
//	input  wire e1B_rx_hi_n,
	input  wire e1B_rx_lo_p,
//	input  wire e1B_rx_lo_n,
	output wire e1B_tx_hi,
	output wire e1B_tx_lo,

	output wire [1:0] e1_rx_bias,

	// USB
	inout  wire usb_dp,
	inout  wire usb_dn,
	output wire usb_pu,

	// Flash
	inout  wire flash_mosi,
	inout  wire flash_miso,
	inout  wire flash_clk,
	inout  wire flash_cs_n,

	// LED Shift register + Button input
	inout  wire e1_led_rclk,

	// GPS
	output wire gps_reset_n,
	input  wire gps_rx,
	output wire gps_tx,
	input  wire gps_pps,

	// I2C
	inout  wire i2c_sda,
	inout  wire i2c_scl,

	// GPIOs
	inout  wire [2:0] gpio,

	// Clock (30.72 MHz)
	input  wire clk_in,
	output wire clk_tune_hi,
	output wire clk_tune_lo,

	// Debug UART
	input  wire dbg_rx,
	output wire dbg_tx,

	// RGB LEDs
	output wire [2:0] rgb
);

	localparam integer WB_N = 3;

	genvar i;


	// Signals
	// -------

	// Flash SPI internal signals
	wire flash_mosi_i,  flash_miso_i,  flash_clk_i;
	wire flash_mosi_o,  flash_miso_o,  flash_clk_o;
	wire flash_mosi_oe, flash_miso_oe, flash_clk_oe;
	wire flash_csn_o;

	// Peripheral wishbone
	wire     [15:0] wb_addr;
	wire     [31:0] wb_rdata [0:WB_N-1];
	wire     [31:0] wb_wdata;
	wire     [ 3:0] wb_wmsk;
	wire            wb_we;
	wire [WB_N-1:0] wb_cyc;
	wire [WB_N-1:0] wb_ack;

	wire [(WB_N*32)-1:0] wb_rdata_flat;

	// Ticks
	wire [1:0] tick_e1_rx;
	wire [1:0] tick_e1_tx;
	wire       tick_usb_sof;

	// I2C
	wire i2c_scl_oe;
	wire i2c_sda_oe;
	wire i2c_sda_i;

	// Led & Button
	wire [7:0] e1_led_state;
	wire       e1_led_run;
	wire       e1_led_active;

	wire       spi_req;
	wire       spi_gnt;

	wire [7:0] sr_val;
	wire       sr_go;
	wire       sr_rdy;

	wire       btn_val;
	wire       btn_stb;

	// Clocks / Reset
	wire rst_req;

	wire clk_sys;
	wire rst_sys;
	wire clk_48m;
	wire rst_48m;


	// SoC base
	// --------

	// Instance
	soc_base #(
		.WB_N(WB_N),
		.E1_N(2),
`ifdef WITH_SINGLE_CHANNEL
		.E1_UNIT_HAS_RX(2'b01),
		.E1_UNIT_HAS_TX(2'b01),
`else
		.E1_UNIT_HAS_RX(2'b11),
		.E1_UNIT_HAS_TX(2'b11),
`endif
		.E1_LIU(0)
	) soc_I (
		.e1_rx_hi_p   ({e1B_rx_hi_p, e1A_rx_hi_p}),
//		.e1_rx_hi_n   ({e1B_rx_hi_n, e1A_rx_hi_n}),
		.e1_rx_lo_p   ({e1B_rx_lo_p, e1A_rx_lo_p}),
//		.e1_rx_lo_n   ({e1B_rx_lo_n, e1A_rx_lo_n}),
		.e1_tx_hi     ({e1B_tx_hi,   e1A_tx_hi  }),
		.e1_tx_lo     ({e1B_tx_lo,   e1A_tx_lo  }),
		.e1_rx_data   (),
		.e1_rx_clk    (),
		.e1_tx_data   (),
		.e1_tx_clk    (),
		.usb_dp       (usb_dp),
		.usb_dn       (usb_dn),
		.usb_pu       (usb_pu),
		.flash_mosi_i (flash_mosi_i),
		.flash_mosi_o (flash_mosi_o),
		.flash_mosi_oe(flash_mosi_oe),
		.flash_miso_i (flash_miso_i),
		.flash_miso_o (flash_miso_o),
		.flash_miso_oe(flash_miso_oe),
		.flash_clk_i  (flash_clk_i),
		.flash_clk_o  (flash_clk_o),
		.flash_clk_oe (flash_clk_oe),
		.flash_csn_o  (flash_csn_o),
		.dbg_rx       (dbg_rx),
		.dbg_tx       (dbg_tx),
		.rgb          (rgb),
		.wb_m_addr    (wb_addr),
		.wb_m_rdata   (wb_rdata_flat),
		.wb_m_wdata   (wb_wdata),
		.wb_m_wmsk    (wb_wmsk),
		.wb_m_we      (wb_we),
		.wb_m_cyc     (wb_cyc),
		.wb_m_ack     (wb_ack),
		.tick_e1_rx   (tick_e1_rx),
		.tick_e1_tx   (tick_e1_tx),
		.tick_usb_sof (tick_usb_sof),
		.clk_sys      (clk_sys),
		.rst_sys      (rst_sys),
		.clk_48m      (clk_48m),
		.rst_48m      (rst_48m)
	);

	// WB read data flattening
	for (i=0; i<WB_N; i=i+1)
		assign wb_rdata_flat[i*32+:32] = wb_rdata[i];


	// Dummy channel
	// -------------

`ifdef WITH_SINGLE_CHANNEL
	wire [1:0] e1_dummy;

	SB_IO #(
		.PIN_TYPE(6'b000000),
		.PULLUP(1'b0),
		.NEG_TRIGGER(1'b0),
		.IO_STANDARD("SB_LVDS_INPUT")
	) e1_dummy_rx_I[1:0] (
		.PACKAGE_PIN({e1B_rx_hi_p, e1B_rx_lo_p}),
		.LATCH_INPUT_VALUE(1'b0),
		.CLOCK_ENABLE(1'b1),
		.INPUT_CLK(clk_sys),
		.OUTPUT_CLK(1'b0),
		.OUTPUT_ENABLE(1'b0),
		.D_OUT_0(1'b0),
		.D_OUT_1(1'b0),
		.D_IN_0(e1_dummy),
		.D_IN_1()
	);

	SB_IO #(
		.PIN_TYPE(6'b010100),
		.PULLUP(1'b0),
		.NEG_TRIGGER(1'b0),
		.IO_STANDARD("SB_LVCMOS")
	) e1_dummy_tx_I[1:0] (
		.PACKAGE_PIN({e1B_tx_hi, e1B_tx_lo}),
		.LATCH_INPUT_VALUE(1'b0),
		.CLOCK_ENABLE(1'b1),
		.INPUT_CLK(1'b0),
		.OUTPUT_CLK(clk_sys),
		.OUTPUT_ENABLE(1'b0),
		.D_OUT_0(e1_dummy),
		.D_OUT_1(1'b0),
		.D_IN_0(),
		.D_IN_1()
	);
`endif


	// Misc [0]
	// ----

	misc misc_I (
		.e1_rx_bias    (e1_rx_bias),
		.clk_tune_hi   (clk_tune_hi),
		.clk_tune_lo   (clk_tune_lo),
		.gps_reset_n   (gps_reset_n),
		.gps_pps       (gps_pps),
		.gpio          (gpio),
		.e1_led_state  (e1_led_state),
		.e1_led_run    (e1_led_run),
		.e1_led_active (e1_led_active),
		.btn_val       (btn_val),
		.btn_stb       (btn_stb),
		.tick_e1_rx    (tick_e1_rx),
		.tick_e1_tx    (tick_e1_tx),
		.tick_usb_sof  (tick_usb_sof),
		.rst_req       (rst_req),
		.wb_addr       (wb_addr[7:0]),
		.wb_rdata      (wb_rdata[0]),
		.wb_wdata      (wb_wdata),
		.wb_we         (wb_we),
		.wb_cyc        (wb_cyc[0]),
		.wb_ack        (wb_ack[0]),
		.clk           (clk_sys),
		.rst           (rst_sys)
	);


	// GPS UART [1]
	// --------

	uart_wb #(
		.DIV_WIDTH(12),
		.DW(32)
	) gps_uart_I (
		.uart_tx  (gps_tx),
		.uart_rx  (gps_rx),
		.wb_addr  (wb_addr[1:0]),
		.wb_rdata (wb_rdata[1]),
		.wb_wdata (wb_wdata),
		.wb_we    (wb_we),
		.wb_cyc   (wb_cyc[1]),
		.wb_ack   (wb_ack[1]),
		.clk      (clk_sys),
		.rst      (rst_sys)
	);


	// I2C [2]
	// ---

`ifdef WITH_SB_I2C

	// Hard-IP
	ice40_i2c_wb #(
		.WITH_IOB(1),
		.UNIT(0)
	) i2c_I (
		.i2c_scl  (i2c_scl),
		.i2c_sda  (i2c_sda),
		.wb_addr  (wb_addr[3:0]),
		.wb_rdata (wb_rdata[2]),
		.wb_wdata (wb_wdata),
		.wb_we    (wb_we),
		.wb_cyc   (wb_cyc[2]),
		.wb_ack   (wb_ack[2]),
		.clk      (clk_sys),
		.rst      (rst_sys)
	);

`elsif WITH_CUSTOM_I2C

	// Controller
	i2c_master_wb #(
		.DW(3),
		.FIFO_DEPTH(0)
	) i2c_I (
		.scl_oe  (i2c_scl_oe),
		.sda_oe  (i2c_sda_oe),
		.sda_i   (i2c_sda_i),
		.wb_rdata(wb_rdata[2]),
		.wb_wdata(wb_wdata),
		.wb_we   (wb_we),
		.wb_cyc  (wb_cyc[2]),
		.wb_ack  (wb_ack[2]),
		.ready   (),
		.clk     (clk_sys),
		.rst     (rst_sys)
	);

	// IOBs
	SB_IO #(
		.PIN_TYPE(6'b110101),
		.PULLUP(1'b1),
		.IO_STANDARD("SB_LVCMOS")
	) i2c_scl_iob_I (
		.PACKAGE_PIN  (i2c_scl),
		.OUTPUT_CLK   (clk_sys),
		.OUTPUT_ENABLE(i2c_scl_oe),
		.D_OUT_0      (1'b0)
	);

	SB_IO #(
		.PIN_TYPE(6'b110100),
		.PULLUP(1'b1),
		.IO_STANDARD("SB_LVCMOS")
	) i2c_sda_iob_I (
		.PACKAGE_PIN  (i2c_sda),
		.INPUT_CLK    (clk_sys),
		.OUTPUT_CLK   (clk_sys),
		.OUTPUT_ENABLE(i2c_sda_oe),
		.D_OUT_0      (1'b0),
		.D_IN_0       (i2c_sda_i)
	);

`else

	// Dummy
	assign wb_ack[2] = wb_cyc[2];
	assign wb_rdata[2] = 32'h00000000;

`endif


	// E1 LEDs & Button
	// ----------------

	// Blink pattern generator
	led_blinker blinker_I (
		.led_state(e1_led_state),
		.sr_val   (sr_val),
		.sr_go    (sr_go),
		.sr_rdy   (sr_rdy),
		.clk      (clk_sys),
		.rst      (rst_sys)
	);

	// Interface
	sr_btn_if #(
		.TICK_LOG2_DIV(3)
	) spi_mux_I (
		.flash_mosi  (flash_mosi),
		.flash_miso  (flash_miso),
		.flash_clk   (flash_clk),
		.flash_cs_n  (flash_cs_n),
		.e1_led_rclk (e1_led_rclk),
		.spi_mosi_i  (flash_mosi_i),
		.spi_mosi_o  (flash_mosi_o),
		.spi_mosi_oe (flash_mosi_oe),
		.spi_miso_i  (flash_miso_i),
		.spi_miso_o  (flash_miso_o),
		.spi_miso_oe (flash_miso_oe),
		.spi_clk_i   (flash_clk_i),
		.spi_clk_o   (flash_clk_o),
		.spi_clk_oe  (flash_clk_oe),
		.spi_csn_o   (flash_csn_o),
		.spi_csn_oe  (1'b1),
		.spi_req     (spi_req),
		.spi_gnt     (spi_gnt),
		.sr_val      (sr_val),
		.sr_go       (sr_go),
		.sr_rdy      (sr_rdy),
		.btn_val     (btn_val),
		.btn_stb     (btn_stb),
		.clk         (clk_sys),
		.rst         (rst_sys)
	);

	assign spi_req = ~e1_led_run;
	assign e1_led_active = ~spi_gnt;


	// Clock / Reset
	// -------------

	sysmgr sys_mgr_I (
		.clk_in (clk_in),
		.rst_in (rst_req),
		.clk_sys(clk_sys),
		.rst_sys(rst_sys),
		.clk_48m(clk_48m),
		.rst_48m(rst_48m)
	);

endmodule // top
