////-----------------------------------------------------------------
// Philips SCC2691 UART: XJEase device file
// SCC2691.xje Revision: 1.13
// (c)2001-2007 XJTAG Limited
//
// Disclaimer: XJTAG makes no guarantees whatsoever
// about this code.  You use it at your own risk ...
// This code requires XJTAG version 1.4 or later.
//
// This file tests Non-JTAG Serial Device using Test()(INT result)
//-----------------------------------------------------------------
// These tests assume that TXD/RXD and MPO/MPI pairs are
// shorted together externally on a loopback connector.
//-----------------------------------------------------------------

DEVICE NAME := "Philips SCC2691 UART"

  PINS // PLCC package
    RDN   := 2;
    RXD   := 3;
    TXD   := 4;
    MPO   := 5;
    MPI   := 6;
    NC    := 7,8,23,26;
    ADDR  := 9,10,11;
    X1    := 12;
    X2    := 13;
    RESET := 14;
    GND   := 15;
    INTRN := 16;
    CEN   := 17;
    DATA  := 18,19,20,21,22,24,25,27;
    WRN   := 28;
  END;

  DISABLE DEVICE
    CEN :=1;
  END;

  TEST COVERAGE
    RESET := OPEN LO;
    CEN   := OPEN HI;
    WRN   := OPEN HI;
    RDN   := OPEN HI;
    ADDR  := SHORTS OPEN HI LO;
    DATA  := SHORTS OPEN HI LO;
    MPO   := FUNCTIONAL;
    MPI   := FUNCTIONAL;
    TXD   := FUNCTIONAL;
    RXD   := FUNCTIONAL;
  END;
END;

//-----------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------

// Debug mode activated when TRUE
CONST INT DEBUG := FALSE;

// Reset pin is accessible from JTAG device when TRUE
CONST INT USE_RESET := FALSE;

// Misc constants
STRING TAB      := "    ";
CONST INT RESULT_PASS   := 0;
CONST INT RESULT_FAIL := 0xFFFF;

// UART Register addresses
CONST INT REG_MR  := 0;  // Mode register
CONST INT REG_SR  := 1;  // Channel Status register
CONST INT REG_CSR := 1;  // Clock select register
CONST INT REG_BRG := 2;  // Baud rate generator test
CONST INT REG_CR  := 2;  // Command register
CONST INT REG_RHR := 3;  // receive holding register
CONST INT REG_THR := 3;  // Transmit holding register
CONST INT REG_TST := 4;  // 1X/16X Test register
CONST INT REG_ACR := 4;  // Auxiliary control register
CONST INT REG_ISR := 5;  // Interrupt Status register
CONST INT REG_IMR := 5;  // Interrupt mask register
CONST INT REG_CTU := 6;  // Counter/timer Upper register
CONST INT REG_CTL := 7;  // Counter/timer lower register

// ACR (Auxiliary control register)
CONST INT ACR_MPO_RTSN     := 0x00; // ACR[2..0]
CONST INT ACR_MPO_CTO      := 0x01;
CONST INT ACR_MPO_TXCX1    := 0x02;
CONST INT ACR_MPO_TXCX16   := 0x03;
CONST INT ACR_MPO_RXCX1    := 0x04;
CONST INT ACR_MPO_RXCX16   := 0x05;
CONST INT ACR_MPO_TXRDY    := 0x06;
CONST INT ACR_MPO_RXRDY    := 0x07;
CONST INT ACR_POWERDOWN    := 0x00; // ACR[3]
CONST INT ACR_POWERUP      := 0x08;
CONST INT ACR_COUNT_MPI    := 0x00; // ACR[6..4]
CONST INT ACR_COUNT_MPI16  := 0x10;
CONST INT ACR_COUNT_TXC    := 0x20;
CONST INT ACR_COUNT_XTAL   := 0x30;
CONST INT ACR_TIMER_MPI    := 0x40;
CONST INT ACR_TIMER_MPI16  := 0x50;
CONST INT ACR_TIMER_XTAL   := 0x60;
CONST INT ACR_TIMER_XTAL16 := 0x70;
CONST INT ACR_BRG_SET1     := 0x00; // ACR[7]
CONST INT ACR_BRG_SET2     := 0x80;

