
`default_nettype none

`include "pll.v"
`include "ringoscillator-altera.v"
`include "uart-fifo.v"
`include "j1-universal-16kb.v"

module j1a(input  oscillator,

           output TXD,        // UART TX
           input  RXD,        // UART RX

           input KEY0,
           input KEY1,

           output [9:0] LED,

           inout PORTA0,
           inout PORTA1,
           inout PORTA2,
           inout PORTA3,
           inout PORTA4,
           inout PORTA5,
           inout PORTA6,
           inout PORTA7,
           inout PORTA8,
           inout PORTA9,
           inout PORTA10,
           inout PORTA11,
           inout PORTA12,
           inout PORTA13,
           inout PORTA14,
           inout PORTA15,

           output [7:0] HEX0,
           output [7:0] HEX1,
           output [7:0] HEX2,
           output [7:0] HEX3,
           output [7:0] HEX4,
           output [7:0] HEX5
);

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

  wire adc_clk;           // 10 MHz via PLL C0 for ADC
  wire clk;               // 50 MHz main clock for CPU
  wire pll_locked;        // Ready to run ?

  pll _pll (
    .inclk0(oscillator),  // 10 MHz from the debugging section
    .c0(adc_clk),         // 10 MHz
    .c1(clk),             // 50 MHz
    .locked(pll_locked)
  );

  // Note: ADC runs with 10 MHz here and can only use the c0 output of the PLL
  //       as its clock source. The CPU and the other logic runs at 50 MHz,
  //       generated by PLL from the same 10 MHz input into the FPGA.


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

  wire resetanalog_button = pll_locked;

  reg [5:0] resetanalog_cnt = 0;
  wire resetanalogq = &resetanalog_cnt;

  always @(posedge adc_clk) begin
    if (resetanalog_button) resetanalog_cnt <= resetanalog_cnt + !resetanalogq;
    else                    resetanalog_cnt <= 0;
  end

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

  wire reset_button = pll_locked;

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

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

  // ######   ADC   ###########################################

  // Channel selection bits seem random, look them up in
  //   nutsandbolts/chsel_code_converter_sw_to_hw.v

  `define ch01 5'b00100
  `define ch02 5'b00110
  `define ch03 5'b01010
  `define ch04 5'b01100
  `define ch05 5'b10000
  `define ch06 5'b01110

  reg  [4:0] adc_channel = 0;

  wire [4:0] adc_channelN = (adc_channel == `ch01) ? `ch02 :
                            (adc_channel == `ch02) ? `ch03 :
                            (adc_channel == `ch03) ? `ch04 :
                            (adc_channel == `ch04) ? `ch05 :
                            (adc_channel == `ch05) ? `ch06 :
                                                     `ch01 ;

  reg adc_soc = 0;
  wire adc_eoc;
  wire [11:0] adc_dout;

  fiftyfivenm_adcblock_primitive_wrapper #(
    .device_partname_fivechar_prefix ("10M50"), // Select correct FPGA
    .is_this_first_or_second_adc (1),           // Use the first ADC
    .refsel (1)                                 // 0: External reference, 1: Internal 2.5V reference
  ) _adc (

    .clkin_from_pll_c0(adc_clk),  // Clock input through PLL

    .chsel(adc_channel),          // Input   [4:0] Channel selection
    .soc(adc_soc & resetanalogq), // Input         Signal start-of-conversion to ADC
    .tsen(0),                     // Input         Temperature sensing mode
    .usr_pwd(0),                  // Input         User power down. 0 = Power Up; 1 = Power Down.

    .eoc(adc_eoc),                // Output        End of conversion
    .dout(adc_dout)               // Output [11:0] Conversion result
  );

  // Counter to control the stages of the ADC conversion sequence

  reg [3:0] conversioncycle = 0; // From 0 to 15
  always @(posedge adc_clk) conversioncycle <= conversioncycle + 1;

  // Registers holding the most recent conversion results

  reg [15:0] adc_data_ch01 = 0;
  reg [15:0] adc_data_ch02 = 0;
  reg [15:0] adc_data_ch03 = 0;
  reg [15:0] adc_data_ch04 = 0;
  reg [15:0] adc_data_ch05 = 0;
  reg [15:0] adc_data_ch06 = 0;

  // Exponential moving average

  // wire [15:0] adc_data_ch01N = (adc_data_ch01 - (adc_data_ch01 >> 2)) + adc_dout;
  // wire [15:0] adc_data_ch02N = (adc_data_ch02 - (adc_data_ch02 >> 2)) + adc_dout;
  // wire [15:0] adc_data_ch03N = (adc_data_ch03 - (adc_data_ch03 >> 2)) + adc_dout;
  // wire [15:0] adc_data_ch04N = (adc_data_ch04 - (adc_data_ch04 >> 2)) + adc_dout;
  // wire [15:0] adc_data_ch05N = (adc_data_ch05 - (adc_data_ch05 >> 2)) + adc_dout;
  // wire [15:0] adc_data_ch06N = (adc_data_ch06 - (adc_data_ch06 >> 2)) + adc_dout;

  always @(posedge adc_clk) begin

    if (conversioncycle < 4) adc_soc <= 1; else adc_soc <= 0;

    if (adc_eoc & resetanalogq) // Data is valid only while EOC is high.
    begin
      // Store conversion result directly

      if (adc_channel == `ch01) adc_data_ch01 <= adc_dout;
      if (adc_channel == `ch02) adc_data_ch02 <= adc_dout;
      if (adc_channel == `ch03) adc_data_ch03 <= adc_dout;
      if (adc_channel == `ch04) adc_data_ch04 <= adc_dout;
      if (adc_channel == `ch05) adc_data_ch05 <= adc_dout;
      if (adc_channel == `ch06) adc_data_ch06 <= adc_dout;

      // Apply exponential moving average to the conversion result

      // if (adc_channel == `ch01) adc_data_ch01 <= adc_data_ch01N;
      // if (adc_channel == `ch02) adc_data_ch02 <= adc_data_ch02N;
      // if (adc_channel == `ch03) adc_data_ch03 <= adc_data_ch03N;
      // if (adc_channel == `ch04) adc_data_ch04 <= adc_data_ch04N;
      // if (adc_channel == `ch05) adc_data_ch05 <= adc_data_ch05N;
      // if (adc_channel == `ch06) adc_data_ch06 <= adc_data_ch06N;

    end

    if (resetanalogq & (conversioncycle == 14)) adc_channel <= adc_channelN;   // Nächsten Kanal auswählen

    // A little bit time on cycle 15 to settle analog path after switching channels.
  end

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

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

  io_cell ioa0  (.pad(PORTA0 ), .data_in(porta_in[ 0]), .data_out(porta_out[ 0]), .data_out_en(porta_dir[ 0]));
  io_cell ioa1  (.pad(PORTA1 ), .data_in(porta_in[ 1]), .data_out(porta_out[ 1]), .data_out_en(porta_dir[ 1]));
  io_cell ioa2  (.pad(PORTA2 ), .data_in(porta_in[ 2]), .data_out(porta_out[ 2]), .data_out_en(porta_dir[ 2]));
  io_cell ioa3  (.pad(PORTA3 ), .data_in(porta_in[ 3]), .data_out(porta_out[ 3]), .data_out_en(porta_dir[ 3]));
  io_cell ioa4  (.pad(PORTA4 ), .data_in(porta_in[ 4]), .data_out(porta_out[ 4]), .data_out_en(porta_dir[ 4]));
  io_cell ioa5  (.pad(PORTA5 ), .data_in(porta_in[ 5]), .data_out(porta_out[ 5]), .data_out_en(porta_dir[ 5]));
  io_cell ioa6  (.pad(PORTA6 ), .data_in(porta_in[ 6]), .data_out(porta_out[ 6]), .data_out_en(porta_dir[ 6]));
  io_cell ioa7  (.pad(PORTA7 ), .data_in(porta_in[ 7]), .data_out(porta_out[ 7]), .data_out_en(porta_dir[ 7]));
  io_cell ioa8  (.pad(PORTA8 ), .data_in(porta_in[ 8]), .data_out(porta_out[ 8]), .data_out_en(porta_dir[ 8]));
  io_cell ioa9  (.pad(PORTA9 ), .data_in(porta_in[ 9]), .data_out(porta_out[ 9]), .data_out_en(porta_dir[ 9]));
  io_cell ioa10 (.pad(PORTA10), .data_in(porta_in[10]), .data_out(porta_out[10]), .data_out_en(porta_dir[10]));
  io_cell ioa11 (.pad(PORTA11), .data_in(porta_in[11]), .data_out(porta_out[11]), .data_out_en(porta_dir[11]));
  io_cell ioa12 (.pad(PORTA12), .data_in(porta_in[12]), .data_out(porta_out[12]), .data_out_en(porta_dir[12]));
  io_cell ioa13 (.pad(PORTA13), .data_in(porta_in[13]), .data_out(porta_out[13]), .data_out_en(porta_dir[13]));
  io_cell ioa14 (.pad(PORTA14), .data_in(porta_in[14]), .data_out(porta_out[14]), .data_out_en(porta_dir[14]));
  io_cell ioa15 (.pad(PORTA15), .data_in(porta_in[15]), .data_out(porta_out[15]), .data_out_en(porta_dir[15]));

  // ######   Random number generator    ######################

  wire random;

  ring_osc #(.NUM_LUTS(2)) chaos (.resetq(resetq), .osc_out(random));

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

  // ######   Terminal   ######################################

  wire uart0_valid, uart0_busy;
  wire [7:0] uart0_data;
  wire uart0_wr = io_wr & io_addr[12];
  wire uart0_rd = io_rd & io_addr[12];

  buart _uart0 (
     .clk(clk),
     .resetq(resetq),
     .rx(RXD),
     .tx(TXD),
     .rd(uart0_rd),
     .wr(uart0_wr),
     .valid(uart0_valid),
     .busy(uart0_busy),
     .tx_data(io_dout[7:0]),
     .rx_data(uart0_data));

  // ######   Blinky lights   #################################

  reg [9:0] LEDs = 0;

  assign LED = LEDs;

  reg [15:0] hexA = 0;
  reg [15:0] hexB = 0;
  reg [15:0] hexC = 0;

  assign {HEX1, HEX0} = ~hexA;
  assign {HEX3, HEX2} = ~hexB;
  assign {HEX5, HEX4} = ~hexC;

  // ######   Reconfiguration   ###############################

  reg reconfigure = 0;

  wire rublock_answer; // Unused.

  fiftyfivenm_rublock _reconfiguration
  (
    .rconfig(reconfigure | ((KEY0 == 0) & (KEY1 == 0))), // High-Level triggers reconfiguration

    .clk(clk),       // Clock for module is mandatory
    .rsttimer(1),   // Watchdog-Timer keeps cleared
    .shiftnld(0),  // No data to transfer
    .captnupdt(0),
    .regin(0),
    .regout(rublock_answer)
  );

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

  /*        bit READ            WRITE

      0100  8   Port A In
      0200  9   Port A Out      Port A Out
      0400  10  Port A Dir      Port A Dir
      0800  11

      1000  12  UART RX         UART TX

      2000  13  UART Flags
      2010      LEDs            LEDs
      2020      hexA            hexA
      2030      hexB            hexB
      2040      hexC            hexC
      2050      Keys
      2080      ADC Ch 1
      2090      ADC Ch 2
      20A0      ADC Ch 3
      20B0      ADC Ch 4
      20C0      ADC Ch 5
      20D0      ADC Ch 6

      4000  14  Ticks           Set Ticks
      8000  15                  Reset
  */

  assign io_din =

    (io_addr[ 8] ? porta_in                                                         : 16'd0) |
    (io_addr[ 9] ? porta_out                                                        : 16'd0) |
    (io_addr[10] ? porta_dir                                                        : 16'd0) |

    (io_addr[12] ? {8'd0, uart0_data}                                               : 16'd0) |

    (io_addr[13] & (io_addr[7:4] ==  0) ? {random, uart0_valid, !uart0_busy}        : 16'd0) |
    (io_addr[13] & (io_addr[7:4] ==  1) ? LEDs                                      : 16'd0) |
    (io_addr[13] & (io_addr[7:4] ==  2) ? hexA                                      : 16'd0) |
    (io_addr[13] & (io_addr[7:4] ==  3) ? hexB                                      : 16'd0) |
    (io_addr[13] & (io_addr[7:4] ==  4) ? hexC                                      : 16'd0) |
    (io_addr[13] & (io_addr[7:4] ==  5) ? {KEY1, KEY0}                              : 16'd0) |
    (io_addr[13] & (io_addr[7:4] ==  8) ? adc_data_ch01                             : 16'd0) |
    (io_addr[13] & (io_addr[7:4] ==  9) ? adc_data_ch02                             : 16'd0) |
    (io_addr[13] & (io_addr[7:4] == 10) ? adc_data_ch03                             : 16'd0) |
    (io_addr[13] & (io_addr[7:4] == 11) ? adc_data_ch04                             : 16'd0) |
    (io_addr[13] & (io_addr[7:4] == 12) ? adc_data_ch05                             : 16'd0) |
    (io_addr[13] & (io_addr[7:4] == 13) ? adc_data_ch06                             : 16'd0) |

    (io_addr[14] ?         ticks                                                    : 16'd0) ;

  always @(posedge clk) begin

    if (io_wr & io_addr[ 9]) porta_out <= io_dout;
    if (io_wr & io_addr[10]) porta_dir <= io_dout;

    if (io_wr & io_addr[13] & (io_addr[7:4] ==  1))  LEDs <= io_dout;
    if (io_wr & io_addr[13] & (io_addr[7:4] ==  2))  hexA <= io_dout;
    if (io_wr & io_addr[13] & (io_addr[7:4] ==  3))  hexB <= io_dout;
    if (io_wr & io_addr[13] & (io_addr[7:4] ==  4))  hexC <= io_dout;

    if (io_wr & io_addr[15]) reconfigure <= io_dout;

  end

endmodule // j1a


  // ######   Bidirectional Pins #############################

module  io_cell (
  inout pad,                       // I/O Pad
 output data_in,                   // Input value
  input data_out_en,               // Output enable
  input data_out                   // Output value
);

assign  data_in  =  pad;
assign  pad      =  data_out_en ? data_out : 1'bz;

endmodule // io_cell
