/* Copyright 2007-2013, Unpublished Work of Technologic Systems
 * All Rights Reserved.
 *
 * THIS WORK IS AN UNPUBLISHED WORK AND CONTAINS CONFIDENTIAL,
 * PROPRIETARY AND TRADE SECRET INFORMATION OF TECHNOLOGIC SYSTEMS.
 * ACCESS TO THIS WORK IS RESTRICTED TO (I) TECHNOLOGIC SYSTEMS 
 * EMPLOYEES WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE
 * OF THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN TECHNOLOGIC
 * SYSTEMS WHO HAVE ENTERED INTO APPROPRIATE LICENSE AGREEMENTS.  NO
 * PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED, COPIED, 
 * DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED, CONDENSED, 
 * EXPANDED, COLLECTED, COMPILED, LINKED, RECAST, TRANSFORMED, ADAPTED
 * IN ANY FORM OR BY ANY MEANS, MANUAL, MECHANICAL, CHEMICAL, 
 * ELECTRICAL, ELECTRONIC, OPTICAL, BIOLOGICAL, OR OTHERWISE WITHOUT
 * THE PRIOR WRITTEN PERMISSION AND CONSENT OF TECHNOLOGIC SYSTEMS.
 * ANY USE OR EXPLOITATION OF THIS WORK WITHOUT THE PRIOR WRITTEN
 * CONSENT OF TECHNOLOGIC SYSTEMS COULD SUBJECT THE PERPETRATOR TO
 * CRIMINAL AND CIVIL LIABILITY.
 */

/* Syscon memory map:
 * 0x00: reads 0x4740
 * 0x02: Control register:
     bits 3-0:   FPGA revision (RO)
     bits 5-4:   Mode2, Mode1
     bits 7-6:   Scratch register
     bit 8:      Enable CAN (override DIO 15,16)
     bit 9:      Enable 2nd CAN (override DIO 10,11)
     bit 10:     Enable SPI (override DIO 17-20)
     bit 11:     Enable 12.5MHz base board clock (override DIO 3)
     bit 12:     Enable UART0 TXEN (override DIO 12)
     bit 13:     Enable UART4 TXEN (override DIO 14)
     bit 14:     Enable touchscreen (override DIO 30-35)
     bit 15:     Reset switch enable
 * 0x04: external bus config register
 * 0x06: watchdog feed register
 * 0x08: free running 1MHz counter LSB
 * 0x0a: free running 1MHz counter MSB
 * 0x0c: RNG LSB
 * 0x0e: RNG MSB
 * 0x10: DIO group 1 DIO[14:0] output data (bits 14-0)
 * 0x12: DIO group 2 DIO[26:22] DIO[20:15] output data (bits 10-0) 11: Green LED. 12: Red LED.
 * 0x14: DIO group 3 DIO[42:27] output data (bits 15-0)
 * 0x16: DIO group 4 DIO[59:48] output data (bits 11-0)
     bit 12:     Enable UART3 TXEN (override DIO 13)
     bit 13:     Enable UART5 TXEN (override DIO 7)
     bit 14:     Enable UART1 TXEN (override DIO 8)
     bit 15:     Enable UART2 TXEN (override DIO 10)
 * 0x18: DIO group 1 DIO[14:0] data direction (bits 14-0)
 * 0x1a: DIO group 2 DIO[26:22] DIO[20:15] data direction (bits 10-0)
 * 0x1c: DIO group 3 DIO[42:27] data direction (bits 15-0)
 * 0x1e: DIO group 4 DIO[59:48] data direction (bits 11-0)
 * 0x20: DIO group 1 DIO[14:0] input data (bits 14-0)
 * 0x22: DIO group 2 DIO[26:22] DIO[20:15] input data (bits 10-0)
 * 0x24: DIO group 3 DIO[42:27] input data (bits 15-0)
 * 0x26: DIO group 4 DIO[59:48] input data (bits 11-0)
 * 0x28: TAG memory access -- NOTE: not used on TS-4740
 * 0x2a: Custom load ID register (reads 0 on standard load)
 * 0x2c: IRQ register (smc_irq line indicates one of the following is active)
 *   bit 0: XUART IRQ
 *   bit 1: CAN IRQ
 *   bit 2: CAN2 IRQ
 *   bit 3: PC/104 IRQ 5
 *   bit 4: PC/104 IRQ 6
 *   bit 5: PC/104 IRQ 7
 *   bits 6-15: reserved
 * 0x2e: IRQ mask register.  Setting a bit in this register disables the IRQ
 *       for the corresponding bit in the IRQ register.
 * 0x30: DIO group 5 DIO[73:60] output data (bits 13-0)
 * 0x32: DIO group 6 DIO[89:74] output data (bits 15-0)
 * 0x34: DIO group 7 DIO[102:90] output data (bits 12-0)
 * 0x38: DIO group 5 DIO[73:60] data direction (bits 13-0)
 * 0x3a: DIO group 6 DIO[89:74] data direction (bits 15-0)
 * 0x3c: DIO group 7 DIO[102:90] data direction (bits 12-0)
 * 0x40: DIO group 5 DIO[73:60] input data (bits 13-0)
 * 0x42: DIO group 6 DIO[89:74] input data (bits 15-0)
 * 0x44: DIO group 7 DIO[102:90] input data (bits 12-0)
 * 0x46: ICAP data register
 * 0x48: ICAP control bits
 * 0x4a: IPC FIFO status, LM32 reset
 * 0x4c: IPC FIFO data
 */
 
