/*
 * top.v
 *
 * vim: ts=4 sw=4
 *
 * Copyright (C) 2019-2020  Sylvain Munaut <tnt@246tNt.com>
 * SPDX-License-Identifier: CERN-OHL-P-2.0
 */

`default_nettype none
`include "boards.vh"

module top (
	// Special features
`ifdef MISC_SEL
	output wire [($bits(`MISC_SEL)/2)-1:0] misc,
`endif

	// LED
`ifdef HAS_1LED
	output wire led,
`endif
`ifdef HAS_RGB
	output wire [2:0] rgb,
`endif

	// Debug UART
`ifdef ENABLE_UART
	input  wire uart_rx,
	output wire uart_tx,
`endif

	// Clock
`ifndef USE_HF_OSC
	input  wire clk_in,
`endif

	// Button
	input  wire btn,

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

	// SPI
	inout  wire spi_mosi,
	inout  wire spi_miso,
	inout  wire spi_clk,
	inout  wire spi_cs_n
);

	localparam WB_N  =  6;
	localparam WB_DW = 32;
	localparam WB_AW = 16;
	localparam WB_AI =  2;

	localparam SPRAM_AW = 14; /* 14 => 64k, 15 => 128k */

	genvar i;


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

	// Memory bus
	wire        mem_valid;
	wire        mem_instr;
	wire        mem_ready;
	wire [31:0] mem_addr;
	wire [31:0] mem_rdata;
	wire [31:0] mem_wdata;
	wire [ 3:0] mem_wstrb;

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

	// Wishbone
	wire [ WB_AW   -1:0] wb_addr;
	wire [ WB_DW-1   :0] wb_rdata [0:WB_N-1];
	wire [ WB_DW   -1:0] wb_wdata;
	wire [(WB_DW/8)-1:0] wb_wmsk;
	wire                 wb_we;
	wire [ WB_N    -1:0] wb_cyc;
	wire [ WB_N    -1:0] wb_ack;

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

	// USB Core
		// EP Buffer
	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;

		// Bus interface
	wire [11:0] ub_addr;
	wire [15:0] ub_wdata;
	wire [15:0] ub_rdata;
	wire        ub_cyc;
	wire        ub_we;
	wire        ub_ack;

	// WarmBoot
	reg         boot_now;
	reg   [1:0] boot_sel;

	// Single led
	reg         led_ena;
	reg  [10:0] led_off;
	reg  [10:0] led_on;
	wire        led_i;

	// Clock / Reset logic
	wire clk_24m;
	wire clk_48m;
	wire rst;


	// SoC
	// ---

	// CPU
	picorv32 #(
		.PROGADDR_RESET(32'h 0000_0000),
		.STACKADDR(32'h 0000_0400),
		.BARREL_SHIFTER(0),
		.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_24m),
		.resetn    (~rst),
		.mem_valid (mem_valid),
		.mem_instr (mem_instr),
		.mem_ready (mem_ready),
		.mem_addr  (mem_addr),
		.mem_wdata (mem_wdata),
		.mem_wstrb (mem_wstrb),
		.mem_rdata (mem_rdata)
	);

	// Bus interface
	soc_picorv32_bridge #(
		.WB_N  (WB_N),
		.WB_DW (WB_DW),
		.WB_AW (WB_AW),
		.WB_AI (WB_AI)
	) pb_I (
		.pb_addr     (mem_addr),
		.pb_rdata    (mem_rdata),
		.pb_wdata    (mem_wdata),
		.pb_wstrb    (mem_wstrb),
		.pb_valid    (mem_valid),
		.pb_ready    (mem_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_wdata    (wb_wdata),
		.wb_wmsk     (wb_wmsk),
		.wb_rdata    (wb_rdata_flat),
		.wb_cyc      (wb_cyc),
		.wb_we       (wb_we),
		.wb_ack      (wb_ack),
		.clk         (clk_24m),
		.rst         (rst)
	);

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

	// Boot memory
	soc_bram #(
		.INIT_FILE("boot.hex")
	) bram_I (
		.addr  (bram_addr),
		.rdata (bram_rdata),
		.wdata (bram_wdata),
		.wmsk  (bram_wmsk),
		.we    (bram_we),
		.clk   (clk_24m)
	);

	// Main memory
	soc_spram #(
		.AW(SPRAM_AW)
	) spram_I (
		.addr  (spram_addr[SPRAM_AW-1:0]),
		.rdata (spram_rdata),
		.wdata (spram_wdata),
		.wmsk  (spram_wmsk),
		.we    (spram_we),
		.clk   (clk_24m)
	);


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

	// Bus interface
	assign wb_rdata[0] = 0;
	assign wb_ack[0] = wb_cyc[0];

	always @(posedge clk_24m or posedge rst)
		if (rst) begin
			boot_now <= 1'b0;
			boot_sel <= 2'b00;
		end else if (wb_cyc[0] & wb_we & (wb_addr[2:0] == 3'b000)) begin
			boot_now <= wb_wdata[2];
			boot_sel <= wb_wdata[1:0];
		end

	always @(posedge clk_24m or posedge rst)
		if (rst) begin
			led_ena <= 1'b0;
			led_off <= 0;
			led_on  <= 0;
		end else if (wb_cyc[0] & wb_we & (wb_addr[2:0] == 3'b001)) begin
			led_ena <= wb_wdata[31];
			led_off <= wb_wdata[26:16];
			led_on  <= wb_wdata[10: 0];
		end

	// Helper
	dfu_helper #(
		.TIMER_WIDTH(24),
		.BTN_MODE(3),
		.DFU_MODE(1)
	) dfu_helper_I (
		.boot_now (boot_now),
		.boot_sel (boot_sel),
		.btn_pad  (btn),
		.btn_val  (),
		.rst_req  (),
		.clk      (clk_24m),
		.rst      (rst)
	);

	// Single led : only variable rate
`ifdef HAS_1LED
	led_blinker blinker_I (
		.led  (led_i),
		.ena  (led_ena),
		.off  (led_off),
		.on   (led_on),
		.clk  (clk_24m),
		.rst  (rst)
	);

	assign led = ~led_i;
