/*
    Copyright 2001 (c) by Authors,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public 
    Licence. A full copy of the GNU GPL is provided in the file COPYING 
    found in the development root of Topsy.
*/
/*
  
    File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/IO/sa1100/Drivers/Attic/IXPuEInit.c,v $
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2936 $
    Last Date of Change:   $Date: 2007-03-21 15:23:42 +0100 (Mit, 21 Mär 2007) $  by: $Author: baderma $
 
*/

// this code is based upon parts of vera from Princeton

/*
 * Utilities for the Intel IXP1200
 * Princeton University
 * Scott Karlin, Tammo Spalink, and Yitzchak Gottlieb
 *
 * Copyright Princeton University Board of Trustees
 * All rights reserved
 */


#include "IXPuEInit.h"
#include "IXPMemAccess.h"
#include "IXPBusInit.h"
#include "IXPUtil.h"
#include "IXPuCode.h"
#include "Syscall.h"
#include "IXPuCode.h"
#include "IXPuEng.h"
#include "Support.h"
#include "types.h"
#include "kalloc_api.h"


/*****************************************************************************/
/*                                                                           */
/* Function Name: uCode_LoadInitial                                          */
/* Parameters:                                                               */
/*      int             uEng    Which microengine                            */
/*      uEng_uCodeInfo  info    The code to load into the IStore             */
/* Returns: <none>                                                           */
/* Effects: Loads the first code into the IStore. Some of the code is        */
/*          written to the end of the IStore.                                */
/*****************************************************************************/

