/* ------------------------------------------------------
SMSC LAN91C111/113 Ethernet Controller (16-bit): XJEase device file
LAN91C11X.xje Revision: 1.18
(c)2002-2007 XJTAG Limited

Disclaimer: XJTAG makes no guarantees whatsoever
about this code.  You use it at your own risk ...

If you find any problems with this file, please
contact support@xjtag.com

This file tests Non-JTAG Ethernet using Test()(INT result)
------------------------------------------------------This file is setup for 16
bit testing ethernet testing.
It tests the Data bus in 16 bits mode and in 32 bit mode
using a ram test. */
DEVICE NAME := "LAN91C111-113"
  COMPAT_VERSION := 1;
  DESCRIPTION := "";

  PINS
    
    //TQFP
    /*    
  ADDR      := 92,91,90,89,88,87,86,85,84,83,82,81,80,79,78;
  DATA      := 48,49,50,51,53,54,55,56,58,59,60,61,63,64,65,66,68,69,70,71,73,74,75,76,99,100,101,102,104,105,106,107;
  nRD       := 31;
  nWR       := 32;
  nADS      := 37;
  ARDY      := 38;
  INTR0     := 29;
  RESET     := 30;
  AEN       := 41;
  nBE       := 97, 96, 95, 94;

  TPO_POS   := 14;
  TPO_NEG   := 15;
  TPI_POS   := 17;
  TPI_NEG   := 28;
  XTALI     := 127;
    */
  
    //QFP
    DATA := 70, 71, 72, 73, 75, 76, 77, 78,
            101, 102, 103, 104, 106, 107, 108, 109;
    ADDR := 94, 93, 92, 91, 90, 89, 88, 87, 96, 85, 84, 83, 82, 81, 80;
    nRD := 33;
    nWR := 34;
    ARDY := 40;
    INTR0 := 31;
    RESET := 32;
    nBE := 97, 96;
    AEN := 43;
    TPO_POS := 16;
    TPO_NEG := 17;
    TPI_POS := 19;
    TPI_NEG := 20;
    nADS := 39;
    LEDa := 24;
    LEDb := 25;
    XTALI := 1;
  END;

  DISABLE DEVICE
    nRD := 1;
    INTR0 := Z;
  END;

  TEST COVERAGE
    DATA := OPEN SHORTS LO HI;
    ADDR := OPEN SHORTS LO HI;
    nRD := OPEN LO HI;
    nWR := OPEN LO HI;
    INTR0 := OPEN LO HI;
    nBE := HI;
    AEN := OPEN HI;
    TPO_POS := FUNCTIONAL;
    TPO_NEG := FUNCTIONAL;
    TPI_POS := FUNCTIONAL;
    TPI_NEG := FUNCTIONAL;
    XTALI := FUNCTIONAL;
  END;
END;

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

// The internal register addresses
CONST INT BANK_SELECT         := 0xe;

CONST INT B0_TRANSMIT_CONTROL := 0x0;
CONST INT B0_EPH_STATUS       := 0x2;
CONST INT B0_RECEIVE_CONTROL  := 0x4;
CONST INT B0_COUNTER          := 0x6;
CONST INT B0_MEMORY_INFO      := 0x8;
CONST INT B0_RXPHY_CONTROL    := 0xa;

CONST INT B1_CONFIG           := 0x0;
CONST INT B1_BASE_ADDRESS     := 0x2;
CONST INT B1_ADDRESS_0        := 0x4;
CONST INT B1_ADDRESS_2        := 0x6;
CONST INT B1_ADDRESS_4        := 0x8;
CONST INT B1_GEN_PUPOSE       := 0xa;
CONST INT B1_CONTROL          := 0xc;

CONST INT B2_MMU_COMMAND      := 0x0;
CONST INT B2_PACKET_NUMBER    := 0x2;
CONST INT B2_FIFO_PORTS       := 0x4;
CONST INT B2_POINTER          := 0x6;
CONST INT B2_DATA             := 0x8;
CONST INT B2_INTERRUPT        := 0xc;

CONST INT B3_MII              := 0x8;
CONST INT B3_REVISION         := 0xa;

