/* 16 bit registers:
 * Offset  Usage
 * 0x0     Board ID: read 0x8100
 * 0x2     Control register:
 *  bits 3:0 PLD revision
 *  bits 7:4 LCD contrast
 *  bits 15:8 control register:
 *    bit 8: USB reset
 *    bit 9: ISA reset
 *    bit 10: enable oscillator
 *    bit 11: enable RS-232
 *    bit 12: enable LCD power
 *    bit 13: CAN1 standby
 *    bit 14: CAN2 standby
 *    bit 15: LCD contrast enable
 * 0x4     DIO output data
 * 0x6     LCD/AVR output data
 *   bit 11: AVR MOSI
 *   bit 12: AVR SCLK
 *   bit 13: AVR RESET
 * 0x8     DIO DDR
 * 0xA     LCD DDR
 * 0xC     DIO input data
 * 0xE     LCD/AVR input data
 *   bit 11: AVR MISO
 *
 * DIO registers:
 * bits 7:0 are odd numbered pins 15:1 on the DIO header
 * bits 13:8 are PC/104 pins A21:A16
 * bits 15:14 are PC/104 pins B12:B11
 *
 * LCD registers:
 * bits 7:0 are data lines (pins 14:7)
 * bit 8 is LCD WR# (pin 6)
 * bit 9 is LCD RS (pin 3)
 * bit 10 is LCD EN (pin 5)
 * higher bits should be zero during LCD manipulations.
 */
module ts8100(

/* control signals */
  pld_24mhz_pad,
  pld_14mhz_pad, // actually 14.3mhz
  usb_resetn_pad,
  isa_reset_pad,
  off_bd_resetn_pad,
  en_osc_pad,
  en_232_pad,
  en_lcd_5v_pad,
  can1_stby_pad,
  can2_stby_pad,
  vcc1_pad, vcc2_pad, // these are vcc on 570 but not 240

/* AD bus slave interface */
  bus_alen_pad,
  bus_csn_pad,
  bus_dir_pad,
  bus_waitn_pad,
  mux_ad_pad,

/* LCD header */
  lcd_d_pad,
  lcd_wrn_pad,
  lcd_rs_pad,
  lcd_en_pad,
  lcd_pwm_pad,

/* DIO */
  dio_pad,

/* AVR programming interface */
  avr_mosi_pad,
  avr_miso_pad,
  avr_sclk_pad,
  avr_reset_pad,

/* PC/104 */
  isa_add_pad,
  isa_iown_pad,
  isa_iorn_pad,
  isa_waitn_pad,
  isa_csn_pad
);

/* control signals */
input pld_24mhz_pad;
input pld_14mhz_pad; // actually 14.3mhz
output usb_resetn_pad;
output isa_reset_pad;
input off_bd_resetn_pad;
output en_osc_pad;
output en_232_pad;
output en_lcd_5v_pad;
output can1_stby_pad;
output can2_stby_pad;
input vcc1_pad;
input vcc2_pad;

/* AD bus slave interface */
input bus_alen_pad;
input bus_csn_pad;
input bus_dir_pad;
output bus_waitn_pad;
inout [15:0] mux_ad_pad;

/* LCD header */
inout [7:0] lcd_d_pad;
inout lcd_wrn_pad;
inout lcd_rs_pad;
inout lcd_en_pad;
output lcd_pwm_pad;

/* DIO */
inout [15:0] dio_pad;

/* AVR programming interface */
output  avr_mosi_pad;
input  avr_miso_pad;
output  avr_sclk_pad;
output  avr_reset_pad;

/* PC/104 */
output [9:0] isa_add_pad;
output isa_iown_pad;
output isa_iorn_pad;
input isa_waitn_pad;
output isa_csn_pad;

localparam [15:0] model = 16'h8100;
localparam [3:0] revision = 4'h0;

/* TS-8100 16 bit registers: */
`define MODEL    3'b000
`define CONTROL  3'b001
`define DIO_OUT  3'b010
`define LCD_OUT  3'b011
`define DIO_DDR  3'b100
`define LCD_DDR  3'b101
`define DIO_IN   3'b110
`define LCD_IN   3'b111

wire clk, rst;
assign clk = pld_24mhz_pad;
assign rst = !off_bd_resetn_pad;

//assign usb_resetn_pad = off_bd_resetn_pad;
//assign isa_reset_pad = !off_bd_resetn_pad;

