
`default_nettype none

`include "../common-verilog/j1-universal-16kb-dualport.v"
`include "../common-verilog/ringoscillator-ecp5.v"
`include "trellis/adcfifo.v"

`include "../common-verilog/usb_cdc/bulk_endp.v"
`include "../common-verilog/usb_cdc/ctrl_endp.v"
`include "../common-verilog/usb_cdc/in_fifo.v"
`include "../common-verilog/usb_cdc/out_fifo.v"
`include "../common-verilog/usb_cdc/phy_rx.v"
`include "../common-verilog/usb_cdc/phy_tx.v"
`include "../common-verilog/usb_cdc/sie.v"
`include "../common-verilog/usb_cdc/usb_cdc.v"

module top(input clk48,

           inout usb_d_p,
           inout usb_d_n,
           inout usb_pullup,

           output rgb_led0_r,
           output rgb_led0_g,
           output rgb_led0_b,

           inout gpio_a0,
           inout gpio_a1,
           inout gpio_a2,
           inout gpio_a3,
           inout gpio_a4,
           inout gpio_a5,
           inout gpio_sck,
           inout gpio_mosi,
           inout gpio_miso,
           inout gpio_0,
           inout gpio_1,

           inout gpio_13,
           inout gpio_12,
           inout gpio_11,
           inout gpio_10,
           inout gpio_9,
           inout gpio_6,
           inout gpio_5,
           inout gpio_scl,
           inout gpio_sda,

           input  adc_sense,
           output adc_ctrl0,
           output adc_ctrl1,
           output adc_mux0,
           output adc_mux1,
           output adc_mux2,
           output adc_mux3,

           input usr_btn
);

  // ######   Clock   #########################################

  wire clk = clk48;

  // ######   USB reset logic   ###############################

  // We do not use the PLL here, so there is no "locked" signal.

  reg [7:0] usb_reset_cnt = 0;
  wire usb_resetq = &usb_reset_cnt;

  always @(posedge clk48) begin
    if (1'b1) usb_reset_cnt <= usb_reset_cnt + !usb_resetq;
    else      usb_reset_cnt <= 0;
  end

  // ######   Reset logic   ###################################

  reg [7:0] reset_cnt = 0;
  wire resetq = &reset_cnt;

  always @(posedge clk) begin
    if (usr_btn) reset_cnt <= reset_cnt + !resetq;
    else         reset_cnt <= 0;
  end

  // ######   Bus    ##########################################

  wire io_rd, io_wr;
  wire [15:0] io_addr;
  wire [15:0] io_dout;
  wire [15:0] io_din;

  reg interrupt = 0;

  // ######   Processor   #####################################

  j1 _j1(
    .clk(clk),
    .resetq(resetq),

    .io_rd(io_rd),
    .io_wr(io_wr),
    .io_dout(io_dout),
    .io_din(io_din),
    .io_addr(io_addr),

    .interrupt_request(interrupt)
  );

  // ######   Ticks   #########################################

  reg [15:0] ticks;

  wire [16:0] ticks_plus_1 = ticks + 1;

  always @(posedge clk)
    if (io_wr & io_addr[14])
      ticks <= io_dout;
    else
      ticks <= ticks_plus_1;

  always @(posedge clk) // Generate interrupt on ticks overflow
    interrupt <= ticks_plus_1[16];

  // ######   Cycles   ########################################

  reg [15:0] cycles;

  always @(posedge clk) cycles <= cycles + 1;

  // ######   PORTA   ###########################################

  reg  [15:0] porta_dir;   // 1:output, 0:input
  reg  [15:0] porta_out;
  wire [10:0] porta_in;

  BB ioa0  (.B( gpio_a0   ), .I(porta_out[ 0]), .O(porta_in[ 0]), .T(~porta_dir[ 0]));
  BB ioa1  (.B( gpio_a1   ), .I(porta_out[ 1]), .O(porta_in[ 1]), .T(~porta_dir[ 1]));
  BB ioa2  (.B( gpio_a2   ), .I(porta_out[ 2]), .O(porta_in[ 2]), .T(~porta_dir[ 2]));
  BB ioa3  (.B( gpio_a3   ), .I(porta_out[ 3]), .O(porta_in[ 3]), .T(~porta_dir[ 3]));
  BB ioa4  (.B( gpio_a4   ), .I(porta_out[ 4]), .O(porta_in[ 4]), .T(~porta_dir[ 4]));
  BB ioa5  (.B( gpio_a5   ), .I(porta_out[ 5]), .O(porta_in[ 5]), .T(~porta_dir[ 5]));
  BB ioa6  (.B( gpio_sck  ), .I(porta_out[ 6]), .O(porta_in[ 6]), .T(~porta_dir[ 6]));
  BB ioa7  (.B( gpio_mosi ), .I(porta_out[ 7]), .O(porta_in[ 7]), .T(~porta_dir[ 7]));
  BB ioa8  (.B( gpio_miso ), .I(porta_out[ 8]), .O(porta_in[ 8]), .T(~porta_dir[ 8]));
  BB ioa9  (.B( gpio_0    ), .I(porta_out[ 9]), .O(porta_in[ 9]), .T(~porta_dir[ 9]));
  BB ioa10 (.B( gpio_1    ), .I(porta_out[10]), .O(porta_in[10]), .T(~porta_dir[10]));

  // ######   PORTB   ###########################################

  reg  [15:0] portb_dir;   // 1:output, 0:input
  reg  [15:0] portb_out;
  wire [ 8:0] portb_in;

  BB iob0  (.B( gpio_13  ), .I(portb_out[ 0]), .O(portb_in[ 0]), .T(~portb_dir[ 0]));
  BB iob1  (.B( gpio_12  ), .I(portb_out[ 1]), .O(portb_in[ 1]), .T(~portb_dir[ 1]));
  BB iob2  (.B( gpio_11  ), .I(portb_out[ 2]), .O(portb_in[ 2]), .T(~portb_dir[ 2]));
  BB iob3  (.B( gpio_10  ), .I(portb_out[ 3]), .O(portb_in[ 3]), .T(~portb_dir[ 3]));
  BB iob4  (.B( gpio_9   ), .I(portb_out[ 4]), .O(portb_in[ 4]), .T(~portb_dir[ 4]));
  BB iob5  (.B( gpio_6   ), .I(portb_out[ 5]), .O(portb_in[ 5]), .T(~portb_dir[ 5]));
  BB iob6  (.B( gpio_5   ), .I(portb_out[ 6]), .O(portb_in[ 6]), .T(~portb_dir[ 6]));
  BB iob7  (.B( gpio_scl ), .I(portb_out[ 7]), .O(portb_in[ 7]), .T(~portb_dir[ 7]));
  BB iob8  (.B( gpio_sda ), .I(portb_out[ 8]), .O(portb_in[ 8]), .T(~portb_dir[ 8]));

  // ######   LEDs   ##########################################

  reg [2:0] leds = 0;

  reg [15:0] sdm_red,   phase_red;   reg sdm_red_out;   always @(posedge clk) {sdm_red_out,   phase_red}   <= phase_red   + sdm_red;
  reg [15:0] sdm_green, phase_green; reg sdm_green_out; always @(posedge clk) {sdm_green_out, phase_green} <= phase_green + sdm_green;
  reg [15:0] sdm_blue,  phase_blue;  reg sdm_blue_out;  always @(posedge clk) {sdm_blue_out,  phase_blue}  <= phase_blue  + sdm_blue;

  wire red   = leds[0] | (|sdm_red   & sdm_red_out  );
  wire green = leds[1] | (|sdm_green & sdm_green_out);
  wire blue  = leds[2] | (|sdm_blue  & sdm_blue_out );

  assign {rgb_led0_b, rgb_led0_g, rgb_led0_r} = ~{blue, green, red};

  // ######   Sigma-Delta ADC   ###############################

  reg [4:0] adc_mux = 0;
  assign {adc_ctrl1, adc_mux3, adc_mux2, adc_mux1, adc_mux0} = adc_mux;

  wire analog_comparator_state = adc_sense;
  // SB_IO #(.PIN_TYPE(6'b0000_00), .IO_STANDARD("SB_LVDS_INPUT") ) analog_input ( .PACKAGE_PIN(adc_sense), .D_IN_0(analog_comparator_state), .INPUT_CLK(clk) );

  reg analog_out_reg = 0;
  always @(posedge clk) analog_out_reg <= analog_comparator_state;
  assign adc_ctrl0 = analog_out_reg;

  // ######   Sigma-Delta bitstream capture   #################

  reg  [3:0] bitstream_sampling;
  wire [4:0] bitstream_sampling_plus_1 = bitstream_sampling + 1;

  reg  [15:0] bitstream;
  reg  [15:0] bitstream_result;

  always @(posedge clk)
  begin
    if (bitstream_sampling_plus_1[4]) bitstream_result <= bitstream;

    bitstream_sampling <= bitstream_sampling_plus_1;
    bitstream <= {analog_out_reg, bitstream[15:1]};
  end

  // ######   Sigma-Delta decimation filter   #################

  localparam DECBITS = 16; // Maximum is 16

  reg  [DECBITS-1:0] decimated_sampling;
  wire [DECBITS  :0] decimated_sampling_plus_1 = decimated_sampling + 1;

  reg  [15:0] decimated;
  reg  [15:0] decimated_result;

  always @(posedge clk)
  begin
    if (decimated_sampling_plus_1[DECBITS]) decimated_result <= decimated;

    decimated_sampling <= decimated_sampling_plus_1;
    decimated <= decimated_sampling_plus_1[DECBITS] ? /* analog_out_reg */ 0 : decimated + analog_out_reg;
  end                                                 // ^ Can overflow register by one.


  // ######   ADC-FIFO   ########################################

  // Raw sigma-delta bitstream data:

  wire bitstream_fifo_store = bitstream_sampling_plus_1[4];
  wire bitstream_fifo_fetch = io_rd & io_addr[10] & (io_addr[7:4] == 6);

  wire adc_bitstream_valid; wire [15:0] adc_bitstream_fifo;

  adcfifo _bitstream_fifo (
    .clk(clk),
    .resetq(resetq),
    .wr(bitstream_fifo_store),
    .rd(bitstream_fifo_fetch),
    .store_data(bitstream_result),
    .fetch_data(adc_bitstream_fifo),
    .valid(adc_bitstream_valid)
  );

  // Decimated "average" ADC readings:

  wire decimated_fifo_store = decimated_sampling_plus_1[DECBITS];
  wire decimated_fifo_fetch = io_rd & io_addr[10] & (io_addr[7:4] == 2);

  wire adc_decimated_valid; wire [15:0] adc_decimated_fifo;

  adcfifo _decimated_fifo (
    .clk(clk),
    .resetq(resetq),
    .wr(decimated_fifo_store),
    .rd(decimated_fifo_fetch),
    .store_data(decimated_result),
    .fetch_data(adc_decimated_fifo),
    .valid(adc_decimated_valid)
  );

  // ######   USB Terminal   ##################################

  wire usb_valid, usb_ready, usb_configured;
  wire [7:0] usb_data;
  wire usb_wr = io_wr & io_addr[12];
  wire usb_rd = io_rd & io_addr[12];

  usb_cdc #(.IN_BULK_MAXPACKETSIZE('d64), .OUT_BULK_MAXPACKETSIZE('d64), .VENDORID(16'h0483), .PRODUCTID(16'h5740), .USE_APP_CLK(0)) _terminal
  (
    // Part running on 48 MHz:

    .clk_i(clk48),

    .dp_pu_o(usb_pullup_signal),
    .tx_en_o(usb_tx_en),
    .dp_tx_o(usb_p_tx),
    .dn_tx_o(usb_n_tx),
    .dp_rx_i(usb_p_rx),
    .dn_rx_i(usb_n_rx),

    // Part also running on 48 MHz:

    .app_clk_i(clk),
    .rstn_i(usb_resetq), // Keep connection alive.
    .configured_o(usb_configured),

    .out_data_o(usb_data),
    .out_valid_o(usb_valid),
    .out_ready_i(usb_rd),

    .in_data_i(io_dout[7:0]),
    .in_ready_o(usb_ready),
    .in_valid_i(usb_wr)
  );

  wire usb_p_tx;
  wire usb_n_tx;
  wire usb_p_rx;
  wire usb_n_rx;
  wire usb_tx_en;
  wire usb_pullup_signal;

  wire usb_p_in;
  wire usb_n_in;

  assign usb_p_rx = usb_tx_en ? 1'b1 : usb_p_in;
  assign usb_n_rx = usb_tx_en ? 1'b0 : usb_n_in;

  // T = TRISTATE (not transmit)
  BB io_p( .I( usb_p_tx ), .T( !usb_tx_en ), .O( usb_p_in ), .B( usb_d_p ) );
  BB io_n( .I( usb_n_tx ), .T( !usb_tx_en ), .O( usb_n_in ), .B( usb_d_n ) );

  assign usb_pullup = usb_pullup_signal ? 1'b1 : 1'bz;


  // ######   RING OSCILLATOR   ###############################

  wire random_raw;
  ring_osc #( .NUM_LUTS(1) ) chaos ( .osc_out(random_raw), .resetq(resetq) );

  reg random_sync; always @(posedge clk) random_sync <= random_raw;
  reg random;      always @(posedge clk) random      <= random_sync;

  // ######   IO Ports   ######################################

  /*        bit READ            WRITE

    + ...0                      Write as usual
    + ...1                      _C_lear bits
    + ...2                      _S_et bits
    + ...3                      _T_oggle bits

      0008  3   Unused

      0010  4   \
      0020  5    \  Selector bits
      0040  6    /  for further decoding
      0080  7   /

      0100  8   PORT A...
      0200  9   PORT B...
      0400  10  ADC...
      0800  11  LEDs...

      1000  12  UART RX         UART TX
      2000  13  UART Flags
      4000  14  Ticks           Set Ticks
      8000  15  Cycles
  */

  assign io_din =

    (io_addr[ 8] & io_addr[4]          ? porta_in                                   : 16'd0) |
    (io_addr[ 8] & io_addr[5]          ? porta_out                                  : 16'd0) |
    (io_addr[ 8] & io_addr[6]          ? porta_dir                                  : 16'd0) |

    (io_addr[ 9] & io_addr[4]          ? portb_in                                   : 16'd0) |
    (io_addr[ 9] & io_addr[5]          ? portb_out                                  : 16'd0) |
    (io_addr[ 9] & io_addr[6]          ? portb_dir                                  : 16'd0) |

    (io_addr[10] & (io_addr[7:4] == 0) ? decimated_result                           : 16'd0) |
    (io_addr[10] & (io_addr[7:4] == 1) ? adc_decimated_valid                        : 16'd0) |
    (io_addr[10] & (io_addr[7:4] == 2) ? adc_decimated_fifo                         : 16'd0) |
    (io_addr[10] & (io_addr[7:4] == 3) ? adc_mux                                    : 16'd0) |
    (io_addr[10] & (io_addr[7:4] == 4) ? bitstream_result                           : 16'd0) |
    (io_addr[10] & (io_addr[7:4] == 5) ? adc_bitstream_valid                        : 16'd0) |
    (io_addr[10] & (io_addr[7:4] == 6) ? adc_bitstream_fifo                         : 16'd0) |

    (io_addr[11] & (io_addr[7:4] == 0) ? leds                                       : 16'd0) |
    (io_addr[11] & (io_addr[7:4] == 1) ? sdm_red                                    : 16'd0) |
    (io_addr[11] & (io_addr[7:4] == 2) ? sdm_green                                  : 16'd0) |
    (io_addr[11] & (io_addr[7:4] == 3) ? sdm_blue                                   : 16'd0) |

    (io_addr[12] ? { 8'd0, usb_data}                                                : 16'd0) |
    (io_addr[13] ? {12'd0, usb_configured, random, usb_valid, usb_ready}            : 16'd0) |
    (io_addr[14] ?         ticks                                                    : 16'd0) |
    (io_addr[15] ?         cycles                                                   : 16'd0) ;

  wire [15:0] io_modifier = (io_addr[1:0] == 2'b01)    ? ~io_dout & io_din :  // Clear
                            (io_addr[1:0] == 2'b10)    ?  io_dout | io_din :  // Set
                            (io_addr[1:0] == 2'b11)    ?  io_dout ^ io_din :  // Toggle
                         /* (io_addr[1:0] == 2'b00) */    io_dout          ;

  always @(posedge clk)
  begin
    if (io_wr & io_addr[ 8] & io_addr[5])          porta_out <= io_modifier;
    if (io_wr & io_addr[ 8] & io_addr[6])          porta_dir <= io_modifier;
    if (io_wr & io_addr[ 9] & io_addr[5])          portb_out <= io_modifier;
    if (io_wr & io_addr[ 9] & io_addr[6])          portb_dir <= io_modifier;

    if (io_wr & io_addr[10] & (io_addr[7:4] == 3)) adc_mux   <= io_modifier;

    if (io_wr & io_addr[11] & (io_addr[7:4] == 0)) leds      <= io_modifier;
    if (io_wr & io_addr[11] & (io_addr[7:4] == 1)) sdm_red   <= io_modifier;
    if (io_wr & io_addr[11] & (io_addr[7:4] == 2)) sdm_green <= io_modifier;
    if (io_wr & io_addr[11] & (io_addr[7:4] == 3)) sdm_blue  <= io_modifier;
  end

endmodule // top