// CSR (Clock select register)
CONST INT CSR_TX_50_75        := 0x0;
CONST INT CSR_TX_110_110      := 0x1;
CONST INT CSR_TX_135_135      := 0x2;
CONST INT CSR_TX_200_150      := 0x3;
CONST INT CSR_TX_300_300      := 0x4;
CONST INT CSR_TX_600_600      := 0x5;
CONST INT CSR_TX_1200_1200    := 0x6;
CONST INT CSR_TX_1050_2000    := 0x7;
CONST INT CSR_TX_2400_2400    := 0x8;
CONST INT CSR_TX_4800_4800    := 0x9;
CONST INT CSR_TX_7200_1800    := 0xA;
CONST INT CSR_TX_9600_9600    := 0xB;
CONST INT CSR_TX_38K4_19K2    := 0xC;
CONST INT CSR_TX_TIMER        := 0xD;
CONST INT CSR_TX_MPIX16       := 0xE;
CONST INT CSR_TX_MPIX1        := 0xF;
CONST INT CSR_RX_50_75        := 0x00;
CONST INT CSR_RX_110_110      := 0x10;
CONST INT CSR_RX_135_135      := 0x20;
CONST INT CSR_RX_200_150      := 0x30;
CONST INT CSR_RX_300_300      := 0x40;
CONST INT CSR_RX_600_600      := 0x50;
CONST INT CSR_RX_1200_1200    := 0x60;
CONST INT CSR_RX_1050_2000    := 0x70;
CONST INT CSR_RX_2400_2400    := 0x80;
CONST INT CSR_RX_4800_4800    := 0x90;
CONST INT CSR_RX_7200_1800    := 0xA0;
CONST INT CSR_RX_9600_9600    := 0xB0;
CONST INT CSR_RX_38K4_19K2    := 0xC0;
CONST INT CSR_RX_TIMER        := 0xD0;
CONST INT CSR_RX_MPIX16       := 0xE0;
CONST INT CSR_RX_MPIX1        := 0xF0;

// CR (Command Register)
CONST INT CR_ENABLE_RX        := 0x01; // enable receiver
CONST INT CR_DISABLE_RX       := 0x02; // disable receiver
CONST INT CR_ENABLE_TX        := 0x04; // enable transmitter
CONST INT CR_DISABLE_TX       := 0x08; // disable transmitter

// Misc commands in Command register bits [7..4]
CONST INT CMD_NULL         := 0x00; // null command
CONST INT CMD_RESET_MR     := 0x10; // reset mode register pointer
CONST INT CMD_RESET_RX     := 0x20; // reset receiver
CONST INT CMD_RESET_TX     := 0x30; // reset transmitter
CONST INT CMD_RESET_ERROR  := 0x40; // reset error status
CONST INT CMD_RESET_BCINT  := 0x50; // reset break change interrupt
CONST INT CMD_START_BREAK  := 0x60; // force break condition
CONST INT CMD_STOP_BREAK   := 0x70; // stop break condition
CONST INT CMD_START_CT     := 0x80; // Start counter/timer
CONST INT CMD_STOP_CT      := 0x90; // Stop counter
CONST INT CMD_ASSERT_RTSN  := 0xa0; // assert RTSN
CONST INT CMD_NEGATE_RTSN  := 0xb0; // negate RTSN
CONST INT CMD_RESET_MCINT  := 0xc0; // Reset MPI change interrupt

// MR1 (Mode register 1)
CONST INT MR1_5BIT         := 0x0;  // Bits per character (bits 1..0)
CONST INT MR1_6BIT         := 0x1;
CONST INT MR1_7BIT         := 0x2;
CONST INT MR1_8BIT         := 0x3;
CONST INT MR1_EVEN_PARITY  := 0x0;  // Parity (bit 2)
CONST INT MR1_ODD_PARITY   := 0x4;
CONST INT MR1_WITH_PARITY  := 0x00; // Parity type (bits 4..3)
CONST INT MR1_FORCE_PARITY := 0x08;
CONST INT MR1_NO_PARITY    := 0x10;
CONST INT MR1_SPEC_PARITY  := 0x18;
CONST INT MR1_ERROR_CHAR   := 0x00; // Error mode (bit 5)
CONST INT MR1_ERROR_BLOCK  := 0x20;
CONST INT MR1_RXINT_RXRDY  := 0x00; // RxINT select (bit 6)
CONST INT MR1_RXINT_FFULL  := 0x40;
CONST INT MR1_RXRTS_OFF    := 0x00; // RxRTS Control (bit 7)
CONST INT MR1_RXRTS_ON     := 0x80;

