/***********************************  dwarf.h  ********************************
 * $Source: /home/cvs/ESP/gen2/software/msp430/include/dwarf.h,v $
 *  Copyright (C) 2003 MBARI
 *
 *  MBARI Proprietary Information. All rights reserved.
 * $Id: dwarf.h,v 1.16 2005/05/09 18:04:32 sjensen Exp $
 *
 * This header documents the hardware interface between the
 * MSP430F1xx microcontroller and all ESP Dwarf flavors.
 *
 *****************************************************************************/

#ifndef dwarf_h
#define dwarf_h

#include "types.h"      //basic data types and definitions
#include "msp430.h"     //register and I/O definitions

// Direct I/O bits

// I2C Clock Counter
#define DWRFI2COUT  P2OUT
#define DWRFI2CHOLD 0x01    //hold I2C counter at 1, 9 or 10 [active high]
#define DWRFI2CINC  0x02    //increment count by 9 on falling edge

// Dwarf Core Interrupt Inputs
#define DWRFIRQIN   P2IN
#define DWRFIRQIES  P2IES
#define DWRFIRQIE   P2IE
#define DWRFIRQIFG  P2IFG
#define DWRF_VECTOR PORT2_VECTOR

//All inputs that generate interrupts must be on DWRFIRQIN port
#define DWRFRTS     0x04    //USART1 RTS input [active low]
#define DWRFRxD     0x08    //USART1 RxD connected to 3.7 [break is active low]
#define DWRFI2CSCL  0x10    //tied to I2C SCL
#define DWRFRosc    0x20    //MSP430 external oscillator resister in
#define DWRFCPLDIRQ 0x80    //CPLD interrupt request [active high]

#define DWRFIRQS   (DWRFRTS|DWRFRxD|DWRFCPLDIRQ|DWRFI2CSCL)  //all irqs

//specials = DWRFRosc to enable external Rosc resister, 0 otherwise
#define initDWRFIRQS(specials) { \
  P2DIR = P2OUT = ~(DWRFIRQS | (specials)); \
  P2SEL = (specials); \
  if (specials & DWRFRosc) set8 (BCSCTL2, DCOR); \
  P2IFG = P2IES = 0; \
}

// RS232 handshake outputs for USART1
#define DWRFHANDOUT P5OUT
#define DWRFCTS     0x80    //CTS output [active low]

#ifndef initDWRFCLKS  //allow application to override this default initialization
#define initDWRFCLKS() { \
  P5DIR = P5OUT = 0xff;  /* all port 5 bits are outputs + deassert CTS */ \
  P5SEL = 0x70;  /* drive MCLK out P5.4, SMCLK out P5.5, ACLK out P5.6 */ \
}
#endif


// Dwarf Core I/O bus
#define DWRFBUSIN    P1IN    //byte wide bus on port 1
#define DWRFBUSOUT   P1OUT
#define DWRFBUSDIR   P1DIR
#define DWRFBUSSEL   P1SEL

#define DWRFSTRBOUT  P3OUT   //all bus strobes on port 3
#define DWRFSTRBDIR  P3DIR
#define DWRFSTRBIN   P3IN
#define DWRFBUSAS    0x01    //latch register # on leading edge [active low]
#define DWRFBUSWR    0x04    //CPLD samples bus on trailing edge [active low]
#define DWRFBUSRD    0x10    //CPLD drives bus on while active [active low]
#define DWRFBUSRESET 0x20    //reset CPLDs on next MCLK [active low, open collector]

#define DWRFSTRBS   (DWRFBUSAS|DWRFBUSWR|DWRFBUSRD)


static inline void initDWRFBUS (void)
// note that this leaves bus reset asserted!
{
  P3SEL = 0xc0;   //for USART1 RxD and TxD
  P3OUT = ~(DWRFBUSRESET|0x40); //reset bus, do RS-232 TX "BREAK" if P3SEL.6 cleared
  P3DIR = ~0x8A; //drive CPLD reset low, USART1 RxD is an input, TxD is an output
                 //I2C SDA & SCL float
  DWRFBUSDIR = DWRFBUSOUT = ~(DWRFBUSSEL = 0);
}


//one-time initialization of all digital I/O ports except 4 & 6
//also resets the bus synchronously
#define initDWRF(specials) { \
  initDWRFIRQS(specials); \
  initDWRFBUS(); \
  initDWRFCLKS(); \
}


static inline void endDWRFreset (void)
// end the reset pulse begun by initDWRFBUS or initDWRF
{
  clear8 (DWRFSTRBDIR, DWRFBUSRESET); //let it float high by making it an input
}


// CPLD Registers common to all dwarves

/*
  Read Only I2C Clock counter register
*/
#define DWRFI2CSCLCNT  0      //6-bit counter.  uppper bits are zero.

/*
  Interrupt Enable and Flag Registers
*/
#define DWRFIE       0    //write only interrupt enable mask [active high]
#define DWRFIFG      1    //read/write interrupt flag mask [active high]
//writing a one bit to DWRFIFG clears the corresponding interrupt flag

#define DWRFBUS      0x01   //interrupt from dwarf daughter card
#define DWRFI2CHELD  0x80   //I2C clock being held due to zero count


/*
  Primitives for CPLD register access
    These exploit the address register's autoincrementing feature
    where address is incremented on the trailing edge of 
    (WR|RD) iff AS was active at its leading edge
    Interrupts must be disabled throughout a sequence of accesses.
    
    To read/write a sequence of registers:
      setDWRFadr (firstRegsAdr);
      (read|write)DWRFnext();  //bumps adr afterwords
      ...
      (read|write)DWRF();  //last register in sequence
      
    To access the same register repeatedly:
      setDWRFadr (regsAdr);
      freezeDWRFadr ();     //cancels autoincrement of adr
      (read|write)DWRF();   //accesses regsAdr
      ...
      (read|write)DWRF();   //accesses regsAdr again
*/