/* DIO pin numbering conventions:
 * DIO 0-8:    CN1 odd pins 93 to 77
 * DIO 9-14:   CN1 odd pins 73 to 63
 * DIO 15-16:  CN2 pins 97 and 99
 * DIO 17-20:  CN2 odd pins 65 to 71 (SPI)
 * DIO 22-26:  CN1 pins 97, 99, 100, 98, 96 (bus control signals)
 * DIO 27-34:  CN1 even pins 78 to 64 (AD bus 8 to 15)
 * DIO 35-42:  CN1 even pins 94 to 80 (AD bus 0 to 7)
 * DIO 48-59:  CN2 even pins 78 to 100 (UARTS)
 * DIO 60-73:  CN1 various -- labeled DIO 17-30 on the schematic
 * DIO 74-97:  CN1 LCD data signals signals -- LCD 0-23
 * DIO 98-102: CN1 odd pins 49-57 (LCD control signals)
 *
 * A total of 97 FPGA DIO pins are available, divided into 4 groups.
 * group 1: 0-14
 * group 2: 15-20, 22-26
 * group 3: 27-42
 * group 4: 48-59
 * group 5: 60-73
 * group 6: 74-89
 * group 7: 90-102
 * Each group has an output data register, a data direction register, 
 * and a pin data register.  The PDR reports the actual status of the pin.
 * The ODR is ignored if the DDR is 0.
 */

/* Notes:
 * - 2nd CAN port will not be implemented unless a bigger FPGA is used.
 * - DIO DDR/ODR registers have no effect if other functionality is enabled
 *   for the pin in question.  Muxing is done in top module.
 */

module syscon(
  wb_clk_i,
  wb_rst_i,

  wb_cyc_i,
  wb_stb_i,
  wb_we_i,
  wb_adr_i,
  wb_dat_i,
  wb_sel_i,
  wb_dat_o,
  wb_ack_o,

  dio_i,
  dio_oe_o,
  dio_o,
  usec_i,

  bus_config_o,
  en_can_o,
  en_can2_o,
  en_spi_o,
  en_bbclk_o,
  en_txen_o,
  en_touch_o,
  red_led_o,
  green_led_o,
  reboot_o,
  irqs_i,
  irq_o,
  lm32_rst_o,
  sd_busy_i,
  dma_idle_i

);