// PHY MII interface Registers
CONST INT PHY_MII_CONTROL     := 0x0;
CONST INT PHY_MII_STATUS      := 0x1;
CONST INT PHY_MII_ID0         := 0x2;
CONST INT PHY_MII_ID1         := 0x3;
CONST INT PHY_MII_ADVERT      := 0x4;
CONST INT PHY_MII_REMCAP      := 0x5;
CONST INT PHY_MII_CONFIG1     := 0x16;
CONST INT PHY_MII_CONFIG2     := 0x17;
CONST INT PHY_MII_STATUS_OUT  := 0x18;
CONST INT PHY_MII_MASK        := 0x19;

// Constants
CONST INT DEBUG               := FALSE;
CONST INT PASS                := FALSE;
CONST INT FAIL                := TRUE;

// Static variables
INT current_bank := 0xffff; // Initialised to an invalid value
INT BE_16 := 0b1100[WIDTHOF(nBE) - 1 .. 0];

//-----------------------------------------------------------------------
// Main Test Routine
//-----------------------------------------------------------------------

Test()(INT result)

  INT base_address;
  INT loopback_mode WIDTH 1 := 0;

  PRINT("Testing Ethernet controller ", DEVICE_REF, " on board ", BOARD_NAME, "\n");

  Reset()(result);
  IF (result != PASS) THEN
    RETURN;
  END;

  // Check the base address
  GetBaseAddress()(base_address);
  IF (base_address != 0x0300) THEN
    PRINT("Invalid base address found: 0x", HEX(base_address), "\n");
    result := FAIL;
    RETURN;
  END;

  // RAM buffer tests ( 16bit mode )
  RamTest(256)(result);
  IF (result != PASS) THEN
    RETURN;
  END;
 
  // RAM buffer tests ( 32 bit mode)
  IF WIDTHOF(DATA) = 32 THEN
    RamTest32(256)(result);
    IF (result != PASS) THEN
      RETURN;
    END;
  END;
  
  // Tx Tests
  PRINT("Testing transmitter and interrupts...\n");
  TxPacket(loopback_mode)(result);
  IF (result != PASS) THEN
    RETURN;
  END;

  // Internal loopback test
  PRINT("Internal loopback test...\n");
  LoopbackTest(TRUE)(result);
  IF (result != PASS) THEN
    RETURN;
  END;

  // External loopback test
  PRINT("External loopback test...\n");
  LoopbackTest(FALSE)(result);
  IF (result != PASS) THEN
    PRINT("Is the loopback cable installed correctly?\n");
    RETURN;
  END;

  result := PASS;

END;

//----------------------------------------------------
// GetBaseAddress
//  Return the device's I/O base address
//----------------------------------------------------

GetBaseAddress()(INT result)

  INT base_address;

  IF DEBUG THEN PRINT("Reading device Base Address\n"); END;

  Read(1, B1_BASE_ADDRESS)(base_address);

  result         := 0;
  result[15..13] := base_address[15..13];
  result[9..5]   := base_address[12..8];

  IF DEBUG THEN PRINT("Base address = 0x", HEX(result), "\n"); END;
END;

//----------------------------------------------------
// LoopbackTest
//     External loopback test
//     Requires RJ45 cable with pins 1 & 3 connected,
//     and pins 2 & 6 connected
//----------------------------------------------------