// MR2 (Mode register 2)
CONST INT MR2_STOP1        := 0x07; // Stop bits (bits 3..0)
CONST INT MR2_STOP2        := 0x0f;
CONST INT MR2_CTSENBTX_OFF := 0x00; // CTS Enable TX control (bit 4)
CONST INT MR2_CTSENBTX_ON  := 0x10;
CONST INT MR2_TXRTS_OFF    := 0x00; // TxRTS Control (bit 5)
CONST INT MR2_TXRTS_ON     := 0x20;
CONST INT MR2_NORMAL       := 0x00; // Channel Mode (bits 7..6)
CONST INT MR2_AUTO_ECHO    := 0x40;
CONST INT MR2_LOCAL_LOOP   := 0x80;
CONST INT MR2_REMOTE_LOOP  := 0xC0;

// ISR (interrupt status register)
CONST INT ISR_TXRDY        := 0x01; // transmitter ready
CONST INT ISR_TXEMT        := 0x02; // transmitter empty
CONST INT ISR_RXRDY        := 0x04; // receiver ready
CONST INT ISR_DELTA_BREAK  := 0x08; // break change
CONST INT ISR_COUNTER_RDY  := 0x10; // counter ready
CONST INT ISR_UNUSED       := 0x20; // unused bit
CONST INT ISR_MPI_STATE    := 0x40; // MPI current state
CONST INT ISR_MPI_CHANGE   := 0x60; // MPI state changed

//--------------------------------------------------------
// Device tests
//--------------------------------------------------------

Test()(INT result)

  INT data;
  INT errors;

  Initialise()();

  TestDataBus()(result);  IF result != 0 THEN errors := errors + 1; END;
  TestIo()(result);       IF result != 0 THEN errors := errors + 1; END;
  TestInt()(result);      IF result != 0 THEN errors := errors + 1; END;
  LoopbackTest()(result); IF result != 0 THEN errors := errors + 1; END;

  // Check for any errors
  IF errors != 0 THEN
    result := RESULT_FAIL;
    PRINT(TAB, "One or more tests failed.\n");
  ELSE
    result := RESULT_PASS;
    PRINT(TAB, "All tests passed.\n");
  END;

END;

//--------------------------------------------------------
// LoopBackTest
// Requires TXD and RXD to be connected together externally
//--------------------------------------------------------

LoopbackTest()(INT result)

  INT isr WIDTH 8;
  INT timeout;
  INT txdata WIDTH 8 := 0x5A;
  INT data WIDTH 8;
  INT pass;

  PRINT(TAB, "Data loopback test.");

  // Disable RTS control and CTS enable
  WriteCycle(REG_CR, CMD_RESET_MR + CR_DISABLE_TX + CR_DISABLE_RX);
  WriteCycle(REG_MR, MR1_RXINT_RXRDY + MR1_8BIT + MR1_NO_PARITY + MR1_RXRTS_OFF); // Write MR1
  WriteCycle(REG_MR, MR2_STOP1 + MR2_CTSENBTX_OFF + MR2_TXRTS_OFF + MR2_NORMAL);  // Write MR2

  // Reset any outstanding errors and transmitter and rxvr
  WriteCycle(REG_CR, CMD_STOP_CT);
  WriteCycle(REG_CR, CMD_RESET_ERROR);
  WriteCycle(REG_CR, CMD_RESET_RX);
  WriteCycle(REG_CR, CMD_RESET_TX);

  // Set MPO pin to be 1 x TxC output  and the time/counter to count MPI transitions
  WriteCycle(REG_ACR, (ACR_POWERUP + ACR_BRG_SET1));

  // Set baud rate to 50baud TX/RX (baud rate set 1)
  WriteCycle(REG_CSR, (CSR_TX_50_75 + CSR_RX_50_75));

  // Disable then re-enable transmitter and receiver
  WriteCycle(REG_CR, (CMD_NULL + CR_DISABLE_TX + CR_DISABLE_RX));
  WriteCycle(REG_CR, (CMD_NULL + CR_ENABLE_TX + CR_ENABLE_RX));

  // Wait for TxRdy bit in ISR
  timeout := NOW() + 1000;
  DO
    ReadCycle(REG_ISR)(isr);
    IF DEBUG THEN
      PRINT("ISR = 0x", HEX(isr), "\n");
    END;
  WHILE !(isr & ISR_TXRDY) && (NOW() < timeout)
  END;

  IF !(isr & ISR_TXRDY) THEN
    PRINT("Failed to read TXRDY bit in ISR.\n");
    result := RESULT_FAIL;
    RETURN;
  END;

  FOR pass := 0 FOR 16
    // Put character into THR
    WriteCycle(REG_THR, txdata + pass);

    // Wait for RxRDY bit in ISR
    timeout := NOW() + 1000;
    DO
      ReadCycle(REG_ISR)(isr);
      IF DEBUG THEN PRINT("ISR = 0x", HEX(isr), "\n"); END;
    WHILE !(isr & ISR_RXRDY) && (NOW() < timeout)
    END;

    IF !(isr & ISR_RXRDY) THEN
      PRINT("Timeout waiting for RXRDY bit in ISR.\n");
      result := RESULT_FAIL;
      RETURN;
    END;

    // Read character from RHR
    ReadCycle(REG_RHR)(data);
    IF data != (txdata + pass) THEN
      PRINT("Receive data error 0x", HEX(data), ", expected 0x", HEX(txdata), "\n");
      result := RESULT_FAIL;
      RETURN;
    END;

    PRINT(".");
  END;

  PRINT("passed OK.\n");
  result := RESULT_PASS;
  RETURN;

