
/******************************************************************************/
// A lot of MSP430 on the ULX3S
/******************************************************************************/

`default_nettype none // Makes it easier to detect typos !

`include "../../common-verilog/femtomsp430.v"

`include "../../common-verilog/uart-fifo.v"
`include "../../common-verilog/ringoscillator-ecp5.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"

`include "../../common-verilog/TMDS_encoder.v"

module ulx3s(

  input clk_25mhz,
  input [6:0] btn,
   output [7:0] led,

  output wifi_gpio0,
  output wifi_en,

  inout [27:0] gp,
  inout [27:0] gn,

  output flash_csn,
  // output flash_clk, // This is a special pin on ECP5 and requires using the USRMCLK macro.
  output flash_mosi,   // IO0
  input  flash_miso,   // IO1
  output flash_wpn,    // IO2
  output flash_holdn,  // IO3

  output adc_sclk,
  output adc_csn,
  output adc_mosi,
  input  adc_miso,

  output sd_cmd,
  output sd_clk,
  output sd_d3,
  input  sd_d0,

  output [3:0] audio_l,
  output [3:0] audio_r,
  output [3:0] audio_v,

  inout oled_clk,
  inout oled_mosi,
  inout oled_resn,
  inout oled_dc,
  inout oled_csn,

  output ftdi_rxd, // UART TX
  input  ftdi_txd, // UART RX

  inout  usb_fpga_bd_dp,
  inout  usb_fpga_bd_dn,
  output usb_fpga_pu_dp,

  output [3:0] gpdi_dp   // 0: blue 1: green 2: red 3: pixel clock
                         // gpdi_dn[3:0] generated automatically
                         // using IO_TYPE=LVCMOS33D in ulx3s_v20.lpf
                         // (D=Differential)
);

  // ---------------------------------------------------------------------------
  // Clock.
  // ---------------------------------------------------------------------------

  wire clk = pixclk; // Use pixel clock as main clock
  wire pll_locked40 = pixclk_locked;
  // pll40 _pll_clk( .clkin(clk_25mhz), .clkout0(clk), .locked(pll_locked40) );  // 40 MHz

  wire clk_48mhz;
  wire pll_locked48;
  pll48 _pll_usb( .clkin(clk_25mhz), .clkout0(clk_48mhz), .locked(pll_locked48) );  // 48 MHz

  wire pll_locked = pll_locked40 & pll_locked48;

  // Tie GPIO0 high, keep board from rebooting
  assign wifi_gpio0 = 1;

  // Cut off wifi_en
  assign wifi_en = 0;

  // ---------------------------------------------------------------------------
  // Reset logic.
  // ---------------------------------------------------------------------------

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

  always @(posedge clk)
  begin                           // Trigger Reset on invalid watchdog write, too
    if (~(btn[0] & pll_locked) | (io_wstrb & WDTCTL & (mem_wdata[15:8] != 8'h5A)) )
         reset_cnt <= 0;
    else reset_cnt <= reset_cnt + !resetq;
  end

  // ---------------------------------------------------------------------------
  // LEDs.
  // ---------------------------------------------------------------------------

  wire [7:0] leds_in;
  reg  [7:0] leds_out;
  reg  [7:0] leds_dir = 8'hFF;   // 1:output, 0:input

  BB iol0  (.B(led[0]), .I(leds_out[ 0]), .O(leds_in[ 0]), .T(~leds_dir[ 0]));
  BB iol1  (.B(led[1]), .I(leds_out[ 1]), .O(leds_in[ 1]), .T(~leds_dir[ 1]));
  BB iol2  (.B(led[2]), .I(leds_out[ 2]), .O(leds_in[ 2]), .T(~leds_dir[ 2]));
  BB iol3  (.B(led[3]), .I(leds_out[ 3]), .O(leds_in[ 3]), .T(~leds_dir[ 3]));
  BB iol4  (.B(led[4]), .I(leds_out[ 4]), .O(leds_in[ 4]), .T(~leds_dir[ 4]));
  BB iol5  (.B(led[5]), .I(leds_out[ 5]), .O(leds_in[ 5]), .T(~leds_dir[ 5]));
  BB iol6  (.B(led[6]), .I(leds_out[ 6]), .O(leds_in[ 6]), .T(~leds_dir[ 6]));
  BB iol7  (.B(led[7]), .I(leds_out[ 7]), .O(leds_in[ 7]), .T(~leds_dir[ 7]));

  // ---------------------------------------------------------------------------
  // Ring oscillator for random numbers.
  // ---------------------------------------------------------------------------

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

  // ---------------------------------------------------------------------------
  // Timer with interrupt.
  // ---------------------------------------------------------------------------

  reg  interrupt = 0;
  reg  [15:0] ticks;
  wire [16:0] ticks_plus_1 = ticks + 1;
  reg  [15:0] reload = 0;
  wire [15:0] next_ticks = ticks_plus_1[16] ? reload[15:0] : ticks_plus_1[15:0];

  always @(posedge clk)
  begin
    if (io_wstrb & TICKS)  ticks  <= mem_wdata; else ticks <= next_ticks;
    if (io_wstrb & RELOAD) reload <= mem_wdata;

    interrupt <= ticks_plus_1[16]; // Generate interrupt on ticks overflow
  end

  // ---------------------------------------------------------------------------
  // Cycle counter.
  // ---------------------------------------------------------------------------

  reg [15:0] cycles;
  always @(posedge clk) cycles <= cycles + 1;

  // ---------------------------------------------------------------------------
  // UART.
  // ---------------------------------------------------------------------------

  wire serial_valid, serial_busy;
  wire [7:0] serial_data;

  wire serial_wr = io_wstrb & UCA0TXBUF;
  wire serial_rd = io_rstrb & UCA0RXBUF;

  buart #(
    .FREQ_MHZ(40),
    .BAUDS(115200)
  ) buart (
     .clk(clk),
     .resetq(resetq),
     .rx(ftdi_txd),
     .tx(ftdi_rxd),
     .rd(serial_rd),
     .wr(serial_wr),
     .valid(serial_valid),
     .busy(serial_busy),
     .tx_data(mem_wdata[7:0]),
     .rx_data(serial_data)
  );

  // ---------------------------------------------------------------------------
  // USB Terminal.
  // ---------------------------------------------------------------------------

  wire usb_valid, usb_ready, usb_configured;
  wire [7:0] usb_data;
  wire usb_wr = io_wstrb & UCB0TXBUF;
  wire usb_rd = io_rstrb & UCB0RXBUF;

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

    .clk_i(clk_48mhz),

    .dp_pu_o(usb_pullup),
    .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 running on 40 MHz:

    .app_clk_i(clk),
    .rstn_i(pll_locked), // Keep connection alive, only reset when PLL is trying to lock.
    .configured_o(usb_configured),

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

    .in_data_i(mem_wdata[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;

  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_fpga_bd_dp ) );
  BB io_n( .I( usb_n_tx ), .T( !usb_tx_en ), .O( usb_n_in ), .B( usb_fpga_bd_dn ) );

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

  // ---------------------------------------------------------------------------
  // Port A
  // ---------------------------------------------------------------------------

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

  BB ioa0  (.B(gp[ 0]), .I(porta_out[ 0]), .O(porta_in[ 0]), .T(~porta_dir[ 0]));
  BB ioa1  (.B(gp[ 1]), .I(porta_out[ 1]), .O(porta_in[ 1]), .T(~porta_dir[ 1]));
  BB ioa2  (.B(gp[ 2]), .I(porta_out[ 2]), .O(porta_in[ 2]), .T(~porta_dir[ 2]));
  BB ioa3  (.B(gp[ 3]), .I(porta_out[ 3]), .O(porta_in[ 3]), .T(~porta_dir[ 3]));
  BB ioa4  (.B(gp[ 4]), .I(porta_out[ 4]), .O(porta_in[ 4]), .T(~porta_dir[ 4]));
  BB ioa5  (.B(gp[ 5]), .I(porta_out[ 5]), .O(porta_in[ 5]), .T(~porta_dir[ 5]));
  BB ioa6  (.B(gp[ 6]), .I(porta_out[ 6]), .O(porta_in[ 6]), .T(~porta_dir[ 6]));
  BB ioa7  (.B(gp[ 7]), .I(porta_out[ 7]), .O(porta_in[ 7]), .T(~porta_dir[ 7]));
  BB ioa8  (.B(gp[ 8]), .I(porta_out[ 8]), .O(porta_in[ 8]), .T(~porta_dir[ 8]));
  BB ioa9  (.B(gp[ 9]), .I(porta_out[ 9]), .O(porta_in[ 9]), .T(~porta_dir[ 9]));
  BB ioa10 (.B(gp[10]), .I(porta_out[10]), .O(porta_in[10]), .T(~porta_dir[10]));
  BB ioa11 (.B(gp[11]), .I(porta_out[11]), .O(porta_in[11]), .T(~porta_dir[11]));
  BB ioa12 (.B(gp[12]), .I(porta_out[12]), .O(porta_in[12]), .T(~porta_dir[12]));
  BB ioa13 (.B(gp[13]), .I(porta_out[13]), .O(porta_in[13]), .T(~porta_dir[13]));

  // ---------------------------------------------------------------------------
  // Port B
  // ---------------------------------------------------------------------------

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

  BB iob0  (.B(gn[ 0]), .I(portb_out[ 0]), .O(portb_in[ 0]), .T(~portb_dir[ 0]));
  BB iob1  (.B(gn[ 1]), .I(portb_out[ 1]), .O(portb_in[ 1]), .T(~portb_dir[ 1]));
  BB iob2  (.B(gn[ 2]), .I(portb_out[ 2]), .O(portb_in[ 2]), .T(~portb_dir[ 2]));
  BB iob3  (.B(gn[ 3]), .I(portb_out[ 3]), .O(portb_in[ 3]), .T(~portb_dir[ 3]));
  BB iob4  (.B(gn[ 4]), .I(portb_out[ 4]), .O(portb_in[ 4]), .T(~portb_dir[ 4]));
  BB iob5  (.B(gn[ 5]), .I(portb_out[ 5]), .O(portb_in[ 5]), .T(~portb_dir[ 5]));
  BB iob6  (.B(gn[ 6]), .I(portb_out[ 6]), .O(portb_in[ 6]), .T(~portb_dir[ 6]));
  BB iob7  (.B(gn[ 7]), .I(portb_out[ 7]), .O(portb_in[ 7]), .T(~portb_dir[ 7]));
  BB iob8  (.B(gn[ 8]), .I(portb_out[ 8]), .O(portb_in[ 8]), .T(~portb_dir[ 8]));
  BB iob9  (.B(gn[ 9]), .I(portb_out[ 9]), .O(portb_in[ 9]), .T(~portb_dir[ 9]));
  BB iob10 (.B(gn[10]), .I(portb_out[10]), .O(portb_in[10]), .T(~portb_dir[10]));
  BB iob11 (.B(gn[11]), .I(portb_out[11]), .O(portb_in[11]), .T(~portb_dir[11]));
  BB iob12 (.B(gn[12]), .I(portb_out[12]), .O(portb_in[12]), .T(~portb_dir[12]));
  BB iob13 (.B(gn[13]), .I(portb_out[13]), .O(portb_in[13]), .T(~portb_dir[13]));

  // ---------------------------------------------------------------------------
  // Port C
  // ---------------------------------------------------------------------------

  wire [13:0] portc_in;
  reg  [13:0] portc_out;
  reg  [13:0] portc_dir;   // 1:output, 0:input

  BB ioc0  (.B(gn[14]), .I(portc_out[ 0]), .O(portc_in[ 0]), .T(~portc_dir[ 0]));
  BB ioc1  (.B(gn[15]), .I(portc_out[ 1]), .O(portc_in[ 1]), .T(~portc_dir[ 1]));
  BB ioc2  (.B(gn[16]), .I(portc_out[ 2]), .O(portc_in[ 2]), .T(~portc_dir[ 2]));
  BB ioc3  (.B(gn[17]), .I(portc_out[ 3]), .O(portc_in[ 3]), .T(~portc_dir[ 3]));
  BB ioc4  (.B(gn[18]), .I(portc_out[ 4]), .O(portc_in[ 4]), .T(~portc_dir[ 4]));
  BB ioc5  (.B(gn[19]), .I(portc_out[ 5]), .O(portc_in[ 5]), .T(~portc_dir[ 5]));
  BB ioc6  (.B(gn[20]), .I(portc_out[ 6]), .O(portc_in[ 6]), .T(~portc_dir[ 6]));
  BB ioc7  (.B(gn[21]), .I(portc_out[ 7]), .O(portc_in[ 7]), .T(~portc_dir[ 7]));
  BB ioc8  (.B(gn[22]), .I(portc_out[ 8]), .O(portc_in[ 8]), .T(~portc_dir[ 8]));
  BB ioc9  (.B(gn[23]), .I(portc_out[ 9]), .O(portc_in[ 9]), .T(~portc_dir[ 9]));
  BB ioc10 (.B(gn[24]), .I(portc_out[10]), .O(portc_in[10]), .T(~portc_dir[10]));
  BB ioc11 (.B(gn[25]), .I(portc_out[11]), .O(portc_in[11]), .T(~portc_dir[11]));
  BB ioc12 (.B(gn[26]), .I(portc_out[12]), .O(portc_in[12]), .T(~portc_dir[12]));
  BB ioc13 (.B(gn[27]), .I(portc_out[13]), .O(portc_in[13]), .T(~portc_dir[13]));

  // ---------------------------------------------------------------------------
  // Port D
  // ---------------------------------------------------------------------------

  wire [13:0] portd_in;
  reg  [13:0] portd_out;
  reg  [13:0] portd_dir;   // 1:output, 0:input

  BB iod0  (.B(gp[14]), .I(portd_out[ 0]), .O(portd_in[ 0]), .T(~portd_dir[ 0]));
  BB iod1  (.B(gp[15]), .I(portd_out[ 1]), .O(portd_in[ 1]), .T(~portd_dir[ 1]));
  BB iod2  (.B(gp[16]), .I(portd_out[ 2]), .O(portd_in[ 2]), .T(~portd_dir[ 2]));
  BB iod3  (.B(gp[17]), .I(portd_out[ 3]), .O(portd_in[ 3]), .T(~portd_dir[ 3]));
  BB iod4  (.B(gp[18]), .I(portd_out[ 4]), .O(portd_in[ 4]), .T(~portd_dir[ 4]));
  BB iod5  (.B(gp[19]), .I(portd_out[ 5]), .O(portd_in[ 5]), .T(~portd_dir[ 5]));
  BB iod6  (.B(gp[20]), .I(portd_out[ 6]), .O(portd_in[ 6]), .T(~portd_dir[ 6]));
  BB iod7  (.B(gp[21]), .I(portd_out[ 7]), .O(portd_in[ 7]), .T(~portd_dir[ 7]));
  BB iod8  (.B(gp[22]), .I(portd_out[ 8]), .O(portd_in[ 8]), .T(~portd_dir[ 8]));
  BB iod9  (.B(gp[23]), .I(portd_out[ 9]), .O(portd_in[ 9]), .T(~portd_dir[ 9]));
  BB iod10 (.B(gp[24]), .I(portd_out[10]), .O(portd_in[10]), .T(~portd_dir[10]));
  BB iod11 (.B(gp[25]), .I(portd_out[11]), .O(portd_in[11]), .T(~portd_dir[11]));
  BB iod12 (.B(gp[26]), .I(portd_out[12]), .O(portd_in[12]), .T(~portd_dir[12]));
  BB iod13 (.B(gp[27]), .I(portd_out[13]), .O(portd_in[13]), .T(~portd_dir[13]));

  // ---------------------------------------------------------------------------
  // OLED.
  // ---------------------------------------------------------------------------

  wire [4:0] oled_in;
  reg  [4:0] oled_out;
  reg  [4:0] oled_dir;   // 1:output, 0:input

  BBPU oled0  (.B(oled_clk  ), .I(oled_out[ 0]), .O(oled_in[ 0]), .T(~oled_dir[ 0]));
  BBPU oled1  (.B(oled_mosi ), .I(oled_out[ 1]), .O(oled_in[ 1]), .T(~oled_dir[ 1]));
  BBPU oled2  (.B(oled_resn ), .I(oled_out[ 2]), .O(oled_in[ 2]), .T(~oled_dir[ 2]));
  BBPU oled3  (.B(oled_dc   ), .I(oled_out[ 3]), .O(oled_in[ 3]), .T(~oled_dir[ 3]));
  BBPU oled4  (.B(oled_csn  ), .I(oled_out[ 4]), .O(oled_in[ 4]), .T(~oled_dir[ 4]));

  // ---------------------------------------------------------------------------
  // SPI Flash.
  // ---------------------------------------------------------------------------

  wire flash_in = flash_miso;
  reg [2:0] flash_out;

  wire flash_clk;
  assign {flash_clk, flash_mosi, flash_csn} = flash_out;

  // A special macro is necessary to access the clock wire of the SPI flash memory chip
  wire untristate = 0;
  USRMCLK mclk (.USRMCLKTS(untristate), .USRMCLKI(flash_clk));

  assign flash_wpn   = 1;
  assign flash_holdn = 1;

  // ---------------------------------------------------------------------------
  // ADC.
  // ---------------------------------------------------------------------------

  wire      adc_in = adc_miso;
  reg [2:0] adc_out;

  assign {adc_csn, adc_sclk, adc_mosi} = adc_out;

  // ---------------------------------------------------------------------------
  // SD-Card.
  // ---------------------------------------------------------------------------

  wire       sd_in = sd_d0; // MISO
  reg  [2:0] sd_out;

  assign {sd_d3, sd_clk, sd_cmd} = sd_out[2:0]; // CS, SCLK, MOSI

  // ---------------------------------------------------------------------------
  // Analog out.
  // ---------------------------------------------------------------------------

  reg [11:0] analog_out;

  assign audio_l = analog_out[3:0];
  assign audio_r = analog_out[7:4];
  assign audio_v = analog_out[11:8];

 // ---------------------------------------------------------------------------
 // IO Ports.
 // ---------------------------------------------------------------------------

//wire IFG1      = byte_io & (mem_address[7:0] == 8'h02);
  wire IFG2      = byte_io & (mem_address[7:0] == 8'h03);

  wire UCA0RXBUF = byte_io & (mem_address[7:0] == 8'h66);
  wire UCA0TXBUF = byte_io & (mem_address[7:0] == 8'h67);

  wire UCB0RXBUF = byte_io & (mem_address[7:0] == 8'h6E);
  wire UCB0TXBUF = byte_io & (mem_address[7:0] == 8'h6F);

  wire P1IN      = byte_io & (mem_address[7:0] == 8'h20);
  wire P1OUT     = byte_io & (mem_address[7:0] == 8'h21);
  wire P1DIR     = byte_io & (mem_address[7:0] == 8'h22);

  wire P2IN      = byte_io & (mem_address[7:0] == 8'h28);
  wire P2OUT     = byte_io & (mem_address[7:0] == 8'h29);
  wire P2DIR     = byte_io & (mem_address[7:0] == 8'h2A);

  wire P3IN      = byte_io & (mem_address[7:0] == 8'h18);
//wire P3OUT     = byte_io & (mem_address[7:0] == 8'h19);
//wire P3DIR     = byte_io & (mem_address[7:0] == 8'h1A);

  wire P4IN      = byte_io & (mem_address[7:0] == 8'h1C);
//wire P4OUT     = byte_io & (mem_address[7:0] == 8'h1D);
//wire P4DIR     = byte_io & (mem_address[7:0] == 8'h1E);

  wire ADCIN     = byte_io & (mem_address[7:0] == 8'hF0);
  wire ADCOUT    = byte_io & (mem_address[7:0] == 8'hF1);

  wire SDIN      = byte_io & (mem_address[7:0] == 8'hF2);
  wire SDOUT     = byte_io & (mem_address[7:0] == 8'hF3);

  wire SPIIN     = byte_io & (mem_address[7:0] == 8'hF4);
  wire SPIOUT    = byte_io & (mem_address[7:0] == 8'hF5);


  wire WDTCTL    = mem_address == 16'h0120;

  wire PAIN      = mem_address == 16'h01A0;
  wire PAOUT     = mem_address == 16'h01A1;
  wire PADIR     = mem_address == 16'h01A2;

  wire PBIN      = mem_address == 16'h01B0;
  wire PBOUT     = mem_address == 16'h01B1;
  wire PBDIR     = mem_address == 16'h01B2;

  wire PCIN      = mem_address == 16'h01C0;
  wire PCOUT     = mem_address == 16'h01C1;
  wire PCDIR     = mem_address == 16'h01C2;

  wire PDIN      = mem_address == 16'h01D0;
  wire PDOUT     = mem_address == 16'h01D1;
  wire PDDIR     = mem_address == 16'h01D2;

  wire CYCLES    = mem_address == 16'h01F0;
  wire TICKS     = mem_address == 16'h01F2;
  wire RELOAD    = mem_address == 16'h01F4;
  wire ANAOUT    = mem_address == 16'h01F6;

  wire [7:0] io_rdata_byte =

    (IFG2      ? {usb_configured, usb_ready, usb_valid, ~serial_busy, serial_valid} : 8'd0) |
    (UCA0RXBUF ? serial_data                                                        : 8'd0) |
    (UCB0RXBUF ? usb_data                                                           : 8'd0) |

    (P1IN      ? oled_in                                                            : 8'd0) |
    (P1OUT     ? oled_out                                                           : 8'd0) |
    (P1DIR     ? oled_dir                                                           : 8'd0) |

    (P2IN      ? leds_in                                                            : 8'd0) |
    (P2OUT     ? leds_out                                                           : 8'd0) |
    (P2DIR     ? leds_dir                                                           : 8'd0) |

    (P3IN      ? btn                                                                : 8'd0) |

    (P4IN      ? random                                                             : 8'd0) |

    (ADCIN     ? adc_in                                                             : 8'd0) |
    (ADCOUT    ? adc_out                                                            : 8'd0) |
    (SDIN      ? sd_in                                                              : 8'd0) |
    (SDOUT     ? sd_out                                                             : 8'd0) |
    (SPIIN     ? flash_in                                                           : 8'd0) |
    (SPIOUT    ? flash_out                                                          : 8'd0) ;

  wire [15:0] io_rdata =

    (PAIN      ? porta_in              : 16'h0000) |
    (PAOUT     ? porta_out             : 16'h0000) |
    (PADIR     ? porta_dir             : 16'h0000) |

    (PBIN      ? portb_in              : 16'h0000) |
    (PBOUT     ? portb_out             : 16'h0000) |
    (PBDIR     ? portb_dir             : 16'h0000) |

    (PCIN      ? portc_in              : 16'h0000) |
    (PCOUT     ? portc_out             : 16'h0000) |
    (PCDIR     ? portc_dir             : 16'h0000) |

    (PDIN      ? portd_in              : 16'h0000) |
    (PDOUT     ? portd_out             : 16'h0000) |
    (PDDIR     ? portd_dir             : 16'h0000) |

    (CYCLES    ? cycles                : 16'h0000) |
    (TICKS     ? ticks                 : 16'h0000) |
    (RELOAD    ? reload                : 16'h0000) |
    (ANAOUT    ? analog_out            : 16'h0000) |

    (byte_io ? {io_rdata_byte, io_rdata_byte} : 16'h0000) ; // Trick to coax a word-adressed CPU into byte access

  always @(posedge clk)
  begin
    if (io_wstrb & P1OUT)  oled_out   <= mem_wdata[7:0];
    if (io_wstrb & P1DIR)  oled_dir   <= mem_wdata[7:0];

    if (io_wstrb & P2OUT)  leds_out   <= mem_wdata[7:0];
    if (io_wstrb & P2DIR)  leds_dir   <= mem_wdata[7:0];

    if (io_wstrb & SDOUT)  sd_out     <= mem_wdata[7:0];
    if (io_wstrb & ADCOUT) adc_out    <= mem_wdata[7:0];
    if (io_wstrb & SPIOUT) flash_out  <= mem_wdata[7:0];

    if (io_wstrb & PAOUT)  porta_out  <= mem_wdata;
    if (io_wstrb & PADIR)  porta_dir  <= mem_wdata;

    if (io_wstrb & PBOUT)  portb_out  <= mem_wdata;
    if (io_wstrb & PBDIR)  portb_dir  <= mem_wdata;

    if (io_wstrb & PCOUT)  portc_out  <= mem_wdata;
    if (io_wstrb & PCDIR)  portc_dir  <= mem_wdata;

    if (io_wstrb & PDOUT)  portd_out  <= mem_wdata;
    if (io_wstrb & PDDIR)  portd_dir  <= mem_wdata;

    if (io_wstrb & ANAOUT) analog_out <= mem_wdata;
  end

  // The processor reads the contents one clock cycle after the read strobe has been active.
  // Buffering it for getting IO register contents of the read strobe cycle.

  reg  [15:0] io_rdata_buffered;

  always @(posedge clk)
     if (mem_address_is_io & io_rstrb) io_rdata_buffered <= io_rdata;

  // ---------------------------------------------------------------------------
  // The memory bus.
  // ---------------------------------------------------------------------------

  wire [15:0] mem_address; // 16 bits are used internally. The LSB is ignored (using word addresses)
  wire  [1:0] mem_wmask;   // mem write mask and strobe
  wire [15:0] mem_rdata;   // processor <- (mem and peripherals)
  wire [15:0] mem_wdata;   // processor -> (mem and peripherals)
  wire        mem_rstrb;   // mem read strobe. Goes high to initiate memory read.
  wire        mem_busy;    // processor <- (mem and peripherals). Stays high until a read or write transfer is finished.

  // ---------------------------------------------------------------------------
  // The processor.
  // ---------------------------------------------------------------------------

  femtomsp430 processor
  (
    .clk(clk),
    .mem_addr(mem_address),
    .mem_wdata(mem_wdata),
    .mem_wmask(mem_wmask),
    .mem_rdata(mem_rdata),
    .mem_rstrb(mem_rstrb),
    .mem_busy (mem_busy),
    .interrupt_request(interrupt),
    .resetq(resetq)
  );

  assign mem_busy = (mem_address_is_char & char_rbusy);

  // ---------------------------------------------------------------------------
  // Memory and register access control wires.
  // ---------------------------------------------------------------------------

  wire mem_wstrb = |mem_wmask; // mem write strobe, goes high to initiate memory write (deduced from wmask)

  // RAM, IO or character memory ?

  wire           byte_io   = (mem_address < 16'h0100);        // $0000 - $00FF
  wire mem_address_is_io   = (mem_address < 16'h0200);        // $0100 - $01FF
  wire mem_address_is_char = (mem_address[15:13] == 3'b011);  // $6000 - $7FFF
  wire mem_address_is_ram  = ~(mem_address_is_io | mem_address_is_char); // Everything else

  wire io_rstrb = mem_rstrb & mem_address_is_io;
  wire io_wstrb = mem_wstrb & mem_address_is_io;

  // ---------------------------------------------------------------------------
  // RAM.
  // ---------------------------------------------------------------------------

  reg  [15:0] RAM[(64*1024/2)-1:0]; initial $readmemh("memimage-msp430g2755-with-tools.hex", RAM);
  reg  [15:0] ram_rdata;

  always @(posedge clk) begin

    if(mem_wmask[0] & mem_address_is_ram) RAM[mem_address[15:1]][ 7:0 ] <= mem_wdata[ 7:0 ];
    if(mem_wmask[1] & mem_address_is_ram) RAM[mem_address[15:1]][15:8 ] <= mem_wdata[15:8 ];

    ram_rdata <= RAM[mem_address[15:1]];
  end

  // ---------------------------------------------------------------------------
  // Connect the read wires of memories and IO registers to the memory bus.
  // ---------------------------------------------------------------------------

  assign mem_rdata =

     (mem_address_is_char      ? char_rdata             : 16'd0) |
     (mem_address_is_io        ? io_rdata_buffered      : 16'd0) |
     (mem_address_is_ram       ? ram_rdata              : 16'd0) ;

  // ---------------------------------------------------------------------------
  // Video mode constants and clocks
  // ---------------------------------------------------------------------------

  // The video signal generator is based on HDMI experiments by Bruno Levy:
  // https://github.com/BrunoLevy/learn-fpga/tree/master/Basic/ULX3S/ULX3S_hdmi

  wire pixclk;        // Pixel clock
  wire pixclk_locked; // Pixel clock locked signal
  wire half_clk_TMDS; // TMDS clock at half freq (5*pixclk)

  // Select mode by uncommenting one of the lines below
  // Note UART frequency divider and USB app clock frequency also needs to be changed.

   `define MODE_800x600_40MHz