input wb_clk_i, wb_rst_i, wb_cyc_i, wb_stb_i, wb_we_i;
input [31:0] wb_adr_i;
input [15:0] wb_dat_i;
input [1:0] wb_sel_i;
output [15:0] wb_dat_o;
output wb_ack_o;

input [102:0] dio_i;
output [102:0] dio_oe_o;
output [102:0] dio_o;

input [31:0] usec_i;

output [15:0] bus_config_o;
output en_can_o;
output en_can2_o;
output en_spi_o;
output en_bbclk_o;
output [5:0] en_txen_o;
output en_touch_o;
output red_led_o;
output green_led_o;
output reboot_o;
input [15:0] irqs_i;
output irq_o;
output lm32_rst_o;
input dma_idle_i, sd_busy_i;

localparam [15:0] model = 16'h4740;
localparam [3:0] revision = 4'h1;
localparam [15:0] custom = 16'habcd;

reg icap_clk, icap_en, icap_we;
reg [15:0] icap_dat;
wire [15:0] icap_swap = {
  icap_dat[8],
  icap_dat[9],
  icap_dat[10],
  icap_dat[11],
  icap_dat[12],
  icap_dat[13],
  icap_dat[14],
  icap_dat[15],
  icap_dat[0],
  icap_dat[1],
  icap_dat[2],
  icap_dat[3],
  icap_dat[4],
  icap_dat[5],
  icap_dat[6],
  icap_dat[7]
};

ICAP_SPARTAN6
  //#( .DEVICE_ID(32'h04024093)) // Specifies the pre-programmed Device ID value
icapcore (
  //.BUSY(BUSY),   // 1-bit output: Busy/Ready output
  //.O(O),         // 16-bit output: Configuartion data output bus
  .CE(icap_en),       // 1-bit input: Active-Low ICAP Enable input
  .CLK(icap_clk),     // 1-bit input: Clock input
  .I(icap_swap),         // 16-bit input: Configuration data input bus
  .WRITE(icap_we)  // 1-bit input: Read/Write control input
);

wire [7:0] fifo_dout;
wire fifo_full, fifo_empty;
wire [10:0] fifo_count;
reg wr_en, rd_en;
reg [7:0] fifo_din;

fifo_1bytex1k fifocore(
  .clk(wb_clk_i),
  .rst(wb_rst_i),
  .din(fifo_din),
  .wr_en(wr_en),
  .rd_en(rd_en),
  .dout(fifo_dout),
  .full(fifo_full),
  .empty(fifo_empty),
  .data_count(fifo_count)
);