LoopbackTest(INT internal_loopback WIDTH 1)(INT result)

  INT data WIDTH 16;
  INT loopback_mode WIDTH 1 := 1;

  INT stoptime;

  result := FAIL;

  ResetMmu()(result);
  IF result THEN
    PRINT("MMU initial reset failed.\n");
    RETURN;
  END;

  // Enable the transmitter in Full duplex mode (EPH LOOP)
  IF internal_loopback THEN
    // Set EPH loop bit
    Write(0, B0_TRANSMIT_CONTROL, 0x2001);
  ELSE
    // Enable Phy
    PhyWrite(PHY_MII_CONTROL,0x100);    
  
    // Wait for link else time out ( no cable ?)
    stoptime := NOW() + 2000;  
    DO
      PhyRead(PHY_MII_STATUS)(data); 
    UNTIL (data[2] | NOW() > stoptime) END;

    IF (data[2]=0) THEN
       IF DEBUG THEN PRINT ("No Link..\n"); END;
       result := 1;
       RETURN;
    END;
  
    // Set FULL_DUPLEX bit
    Write(0, B0_TRANSMIT_CONTROL, 0x0801); 
    Write(0,B0_RXPHY_CONTROL, 0x0800); // needed
  END;

  // Enable the receiver (RXEN)
  Write(0, B0_RECEIVE_CONTROL, 0x0100);

  // Make sure we Receive bad packets (bit 14 of CONTROL reg)
  Read(1, B1_CONTROL)(data);
  Write(1, B1_CONTROL, (data | 0x4000));

  // Transmit a packet...
  TxPacket(loopback_mode)(result);
  IF (result != PASS) THEN
    PRINT("Packet transmit failed in LoopbackTest.\n");
    RETURN;
  END;

  // ...then receive it back again
  RxPacket(loopback_mode)(result);
  IF (result != PASS) THEN
    PRINT("Packet receive failed in LoopbackTest.\n");
    RETURN;
  END;

  // Loopback completed OK
  result := PASS;
  IF (DEBUG) THEN PRINT("Loopback test completed OK.\n"); END;

END;

//----------------------------------------------------
// Phy Write
//     Write to Phy
//----------------------------------------------------

PhyWrite( INT address WIDTH 5, INT data WIDTH 16 )()
    
    INT i;
    
    // Write at leat 32 1's to sync the interface
    FOR i := 0 FOR 32
      Write(3,B3_MII, 0x3339);
      Write(3,B3_MII, 0x333D);
    END;
    
    // Start bits 0,1
    PhyWriteBit(0)();
    PhyWriteBit(1)();
    
    // Command bits write 0,1
    PhyWriteBit(0)();
    PhyWriteBit(1)();
    
    // Phy Address which is 00000 for internal phy
    PhyWriteBit(0)();
    PhyWriteBit(0)();
    PhyWriteBit(0)();
    PhyWriteBit(0)();
    PhyWriteBit(0)();
    
    // Phy register number 5bits MSB first
    FOR i := 0 FOR 5
      PhyWriteBit( address[4] )();
      address := address << 1;
    END;
    
    // Send the turn around bits 1,0
    PhyWriteBit(1)();
    PhyWriteBit(0)();
    
    FOR i := 0 FOR 16
          PhyWriteBit( data[15] )();
          data := data << 1;
    END;
    
    Write(3,B3_MII, 0x3330 );
   
END;    

PhyWriteBit(INT bit WIDTH 1)()   
    Write(3,B3_MII, 0x3338 | bit );
    Write(3,B3_MII, 0x333C | bit );
    Write(3,B3_MII, 0x3338 | bit );
END;

//----------------------------------------------------
// Phy read
//    Read from Phy
//----------------------------------------------------

PhyRead( INT address WIDTH 5)(INT result)

  INT i;
  INT data;

  IF DEBUG THEN PRINT ("Writing to Phy...\n"); END;

  // Write at leat 32 1's to sync the interface
  FOR i := 0 FOR 32
    Write(3,B3_MII, 0x3339);
    Write(3,B3_MII, 0x333D);
  END;

  // Start bits 0,1
  PhyWriteBit(0)();
  PhyWriteBit(1)();

  // Command bits write 1,0
  PhyWriteBit(1)();
  PhyWriteBit(0)();

  // Phy Address which is 00000 for internal phy
  PhyWriteBit(0)();
  PhyWriteBit(0)();
  PhyWriteBit(0)();
  PhyWriteBit(0)();
  PhyWriteBit(0)();

  // Phy register number 5bits MSB first
  FOR i := 0 FOR 5
    PhyWriteBit( address[4] )();
    address := address << 1;
  END;

  // Send the turn around bits 1,0

  PhyWriteZ()();
  PhyWriteBit(0)();

  result := 0;

  FOR i := 0 FOR 16
    result := result << 1;
    Write(3,B3_MII, 0x3330 );
    Write(3,B3_MII, 0x3334 );
    Read(3, B3_MII)(data);
    IF data[1] THEN 
        result := (result | 1);
    END;    

  END;
  result := result >>1;
  PhyWriteZ()();
    