END;

//--------------------------------------------------------
// TestIo - test the MPI and MPO pins by driving the baud rate
// clock out of MPO and counting transitions on MPI.
// Requires MPI and MPO to be connected together externally
//--------------------------------------------------------

TestIo()(INT result)

  INT ctur, ctlr, isr WIDTH 8;
  INT startTime, endTime, period;
  INT cycles := 50;

  PRINT(TAB, "Testing IO pins and baud rate generator...");

  // Disable RTS control and CTS enable
  WriteCycle(REG_CR, CMD_RESET_MR + CR_DISABLE_TX + CR_DISABLE_RX);
  WriteCycle(REG_MR, MR1_8BIT  + MR1_NO_PARITY    + MR1_RXRTS_OFF);              // Write MR1
  WriteCycle(REG_MR, MR2_STOP1 + MR2_CTSENBTX_OFF + MR2_TXRTS_OFF + MR2_NORMAL); // Write MR2

  // Reset any outstanding errors and transmitter and rxvr
  WriteCycle(REG_CR, CMD_RESET_ERROR);
  WriteCycle(REG_CR, CMD_RESET_RX);
  WriteCycle(REG_CR, CMD_RESET_TX);

  // Set MPO pin to be 1 x TxC output  and the time/counter to count MPI transitions
  WriteCycle(REG_ACR, (ACR_COUNT_MPI + ACR_POWERUP + ACR_BRG_SET1 + ACR_MPO_TXCX1));

  // Set baud rate to 50baud TX/RX (baud rate set 1)
  WriteCycle(REG_CSR, (CSR_TX_50_75 + CSR_RX_50_75));

  // Enable transmitter and receiver and start the Counter/timer
  WriteCycle(REG_CR, (CMD_STOP_CT));
  WriteCycle(REG_CTU, ((cycles-1) >> 8));
  WriteCycle(REG_CTL, ((cycles-1) & 0xff));
  startTime := NOW();
  WriteCycle(REG_CR, (CMD_START_CT + CR_ENABLE_TX + CR_ENABLE_RX));

  // Wait for counter/timer interrupt or timeout
  DO
    ReadCycle(REG_ISR)(isr);
    IF DEBUG THEN
      ReadCycle(REG_CTU)(ctur);
      ReadCycle(REG_CTL)(ctlr);
      PRINT("ISR = 0x", HEX(isr), "CT = ", (ctur*256 + ctlr), "\n");
    END;
  WHILE !(isr & ISR_COUNTER_RDY) && (NOW() < (40 * cycles) + startTime)
  END;
  endTime := NOW();

  period := (endTime - startTime)/cycles;

  // Check the measured time for the cycles Allow +/- 1mSec and 50 baud
  IF (period > 21) || (period < 19) THEN
    PRINT("Failed, elapsed time for ", cycles, " cycles was ", (endTime - startTime), "mSec\n");
    result := RESULT_FAIL;
    RETURN;
  END;

  PRINT("Passed, CLK period = ", period, "mSec\n");
  result := RESULT_PASS;
  RETURN;

