// 02/03/06
// for rev B board
// change SCL, SDA to SCL0, SDA0, and add SCL1, SDA1
// add a select register (may not be needed as addresses are different
// and the buses are pulled up)
// pins have changed
// 07/11/05
// add i2c interface
// 03/10/05
// CPLD for STX DMB TC2133/56, 48V 7K 11949 Rev A
// add DLSEL0-, DLSEL1-, BRn,  SCL, SDA
// DLSEL0- and DLSEL1- are currently driven high
// need a register to control it
// need logic for SCL and SDA, just set to 1 right now
// BRn is set to 1
// add revision number
// Remove SPI stuff
// 05/20/04
// modify the ID chip and SPI register
// CFLG0- is used both for the IDL and the SPI lines
// SYSID is now bit 1
// 05/03/04
// same as stxcp04.v
// 02/06/04
// use stxcp03
// change address map so that cpld registers decode at A9=1 and
// the fpga registers decode at A9 = 0
// 01/09/04
// use stxcp01 
// to create a version for XST
// also change the ID for dsp to CFLG0n as in iop
// 12/01/03 - kdb
// copyied file from lmcp.v to stxcp.c for 
// STX Control Module CPLD base.  Modified
// pin definitions for new pcb.
//
// 10/21/03
// added comments before checkin
//
// 08/11/03
// revision 03 
// include ID chip selector
//
// 07/29/03
// version to have supervisor and XCS
// identical to lmcp.v
// 07/10/03
// version for bring up of the lm card derived
// derived from the iop board in turn derived
// from the cmd link test board
// 
// 05/22/03
// fixed bug in XCCLK equation
// was 
// assign XCCLK = ( (A == XCFGDATA) && (MS1n == 1'b0) && 
//               (WRn == 1'b1) ) ? 1'b1 : 1'b1;
//
// should be
// assign XCCLK = ( (A == XCFGDATA) && (MS1n == 1'b0) && 
//               (WRn == 1'b0) ) ? 1'b0 : 1'b1;
//
// making 2 xclks per word hence no sync seen
// 05/19/03 Rev2
// Use version from Ken
// change the Xilinx Programming registers from IOMS to MS1
// 10/14/02 Rev 1
// changed signal name from DCLKO to DCLK0 and XCLKO to XCLK0
//
// assuming that RSTn the reset signal is active low
//
// add Virtex programming interface and temporary decodes
// that need to be adjusted
//
// XCCLK is a combinatorial version of WRn and the decode
// and DSP should assert a write hold
//
// WARNING !! WRn does not like to be on a non clock pin with Synpilfy
// but is ok with XST Verilog
//
// FLASH decode needs to fixed -- currently arbitrarily set to all
// of MS and BMS
// 
// Virtex Config  bit positions
// 
//     +-------+-------+-------+--------+--------+------+--------+--------+
// Bit |  15   |  14   |  13   |   12   |   11   |  10  |   9    |    8   |
//     +-------+-------+-------+--------+--------+------+--------+--------+
//     |       |       |       |        |        |      |        |        |
//     +-------+-------+-------+--------+--------+------+--------+--------+
//     +-------+-------+-------+--------+--------+------+--------+--------+
// Bit |  7    |   6   |   5   |   4    |   3    |   2  |   1    |    0   |
//     +-------+-------+-------+--------+--------+------+--------+--------+
//     | FBUSYn| XDONE | XBUSY | XINITn | XRDWRn | XCSn | XPROGn | XPWDNn |
//     +-------+-------+-------+--------+--------+------+--------+--------+
//
// XBUSY has no logic and is useless really
// According to Xilinx we dont have to look at it if frequency
// is less than 66MHz
//
// XINITn is an open collector signal that will drive a 0 when
// set to 0, tristate on 1 (reset value is 1)
//
// XCFGDATA is the address the dsp writes the data
// XCFGCTL  is the address the dsp writes the control / reads the status
//
// FBUSY and ACK are without logic
//
// 10/01/02 Rev 0
// test module for initial check out
// connect the input clock (SCLK) to the 2 outgoing clocks (DCLK0,XCLK0)
// ensure the pins that talk to the virtex during configuration are
// in the correct state
// convention signals with - are n and / is deleted
//
///