wire ale, csn, dir;
assign ale = bus_alen_pad;
assign csn = bus_csn_pad;
assign dir = bus_dir_pad;

reg [9:0] addr;
assign isa_add_pad = addr;

reg [26:0] dio;
reg [26:0] dio_dir;
reg [26:0] dio_reg;
integer i;
always @(*) begin
  for (i = 0; i <= 26; i = i + 1) begin
    dio_reg[i] = dio_dir[i] ? dio[i] : 1'bz;
  end
end

assign dio_pad = dio_reg[15:0];
assign lcd_d_pad = dio_reg[23:16];
assign lcd_wrn_pad = dio_reg[24];
assign lcd_rs_pad = dio_reg[25];
assign lcd_en_pad = dio_reg[26];

reg [15:0] data;
wire data_oe;
assign mux_ad_pad = data_oe ? data : 16'hzzzz;

reg between;
wire pc104_en = addr[9] | addr[8];
assign isa_csn_pad = !((!csn && pc104_en) || (between && !dir && ale));
// for dir, 0 = write
assign isa_iown_pad = !(!csn && !dir && pc104_en); 
assign isa_iorn_pad = !(!csn && dir && pc104_en); 
assign bus_waitn_pad = isa_waitn_pad || !pc104_en;

assign data_oe = (!csn && dir && !pc104_en);

reg [7:0] control;
assign usb_resetn_pad = control[0];
assign isa_reset_pad = control[1];
assign en_osc_pad = control[2];
assign en_232_pad = control[3];
assign en_lcd_5v_pad = control[4];
assign can1_stby_pad = control[5];
assign can2_stby_pad = control[6];

reg [7:0] pwm_cnt;
reg [3:0] contrast;
// control[7] is LCD contrast PWM enable:
assign lcd_pwm_pad = control[7] ? (pwm_cnt[7:3] > {1'b1, contrast[3:0]}) : 1'b0;

reg [2:0] avr_o;
assign avr_mosi_pad = avr_o[0];
assign avr_sclk_pad = avr_o[1];
assign avr_reset_pad = avr_o[2];

always @(posedge ale or posedge rst) begin
  if (rst) begin
    addr <= 10'd0;
  end else begin
    addr <= mux_ad_pad[9:0];
  end
end

// The between register indicates that there has not been a CS since ALE.
always @(posedge !ale or posedge csn or posedge rst) begin
  if (rst) between <= 1'b0;
  else if (!ale) between <= 1'b1;
  else between <= 1'b0;
end

always @(posedge csn or posedge rst) begin
  if (rst) begin
    dio <=     27'h000c000; // DIO inits as per schematic notes
    dio_dir <= 27'h400ff00;
    control <= 8'b01101101;
    avr_o <= 3'b000;
    contrast <= 4'h0;
  end else begin
    if (!pc104_en && !dir && (addr[9:4] == 6'd0)) begin
      case (addr[3:1])
        `CONTROL: {control, contrast} <= mux_ad_pad[15:4];
        `DIO_OUT: dio[15:0] <= mux_ad_pad;
        `LCD_OUT: {avr_o, dio[26:16]} <= mux_ad_pad[13:0];
        `DIO_DDR: dio_dir[15:0] <= mux_ad_pad;
        `LCD_DDR: dio_dir[26:16] <= mux_ad_pad[10:0];
      endcase
    end
  end
end

always @(posedge clk or posedge rst) begin
  if (rst) pwm_cnt <= 8'h00;
  else pwm_cnt <= pwm_cnt + 1'b1;
end

always @(*) begin
  casex(addr[3:1])
    `MODEL:    data = model;
    `CONTROL:  data = {control, contrast, revision};
    `DIO_OUT:  data = dio[15:0];
    `LCD_OUT:  data = {2'bxx, avr_o, dio[26:16]};
    `DIO_DDR:  data = dio_dir[15:0];
    `LCD_DDR:  data = {5'bxxxxx, dio_dir[26:16]};
    `DIO_IN:   data = dio_pad[15:0];
    `LCD_IN:   data = {1'bx, pld_14mhz_pad, vcc1_pad, vcc2_pad, avr_miso_pad,
                  lcd_en_pad, lcd_rs_pad, lcd_wrn_pad, lcd_d_pad[7:0]};
    default: data = 16'hxxxx;
  endcase
end

endmodule