// `define MODE_800x600_50MHz


`ifdef MODE_800x600_40MHz // SVGA 800x600 @ 60 Hz
   // localparam GFX_pixel_clock   = 40;
   localparam GFX_width         = 800;
   localparam GFX_height        = 600;
   localparam GFX_h_front_porch = 40;
   localparam GFX_h_sync_width  = 128;
   localparam GFX_h_back_porch  = 88;
   localparam GFX_v_front_porch = 1;
   localparam GFX_v_sync_width  = 4;
   localparam GFX_v_back_porch  = 23;

  // Parameters of the PLL, found using: ecppll -i 25 -o 200 -f foobar.v
   localparam CLKI_DIV = 1;
   localparam CLKOP_DIV = 3;
   localparam CLKOP_CPHASE = 1;
   localparam CLKOP_FPHASE = 0;
   localparam CLKFB_DIV = 8;
`endif

`ifdef MODE_800x600_50MHz // VESA 800x600 @ 72 Hz
   // localparam GFX_pixel_clock   = 50;
   localparam GFX_width         = 800;
   localparam GFX_height        = 600;
   localparam GFX_h_front_porch = 56;
   localparam GFX_h_sync_width  = 120;
   localparam GFX_h_back_porch  = 64;
   localparam GFX_v_front_porch = 37;
   localparam GFX_v_sync_width  = 6;
   localparam GFX_v_back_porch  = 23;

  // Parameters of the PLL, found using: ecppll -i 25 -o 250 -f foobar.v
   localparam CLKI_DIV = 1;
   localparam CLKOP_DIV = 2;
   localparam CLKOP_CPHASE = 0;
   localparam CLKOP_FPHASE = 0;
   localparam CLKFB_DIV = 10;
`endif

  // ---------------------------------------------------------------------------
  // The PLL
  // ---------------------------------------------------------------------------

  // The PLL converts a 25 MHz clock into a (pixel_clock_freq * 5) clock
  // The TMDS serializers operate at (pixel_clock_freq * 10), but we use
  // DDR mode, hence (pixel_clock_freq * 5).
  // The (half) TMDS serializer clock is generated on pin CLKOP.
  // In addition, the pixel clock (at TMDS freq/5) is generated on
  // pin CLKOS (hence CLKOS_DIV = 5*CLKOP_DIV).

  (* ICP_CURRENT="12" *) (* LPF_RESISTOR="8" *) (* MFG_ENABLE_FILTEROPAMP="1" *) (* MFG_GMCREF_SEL="2" *)
   EHXPLLL #(
       .CLKI_DIV(CLKI_DIV),
       .CLKOP_DIV(CLKOP_DIV),
       .CLKOP_CPHASE(CLKOP_CPHASE),
       .CLKOP_FPHASE(CLKOP_FPHASE),
       .CLKOS_ENABLE("ENABLED"),
       .CLKOS_DIV(5*CLKOP_DIV),
       .CLKOS_CPHASE(CLKOP_CPHASE),
       .CLKOS_FPHASE(CLKOP_FPHASE),
       .CLKFB_DIV(CLKFB_DIV)
   ) pll_i (
       .CLKI(clk_25mhz),
       .CLKOP(half_clk_TMDS),
       .CLKFB(half_clk_TMDS),
       .CLKOS(pixclk),
       .PHASESEL0(1'b0),
       .PHASESEL1(1'b0),
       .PHASEDIR(1'b1),
       .PHASESTEP(1'b1),
       .PHASELOADREG(1'b1),
       .PLLWAKESYNC(1'b0),
       .ENCLKOP(1'b0),
       .LOCK(pixclk_locked)
    );

  // ---------------------------------------------------------------------------
  // X, Y, hSync, vSync, DrawArea
  // ---------------------------------------------------------------------------

  localparam GFX_line_width = GFX_width  + GFX_h_front_porch + GFX_h_sync_width + GFX_h_back_porch;
  localparam GFX_lines      = GFX_height + GFX_v_front_porch + GFX_v_sync_width + GFX_v_back_porch;

  reg [10:0] GFX_X, GFX_Y;
  reg hSync, vSync, DrawArea;

  always @(posedge pixclk) DrawArea <= (GFX_X<GFX_width) && (GFX_Y<GFX_height);

  always @(posedge pixclk) GFX_X <= (GFX_X==GFX_line_width-1) ? 0 : GFX_X+1;
  always @(posedge pixclk) if(GFX_X==GFX_line_width-1) GFX_Y <= (GFX_Y==GFX_lines-1) ? 0 : GFX_Y+1;

  always @(posedge pixclk) hSync <=
     (GFX_X>=GFX_width+GFX_h_front_porch) && (GFX_X<GFX_width+GFX_h_front_porch+GFX_h_sync_width);

  always @(posedge pixclk) vSync <=
     (GFX_Y>=GFX_height+GFX_v_front_porch) && (GFX_Y<GFX_height+GFX_v_front_porch+GFX_v_sync_width);

  // ---------------------------------------------------------------------------
  // Text mode with 8x16 bitmap font
  // ---------------------------------------------------------------------------

  wire [10:0] xpos = GFX_X;
  wire [ 9:0] ypos = GFX_Y;
  wire vga_enable = DrawArea;
  wire hsync = hSync;
  wire vsync = vSync;

  reg [7:0] red, green, blue;

  reg  hsync_d1,  hsync_d2,  hsync_d3;
  reg  vsync_d1,  vsync_d2,  vsync_d3;
  reg enable_d1, enable_d2, enable_d3;

  always @(posedge clk)
  begin
    // The pixel pipeline needs three clock cycles to provide bitmap data after
    // the coordinates are valid, but the sync signals arrive the same cycle as the coordinates.
    // Delay the timing signals for three cycles to get both in sync.

    hsync_d1 <= hsync;
    hsync_d2 <= hsync_d1;
    hsync_d3 <= hsync_d2;

    vsync_d1 <= vsync;
    vsync_d2 <= vsync_d1;
    vsync_d3 <= vsync_d2;

    enable_d1 <= vga_enable;
    enable_d2 <= enable_d1;
    enable_d3 <= enable_d2;
                                     // Black (required by VGA during blanking)
    {red, green, blue}  <= ~enable_d2 ? 24'b00000000_00000000_00000000 :
                                     // Cyan (highlight)                  Orange (normal)                  Navy background
        bitmap_shift[7] ? colorswitch ? 24'b00000000_11100000_11100000 :  24'b11100000_10100000_00000000 : 24'b00000000_00000000_01100000;
  end

  // Combined memory for characters and font data
  reg [7:0] characters [2048 + 4095:0]; initial $readmemh("font-vga.hex", characters, 4096 + 32*16); // The font file contains 96 glyphs only, shift to ASCII 32

  reg [7:0] char, bitmap_shift;
  reg colorswitch, colorswitch_delay;

  wire [12:0] characterindex = xpos[2:0] == 3'b000 ? xpos[9:3] + 100 * ypos[9:4] :
                               xpos[2:0] == 3'b001 ? {char[6:0], ypos[3:0]}  +  13'd4096 :
                               mem_address[12:0];
  reg char_rbusy;

  wire remove = ypos[9:4] == 6'd37; // Do not display line 38 as this is cut in half

  always @(posedge clk) // Pixel pipeline.
  begin
     // First & second cycle:
     char    <= characters[ characterindex ]; // First cycle: 7-Bit ASCII. Using char[7] for highlight color.
                                             // Second cycle: 8x16 pixel font bitmap data.
     char_rbusy <= xpos[2:1] == 2'b00;

     // Second cycle:
     colorswitch_delay <= char[7];

     // Third cycle:
     if (xpos[2:0] == 3'b010) begin if (~remove) bitmap_shift <= char; colorswitch <= colorswitch_delay; end
     else                     begin bitmap_shift <= {bitmap_shift[6:0], 1'b0}; end
  end

  // This is a little trick to coax a word-addressed CPU into byte aligned reads and writes:

  wire [15:0] char_rdata = {char, char};

  always @(posedge clk)
    if(mem_wstrb & mem_address_is_char) characters[mem_address[12:0]] <= mem_wdata[7:0];

  // ---------------------------------------------------------------------------
  // RGB TMDS encoding
  // ---------------------------------------------------------------------------

  // Generate 10-bits TMDS red,green,blue signals.
  // Blue embeds HSync/VSync in its control part.

  wire [9:0] TMDS_red, TMDS_green, TMDS_blue;

  TMDS_encoder encode_R(.clk(pixclk), .VD(red  ), .CD(2'b00),               .VDE(enable_d3), .TMDS(TMDS_red));
  TMDS_encoder encode_G(.clk(pixclk), .VD(green), .CD(2'b00),               .VDE(enable_d3), .TMDS(TMDS_green));
  TMDS_encoder encode_B(.clk(pixclk), .VD(blue ), .CD({vsync_d3,hsync_d3}), .VDE(enable_d3), .TMDS(TMDS_blue));

  // ---------------------------------------------------------------------------
  // Shifter
  // ---------------------------------------------------------------------------

  // Serialize the three 10-bits TMDS red, green, blue signals.
  // This version of the shifter shifts and sends two bits per clock,
  // using the ODDRX1F block of the ULX3S.

  // The counter counts modulo 5 instead of modulo 10 (because we shift two
  // bits at each clock)
  reg [4:0] TMDS_mod5=1;
  always @(posedge half_clk_TMDS) TMDS_mod5 <= {TMDS_mod5[3:0],TMDS_mod5[4]};
  wire TMDS_shift_load = TMDS_mod5[4];

  // Shifter now shifts two bits at each clock
  reg [9:0] TMDS_shift_red=0, TMDS_shift_green=0, TMDS_shift_blue=0;
  always @(posedge half_clk_TMDS) begin
     TMDS_shift_red   <= TMDS_shift_load ? TMDS_red   : TMDS_shift_red  [9:2];
     TMDS_shift_green <= TMDS_shift_load ? TMDS_green : TMDS_shift_green[9:2];
     TMDS_shift_blue  <= TMDS_shift_load ? TMDS_blue  : TMDS_shift_blue [9:2];
  end

  // DDR serializers: they send D0 at the rising edge and D1 at the falling edge.
  ODDRX1F ddr_red  (.D0(TMDS_shift_red[0]),   .D1(TMDS_shift_red[1]),   .Q(gpdi_dp[2]), .SCLK(half_clk_TMDS), .RST(1'b0));
  ODDRX1F ddr_green(.D0(TMDS_shift_green[0]), .D1(TMDS_shift_green[1]), .Q(gpdi_dp[1]), .SCLK(half_clk_TMDS), .RST(1'b0));
  ODDRX1F ddr_blue (.D0(TMDS_shift_blue[0]),  .D1(TMDS_shift_blue[1]),  .Q(gpdi_dp[0]), .SCLK(half_clk_TMDS), .RST(1'b0));

  // The pixel clock is sent through the fourth differential pair.
  assign gpdi_dp[3] = pixclk;

  // Note (again): gpdi_dn[3:0] is generated automatically by LVCMOS33D mode in ulx3s_v20.lpf
endmodule


// 40 MHz system clock
// ecppll -i 25 -o 40 -f /dev/stdout

module pll40
(
    input clkin, // 25 MHz, 0 deg
    output clkout0, // 40 MHz, 0 deg
    output locked
);
(* FREQUENCY_PIN_CLKI="25" *)
(* FREQUENCY_PIN_CLKOP="40" *)
(* ICP_CURRENT="12" *) (* LPF_RESISTOR="8" *) (* MFG_ENABLE_FILTEROPAMP="1" *) (* MFG_GMCREF_SEL="2" *)
EHXPLLL #(
        .PLLRST_ENA("DISABLED"),
        .INTFB_WAKE("DISABLED"),
        .STDBY_ENABLE("DISABLED"),
        .DPHASE_SOURCE("DISABLED"),
        .OUTDIVIDER_MUXA("DIVA"),
        .OUTDIVIDER_MUXB("DIVB"),
        .OUTDIVIDER_MUXC("DIVC"),
        .OUTDIVIDER_MUXD("DIVD"),
        .CLKI_DIV(5),
        .CLKOP_ENABLE("ENABLED"),
        .CLKOP_DIV(15),
        .CLKOP_CPHASE(7),
        .CLKOP_FPHASE(0),
        .FEEDBK_PATH("CLKOP"),
        .CLKFB_DIV(8)
    ) pll_i (
        .RST(1'b0),
        .STDBY(1'b0),
        .CLKI(clkin),
        .CLKOP(clkout0),
        .CLKFB(clkout0),
        .CLKINTFB(),
        .PHASESEL0(1'b0),
        .PHASESEL1(1'b0),
        .PHASEDIR(1'b1),
        .PHASESTEP(1'b1),
        .PHASELOADREG(1'b1),
        .PLLWAKESYNC(1'b0),
        .ENCLKOP(1'b0),
        .LOCK(locked)
        );
endmodule


// 48 MHz clock for USB
// ecppll -i 25 -o 48 --highres -f /dev/stdout

module pll48
(
    input clkin, // 25 MHz, 0 deg
    output clkout0, // 48 MHz, 0 deg
    output locked
);
wire clkfb;
(* FREQUENCY_PIN_CLKI="25" *)
(* FREQUENCY_PIN_CLKOS="48" *)
(* ICP_CURRENT="12" *) (* LPF_RESISTOR="8" *) (* MFG_ENABLE_FILTEROPAMP="1" *) (* MFG_GMCREF_SEL="2" *)
EHXPLLL #(
        .PLLRST_ENA("DISABLED"),
        .INTFB_WAKE("DISABLED"),
        .STDBY_ENABLE("DISABLED"),
        .DPHASE_SOURCE("DISABLED"),
        .OUTDIVIDER_MUXA("DIVA"),
        .OUTDIVIDER_MUXB("DIVB"),
        .OUTDIVIDER_MUXC("DIVC"),
        .OUTDIVIDER_MUXD("DIVD"),
        .CLKI_DIV(5),
        .CLKOP_ENABLE("ENABLED"),
        .CLKOP_DIV(48),
        .CLKOP_CPHASE(9),
        .CLKOP_FPHASE(0),
        .CLKOS_ENABLE("ENABLED"),
        .CLKOS_DIV(10),
        .CLKOS_CPHASE(0),
        .CLKOS_FPHASE(0),
        .FEEDBK_PATH("CLKOP"),
        .CLKFB_DIV(2)
    ) pll_i (
        .RST(1'b0),
        .STDBY(1'b0),
        .CLKI(clkin),
        .CLKOP(clkfb),
        .CLKOS(clkout0),
        .CLKFB(clkfb),
        .CLKINTFB(),
        .PHASESEL0(1'b0),
        .PHASESEL1(1'b0),
        .PHASEDIR(1'b1),
        .PHASESTEP(1'b1),
        .PHASELOADREG(1'b1),
        .PLLWAKESYNC(1'b0),
        .ENCLKOP(1'b0),
        .LOCK(locked)
        );
endmodule