END;    

PhyWriteZ()()

  Write(3,B3_MII, 0x3330 );
  Write(3,B3_MII, 0x3334 );
  Write(3,B3_MII, 0x3330 );
  
END;

//----------------------------------------------------
// TxPacket
//     Transmit a Packet
//----------------------------------------------------

TxPacket( INT loopback_mode WIDTH 1)( INT result )

  INT packet_number;
  INT i;
  INT data WIDTH 16;
  INT stoptime, timeout;
  INT interrupt WIDTH 1;

  IF DEBUG THEN PRINT ("Testing transmitter and interrupts...\n"); END;

  // Clear any pending TX interrupts
  Read(2, B2_INTERRUPT)(data);
  IF (data[1]) THEN
    Write(2, B2_INTERRUPT, 0x02);
  END;

  // Allocate a tx packet
  AllocPacket()(result, packet_number);
  IF (result != PASS) THEN
    PRINT("Unable to allocate a TX packet.\n");
    RETURN;
  END;

  // Write this to TX packet number reg
  Write(2, B2_PACKET_NUMBER, packet_number);

  // Write data pointer reg for TX, WR & autoinc
  Write(2, B2_POINTER, (1 << 14));

  // Write the packet data
  Write(2, B2_DATA, 0x0000); // Status word
  Write(2, B2_DATA, 76);     // byte count (70 bytes)
  Write(2, B2_DATA, 0xffff); // destination address 0xffffffffffff
  Write(2, B2_DATA, 0xffff);
  Write(2, B2_DATA, 0xffff);
  Write(2, B2_DATA, 0x1234); // source address 0x000000000000
  Write(2, B2_DATA, 0x5678);
  Write(2, B2_DATA, 0xabcd);
  Write(2, B2_DATA, 0x0040); // packet size (64 bytes)
  FOR i := 0 FOR 23
    data := i[3..0]:~i[3..0]:i[3..0]:~i[3..0];
    Write(2, B2_DATA, data);  // 46 bytes of data
  END;
  Write(2, B2_DATA, (1 << 12)); // Control word & last byte (append CRC)

  // Check the pointer register
  Read(2, B2_POINTER)(result);
  result := result & 0x7f;
  IF (result != 0x042) THEN
    PRINT("Pointer register error, reads 0x", HEX(result), "\n");
    result := FAIL;
    RETURN;
  END;

    // Turn on the transmitter (if we're not in loopback mode)
  IF (loopback_mode = 0) THEN
    Write(0, B0_TRANSMIT_CONTROL, 1);
  END;

  // Queue the packet
  Write(2, B2_MMU_COMMAND, (6 << 5));

  // Wait until packet transmitted or timeout
  stoptime := NOW() + 1000;
  timeout := 0;
  DO
    Read(2, B2_INTERRUPT)(data);
    IF (NOW() > stoptime) THEN timeout := 1; END;
  UNTIL (data[1] || timeout) END;

  IF (timeout) THEN
    PRINT("Timeout waiting for TX_INT in TxPacket.\n");
    result := FAIL;
    RETURN;
  END;

  // Check the interrupt pin & mask register operation
  SET interrupt := INTR0;
  IF interrupt = 1 THEN
    PRINT("Interrupt incorrectly asserted.\n");
    result := FAIL;
    RETURN;
  END;
  Write(2, B2_INTERRUPT, 0x0200);
  SET interrupt := INTR0;
  IF interrupt = 0 THEN
    PRINT("Interrupt pin not asserted.\n");
    result := FAIL;
    RETURN;
  END;
  Write(2, B2_INTERRUPT, 0x0000);

  // Ack & Clear the TX interrupt
  Write(2, B2_INTERRUPT, 0x02);

  // Get the status
  Write(2, B2_POINTER, 0x6000); // Pointer=0x000, auto-incr, read
  Read(2, B2_DATA)(data);

  // Release the TX packet buffer
  Write(2, B2_MMU_COMMAND, 0x00a0);

  // Report any errors detected
  IF (data[15] != 0) THEN PRINT("Transmitter underrun error\n"); END;
  IF (data[11] != 0) THEN PRINT("Transmitter excessive deferral error\n"); END;
  IF (data[10] != 0) THEN PRINT("Transmitter lost carrier error\n"); END;
  IF (data[9]  != 0) THEN PRINT("Late collision detect error\n"); END;
  //IF (data[5]  != 0) THEN PRINT("Signal quality error test failed\n"); END;
  IF (data[4]  != 0) THEN PRINT("Too many collisions error\n"); END;
  IF (data[2]  != 0) THEN PRINT("Multiple collisions error\n"); END;
  IF (data[1]  != 0) THEN PRINT("Single collision error\n"); END;

  // Were any of the errors fatal?
  IF (data[0] != 1) THEN
    PRINT("Transmitter fatal error detected, status ", HEX(data), "\n");
    RETURN;
  END;

  // Wait until MMU is no longer busy (after releasing the packet buffer)
  WaitMmu()(result);
  IF (result != PASS) THEN
    PRINT("MMU busy error in TxPacket.\n");
    RETURN;
  END;

  // Tx completed OK
  PRINT ("Transmitted Packet OK.\n");
  result := PASS;

