/*
 * soc_base.v
 *
 * vim: ts=4 sw=4
 *
 * Minimal common base for the E1 project SoC
 *
 * Copyright (C) 2019-2020  Sylvain Munaut <tnt@246tNt.com>
 * SPDX-License-Identifier: CERN-OHL-S-2.0
 */

`default_nettype none

module soc_base #(
	parameter integer WB_N = 1,
	parameter integer E1_N = 1,
	parameter         E1_UNIT_HAS_RX = 1'b1,
	parameter         E1_UNIT_HAS_TX = 1'b1,
	parameter integer E1_LIU = 0
)(
	// E1 pads
		// Raw PHY
	input  wire [E1_N-1:0] e1_rx_hi_p,
	input  wire [E1_N-1:0] e1_rx_hi_n,
	input  wire [E1_N-1:0] e1_rx_lo_p,
	input  wire [E1_N-1:0] e1_rx_lo_n,

	output wire [E1_N-1:0] e1_tx_hi,
	output wire [E1_N-1:0] e1_tx_lo,

		// LIU
	input  wire [E1_N-1:0] e1_rx_data,
	input  wire [E1_N-1:0] e1_rx_clk,

	output wire [E1_N-1:0] e1_tx_data,
	output wire [E1_N-1:0] e1_tx_clk,

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

	// Flash SPI (raw)
	input  wire flash_mosi_i,
	output wire flash_mosi_o,
	output wire flash_mosi_oe,

	input  wire flash_miso_i,
	output wire flash_miso_o,
	output wire flash_miso_oe,

	input  wire flash_clk_i,
	output wire flash_clk_o,
	output wire flash_clk_oe,

	output wire flash_csn_o,

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

	// RGB LEDs
	output wire [2:0] rgb,

	// External Master Wishbone bus (CPU -> Peripheral)
	output wire          [15:0] wb_m_addr,
	input  wire [(WB_N*32)-1:0] wb_m_rdata,
	output wire          [31:0] wb_m_wdata,
	output wire          [ 3:0] wb_m_wmsk,
	output wire                 wb_m_we,
	output wire [ WB_N    -1:0] wb_m_cyc,
	input  wire [ WB_N    -1:0] wb_m_ack,

	// Ticks
	output wire [E1_N-1:0] tick_e1_rx,
	output wire [E1_N-1:0] tick_e1_tx,
	output wire            tick_usb_sof,

	// Clock / Reset
	input  wire clk_sys,
	input  wire rst_sys,
	input  wire clk_48m,
	input  wire rst_48m
);

	genvar i;

	localparam integer WB_LN = 8;
	localparam integer WB_TN = WB_N + WB_LN;
	localparam integer WB_DW = 32;
	localparam integer WB_MW = WB_DW / 8;
	localparam integer WB_AW = 16;
	localparam integer WB_AI =  2;


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

	// Picorv32 native bus
	wire        pb_valid;
	wire        pb_instr;
	wire        pb_ready;
	wire [31:0] pb_addr;
	wire [31:0] pb_rdata;
	wire [31:0] pb_wdata;
	wire [ 3:0] pb_wstrb;

	// SoC RAM
		// BRAM
	wire [ 7:0] bram_addr;
	wire [31:0] bram_rdata;
	wire [31:0] bram_wdata;
	wire [ 3:0] bram_wmsk;
	wire        bram_we;

		// SPRAM
	wire [14:0] spram_addr;
	wire [31:0] spram_rdata;
	wire [31:0] spram_wdata;
	wire [ 3:0] spram_wmsk;
	wire        spram_we;

	// Peripheral wishbone
	wire [WB_AW-1:0] wb_addr;
	wire [WB_DW-1:0] wb_rdata [0:WB_LN-1];
	wire [WB_DW-1:0] wb_wdata;
	wire [WB_MW-1:0] wb_wmsk;
	wire             wb_we;
	wire [WB_TN-1:0] wb_cyc;
	wire [WB_TN-1:0] wb_ack;

	wire [(WB_DW*WB_TN)-1:0] wb_rdata_flat;

	// USB
		// Wishbone ( @ 48 MHz )
	wire [11:0] ub_addr;
	wire [15:0] ub_rdata;
	wire [15:0] ub_wdata;
	wire        ub_we;
	wire        ub_cyc;
	wire        ub_ack;

		// EP interface
	wire [ 8:0] ep_tx_addr_0;
	wire [31:0] ep_tx_data_0;
	wire        ep_tx_we_0;

	wire [ 8:0] ep_rx_addr_0;
	wire [31:0] ep_rx_data_1;
	wire        ep_rx_re_0;

		// SoF
	wire usb_sof;

	// Wishbone bus E1 to IO buffers
	wire [13:0] wb_e1_addr;
	wire [31:0] wb_e1_rdata;
	wire [31:0] wb_e1_wdata;
	wire [ 3:0] wb_e1_wmsk;
	wire        wb_e1_we;
	wire        wb_e1_cyc;
	wire        wb_e1_ack;

	// E1 buffer interface
	wire [(E1_N*8)-1:0] e1_buf_rx_data;
	wire [(E1_N*5)-1:0] e1_buf_rx_ts;
	wire [(E1_N*4)-1:0] e1_buf_rx_frame;
	wire [(E1_N*7)-1:0] e1_buf_rx_mf;
	wire [ E1_N   -1:0] e1_buf_rx_we;
	wire [ E1_N   -1:0] e1_buf_rx_rdy;
	wire [(E1_N*8)-1:0] e1_buf_tx_data;
	wire [(E1_N*5)-1:0] e1_buf_tx_ts;
	wire [(E1_N*4)-1:0] e1_buf_tx_frame;
	wire [(E1_N*7)-1:0] e1_buf_tx_mf;
	wire [ E1_N   -1:0] e1_buf_tx_re;
	wire [ E1_N   -1:0] e1_buf_tx_rdy;


	// SoC core
	// --------

	// Local CPU reset
	reg pb_rst_n;

	always @(posedge clk_sys or posedge rst_sys)
		if (rst_sys)
			pb_rst_n <= 1'b0;
		else
			pb_rst_n <= 1'b1;

	// CPU
	picorv32 #(
		.PROGADDR_RESET(32'h 0000_0000),
		.STACKADDR(32'h 0000_0400),
		.BARREL_SHIFTER(0),
`ifdef BOARD_E1_TRACER
		.TWO_CYCLE_COMPARE(0),
		.TWO_CYCLE_ALU(0),
