/* This is a very mangled version of Vadc__main.cpp as generated by Verilator. */

// Verilated -*- C++ -*-
// DESCRIPTION: main() calling loop, created with Verilator --main

#include "verilated.h"
#include "Vlng.h"

#include "ngspice/cmtypes.h" // For Digital_t
#include "ngspice/cosim.h"   // For struct co_info and prototypes

//======================

/* Structure for the input table. */

struct input {
  const char   *name;
  unsigned int  offset;
  unsigned int  count;
};

/* This VL_DATA macro is used in header files inputs.h, outputs.h and inouts.h
 * to write functions accept_input() and step().
 * The macro is used several times with different definitions.
 */

/* Generate the previous values table used by step(). */

#define VL_DATA(size, name, msb, lsb) + msb - lsb + 1
static const unsigned int outs = 0
#include "outputs.h"
  ;
static const unsigned int inouts = 0
#include "inouts.h"
  ;

static unsigned char previous_output[outs + inouts];

#undef VL_DATA

/* The input function: it should ignore out-of-range values of index. */

#define VL_DATA(size, name, msb, lsb)      \
  if (index >= msb - lsb + 1) {            \
    index -= msb - lsb + 1;                \
  } else if (msb == 0 && lsb == 0) {       \
    topp->name = val ? 1 : 0;              \
    return;                                \
  } else {                                 \
    if (val)                               \
      topp->name |= (1 << (msb - index));  \
    else                                   \
      topp->name &= ~(1 << (msb - index)); \
    return;                                \
  }

static void accept_input(struct co_info *pinfo,
			 unsigned int index, Digital_t *vp)
{
    Vlng          *topp = (Vlng *)pinfo->handle;
    unsigned int   val, offset;

    val = vp->state;
    if (val == UNKNOWN)
        return;    // Verilator simulations are two-state.

#include "inputs.h"

    /* For inout ports the new value must be stored to detect changes. */

    offset = outs;
#undef VL_DATA
#define VL_DATA(size, name, msb, lsb)       \
  if (index >= msb - lsb + 1) {             \
    index -= msb - lsb + 1;                 \
    offset += msb - lsb + 1;                \
  } else if (msb == 0 && lsb == 0) {        \
    topp->name = val ? 1 : 0;               \
    previous_output[index + offset] = val;  \
    return;                                 \
  } else {                                  \
    if (val) 			 	    \
      topp->name | (1 << (msb - index));    \
    else                                    \
      topp->name &= ~(1 << (msb - index));  \
    previous_output[index + offset] = val;  \
    return;                                 \
  }

#include "inouts.h"
}
#undef VL_DATA

/* The step function that calls the Verilator code. */

#ifndef WITH_TIMING
/* The simple case is when the Verilog source contained no time delays,
 * or was compiled with compiled with --no-timing.
 */

#define VL_DATA(size, name, msb, lsb)        \
  for (i = msb; i >= lsb; --i) {             \
    if (topp->name & (1 << i))               \
      bit = 1;                               \
    else                                     \
      bit = 0;                               \
    if (bit ^ previous_output[index]) {      \
      previous_output[index] = bit;          \
      oval.state = (Digital_State_t)bit;     \
      (*pinfo->out_fn)(pinfo, index, &oval); \
    }                                        \
    ++index;                                 \
  }

static void step(struct co_info *pinfo)
{
    static Digital_t  oval = {ZERO, STRONG};
    Vlng             *topp;
    int               index, i;
    unsigned char     bit;

    topp = (Vlng *)pinfo->handle;
    topp->eval();

    /* Now scan the outputs for changes. */

    index = 0;
#include "outputs.h"
#include "inouts.h"
}

#else /* WITH_TIMING */

#define VL_DATA(size, name, msb, lsb)        \
  for (i = msb; i >= lsb; --i) {             \
    if (topp->name & (1 << i))               \
      bit = 1;                               \
    else                                     \
      bit = 0;                               \
    if (bit ^ previous_output[index]) {      \
      stop = 1;                              \
      previous_output[index] = bit;          \
      oval.state = (Digital_State_t)bit;     \
      (*pinfo->out_fn)(pinfo, index, &oval); \
    }                                        \
    ++index;                                 \
  }

static void step(struct co_info *pinfo)
{
    static Digital_t  oval = {ZERO, STRONG};
    VerilatedContext *contextp;
    Vlng             *topp;
    double            tick;
    uint64_t          target, next;
    int               index, i, stop = 0;
    unsigned char     bit;

    /* When Verilog source was compiled with --timing, run queued evants
     * until the Verilog simulation catches up with SPICE.
     */

    topp = (Vlng *)pinfo->handle;
    contextp = topp->contextp();
    tick = pow(10, contextp->timeprecision());
    target = pinfo->vtime / tick;

    /* Step the Verilog simulation towards the target time. */

    do {
      if (topp->eventsPending())
	next = topp->nextTimeSlot();
      else
	next = target;
      if (next >= target) {
	stop = 1;
	next = target;
      }
      contextp->time(next);
      topp->eval();

      /* Scan for output and stop early if found. */

      index = 0;
#include "outputs.h"
#include "inouts.h"
    } while (!stop);

    /* Update the shared simulation time on early exit. */

    if (next < target)
      pinfo->vtime = next * tick;
}

#endif /* WITH_TIMING */
#undef VL_DATA

extern "C" void Cosim_setup(struct co_info *pinfo)
{
    int i;

    // Setup context, and defaults

    Verilated::debug(0);
    const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};

    // Construct the Verilated model, from Vtop.h generated from Verilating

    Vlng *topp{new Vlng{contextp.get()}};

    /* Return information to caller. */

    pinfo->handle = topp;
    pinfo->step = step;

#define VL_DATA(size, name, msb, lsb) i += msb - lsb + 1; // Count ports

    i = 0;
#include "inputs.h"
    pinfo->in_count = i;

    pinfo->out_count = outs;
    pinfo->inout_count = inouts;
    pinfo->in_fn = accept_input;
#ifdef WITH_TIMING
    pinfo->method = Both;         // There may be immediate results from input.
#else
    pinfo->method = After_input;  // Verilator requires input to advance.
#endif
}
#undef VL_DATA
