/*
 * 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 (
	// SPI
	inout  wire spi_mosi,
	inout  wire spi_miso,
	inout  wire spi_clk,
	output wire spi_flash_cs_n,
`ifdef HAS_PSRAM
	output wire spi_ram_cs_n,
`endif

	// USB TODO: remove later
	inout  wire usb_dp,
	inout  wire usb_dn,
	output wire usb_pu,

	// Debug UART
	input  wire uart_rx,
	output wire uart_tx,

	// Buttons (2 for now to test up and down)
	input  wire btn_n,
    input wire btn_1,
    input wire btn_2,

    // LEDs to blink to show that a value change has been registered?
    output wire[4:0] led,

    // GPIOs for out signal
    output wire out1,
    output wire out2,
    output wire out3,

	// LED TODO: remove later
	output wire [2:0] rgb,

	// Clock
	input  wire clk_in
);

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

	localparam integer WB_DW = 32;
	localparam integer WB_AW = 16;
	localparam integer WB_RW = WB_DW * WB_N;
	localparam integer WB_MW = WB_DW / 8;

    localparam integer FAST_PWM_WIDTH = 8;
    localparam integer PULSE_COUNTER_WIDTH = 8;
    localparam integer SLOW_PWM_WIDTH = 14;

    localparam integer BUTTON_COUNTER_WIDTH = 5;

	genvar i;


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

	// Wishbone
	wire [WB_AW-1:0] wb_addr;
	wire [WB_DW-1:0] wb_rdata [0:WB_N-1];
	wire [WB_RW-1:0] wb_rdata_flat;
	wire [WB_DW-1:0] wb_wdata;
	wire [WB_MW-1:0] wb_wmsk;
	wire [WB_N -1:0] wb_cyc;
	wire             wb_we;
	wire [WB_N -1:0] wb_ack;

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

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

    // 3 signal
    wire [SLOW_PWM_WIDTH-1:0] period1;
    wire [SLOW_PWM_WIDTH-1:0] delay1;
    wire [SLOW_PWM_WIDTH-1:0] period2;
    wire [SLOW_PWM_WIDTH-1:0] delay2;
    wire [FAST_PWM_WIDTH-1:0] period3;
    wire [FAST_PWM_WIDTH-1:0] duty3;
    wire [SLOW_PWM_WIDTH-1:0] delay3;
    wire [PULSE_COUNTER_WIDTH-1:0] npuls3;
    wire [1:0] odd_train_flag;
    wire ena_odd_out3;
    wire write_flash;

    // Buttons
    wire [BUTTON_COUNTER_WIDTH-1:0] inc_f1;
    wire [BUTTON_COUNTER_WIDTH-1:0] dec_f1;
    wire [BUTTON_COUNTER_WIDTH-1:0] inc_d1;
    wire [BUTTON_COUNTER_WIDTH-1:0] dec_d1;
    wire [BUTTON_COUNTER_WIDTH-1:0] inc_d2;
    wire [BUTTON_COUNTER_WIDTH-1:0] dec_d2;
    wire [BUTTON_COUNTER_WIDTH-1:0] inc_ph2;
    wire [BUTTON_COUNTER_WIDTH-1:0] dec_ph2;
    wire [BUTTON_COUNTER_WIDTH-1:0] inc_f3;
    wire [BUTTON_COUNTER_WIDTH-1:0] dec_f3;
    wire [BUTTON_COUNTER_WIDTH-1:0] inc_d3;
    wire [BUTTON_COUNTER_WIDTH-1:0] dec_d3;
    wire [BUTTON_COUNTER_WIDTH-1:0] inc_ph3;
    wire [BUTTON_COUNTER_WIDTH-1:0] dec_ph3;
    wire [BUTTON_COUNTER_WIDTH-1:0] inc_n3;
    wire [BUTTON_COUNTER_WIDTH-1:0] dec_n3;

	// Mailbox signal wires
    wire [16*16-1:0] mailbox_regs_flat;  // Flattened register array (16 registers of 16 bits each)
    wire [16*16-1:0] mailbox_btns_flat;

	// SoC
	// ---

	soc_picorv32_base #(
		.WB_N    (WB_N),
		.WB_DW   (WB_DW),
		.WB_AW   (WB_AW),
		.SPRAM_AW(SPRAM_AW)
	) base_I (
		.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_24m),
		.rst     (rst)
	);

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


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

	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_we    (wb_we),
		.wb_wdata (wb_wdata),
		.wb_cyc   (wb_cyc[1]),
		.wb_ack   (wb_ack[1]),
		.clk      (clk_24m),
		.rst      (rst)
	);


	// SPI [2]
	// ---

	ice40_spi_wb #(
`ifdef HAS_PSRAM
		.N_CS(2),
`else
		.N_CS(1),
`endif
		.WITH_IOB(1),
		.UNIT(0)
	) spi_I (
		.pad_mosi (spi_mosi),
		.pad_miso (spi_miso),
		.pad_clk  (spi_clk),
`ifdef HAS_PSRAM
		.pad_csn  ({spi_ram_cs_n, spi_flash_cs_n}),
`else
		.pad_csn  (spi_flash_cs_n),
`endif
		.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]
	// --------

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

	// WB Register Mailbox [4]
	// ----------
	mailbox_wb #(
		.AW(4),
		.DW(WB_DW)
	) mailbox_regs_I (
		.clk(clk_24m),
		.rst(rst),
		.wb_addr(wb_addr[3:0]),
		.wb_wdata(wb_wdata),
		.wb_rdata(wb_rdata[4]),
		.wb_we(wb_we),
		.wb_cyc(wb_cyc[4]),
		.wb_ack(wb_ack[4]),
		.registers_flat(mailbox_regs_flat)
	);

    // 3 Signal
    // --------
    assign period1 = mailbox_regs_flat[15:0];        // First 16 bits for period1
    assign delay1 = mailbox_regs_flat[31:16];        // Next 16 bits for delay1
    assign period2 = mailbox_regs_flat[47:32];       // Next 16 bits for period2
    assign delay2 = mailbox_regs_flat[63:48];        // Next 16 bits for delay2
    assign period3 = mailbox_regs_flat[79:64];       // Next 16 bits for period3
    assign duty3 = mailbox_regs_flat[95:80];         // Next 16 bits for duty3
    assign delay3 = mailbox_regs_flat[111:96];       // Next 16 bits for delay3
    assign npuls3 = mailbox_regs_flat[127:112];      // Next 16 bits for npuls3
    assign odd_train_flag = mailbox_regs_flat[143:128]; // Next 16 bits for odd_train_flag
    assign ena_odd_out3 = mailbox_regs_flat[159:144]; // Next 16 bits for ena_odd_out3
    assign write_flash = mailbox_regs_flat[175:160]; // Next 16 bits for write_flash (triggered from keyboard)

    three_signal #(
        .FAST_PWM_WIDTH(FAST_PWM_WIDTH),
        .PULSE_COUNTER_WIDTH(PULSE_COUNTER_WIDTH),
        .SLOW_PWM_WIDTH(SLOW_PWM_WIDTH)
    ) three_signal_I(
        .nrst(~rst),
        .clk(clk_48m), // TODO: fix later we have CDC problem here
        .period1(period1),
        .delay1(delay1),
        .period2(period2),
        .delay2(delay2),
        .period3(period3),
        .duty3(duty3),
        .delay3(delay3),
        .npuls3(npuls3),
        .odd_train_flag(odd_train_flag),
        .ena_odd_out3(ena_odd_out3),
        .Out1(out1),
        .Out2(out2),
        .Out3(out3)
    );

	// WB Button Mailbox [5]
	// ----------
	mailbox_wb #(
		.AW(4),
		.DW(WB_DW)
	) mailbox_btns_I (
		.clk(clk_24m),
		.rst(rst),
		.wb_addr(wb_addr[3:0]),
		.wb_wdata(wb_wdata),
		.wb_rdata(wb_rdata[5]),
		.wb_we(wb_we),
		.wb_cyc(wb_cyc[5]),
		.wb_ack(wb_ack[5]),
		.registers_flat(mailbox_btns_flat)
	);


    // Buttons
    assign inc_f1 = mailbox_btns_flat[15:0];
    assign dec_f1 = mailbox_btns_flat[31:16];
    assign inc_d1 = mailbox_btns_flat[47:32];
    assign dec_d1 = mailbox_btns_flat[63:48];
    assign inc_d2 = mailbox_btns_flat[79:64];
    assign dec_d2 = mailbox_btns_flat[95:80];
    assign inc_ph2 = mailbox_btns_flat[111:96];
    assign dec_ph2 = mailbox_btns_flat[127:112];
    assign inc_f3 = mailbox_btns_flat[143:128];
    assign dec_f3 = mailbox_btns_flat[159:144];
    assign inc_d3 = mailbox_btns_flat[175:160];
    assign dec_d3 = mailbox_btns_flat[191:176];
    assign inc_ph3 = mailbox_btns_flat[207:192];
    assign dec_ph3 = mailbox_btns_flat[223:208];
    assign inc_n3 = mailbox_btns_flat[239:224];
    assign dec_n3 = mailbox_btns_flat[255:240];

	reg [31:0] pcount; //dummy variable + usage to make sure button will not be optimized out

	button b1(
		.clk(clk_24m),
		.nrst(~rst),
		.butt(btn_1),

		.press_count(pcount)
	);

    // TODO: dummy led onoff when value has been written
    always @(posedge clk_24m or posedge rst)
        if (rst) begin
            led[0] = 1'b0;
            led[1] = 1'b0;
            led[2] = 1'b0 & pcount[25];
        end else if (period1 == 1) begin
            led[0] = 1'b1;
            led[1] = 1'b0;
            led[2] = 1'b0;
        end else if (period1 == 2) begin
            led[0] = 1'b0;
            led[1] = 1'b1;
            led[2] = 1'b0;
        end else if (period1 == 4) begin
            led[0] = 1'b0;
            led[1] = 1'b0;
            led[2] = 1'b1;
        end else if (period1 != 0) begin
            led[0] = 1'b1;
            led[1] = 1'b1;
            led[2] = 1'b1;
        end else begin
            led[0] = 1'b0;
            led[1] = 1'b0;
            led[2] = 1'b0;
        end

    always @(posedge clk_24m or posedge rst)
        if (rst) begin
            led[3] = 1'b0;
        end else if (btn_2) begin
            led[3] = 1'b1;
        end else begin
            led[3] = 1'b0;
        end

    //// TODO: dummy driving from delay1
    always @(posedge clk_24m or posedge rst)
        if (rst) begin
            led[4] = 1'b0;
        end else if (delay1 != 0) begin
            led[4] = 1'b1;
        end else begin
            led[4] = 1'b0;
        end

    //always @(posedge clk_48m or posedge rst)
    //    if (rst) begin
    //        led[3] = 1'b0;
    //    end else if (delay2 != 0) begin
    //        led[3] = 1'b1;
    //    end else begin
    //        led[3] = 1'b0;
    //    end

	// Warm Boot
	// ---------

	// Bus interface
	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

	assign wb_rdata[0] = 0;
	assign wb_ack[0] = wb_cyc[0];

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


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

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

	always #5 clk_48m_s <= !clk_48m_s;
	always #20 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
	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