void
uCode_LoadInitial(int uEng, uEng_uCodeInfo info)
{
  uint32 addr = 0;
  uint32 nmovedi = 1 + EOF_NOP_COUNT;    // Number of instructions moved to end
  //                                  // of ISTORE

  led(0x51);
  addr = uEng_LoadAtAddress(uEng, info->code,
                            info->length - nmovedi, addr);

  /* Move the last microcode instructions, which should be a branch (for
   * receive) or nop (transmit) to the end of the I-Store. */
  led(0x52);
  uEng_LoadAtAddress(uEng, info->code + info->length - nmovedi,
                     nmovedi, 1020);
  led(0x53);

  sramWrite32(VRP_ALL_FORWARDERS, 1020);
  led(0x54);
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: init_UE_process                                            */
/* Parameters:                                                               */
/*      uEng_uCodeInfo  ci              The code                             */
/*      int             UE_base         The first microengine                */
/*      int             UE_count        The number of microengines to init   */
/*      int             CTX_count       The number of contexts to init.      */
/* Returns: <none>                                                           */
/* Effects: Loads the code into the relevant microengines. Start the threads */
/*          operating on those microengines.                                 */
/*****************************************************************************/

static void
init_UE_process(uEng_uCodeInfo ci, int UE_base, int UE_count, int CTX_count)
{
  int CTX_available = UE_count * 4;
  Boolean available[CTX_available];
  int tid = 0;
  int count = 0;
  int i = 0;

  /* Initialize each microengin and then load code into it. */
  for (i = UE_base; i < UE_base + UE_count; i++)
  {
    uEng_Init(i);
    uCode_LoadInitial(i, ci);
  }
  led(0x90);

  /* Mark all threads in the microengines as available. */
  for (tid = 0; tid < CTX_available; tid++)
    available[tid] = TRUE;

  tid = 0;


  for (count = 1; count <= CTX_count; count++)
  {
    int next = 0;

    available[tid] = FALSE;

    /* Choose the next thread for this thread. */
    if (count == CTX_count)
    {
      next = 0;                   // Wrap around at end
    }
    else
    {
      /* Choose the next available thread at least 4 threads away. */
      next = (tid + 4) % CTX_available;
      while (! available[next])
      {
        next = (next + 1) % CTX_available;
      }
    }

    {
      int realtid = 0;
      int realntid = 0;

      /* Find the actual thread and next thread IDs */
      realtid = (UE_base * 4) + tid;
      realntid = (UE_base * 4) + next;

      printf("  tid %2d, pid %d, ntid %2d\n",
             realtid, count % 8, realntid);

      /* Write the numbers out to the threads. */
      scratchWrite32(SA_PID_MSG_ARR_ADDR + realtid, count % 8);
      scratchWrite32(SA_NTID_MSG_ARR_ADDR + realtid, realntid);

      /* Prepare each context and enable it. */
      uEng_PrepareCtx(realtid / 4, realtid % 4);
      uEng_EnableCtx(realtid / 4, realtid % 4);
    }

    tid = next;
  }
  led(0x91);

  /* Signal all the relvant threads. */
  for (tid = 0; tid < CTX_available; tid++)
  {
    if (available[tid])
      continue;
    IXBus_SignalThread((UE_base * 4) + tid);
  }
  led(0x92);

  /* If there were active contects signal the base thread. */
  if (CTX_count > 0)
    IXBus_SignalThread(UE_base * 4);
  led(0x93);
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: Control_BootUEs                                            */
/* Parameters:                                                               */
/*      uEng_uCodeInfo  rcv_uCode       The microcode for receive uEngines.  */
/*      uEng_uCodeInfo  xmt_uCode       The microcode for transmit uEngines. */
/* Returns: <none>                                                           */
/* Effects: Loads the code into the microengines and starts the program      */
/*                                                                           */
/*****************************************************************************/

void
Control_BootUEs(uEng_uCodeInfo rcv_uCode, uEng_uCodeInfo xmt_uCode)
{
  printf("\n--- Control_BootUEs\n");

  uEng_ResetAll();

  init_UE_process(xmt_uCode, XMT_BASE_UE, XMT_UE_COUNT, XMT_CTX_COUNT);
  init_UE_process(rcv_uCode, RCV_BASE_UE, RCV_UE_COUNT, RCV_CTX_COUNT);

  printf("--- Control_BootUEs done\n\n");
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: Control_LoadUEs                                            */
/* Parameters:                                                               */
/*      uEng_uCodeInfo  info    Code information                             */
/*      int             where   Where to load, -1 indicates at all_forwarders*/
/*      uint32          data                                                 */
/* Returns: <none>                                                           */
/* Effects: Loads a piece of microcode into the IStore. -1 in where indicates*/
/*          that this is code which should execute for all packets and should*/
/*          be loaded accordingly.                                           */
/*****************************************************************************/

void
Control_LoadUEs(uEng_uCodeInfo info, int where, uint32 data)
{
  uEng_uCodeInfo code = NULL;
  uint32 addr = 0;
  uint32 * uenginedata[4];
  uint8 i = 0;

  if (info != NULL)
  {
    uint16 diff = 0;

    /* Find the address at which to load the code. */
    if (where == -1)
    {
      code = uCode_Add_immed(info, data);         // Add the load data
      // command.
      /* The code is at the prepended to ALL_FORWARDERS */
      addr = sramRead32(VRP_ALL_FORWARDERS);
      addr-= code->length;

    }
    else
    {
      code = uCode_Add_rtn(info);                 // Add an implicit
      // return command.
      addr = where;
    }

    diff = code->length - info->length;

    /* Relocate the brances in the code. */
    uCode_RelocateBranches(code, addr + diff);

    /* Load the code into all the microengines. */
    for (i = 0; i < 4; i++)
    {
      uenginedata[i] = uEng_Stop(i);

      uEng_LoadAtAddress(i, code->code, code->length, addr);
    }

    if (where == -1)
      sramWrite32(VRP_ALL_FORWARDERS, addr);

    /* Restart the microengines. */
    for (i = 0; i < 4; i++)
    {

      uEng_Restart(i, uenginedata[i]);

      km_free(uenginedata[i]);
    }

    /* Start any running threads. */
    if (XMT_CTX_COUNT > 0)
    {
      IXBus_SignalThread(XMT_BASE_UE * 4);
    }

    if (RCV_CTX_COUNT > 0)
    {
      IXBus_SignalThread(RCV_BASE_UE * 4);
    }
  }
}