END;

//----------------------------------------------------
// RxPacket
//     Receive a Packet
//----------------------------------------------------

RxPacket(INT loopback_mode WIDTH 1)(INT result)

  INT stoptime, timeout;
  INT data WIDTH 16;
  INT packet_number, packet_size;
  INT i, expectedValue;

  IF DEBUG THEN PRINT("Receiving a packet...\n"); END;

  result := FAIL;

  // Turn on the receiver
  IF (loopback_mode = 0) THEN
    Write(0, B0_RECEIVE_CONTROL, 0x0100);
  END;

  // Wait for a receiver Interrupt
  timeout := 0;
  stoptime := NOW() + 1000;
  DO
    Read(2, B2_INTERRUPT)(data);
    IF (NOW() > stoptime) THEN
      timeout := 1;
    END;
  UNTIL (timeout || data[0]) END;

  IF (timeout != 0) THEN
    PRINT("Timeout waiting for receiver interrupt in RxPacket\n");
    RETURN;
  END;

  // get the receive packet number from the top of the Rx FIFO
  Read(2, B2_FIFO_PORTS)(packet_number);
  IF DEBUG THEN PRINT("Rx packet number ", packet_number[15..8], "\n"); END;

  // Set up the data pointer register (RCV, RD, AUTOINC)
  Write(2, B2_POINTER, 0xe000);

  // Read the status
  Read(2, B2_DATA)(data);
  // Read the byte counter
  Read(2, B2_DATA)(data);
  // destination address
  Read(2, B2_DATA)(data);
  Read(2, B2_DATA)(data);
  Read(2, B2_DATA)(data);
  // Source Address
  Read(2, B2_DATA)(data);
  Read(2, B2_DATA)(data);
  Read(2, B2_DATA)(data);
  // Packet Size
  Read(2, B2_DATA)(packet_size);
  packet_size := (packet_size/2)-10;
  FOR i := 0 FOR packet_size
    Read(2, B2_DATA)(data);
    expectedValue := i[3..0]:~i[3..0]:i[3..0]:~i[3..0];
    IF data != expectedValue THEN
      PRINT("Receive packet data error, expected 0x", HEX(result), " but received 0x", HEX(data), "\n");
      result := FAIL;
      RETURN;
    END;
  END;

  // Finally read the Control Word
  Read(2, B2_DATA)(data);

  // Check the pointer register
  Read(2, B2_POINTER)(data);
  data := data & 0x7f;
  IF (data != 0x40) THEN
    PRINT("Pointer register error, reads 0x", HEX(data), "\n");
    result := FAIL;
    RETURN;
  END;

  // Release the Rx packet buffer at top of Rx FIFO
  Write(2, B2_MMU_COMMAND, 0x0080);

  // Wait until MMU is no longer busy (after releasing the packet buffer)
  WaitMmu()(result);
  IF (result != PASS) THEN
    PRINT("MMU busy error in TxPacket.\n");
    RETURN;
  END;

  PRINT("Packet received OK.\n");
  result := PASS;

END;

//----------------------------------------------------
// RamTest
//     RAM buffer test
//----------------------------------------------------