module stxcp12b ( RSTn,

                  SCLK, 
                  DCLK1,
                  DCLK0, 
                  XCLK0,
                  
                  XINITn, 
                  XBUSY, 
                  XDONE, 
                  XPWDNn, 
                  XCCLK, 
                  XCSn, 
                  XRDWRn, 
                  XPROGn, 
 
                  D,
                  A,
                  RDn,
                  WRn,
                  BRn,
 
                  CFLG0n,
                  BMSn,
                  MS0n, 
                  MS1n, 
                  MS2n, 
                  MS3n, 
                  IOMSn,
                  ACK,
 
                  FBUSYn,
                  FCSn,
                  
                  SYNC15,
                  SYNC25,
 
                  SCL0,
                  SDA0,

                  SCL1,
                  SDA1,

                  DLSEL0n,
                  DLSEL1n,

                  DUMMY
);

input   RSTn;

input   SCLK;
input   DCLK1;
output  DCLK0;
output  XCLK0;

inout   XINITn;
input   XBUSY;
input   XDONE;
output  XPWDNn;
output  XCCLK;
output  XCSn;
output  XRDWRn;
output  XPROGn;

inout   [15:0] D;
input   [9:0] A;
input   RDn;
input   WRn;
output  BRn;

inout   CFLG0n;
input   BMSn;
input   MS0n;
input   MS1n;
input   MS2n;
input   MS3n;
input   IOMSn;
output  ACK;

input   FBUSYn;
output  FCSn;

output  SYNC15;
output  SYNC25;

output  SCL0;
inout   SDA0;

output  SCL1;
inout   SDA1;

output  DLSEL0n;
output  DLSEL1n;

output  DUMMY;

wire    DCLK0; 
wire    XCLK0;

wire    [15:0] DIN; 
reg     [15:0] DOUT; 

wire    RESET;

assign RESET = ~ RSTn; // invert sense for internal use to use posedge

// repeat clocks
assign DCLK0 = SCLK;

assign XCLK0 = SCLK;

// drive ACK high is this OK
assign ACK = 1'b1;

// Flash memory located at 0x800000 (Boot sector will lie at 0x810000)
assign FCSn = MS2n & BMSn;

wire    BRn;

// set BRn to 1
assign BRn = 1'b1;

// temporary address decodes for virtex programming interface
// modify these appropriately
// change A[9] to 1

parameter [9:0]
  ISQCSEL  = 10'b1000010010,    // i2c interface select (decoded)
  ISQC     = 10'b1000010000,    // i2c interface
  REVISION = 10'b1000001101,    // revision register
  SPICTL   = 10'b1000001100,    // spi chip select controls (all in 1)    
  IDDIR    = 10'b1000001001,    // write (direction) ctl register for ID chip
  IDSELECT = 10'b1000001000,    // select control register for ID chip
  CONTROL  = 10'b1000000010,    // control register (supervisor)
  XCFGDATA = 10'b1000000001,    // dsp writing this address makes XCCLK
  XCFGCTL  = 10'b1000000000;    // dsp writes control at this address

// Virtex configuration signals
wire    XCCLK;

reg     XPWDNn;
reg     XPROGn;
reg     XCSPn;    // was XCSn
reg     XRDWRn;
wire    XINITIn;  // incoming XINITn
reg     XINITOn;  // outgoing XINITn
reg     XINITEN;  // open collector enable for XINITn