static inline
  void driveDWRFbus (void)
{
  DWRFBUSDIR = -1;    //drive the bus
}

static inline
  void floatDWRFbus (void)
{
  DWRFBUSDIR = 0;     //prepare to read the bus
}

static inline
  void setDWRFadr (byte adr)
/*
  assign the CPLD's 4-bit address register to the 4 LSBs of adr
  leaves the address strobe activated and MSP430 driving bus.
*/
{
  DWRFBUSOUT = adr;
  driveDWRFbus();
  clear8 (DWRFSTRBOUT, DWRFBUSAS);  //CPLD latches address on leading edge of AS
} //AS is active and we are driving on the bus!


static inline 
  void refreshDWRFadr (byte adr)
/*
  assign the CPLD's 4-bit address register to the 4 LSBs of adr
  leaves the address strobe activated and MSP430 driving bus.
  like setDWRFadr, but assumes that bus already being driven by the MSP430!
*/
{
  DWRFBUSOUT = adr;
  clear8 (DWRFSTRBOUT, DWRFBUSAS);  //CPLD latches address on leading edge of AS
}  //AS is active and we are (still) driving on the bus!


static inline
  void freezeDWRFadr (void)
/*
  deassert AS before next read or write so that the register
  at the current CPLD address remains selected afterwords
*/
{
  set8 (DWRFSTRBOUT, DWRFBUSAS);
}


static inline
  byte readDWRFnext (void)
/*
  The DWRFbus must be "floating" 
  return the value of the currently selected CPLD register without
  deactivating the address strobe so that, unless freezeDWRFadr() was
  called, the next read or write will access the next register.
*/
{
  byte result;
  clear8 (DWRFSTRBOUT, DWRFBUSRD);
  result = DWRFBUSIN;
  set8 (DWRFSTRBOUT, DWRFBUSRD);
  return result;
}


static inline
  byte readDWRFlast (void)
/*
  The DWRFbus must be "floating" 
  return the value of the currently selected CPLD register and
  deactivate the address strobe
*/
{
  byte result;
  clear8 (DWRFSTRBOUT, DWRFBUSRD);
  result = DWRFBUSIN;
  set8 (DWRFSTRBOUT, DWRFBUSAS|DWRFBUSRD);
  return result;
}


static inline
  byte readDWRFfirst (byte baseAdr)
/*
  Read first of series of registers.
  Bus is floating and AS is active on exit.
*/
{
  return setDWRFadr(baseAdr), floatDWRFbus(), readDWRFnext();
}


static inline
  byte readDWRF (byte adr)
/*
  return the value of the specified CPLD register
*/
{  
  return setDWRFadr(adr), floatDWRFbus(), readDWRFlast();
}


static inline
  void writeDWRFnext (byte value)
/*
  The DWRFbus must be "driven" 
  assign the currently selected CPLD register without
  deactivating the address strobe so that, unless freezeDWRFadr() was
  called, the next read or write will access the next register.
*/
{
  DWRFBUSOUT = value;  //assume MSP430 is still driving the bus
  clear8 (DWRFSTRBOUT, DWRFBUSWR);
  set8 (DWRFSTRBOUT, DWRFBUSWR);
}


static inline
  void writeDWRFlast (byte value)
/*
  The DWRFbus must be "driven" 
  assign the currently selected CPLD register and
  deactivate the address strobe
*/
{
  DWRFBUSOUT = value;  //assume MSP430 is still driving the bus
  clear8 (DWRFSTRBOUT, DWRFBUSWR);
  set8 (DWRFSTRBOUT, DWRFBUSAS|DWRFBUSWR);
}


static inline
  void writeDWRFfirst (byte baseAdr, byte value)
/*
  Write first of series of registers.
  Bus is driven and AS is active on exit.
*/
{
  setDWRFadr(baseAdr), writeDWRFnext(value);
}


static inline
  void writeDWRF (byte adr, byte value)
/*
  Write value to the specified DWARF CPLD register
*/
{
  setDWRFadr (adr), writeDWRFlast (value);
}


/*
  Primitives for CPLD interrupt management
    [interrupts should be disabled during all of these]
*/

#ifndef DWARFTOP_H //compiling top-level dwarf application
extern
#endif
byte DWRFIEimage;  //readable copy of DWRFIE register

static inline void disableDWRFirqs (byte mask)
{
  writeDWRF(DWRFIE, DWRFIEimage &= ~mask);
}

static inline void enableDWRFirqs (byte mask)
{
  writeDWRF(DWRFIE, DWRFIEimage |= mask);
}

static inline void endDWRFirqs (byte mask)
{
  writeDWRFfirst(DWRFIE, DWRFIEimage &= ~mask);
  writeDWRFlast(mask);
}

static inline void updateDWRFirqs (void)
{
  writeDWRF(DWRFIE, DWRFIEimage);
}

static inline void updateDWRFirqsAndClear (byte cMask)
{
  writeDWRFfirst(DWRFIE, DWRFIEimage);
  writeDWRFlast(cMask);
}

static inline void startDWRFirqs (byte mask)
{
  writeDWRFfirst(DWRFIE, DWRFIEimage |= mask);
  writeDWRFlast(mask);  //note that interrupts are unmasked before cleared!
}

#endif