RamTest(INT test_length)(INT result)

  INT data, expected_data WIDTH 16;
  INT byte_number;
  INT packet_number WIDTH 16;
  INT i;

  PRINT("Testing RAM buffer...\n");
  result := FAIL;

  ResetMmu()(result);
  IF (result != PASS) THEN
    PRINT("MMU initial reset failed.\n");
    RETURN;
  END;

  // Test for four packets buffers
  FOR i := 0 TO 3
    // Allocate packet buffer memory
    AllocPacket()(result, packet_number);
    IF (result != PASS) THEN
      PRINT("Unable to allocate a packet.\n");
      RETURN;
    END;
    // Write this to packet number reg
    Write(2, B2_PACKET_NUMBER, packet_number);
    IF DEBUG THEN PRINT("Writing test Patterns...\n"); END;
    // Write the data
    byte_number := 0;
    Write(2, B2_POINTER, 0x4000);  // use auto increment WRITE
    DO
      Write(2, B2_DATA, 0x2345 * byte_number);
      byte_number := byte_number + 2;
    UNTIL byte_number >= test_length END;
    // Read back and check the data
    IF DEBUG THEN PRINT("Checking test Patterns...\n"); END;
    byte_number := 0;
    Write(2, B2_POINTER, 0x6000); // use auto increment READ
    DO
      Read(2, B2_DATA)(data);
      expected_data := (0x2345 * byte_number)[15..0];
      IF (data != expected_data) THEN
        PRINT("RAM buffer test readback error at test ", byte_number/2, ", write 0x", HEX(expected_data), ", read 0x", HEX(data), "\n");
        ResetMmu()(result);
        result := FAIL;
        RETURN;
      END;
      byte_number := byte_number + 2;
    UNTIL byte_number >= test_length
    END;
  END;

  // Test completed
  ResetMmu()(result);
  IF (result != PASS) THEN
    PRINT("MMU final reset failed.\n");
    result := FAIL;
    RETURN;
  END;

  PRINT("RAM buffer tests completed OK.\n");
  result := PASS;

END;

//----------------------------------------------------
// RamTest
//     RAM buffer test
//----------------------------------------------------

RamTest32(INT test_length)(INT result)

  INT data, expected_data WIDTH 32;
  INT byte_number;
  INT packet_number WIDTH 16;
  INT i;

  PRINT("Testing RAM buffer...\n");
  result := FAIL;

  ResetMmu()(result);
  IF (result != PASS) THEN
    PRINT("MMU initial reset failed.\n");
    RETURN;
  END;

  // Test for four packets buffers
  FOR i := 0 TO 3
    // Allocate packet buffer memory
    AllocPacket()(result, packet_number);
    IF (result != PASS) THEN
      PRINT("Unable to allocate a packet.\n");
      RETURN;
    END;
    // Write this to packet number reg
    Write(2, B2_PACKET_NUMBER, packet_number);
    IF DEBUG THEN PRINT("Writing test Patterns...\n"); END;
    // Write the data
    byte_number := 0;
    Write(2, B2_POINTER, 0x4000);  // use auto increment WRITE
    DO
      Write32(2, B2_DATA, 0x23456789 * byte_number);
      byte_number := byte_number + 4;
    UNTIL byte_number >= test_length END;
    // Read back and check the data
    IF DEBUG THEN PRINT("Checking test Patterns...\n"); END;
    byte_number := 0;
    Write(2, B2_POINTER, 0x6000); // use auto increment READ
    DO
      Read32(2, B2_DATA)(data);
      expected_data := (0x23456789 * byte_number)[31..0];
      IF (data != expected_data) THEN
        PRINT("RAM buffer test readback error at test ", byte_number/4, ", write 0x", HEX(expected_data), ", read 0x", HEX(data), "\n");
        ResetMmu()(result);
        result := FAIL;
        RETURN;
      END;
      byte_number := byte_number + 4;
    UNTIL byte_number >= test_length
    END;
  END;

  // Test completed
  ResetMmu()(result);
  IF (result != PASS) THEN
    PRINT("MMU final reset failed.\n");
    result := FAIL;
    RETURN;
  END;

  PRINT("RAM buffer tests completed OK.\n");
  result := PASS;

END;