`endif


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

`ifdef ENABLE_UART
	uart_wb #(
		.DIV_WIDTH(12),
		.DW(WB_DW)
	) uart_I (
		.uart_tx  (uart_tx),
		.uart_rx  (uart_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_24m),
		.rst      (rst)
	);
`else
	assign wb_ack[1] = wb_cyc[1];
	assign wb_rdata[1] = { wb_cyc[1], 31'h00000000 };	// Always empty
`endif


	// SPI [2]
	// ---

	ice40_spi_wb #(
		.N_CS(1),
		.WITH_IOB(1),
		.UNIT(0)
	) spi_I (
		.pad_mosi (spi_mosi),
		.pad_miso (spi_miso),
		.pad_clk  (spi_clk),
		.pad_csn  (spi_cs_n),
		.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_24m),
		.rst      (rst)
	);


	// RGB LEDs [3]
	// --------

`ifdef HAS_RGB
	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[3]),
		.wb_wdata   (wb_wdata),
		.wb_we      (wb_we),
		.wb_cyc     (wb_cyc[3]),
		.wb_ack     (wb_ack[3]),
		.clk        (clk_24m),
		.rst        (rst)
	);
`else
	reg rgb_ack;
	always @(posedge clk_24m)
		rgb_ack <= wb_cyc[3] & ~rgb_ack;

	assign wb_ack[3] = rgb_ack;
	assign wb_rdata[3] = 0;
`endif


	// USB Core [4 & 5]
	// --------

	// Core
	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_24m),
		.wb_addr      (ub_addr),
		.wb_rdata     (ub_rdata),
		.wb_wdata     (ub_wdata),
		.wb_we        (ub_we),
		.wb_cyc       (ub_cyc),
		.wb_ack       (ub_ack),
		.clk          (clk_48m),
		.rst          (rst)
	);

	// Cross clock bridge
	xclk_wb #(
		.DW(16),
		.AW(12)
	)  wb_48m_xclk_I (
		.s_addr  (wb_addr[11:0]),
		.s_rdata (wb_rdata[4][15:0]),
		.s_wdata (wb_wdata[15:0]),
		.s_we    (wb_we),
		.s_cyc   (wb_cyc[4]),
		.s_ack   (wb_ack[4]),
		.s_clk   (clk_24m),
		.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)
	);

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

	// EP buffer interface
	wb_epbuf #(
		.AW(9),
		.DW(32)
	) epbuf_I (
		.wb_addr     (wb_addr),
		.wb_rdata    (wb_rdata[5]),
		.wb_wdata    (wb_wdata),
		.wb_we       (wb_we),
		.wb_cyc      (wb_cyc[5]),
		.wb_ack      (wb_ack[5]),
		.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_24m),
		.rst         (rst)
	);


	// Special Features
	// ----------------

	// If the boards needs it, we can tie some pins to
	// High-Z, 50% PDM, High, Low
`ifdef MISC_SEL
	reg        misc_osc = 1'b0;
	wire [3:0] misc_opt;
	wire [$bits(`MISC_SEL)-1:0] misc_sel = `MISC_SEL;

	always @(posedge clk_24m)
		misc_osc <= ~misc_osc;

	assign misc_opt = { 2'b10, misc_osc, 1'bz };

	genvar i;
	generate
		for (i=0; i<$bits(`MISC_SEL); i=i+2)
			assign misc[i/2] = misc_opt[misc_sel[i+:2]];
	endgenerate
`endif


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

`ifdef SIM
	reg clk_48m_s = 1'b0;
	reg clk_24m_s = 1'b0;
	reg rst_s = 1'b1;

	always #10.42 clk_48m_s <= !clk_48m_s;
	always #20.84 clk_24m_s <= !clk_24m_s;

	initial begin
		#200 rst_s = 0;
	end

	assign clk_48m = clk_48m_s;
	assign clk_24m = clk_24m_s;
	assign rst = rst_s;
`else

`ifdef USE_HF_OSC
	wire clk_in;

	SB_HFOSC #(
		.CLKHF_DIV("0b10")
	) hfosc_I (
		.CLKHFPU(1'b1),
		.CLKHFEN(1'b1),
		.CLKHF(clk_in)
	);
`endif

	sysmgr sys_mgr_I (
		.clk_in  (clk_in),
		.rst_in  (1'b0),
		.clk_48m (clk_48m),
		.clk_24m (clk_24m),
		.rst_out (rst)
	);
`endif

endmodule // top