reg random_clk;
reg [3:0] count2;
always @(posedge wb_clk_i or posedge wb_rst_i) begin
  if (wb_rst_i) begin
    count2 <= 4'h0;
    random_clk <= 1'b0;
  end else begin
    count2 <= count2 + 1'b1;
    if (count2 == 4'd11) begin
      count2 <= 4'd0;
      random_clk <= ~random_clk;
    end
  end
end
reg [31:0] random;
reg [15:0] rndcnt;
reg [1:0] wdogctl;
reg [9:0] wdogcnt;
reg feed_en, reboot;
wire sed_err;
wire feed_bsy, feed_rdreq;
wire [15:0] wdog_dat;
reg resetsw_en;
assign reboot_o = reboot | (resetsw_en & !dio_i[9]);
always @(posedge random_clk or posedge wb_rst_i) begin
  if (wb_rst_i) begin
    rndcnt <= 16'd0;
    random <= 32'd0;
    wdogctl <= 2'd3; // initialize disabled
    wdogcnt <= 10'd0;
  end else begin
    rndcnt <= rndcnt + 1'b1;
    if (rndcnt == 16'd0) begin
      //random[31:0] <= {random[30:0], wb_clk_i}; we have no RNG
      wdogcnt <= wdogcnt + 1'b1;
    end
    if (feed_rdreq) begin
      wdogctl <= wdog_dat;
      wdogcnt <= 10'd0;
    end
  end
end
always @(*) begin
  case (wdogctl)
  2'd0: reboot = wdogcnt[4]; /* approx .338 seconds */
  2'd1: reboot = wdogcnt[7]; /* approx 2.706 seconds */
  2'd2: reboot = wdogcnt[9]; /* approx 10.824 seconds */
  2'd3: reboot = 1'b0;
  endcase
end

resync_fifo watchdogfifo(
  .rst_i(wb_rst_i),
  .wrclk_i(wb_clk_i),
  .wrreq_i(feed_en),
  .data_i(wb_dat_i),
  .full_wrclk_o(feed_bsy),
  .rdclk_i(random_clk),
  .rdreq_i(feed_rdreq),
  .q_o(wdog_dat),
  .full_rdclk_o(feed_rdreq)
);

reg [102:0] dio_oe;
assign dio_oe_o = dio_oe;
reg [102:0] dio;
assign dio_o = dio;
reg [1:0] scratch, modes; // modes is now also just a scratch register

reg [15:0] bus_config;
reg en_can, en_can2, en_spi, en_bbclk, en_touch, lm32_rst;
reg [5:0] en_txen; 
reg red_led, green_led;
reg [15:0] irq_mask;

assign bus_config_o = bus_config;
assign en_can_o = en_can;
assign en_can2_o = en_can2;
assign en_spi_o = en_spi;
assign en_bbclk_o = en_bbclk;
assign en_txen_o = en_txen;
assign en_touch_o = en_touch;
assign red_led_o = red_led;
assign green_led_o = green_led;
assign lm32_rst_o = lm32_rst;

assign irq_o = ((irqs_i & ~irq_mask) != 16'h0000);

always @(posedge wb_clk_i or posedge wb_rst_i) begin
  if (wb_rst_i) begin
    en_can <= 1'b0;
    en_can2 <= 1'b0;
    en_spi <= 1'b0;
    en_bbclk <= 1'b0;
    en_txen <= 6'd0;
    en_touch <= 1'b0;
    dio_oe <= 103'd0;
    dio <= 103'd0;
    bus_config <= 16'h0000;
    scratch <= 2'b00;
    modes <= 2'b00;
    red_led <= 1'b1;
    green_led <= 1'b1;
    resetsw_en <= 1'b0;
    irq_mask <= 16'h0000;
    icap_clk <= 1'b1;
    icap_dat <= 16'd0;
    icap_en <= 1'b1;
    icap_we <= 1'b1;
    lm32_rst <= 1'b1;
    wr_en <= 1'b0;
    rd_en <= 1'b0;
    fifo_din <= 8'd0;
  end else begin
    wr_en <= 1'b0;
    rd_en <= 1'b0;
    if (wb_cyc_i && wb_stb_i && wb_we_i) case (wb_adr_i[6:0])
      7'h2: {resetsw_en, en_touch, en_txen[4], en_txen[0], en_bbclk, en_spi,
             en_can2, en_can, scratch, modes} <= wb_dat_i[15:4];
      7'h4: bus_config <= wb_dat_i;
      7'h10: dio[14:0] <= wb_dat_i[14:0];
      7'h12: {red_led, green_led, dio[26:22], dio[20:15]} <= wb_dat_i[12:0];
      7'h14: dio[42:27] <= wb_dat_i;
      7'h16: {en_txen[2:1], en_txen[5], en_txen[3], dio[59:48]} <= wb_dat_i;
      7'h18: dio_oe[14:0] <= wb_dat_i[14:0];
      7'h1a: {dio_oe[26:22], dio_oe[20:15]} <= wb_dat_i[10:0];
      7'h1c: dio_oe[42:27] <= wb_dat_i;
      7'h1e: dio_oe[59:48] <= wb_dat_i[11:0];
      7'h2e: irq_mask <= wb_dat_i;
      7'h30: dio[73:60] <= wb_dat_i[13:0];
      7'h32: dio[89:74] <= wb_dat_i[15:0];
      7'h34: dio[102:90] <= wb_dat_i[12:0];
      7'h38: dio_oe[73:60] <= wb_dat_i[13:0];
      7'h3a: dio_oe[89:74] <= wb_dat_i[15:0];
      7'h3c: dio_oe[102:90] <= wb_dat_i[12:0];
      7'h46: icap_dat <= wb_dat_i;
      7'h48: {icap_we, icap_en, icap_clk} <= wb_dat_i[2:0];
      7'h4a: lm32_rst <= wb_dat_i[12];
      7'h4c: begin
        wr_en <= 1'b1;
	fifo_din <= wb_dat_i[7:0];
      end
    endcase
    if (wb_cyc_i && wb_stb_i && !wb_we_i && (wb_adr_i[6:0] == 7'h4c)) begin
      rd_en <= 1'b1;
    end
  end
end

reg [15:0] wb_dat;
assign wb_dat_o = wb_dat;
reg wb_ack;
assign wb_ack_o = wb_ack;
always @(*) begin
  wb_ack = wb_cyc_i && wb_stb_i;
  wb_dat = 16'hxxxx;
  feed_en = 1'b0;

  case (wb_adr_i[6:0]) 
    7'h0: wb_dat = model[15:0];
    7'h2: wb_dat = {resetsw_en, en_touch, en_txen[4], en_txen[0], en_bbclk,
          en_spi, en_can2, en_can, scratch, modes, revision};
    7'h4: wb_dat = bus_config;
    7'h6: begin
      feed_en = wb_cyc_i & wb_stb_i & wb_we_i & !feed_bsy;
      wb_ack = wb_cyc_i & wb_stb_i & ((wb_we_i & !feed_bsy ) | !wb_we_i);
    end
    7'h8: wb_dat = usec_i[15:0];
    7'ha: wb_dat = usec_i[31:16];
    7'hc: wb_dat = random[15:0];
    7'he: wb_dat = random[31:16];
    7'h10: wb_dat = {1'bx, dio[14:0]};
    7'h12: wb_dat = {sd_busy_i, dma_idle_i, 1'bx, red_led, green_led, dio[26:22], dio[20:15]};
    7'h14: wb_dat = dio[42:27];
    7'h16: wb_dat = {en_txen[2:1], en_txen[5], en_txen[3], dio[59:48]};
    7'h18: wb_dat = {1'bx, dio_oe[14:0]};
    7'h1a: wb_dat = {5'bxxxxx, dio_oe[26:22], dio_oe[20:15]};
    7'h1c: wb_dat = dio_oe[42:27];
    7'h1e: wb_dat = {4'bxxxx, dio_oe[59:48]};
    7'h20: wb_dat = {1'bx, dio_i[14:0]};
    7'h22: wb_dat = {5'bxxxxx, dio_i[26:22], dio_i[20:15]};
    7'h24: wb_dat = dio_i[42:27];
    7'h26: wb_dat = {4'bxxxx, dio_i[59:48]};
    7'h28: wb_dat = 16'hffff; // in case obsolete code reads the tagmem
    7'h2a: wb_dat = custom;
    7'h2c: wb_dat = irqs_i & ~irq_mask;
    7'h2e: wb_dat = irq_mask;
    7'h30: wb_dat = {2'bxx, dio[73:60]};
    7'h32: wb_dat = dio[89:74];
    7'h34: wb_dat = {3'bxxx, dio[102:90]};
    7'h38: wb_dat = {2'bxx, dio_oe[73:60]};
    7'h3a: wb_dat = dio_oe[89:74];
    7'h3c: wb_dat = {3'bxxx, dio_oe[102:90]};
    7'h40: wb_dat = {2'bxx, dio_i[73:60]};
    7'h42: wb_dat = dio_i[89:74];
    7'h44: wb_dat = {3'bxxx, dio_i[102:90]};
    7'h46: wb_dat = icap_dat;
    7'h48: wb_dat = {13'd0, icap_we, icap_en, icap_clk};
    7'h4a: wb_dat = {3'd0, lm32_rst, fifo_full, fifo_count};
    7'h4c: wb_dat = {8'd0, fifo_dout};
  endcase
end

endmodule