`else
		.TWO_CYCLE_COMPARE(0),
		.TWO_CYCLE_ALU(1),
`endif
		.COMPRESSED_ISA(0),
		.ENABLE_COUNTERS(0),
		.ENABLE_MUL(0),
		.ENABLE_DIV(0),
		.ENABLE_IRQ(0),
		.ENABLE_IRQ_QREGS(0),
		.CATCH_MISALIGN(0),
		.CATCH_ILLINSN(0)
	) cpu_I (
		.clk       (clk_sys),
		.resetn    (pb_rst_n),
		.mem_valid (pb_valid),
		.mem_instr (pb_instr),
		.mem_ready (pb_ready),
		.mem_addr  (pb_addr),
		.mem_wdata (pb_wdata),
		.mem_wstrb (pb_wstrb),
		.mem_rdata (pb_rdata)
	);

	// CPU bridge
	soc_picorv32_bridge #(
		.WB_N  (WB_TN),
		.WB_DW (32),
		.WB_AW (16),
		.WB_AI ( 2),
		.WB_REG( 4)
	) bridge_I (
		.pb_addr    (pb_addr),
		.pb_rdata   (pb_rdata),
		.pb_wdata   (pb_wdata),
		.pb_wstrb   (pb_wstrb),
		.pb_valid   (pb_valid),
		.pb_ready   (pb_ready),
		.bram_addr  (bram_addr),
		.bram_rdata (bram_rdata),
		.bram_wdata (bram_wdata),
		.bram_wmsk  (bram_wmsk),
		.bram_we    (bram_we),
		.spram_addr (spram_addr),
		.spram_rdata(spram_rdata),
		.spram_wdata(spram_wdata),
		.spram_wmsk (spram_wmsk),
		.spram_we   (spram_we),
		.wb_addr    (wb_addr),
		.wb_rdata   (wb_rdata_flat),
		.wb_wdata   (wb_wdata),
		.wb_wmsk    (wb_wmsk),
		.wb_we      (wb_we),
		.wb_cyc     (wb_cyc),
		.wb_ack     (wb_ack),
		.clk        (clk_sys),
		.rst        (rst_sys)
	);

	for (i=0; i<WB_LN; i=i+1)
		assign wb_rdata_flat[i*WB_DW+:WB_DW] = wb_rdata[i];

	// Boot memory - 1k
	soc_bram #(
		.AW(8),
		.INIT_FILE("boot.hex")
	) bram_I (
		.addr (bram_addr),
		.rdata(bram_rdata),
		.wdata(bram_wdata),
		.wmsk (bram_wmsk),
		.we   (bram_we),
		.clk  (clk_sys)
	);

	// Main SoC memory - 64k
	soc_spram #(
		.AW(14)
	) spram_I (
		.addr (spram_addr[13:0]),
		.rdata(spram_rdata),
		.wdata(spram_wdata),
		.wmsk (spram_wmsk),
		.we   (spram_we),
		.clk  (clk_sys)
	);

	// Peripheral wishbone export
	assign wb_m_addr  = wb_addr;
	assign wb_m_wdata = wb_wdata;
	assign wb_m_wmsk  = wb_wmsk;
	assign wb_m_we    = wb_we;
	assign wb_m_cyc   = wb_cyc[WB_TN-1:WB_LN];

	assign wb_rdata_flat[(WB_TN*WB_DW)-1:(WB_LN*WB_DW)] = wb_m_rdata;
	assign wb_ack[WB_TN-1:WB_LN] = wb_m_ack;


	// SPI [0]
	// ---

	ice40_spi_wb #(
		.N_CS(1),
		.WITH_IOB(0),
		.UNIT(0)
	) spi_I (
		.sio_mosi_i (flash_mosi_i),
		.sio_mosi_o (flash_mosi_o),
		.sio_mosi_oe(flash_mosi_oe),
		.sio_miso_i (flash_miso_i),
		.sio_miso_o (flash_miso_o),
		.sio_miso_oe(flash_miso_oe),
		.sio_clk_i  (flash_clk_i),
		.sio_clk_o  (flash_clk_o),
		.sio_clk_oe (flash_clk_oe),
		.sio_csn_o  (flash_csn_o),
		.sio_csn_oe (),
		.wb_addr    (wb_addr[3: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)
	);


	// Debug UART [1]
	// ----------

	uart_wb #(
		.DIV_WIDTH(12),
		.DW(WB_DW)
	) uart_I (
		.uart_tx  (dbg_tx),
		.uart_rx  (dbg_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)
	);


	// RGB LEDs [2]
	// --------

	ice40_rgb_wb #(
		.CURRENT_MODE("0b1"),
		.RGB0_CURRENT("0b000001"),
		.RGB1_CURRENT("0b000001"),
		.RGB2_CURRENT("0b000001")
	) rgb_I (
		.pad_rgb    (rgb),
		.wb_addr    (wb_addr[4: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)
	);


	// USB [3]
	// ---

	// Core instance ( @ 48 MHz )
	usb #(
		.EPDW(32)
	) usb_I (
		.pad_dp      (usb_dp),
		.pad_dn      (usb_dn),
		.pad_pu      (usb_pu),
		.ep_tx_addr_0(ep_tx_addr_0),
		.ep_tx_data_0(ep_tx_data_0),
		.ep_tx_we_0  (ep_tx_we_0),
		.ep_rx_addr_0(ep_rx_addr_0),
		.ep_rx_data_1(ep_rx_data_1),
		.ep_rx_re_0  (ep_rx_re_0),
		.ep_clk      (clk_sys),
		.wb_addr     (ub_addr),
		.wb_rdata    (ub_rdata),
		.wb_wdata    (ub_wdata),
		.wb_we       (ub_we),
		.wb_cyc      (ub_cyc),
		.wb_ack      (ub_ack),
		.sof         (usb_sof),
		.clk         (clk_48m),
		.rst         (rst_48m)
	);

    // Cross clock bridge
	xclk_wb #(
		.DW(16),
		.AW(12)
	) wb_48m_xclk_I (
		.s_addr (wb_addr[11:0]),
		.s_rdata(wb_rdata[3][15:0]),
		.s_wdata(wb_wdata[15:0]),
		.s_we   (wb_we),
		.s_cyc  (wb_cyc[3]),
		.s_ack  (wb_ack[3]),
		.s_clk  (clk_sys),
		.m_addr (ub_addr),
		.m_rdata(ub_rdata),
		.m_wdata(ub_wdata),
		.m_we   (ub_we),
		.m_cyc  (ub_cyc),
		.m_ack  (ub_ack),
		.m_clk  (clk_48m),
		.rst    (rst_sys)
	);

	assign wb_rdata[3][31:16] = 16'h0000;

	// Cross clock SoF
	xclk_strobe sof_xclk_I (
		.in_stb (usb_sof),
		.in_clk (clk_48m),
		.out_stb(tick_usb_sof),
		.out_clk(clk_sys),
		.rst    (rst_sys)
	);


	// IO buffers & DMA
	// ----------------
	// [4] USB EP buffer
	// [5] E1 SPRAM buffer
	// [6] DMA

	soc_iobuf iobuf_I (
		.wb_cpu_addr (wb_addr),
		.wb_cpu_rdata(wb_rdata[4]),
		.wb_cpu_wdata(wb_wdata),
		.wb_cpu_wmsk (wb_wmsk),
		.wb_cpu_we   (wb_we),
		.wb_cpu_cyc  (wb_cyc[6:4]),
		.wb_cpu_ack  (wb_ack[6:4]),
		.wb_e1_addr  (wb_e1_addr),
		.wb_e1_rdata (wb_e1_rdata),
		.wb_e1_wdata (wb_e1_wdata),
		.wb_e1_wmsk  (wb_e1_wmsk),
		.wb_e1_we    (wb_e1_we),
		.wb_e1_cyc   (wb_e1_cyc),
		.wb_e1_ack   (wb_e1_ack),
		.ep_tx_addr_0(ep_tx_addr_0),
		.ep_tx_data_0(ep_tx_data_0),
		.ep_tx_we_0  (ep_tx_we_0),
		.ep_rx_addr_0(ep_rx_addr_0),
		.ep_rx_data_1(ep_rx_data_1),
		.ep_rx_re_0  (ep_rx_re_0),
		.clk         (clk_sys),
		.rst         (rst_sys)
	);

	assign wb_rdata[5] = 32'h00000000;
	assign wb_rdata[6] = 32'h00000000;


	// E1 [7]
	// --

	// E1 wishbone module
	e1_wb #(
		.N(E1_N),
		.UNIT_HAS_RX(E1_UNIT_HAS_RX),
		.UNIT_HAS_TX(E1_UNIT_HAS_TX),
		.LIU(E1_LIU),
		.MFW(7)
	) e1_I (
		.pad_rx_hi_p (e1_rx_hi_p),
		.pad_rx_hi_n (e1_rx_hi_n),
		.pad_rx_lo_p (e1_rx_lo_p),
		.pad_rx_lo_n (e1_rx_lo_n),
		.pad_tx_hi   (e1_tx_hi),
		.pad_tx_lo   (e1_tx_lo),
		.pad_rx_data (e1_rx_data),
		.pad_rx_clk  (e1_rx_clk),
		.pad_tx_data (e1_tx_data),
		.pad_tx_clk  (e1_tx_clk),
		.buf_rx_data (e1_buf_rx_data),
		.buf_rx_ts   (e1_buf_rx_ts),
		.buf_rx_frame(e1_buf_rx_frame),
		.buf_rx_mf   (e1_buf_rx_mf),
		.buf_rx_we   (e1_buf_rx_we),
		.buf_rx_rdy  (e1_buf_rx_rdy),
		.buf_tx_data (e1_buf_tx_data),
		.buf_tx_ts   (e1_buf_tx_ts),
		.buf_tx_frame(e1_buf_tx_frame),
		.buf_tx_mf   (e1_buf_tx_mf),
		.buf_tx_re   (e1_buf_tx_re),
		.buf_tx_rdy  (e1_buf_tx_rdy),
		.wb_addr     (wb_addr[7:0]),
		.wb_rdata    (wb_rdata[7][15:0]),
		.wb_wdata    (wb_wdata[15:0]),
		.wb_we       (wb_we),
		.wb_cyc      (wb_cyc[7]),
		.wb_ack      (wb_ack[7]),
		.irq         (),
		.tick_rx     (tick_e1_rx),
		.tick_tx     (tick_e1_tx),
		.clk         (clk_sys),
		.rst         (rst_sys)
	);

	assign wb_rdata[7][31:16] = 16'h0000;

	// E1 buffer interface to Wishbone
	e1_buf_if_wb #(
		.N(E1_N),
		.UNIT_HAS_RX(E1_UNIT_HAS_RX),
		.UNIT_HAS_TX(E1_UNIT_HAS_TX),
		.MFW(7),
		.DW(32)
	) e1_buf_I (
		.wb_addr     (wb_e1_addr),
		.wb_rdata    (wb_e1_rdata),
		.wb_wdata    (wb_e1_wdata),
		.wb_wmsk     (wb_e1_wmsk),
		.wb_we       (wb_e1_we),
		.wb_cyc      (wb_e1_cyc),
		.wb_ack      (wb_e1_ack),
		.buf_rx_data (e1_buf_rx_data),
		.buf_rx_ts   (e1_buf_rx_ts),
		.buf_rx_frame(e1_buf_rx_frame),
		.buf_rx_mf   (e1_buf_rx_mf),
		.buf_rx_we   (e1_buf_rx_we),
		.buf_rx_rdy  (e1_buf_rx_rdy),
		.buf_tx_data (e1_buf_tx_data),
		.buf_tx_ts   (e1_buf_tx_ts),
		.buf_tx_frame(e1_buf_tx_frame),
		.buf_tx_mf   (e1_buf_tx_mf),
		.buf_tx_re   (e1_buf_tx_re),
		.buf_tx_rdy  (e1_buf_tx_rdy),
		.clk         (clk_sys),
		.rst         (rst_sys)
	);

endmodule // soc_base