assign XCCLK = ( (A == XCFGDATA) && (MS1n == 1'b0) && 
                 (WRn == 1'b0) ) ? 1'b0 : 1'b1;

assign DIN = D; 

// write control registers
always @ (posedge WRn or posedge RESET) begin
  if (RESET) begin
    XPWDNn  <= 1'b1;
    XPROGn  <= 1'b1;
    XCSPn   <= 1'b1;   // was XCSn
    XRDWRn  <= 1'b1;
    XINITEN <= 1'b1;
    end
  else if ( (A == XCFGCTL) && (MS1n == 1'b0) ) begin
// change bit assignments as needed
    XPWDNn  <= DIN[0];
    XPROGn  <= DIN[1];
    XCSPn   <= DIN[2];
    XRDWRn  <= DIN[3];
    XINITEN <= DIN[4];
    end
  end

assign XINITIn = XINITn;

always @ (XINITEN) begin    
  if (~XINITEN) begin       // drive when XINITEN is low
    XINITOn = 1'b0;
    end
  else begin                // tristate otherwise
    XINITOn = 1'bz;
    end
  end

assign XINITn = XINITOn;

wire [15:0] XSTAT;  // xilinx status to dsp

assign XSTAT = {8'b10100101, FBUSYn , XDONE, XBUSY, XINITIn, XRDWRn, XCSPn, XPROGn, XPWDNn};

// since XCSn has dual use the DSP changes the meaning by setting
// the SUP bit 
// in that case XCSn now corresponds to the decode in address space
// for the FPGA

reg    SUP;

always @ (posedge WRn or posedge RESET) begin
  if (RESET) begin
    SUP <= 1'b1;
    end
  else if ( (A == CONTROL) && (MS1n == 1'b0) ) begin
    SUP <= DIN[0];
    end
  end

wire   XCSUn;

// FPGA decodes when ever A[9] == 1 and MS1n is active
// change FPGA decode to A[9] == 0
assign XCSUn = (A[9] == 1'b0) && (MS1n == 1'b0) ? 1'b0 : 1'b1;

wire   XCSn;

assign XCSn = SUP ? XCSPn : XCSUn;

wire [15:0] CSTAT;  // control status to dsp

assign CSTAT = {15'b0, SUP};

// ID Chip switch
// there are multiple ID chips that the DSP can talk to
// and these are connected to the CPLD
// there is a local id (TXCTLID) wire that connects the CPLD to the DSP
// the DSP sets the IDSEL register which allows it to talk
// to upto 7 ID chips (currently) and then
// it is bidirectionally connected to that ID chip (or
// the corresponding one wire)

reg    [7:0] IDSEL;   // ID select switch 
reg    IDWR;          // Write ID Chip

always @ (posedge WRn or posedge RESET) begin
  if (RESET) begin
    IDSEL <= 8'b0;
    end
  else if ( (A == IDSELECT) && (MS1n == 1'b0) ) begin
    IDSEL <= DIN[7:0];
    end
  end

always @ (posedge WRn or posedge RESET) begin
  if (RESET) begin
    IDWR  <= 1'b0;
    end
  else if ( (A == IDDIR) && (MS1n == 1'b0) ) begin
    IDWR  <= DIN[0];
    end
  end

wire   DIDIN;                   // dsp end of the id (data in) 
reg    DIDOUT;                  // dsp end of the id (data out) 
wire   DIDEN;                   // dsp end of the id (enable)

wire   [7:0] BIDIN;             // board end of the id (data in)
reg    [7:0] BIDOUT;            // board end of the id (data out) 
wire   [7:0] BIDEN;             // board end of the id (enable)

//assign DIDIN = SYSID;
assign DIDIN = CFLG0n;
//assign BIDIN[0] = TXCTLID;
//assign BIDIN[1] = SYSID;

assign DIDEN = ( (IDSEL[7] & ~IDWR) ||
                 (IDSEL[6] & ~IDWR) ||
                 (IDSEL[5] & ~IDWR) ||
                 (IDSEL[4] & ~IDWR) ||
                 (IDSEL[3] & ~IDWR) ||
                 (IDSEL[2] & ~IDWR) ||
                 (IDSEL[1] & ~IDWR) ||
                 (IDSEL[0] & ~IDWR) ) ;

always @ (DIDEN or IDSEL or BIDIN) begin
  DIDOUT = DIDEN ? ( (IDSEL[7] & BIDIN[7]) ||
                     (IDSEL[6] & BIDIN[6]) ||
                     (IDSEL[5] & BIDIN[5]) ||
                     (IDSEL[4] & BIDIN[4]) ||
                     (IDSEL[3] & BIDIN[3]) ||
                     (IDSEL[2] & BIDIN[2]) ||
                     (IDSEL[1] & BIDIN[1]) ||
                     (IDSEL[0] & BIDIN[0]) ) : 1'bz ;
   end

// could be done as a vector ?

assign BIDEN[0] = IDSEL[0] & IDWR;
assign BIDEN[1] = IDSEL[1] & IDWR;
assign BIDEN[2] = IDSEL[2] & IDWR;
assign BIDEN[3] = IDSEL[3] & IDWR;
assign BIDEN[4] = IDSEL[4] & IDWR;
assign BIDEN[5] = IDSEL[5] & IDWR;
assign BIDEN[6] = IDSEL[6] & IDWR;
assign BIDEN[7] = IDSEL[7] & IDWR;

always @ (BIDEN or DIDIN) begin
  BIDOUT[0] = BIDEN[0] ? DIDIN : 1'bz;
  BIDOUT[1] = BIDEN[1] ? DIDIN : 1'bz;
  BIDOUT[2] = BIDEN[2] ? DIDIN : 1'bz;
  BIDOUT[3] = BIDEN[3] ? DIDIN : 1'bz;
  BIDOUT[4] = BIDEN[4] ? DIDIN : 1'bz;
  BIDOUT[5] = BIDEN[5] ? DIDIN : 1'bz;
  BIDOUT[6] = BIDEN[6] ? DIDIN : 1'bz;
  BIDOUT[7] = BIDEN[7] ? DIDIN : 1'bz;
  end

//assign SYSID = DIDOUT;
assign CFLG0n = DIDOUT;
//assign TXCTLID = BIDOUT[0];
//assign SYSID   = BIDOUT[1];

// regulator syncs
parameter [5:0] TWOMHZ = 6'b100111;    // divide by 40 to give 2 MHz
parameter [5:0] CT01   = 6'b000001;    // count of 1
parameter [5:0] CT11   = 6'b001011;    // count of 11
parameter [5:0] CT21   = 6'b010101;    // count of 21
parameter [5:0] CT31   = 6'b011111;    // count of 31

reg     [5:0] SCT;
reg     SYNC15;
reg     SYNC25;

always @ (posedge SCLK or posedge RESET) begin
  if (RESET) begin
    SCT <= 6'b0;
    end
  else if (SCT == TWOMHZ) begin
    SCT <= 6'b0;
    end
  else begin
    SCT <= SCT + 1;
    end
  end

always @ (posedge SCLK or posedge RESET) begin
  if (RESET) begin
    SYNC15 <= 1'b0;
    end
  else if (SCT == CT11) begin          // count = 11
    SYNC15 <= 1'b1;
    end
  else if (SCT == CT31) begin          // count = 31
    SYNC15 <= 1'b0;
    end
  end

always @ (posedge SCLK or posedge RESET) begin
  if (RESET) begin
    SYNC25 <= 1'b0;
    end
  else if (SCT == CT21) begin          // count = 21
    SYNC25 <= 1'b1;
    end
  else if (SCT == CT01) begin          // count = 1
    SYNC25 <= 1'b0;
    end
  end

// Dowlink selection bits
// need a register

wire    DLSEL0n;
wire    DLSEL1n;

assign DLSEL0n = 1'b1;
assign DLSEL1n = 1'b1;

// i squared c interface
// add a select interface
// drive both wires as they have different addresses
// and the inputs as they are pulled up

wire    SCL0;
wire    SDA0;

wire    SCL1;
wire    SDA1;

wire    SDAOUT;                   // out of SDA bus
wire    SDAIN;                    // in of SDA bus

reg     [15:0] DINQ;              // register input on WRn 
reg     TRIG;                     // toggle on decode
reg     [2:0] TRIGQ;              // sync toggle to SCLK
reg     TRIGP;                    // pulse on SCLK
reg     [7:0] SR;                 // input output shift register (msb first)
reg     [13:0] ICT;               // counter for timing

reg     ISTART;                   // start command
reg     ISTOP;                    // stop command
reg     IREAD;                    // read command
reg     IWRITE;                   // write command
reg     IBUSY;                    // CPLD busy
reg     IACK;                     // receiver ack bit

reg     IOE;                      // output enable for SDA

reg     [7:0] I2CSEL;   // I2C select switch 

assign SDAIN = SDA0 & SDA1;

always @ (posedge WRn or posedge RESET) begin
  if (RESET) begin
    I2CSEL <= 8'b0;
    end
  else if ( (A == ISQCSEL) && (MS1n == 1'b0) ) begin
    I2CSEL <= DIN[1:0];
    end
  end

// register the dsp data
// bits[15:8] contain control and bits[7:0] write data
// toggle TRIG on the decode and dual sync to form pulse

always @ (posedge WRn or posedge RESET) begin
  if (RESET) begin
    DINQ <= 16'b0; 
    TRIG <= 1'b0;
    end
  else if (A == ISQC && MS1n == 1'b0) begin
    DINQ <= DIN;
    TRIG <= ~TRIG;
    end
  end

// TRIGP is dual synced pulse on SCLK for subsequent logic

always @ (posedge SCLK or posedge RESET) begin
  TRIGQ <= RESET ? 3'b0 : {TRIGQ[1:0], TRIG};
  TRIGP <= RESET ? 1'b0 : TRIGQ[2] ^ TRIGQ[1];
  end

// use the reigistered bit to form the command decode
// this should meet set up requirements
// command decode gets automatically turned of internally
// note the set term is pulsed 
// (command register persists till dsp changes it)

always @ (posedge SCLK or posedge RESET) begin
  if (RESET) begin
    ISTART <= 1'b0; 
    ISTOP  <= 1'b0;
    IREAD  <= 1'b0; 
    IWRITE <= 1'b0;
    end
  else if (TRIGP) begin
    ISTART <=  DINQ[8];
    ISTOP  <=  DINQ[9];
    IREAD  <=  DINQ[10];
    IWRITE <=  DINQ[11];
    end
  else if (ISTART == 1'b1 && ICT[9:0] == 10'h1FD) begin
    ISTART <= 1'b0;
    end
  else if (ISTOP == 1'b1 && ICT[9:0] == 10'h3FD) begin
    ISTOP <= 1'b0;
    end 
  else if ( (IREAD == 1'b1 || IWRITE == 1'b1) && ICT == 14'h2500) begin
    IREAD  <= 1'b0;
    IWRITE <= 1'b0;
    end
  end

// for start pulse generation
// register the or of the decodes (this also serves as busy indicator for dsp)

always @ (posedge SCLK or posedge RESET) begin
  IBUSY <= RESET ? 1'b0 : ISTART | ISTOP | IREAD | IWRITE;
  end
  
// counter to produce the slower timing for the I2C bus
// one bit is also used as the clock
// set to approriate values as needed

always @ (posedge SCLK or posedge RESET) begin
  if (RESET) begin
    ICT <= 14'h3FFF;
    end
  else if (ISTART & ~IBUSY) begin
    ICT <= 14'h3E00;
    end
  else if (ISTOP & ~IBUSY) begin
    ICT <= 14'h3D00;
    end
  else if ( (IREAD == 1'b1 || IWRITE == 1'b1) &&
            (IBUSY == 1'b0 || ICT == 14'h2500) ) begin
    ICT <= 14'h000;
    end
  else if (IBUSY) begin
    ICT <= ICT + 1;
    end
  end

//  after a byte write capture input data as the recievers ack

always @ (posedge SCLK or posedge RESET) begin
  if (RESET) begin
    IACK <= 1'b1;
    end
  else if (IWRITE == 1'b1 && ICT == 14'h2300) begin
    IACK <= SDAIN;
    end
  end

// load the shift register (with the pulse / should have enough setup)
// note the read and write timing is different on the shift register

always @ (posedge SCLK or posedge RESET) begin
  if (RESET) begin
    SR <= 8'hFF;
    end
  else if (IWRITE & ~IBUSY) begin
    SR <= DINQ[7:0];
    end
  else if (IWRITE == 1'b1 && ICT[13] == 1'b0 && ICT[12:10] != 3'h0 &&
           ICT[9:0] == 10'h100) begin
    SR <= {SR[6:0], SDAIN};
    end
  else if (IREAD == 1'b1 && ICT[13] == 1'b0 && ICT[9:0] == 10'h300) begin
    SR <= {SR[6:0], SDAIN};
    end
  end

// output enable
// 100ns nominally before switching on/off for read write
// start stop does not matter so much

always @ (posedge SCLK or posedge RESET) begin
  if (RESET) begin
    IOE <= 1'b0;
    end
  else if ( (ISTART == 1'b1 || ISTOP == 1'b1) && IBUSY == 1'b0 ||
             IREAD  == 1'b1 && ICT == 14'h2008 ||
             IWRITE == 1'b1 && ICT == 14'h0008) begin
    IOE <= 1'b1;
    end
  else if (ISTART == 1'b1 && ICT[9:0] == 10'h1F8 ||
           ISTOP  == 1'b1 && ICT[9:0] == 10'h3F8 ||
           IREAD  == 1'b1 && ICT == 14'h24F8 ||
           IWRITE == 1'b1 && ICT == 14'h2008) begin
    IOE <= 1'b0;
    end
  end

// set to generate a nack on read unless D[13] set
// IOE takes care of approprete shut off / gating

assign SDAOUT = ISTART & ICT[9] & ~ICT[8] | ISTOP & ICT[9] & ICT[8] |
                IREAD & ~DINQ[13] | IWRITE & SR[7];

assign SDA0 = IOE ? SDAOUT : 1'bz;
assign SDA1 = IOE ? SDAOUT : 1'bz;

assign SCL0 = ICT[9];
assign SCL1 = ICT[9];

wire    [3:0] REV;               // CPLD revision

assign REV = 4'h3;

// read status registers
//always @ (RDn or A or MS1n or XSTAT or FSTAT) begin

always begin
  if ( (A == XCFGCTL) && (MS1n == 1'b0) && (RDn == 1'b0) ) begin
    DOUT = XSTAT;
    end
  else if ( (A == CONTROL) && (MS1n == 1'b0) && (RDn == 1'b0) ) begin
    DOUT = CSTAT;
    end
  else if ( (A == IDSELECT) && (MS1n == 1'b0) && (RDn == 1'b0) ) begin
    DOUT = {8'b0, IDSEL};
    end
  else if ( (A == IDDIR) && (MS1n == 1'b0) && (RDn == 1'b0) ) begin
    DOUT = {15'b0, IDWR};
    end
  else if ( (A == REVISION) && (MS1n == 1'b0) && (RDn == 1'b0) ) begin
    DOUT = {12'b0, REV};
    end
// add i2c interface
  else if ( (A == ISQC) && (MS1n == 1'b0) && (RDn == 1'b0) ) begin
    DOUT = {IBUSY, IACK, DINQ[13], 1'b0, DINQ[11:8], SR};
    end
  else  begin
    DOUT = 16'bz;
    end
  end

assign D = DOUT;

// place holder so as not to loose the signals
// hope it does not mess the dsp up

//assign CFLG0n = DCLK1 | MS0n | MS3n | IOMSn |

assign DUMMY  = DCLK1 | MS0n | MS3n | IOMSn ;

endmodule