//----------------------------------------------------
// WaitMmmu
//     Wait until MMU is no longer busy or timeout
//----------------------------------------------------

WaitMmu()(INT result)

  INT stoptime, timeout;
  INT data WIDTH 16;

  result := FAIL;

  stoptime := NOW() + 1000;
  timeout := 0;
  DO
    Read(2, B2_MMU_COMMAND)(data);
    IF (NOW() > stoptime) THEN timeout := 1; END;
  WHILE (data[0]=1 && timeout=0)
  END;
  IF (timeout) THEN
    PRINT("Timeout waiting for MMU not busy in TxPacket.\n");
    RETURN;
  END;

  IF (DEBUG) THEN PRINT("MMU not busy.\n"); END;
  result := PASS;

END;

//----------------------------------------------------
// AllocPacket
//     Allocate a packet and wait for allocate interrupt bit
//----------------------------------------------------

AllocPacket()(INT result, INT packet_number)

  INT stoptime;
  INT timeout WIDTH 1;
  INT data WIDTH 16;

  IF DEBUG THEN PRINT("Allocating a packet...\n"); END;

  result := FAIL;
  packet_number := 0;

  // Allocate packet memory
  Write(2, B2_MMU_COMMAND, (1 << 5));

  // Wait for MMU no longer busy
  WaitMmu()(result);
  IF (result != PASS) THEN
    PRINT("Packet allocate failed.\n");
    RETURN;
  END;

  // wait for allocate interrupt bit to become set
  stoptime := NOW() + 1000;
  timeout := 0;
  DO
    Read(2, B2_INTERRUPT)(data);
    IF (NOW() > stoptime) THEN timeout := 1; END;
  WHILE (data[3]=0 && timeout=0) END;

  IF (timeout) THEN
    PRINT("Timeout waiting for alloc INT in AllocPacket.\n");
    RETURN;
  END;

  // Read allocated packet number
  Read(2, B2_PACKET_NUMBER)(data);

  // Check the allocate failed flag
  IF data[15]=1 THEN
    PRINT("Allocate packet failed.\n");
    result := FAIL;
    RETURN;
  END;

  // Packet successfully allocated
  packet_number := 0;
  packet_number[5..0] := data[13..8];
  IF DEBUG THEN PRINT("Packet 0x", HEX(packet_number[5..0]), " allocated OK.\n"); END;
  result := PASS;

END;

//----------------------------------------------------
// ResetMmu
//     Release an allocated packet
//----------------------------------------------------

ResetMmu()(INT result)

  IF DEBUG THEN PRINT("Resetting MMU...\n"); END;

  // Reset MMU command
  Write(2, B2_MMU_COMMAND, (2<<5));

  // Wait for MMU no longer busy
  WaitMmu()(result);
  IF (result != PASS) THEN
    PRINT("MMU not ready after reset.\n");
    RETURN;
  END;

  IF DEBUG THEN PRINT("MMU reset completed OK.\n"); END;
  result := PASS;

END;

//----------------------------------------------------
// Reset
//     Reset the device and check bank select register
//----------------------------------------------------

Reset()(INT result)

  INT data WIDTH 16;
  INT bank WIDTH 16;

  IF (DEBUG) THEN PRINT("Resetting device...\n"); END;
  result :=FAIL;

  SET RESET :=1, nWR:=1, nRD:=1;
  FLUSH;
  SLEEP(2);
  SET RESET :=0;
  FLUSH;
  SLEEP(2);

  // Check default value of bank select register
  ReadCycle(BANK_SELECT)(data);
  IF data !=0x3300 THEN
    PRINT("Failed to read BANK SELECT register of LAN91C11x, expected 0x3300, found 0x",HEX(data),"\n");
    RETURN;
  END;

  // Check bank select register updates
  FOR bank := 0 TO 7
    SetBank(bank)();
    ReadCycle(BANK_SELECT)(data);
    IF data != (0x3300 + bank) THEN
      PRINT("Failed to read BANK SELECT register of LAN91C11x, expected ", HEX(0x3300+bank),", but read 0x",HEX(data),"\n");
      RETURN;
    END;
  END;

  /*
  // Now check that register returns to default state on another reset
  // ******** This Reset test fails on all the chips we have seen, is datasheet wrong ?? 

  SET RESET :=1;
  SLEEP(2);
  SET RESET :=0;
  SLEEP(2);

  ReadCycle(BANK_SELECT)(data);
  IF data !=0x3300 THEN
    PRINT("Failed to read BANK SELECT register of LAN91C11x, expected 0x3300, found 0x",HEX(data),"\n");
    RETURN;
  END;
  */

  ResetMmu()(result);
  IF (result != PASS) THEN
    PRINT("MMU reset failed.\n");
    RETURN;
  END;

  // Mask all interrupts
  Write(2, B2_INTERRUPT, 0x0000);

  IF DEBUG THEN PRINT("Reset completed OK.\n"); END;
  result := PASS;