END;

//--------------------------------------------------------
// Test the data bus by using a walking bit in the mode
// registers.
//--------------------------------------------------------

TestDataBus()(INT result)
  INT bit;
  INT data WIDTH 8;
  INT testData WIDTH 8;

  PRINT(TAB, "Testing data bus...\n");
  FOR bit := 0 TO 7
    testData := 1 << bit;
    IF DEBUG THEN PRINT("Test bit ", bit, ", test data 0x", HEX(testData), "\n"); END;
    WriteCycle(REG_CR, CMD_RESET_MR + CR_DISABLE_TX + CR_DISABLE_RX); // reset mode register pointer
    WriteCycle(REG_MR, testData)();            // Write MR1
    WriteCycle(REG_MR, ~testData)();           // Write MR2
    WriteCycle(REG_CR, CMD_RESET_MR + CR_DISABLE_TX + CR_DISABLE_RX); // reset mode register pointer
    ReadCycle(REG_MR)(data);                   // Read MR1
    IF data != testData THEN
      PRINT(TAB, "Failed testing bit ", bit, " in mode register 1.\n");
      result := RESULT_FAIL;
      RETURN;
    END;
    ReadCycle(REG_MR)(data);                   // Read MR2
    IF data != ~testData THEN
      PRINT(TAB, "Failed testing bit ", bit, " in mode register 2.\n");
      result := RESULT_FAIL;
      RETURN;
    END;
  END;

  result := RESULT_PASS;
END;

//--------------------------------------------------------
// Testint - relies on TestIo()() leaving bits set in the ISR
// and should be called after TestIo()()
//--------------------------------------------------------

TestInt()(INT result)

  INT interrupt;

  PRINT(TAB, "Testing interrupt pin...");

  WriteCycle(REG_IMR, 0);
  SET interrupt := INTRN;
  IF (interrupt = 0) THEN
    PRINT("Interrupt pin stuck at 0.\n");
    result := RESULT_FAIL;
    RETURN;
  END;

  WriteCycle(REG_IMR, 0xFF);
  SET interrupt := INTRN;
  IF (interrupt != 0) THEN
    PRINT("Interrupt not detected.\n");
    result := RESULT_FAIL;
    RETURN;
  END;

  WriteCycle(REG_IMR, 0);
  PRINT("Interrupt active & inactive detected OK.\n");
  result := RESULT_PASS;
  RETURN;

END;

//--------------------------------------------------------
// Device write
//--------------------------------------------------------

WriteCycle(INT address, INT data)()

  SET ADDR := address[2..0], DATA := data[7..0], CEN := 0, WRN := 0;
  SET WRN := 1, CEN := 1, DATA := I;
  IF DEBUG THEN PRINT("Write 0b", BIN(data), " to address 0x", HEX(address), "\n"); END;

END;

//--------------------------------------------------------
// Device write
//--------------------------------------------------------

ReadCycle(INT address)(INT data)

  SET ADDR := address[2..0], DATA := I, CEN := 0, RDN := 0;
  SET RDN := 1, CEN := 1, data[7..0] := DATA;
  IF DEBUG THEN PRINT("Read 0b", BIN(data), " from address 0x", HEX(address), "\n"); END;

END;

//--------------------------------------------------------
// Device initialise
//--------------------------------------------------------

Initialise()()

  INT timeout;

  SET WRN := 1, RDN := 1, CEN := 1, ADDR := 0, DATA := I;
  IF (USE_RESET) THEN
    // Perform a reset if the pin is reachable
    SET RESET := 1;
    SLEEP(10);
    SET RESET := 0;
  END;

  // Disable RTS control and CTS enable and reset the mode register pointers
  WriteCycle(REG_CR, CMD_RESET_MR + CR_DISABLE_TX + CR_DISABLE_RX);

  // Stop counter/timer
  // Reset any outstanding errors and transmitter and rxvr
  WriteCycle(REG_CR, CMD_STOP_CT);
  WriteCycle(REG_CR, CMD_RESET_ERROR);
  WriteCycle(REG_CR, CMD_RESET_RX);
  WriteCycle(REG_CR, CMD_RESET_TX);

  // Mask all interrupts
  WriteCycle(REG_IMR, 0);

  // Disable power down mode
  WriteCycle(REG_ACR, ACR_POWERUP);

END;