END;

//----------------------------------------------------
// Bus write
//     Write data to specified address and bank
//----------------------------------------------------

Write(INT bank, INT address, INT data)()

  SetBank(bank)();
  WriteCycle(address, data);

END;

//----------------------------------------------------
// Bus read
//     Read data from specified address and bank
//----------------------------------------------------

Read(INT bank, INT address)(INT data )

  SetBank(bank)();
  ReadCycle(address)(data);

END;

//----------------------------------------------------
// SetBank
//  Set current bank select register
//----------------------------------------------------

SetBank(INT bank)()

  IF (bank != current_bank) THEN
    WriteCycle(BANK_SELECT, bank);
    current_bank := bank;
    IF DEBUG THEN PRINT("Bank ", bank, " selected.\n"); END;
  END;

END;

//----------------------------------------------------
// Bus write cycle
//     Write data to specified address
//----------------------------------------------------

WriteCycle( INT address, INT data )()

  IF DEBUG THEN PRINT("Write 0x", HEX(data), " to bank ", current_bank, " address 0x", HEX(address), "\n"); END;
  SET ADDR:=(address + 0x300)>>1, AEN := 0, nADS := 0, nBE := BE_16;
  SET nWR := 0, DATA := data;
  SET nWR := 1, DATA := I, AEN := 1, nADS := 1;

END;

//----------------------------------------------------
// Bus Read cycle
//     Read data from specified address
//----------------------------------------------------

ReadCycle( INT address )( INT data )

  SET ADDR := (address + 0x300)>>1, AEN :=0, DATA := I, nADS := 0, nBE := BE_16;
  SET nRD := 0;
  SET nRD := 1, data := DATA, AEN := 1, nADS := 1;
  data[31..16] := 0;
  IF DEBUG THEN PRINT("Read 0x", HEX(data), " from bank ", current_bank, " address 0x", HEX(address), "\n"); END;

END;

//----------------------------------------------------
// Bus write
//     Write data to specified address and bank
//----------------------------------------------------

Write32(INT bank, INT address, INT data)()

  SetBank(bank)();
  WriteCycle32(address, data);

END;

//----------------------------------------------------
// Bus read
//     Read data from specified address and bank
//----------------------------------------------------

Read32(INT bank, INT address)(INT data )

  SetBank(bank)();
  ReadCycle32(address)(data);

END;


//----------------------------------------------------
// Bus write cycle
//     Write data to specified address
//----------------------------------------------------

WriteCycle32( INT address, INT data )()

  IF DEBUG THEN PRINT("Write 0x", HEX(data), " to bank ", current_bank, " address 0x", HEX(address), "\n"); END;
  SET ADDR:=(address + 0x300)>>1, AEN := 0, nADS := 0, nBE := 0x0;
  SET nWR := 0, DATA := data;
  SET nWR := 1, DATA := I, AEN := 1, nADS := 1;

END;

//----------------------------------------------------
// Bus Read cycle
//     Read data from specified address
//----------------------------------------------------

ReadCycle32( INT address )( INT data )

  SET ADDR := (address + 0x300)>>1, AEN :=0, DATA := I, nADS := 0, nBE := 0x0;
  SET nRD := 0;
  SET nRD := 1, data := DATA, AEN := 1, nADS := 1;
  IF DEBUG THEN PRINT("Read 0x", HEX(data), " from bank ", current_bank, " address 0x", HEX(address), "\n"); END;

END;