MxV MicroController Emulation (µC Library)

Simulated MicroController - Theory of Operation

The MxV µC library simulates a generic microcontroller.  The library provides an interface to a simulated µC core for simulating interrupt requests and to schedule time for the main() function to execute.  Currently we provide APIs to simulate CAN, EEPROM, Serial I/O, and GPIO.

Invoking main()

The microcontroller simulation employs Win32 Fibers to allow cooperation between MxV and the System Under Test (SUT).  By default the first fiber becomes the MxV fiber and the additional fiber that is created executes the SUT's main() function.  The SUT fiber is given execution time via the MxVuC_Tick API function.  A simple change is required to the SUT code to invoke MxVuC_Yield or MxVuC_Halt to return control to the MxV fiber which allows simulated time to elapse.  MxVuC_Yield will elapse time to the next scheduled MxVuC_Tick and MxVuC_Halt prevents further processing time until an MxVuC_Awake event occurs bringing the microcontroller out of the simulated halt state (e.g. low power mode with the main clock off).  For example, the yield call may be placed at the end of the an infinite main loop or at the end of the idle task in an RTOS.  Harnessing an RTOS will require additional fibers for each task

(The MxVOS API provides an OSEK 2.2.3 implementation for MxV including an OIL v2.5 compiler.)

Note:

MxVMC simulation uses windows fibers, as explained above. Do not use any of the MxVMC C/C++ API in the SUT's main() function, since it runs on an individual fiber and this library is meant to be used in application interface code (AppIF.c). SUT's main() fiber is only permitted to use the uC library (in general, prefixed with "MxVuC_"; for example: MxVuC_Initialize(), and MxVuC_Tick()) to communicate with the MxV fiber.

uC library details are available at 'mxv_uC.h' which is included in the AppIF.c file.

So, instead of using MxVRaiseErr in the main() functionality, use MxVuC_FatalError (syntax and description is available at uC_core.h).

Interrupt Requests (IRQs)

Interrupt requests are serviced synchronously when executing the Software-In-the-Loop environment.  They are only serviced when the microcontroller code (the main() fiber) is not being executed.  This may, at first, seem detrimental to testing efforts since it is not identical behavior to a real microcontroller, however this behavior has several benefits.  Synchronous IRQs allow interrupts to be handled deterministically so that subsequent executions of test-cases have 100% identical behavior to the previous execution.  By maintaining the synchronous model of MxV. we do not re-introduce "heisenbugs" and debugging may continue to occur in an ideal environment and test-cases may still be executed faster than real-time.  If interrupts were handled asynchronously, the SIL environment would be just as complex to test and debug as a real system, yet a PC emulation of a microcontroller would still not behave identically to an actual target embedded system.  Since the benefits of an asynchronous environment appear dubious, we leverage the benefits of a synchronous environment.

MxVuC Harness (AppIF.c changes)

The following details the changes required to the AppIF.c application interface source file in order to use the uC simulation library.

Include

Include the mxv_uC.h header file to include all of the MxVuC Library headers.

#include "MxVHarness.h"

#include "MxVuC/mxv_uC.h"

MxVOpen

Add a call to MxVuC_Initialize() to the MxVOpen method to acquire resources from Windows.

void MxVOpen(void)

{

MxVuC_Initialize();

}

Note: MxVOpen is called when an MxV Module Reset is asserted.

MxVClose

Add a corresponding function call MxVuC_Finalize to the MxVClose method to cleanly release resources back to Windows.

void MxVClose(void)

{

MxVuC_Finalize();

}

SUT_Init

The SUT Initialization function should be changed to re-invoke MxVuC_Initialize() followed by the MxVuC_Tick() function.  This re-initializes the system and prepares it for execution.  This is also the best place to "flash" the simulated EEPROM if needed.  This first call to MxVuC_Tick relies on the embedded main() function to perform the requisite initialization of the system.  GPIO or UART configuration may take place inside the SUT initialization code in place of the target hardware driver initialization code.

void SUT_Init(void)

{

/*Initialization Code, e.g. Flash EEPROM*/

MxVuC_Initialize();

MxVuC_Tick();

}

SUT_Tick

The SUT Tick function should invoke any periodic interrupts and then invoke the MxVuC_Tick() function.

void SUT_Tick(void)

{

  //Periodic task, e.g. main state machine, clock, RTI

  irqRTI();

  MxVuC_Tick();

}

Changes to main()

When compiling with MxVDev, the MXVDEV pre-processor symbol is defined to facilitate conditional compilation.  This enables us to add harness code to the project without affecting the target build.  It is not critical, but if feasible add a uC_Yield() call after the initialization code completes.  This lets the first call to uC_Tick() execute just the initialization code.  Without this yield, the first call to uC_Tick() also starts the OS or executes the first round of tasks.  It is critical that the idle loop incorporates either a uC_Yield() to allow time to elapse or a uC_Halt() to simulate a low-power-mode with the main clock disabled.  Without this hook, the target code goes into a hard-loop and will not function with MxVDev.  To recover from a uC_Halt(), a call to uC_Awake() must occur typically in some asynchronous event handler (such as clock ISR, CAN ISR, etc...).  Like some real microcontrollers, the MxV uC simulation requires an explicit wake-up function call.  Calls to uC_Tick while halted are ignored.

 

#ifdef MXVDEV

#include "MxVuC/mxv_uC.h"

#endif

 

static uint32_t g_clock;

void clock_isr(void)

{

  ++g_clock;

  MxVuC_Awake();

}

 

void main(void)

{

  /* Initialization code */

  #ifdef MXVDEV

  MxVuC_Yield();

  #endif

 

  for(;;)

  {

      /* Task code */

      if(nothing_to_do)

      {

          /*Go to sleep*/

          #ifdef MXVDEV

          MxVuC_Halt();

          #endif

      }

  }

}

 

Vector Table

The MxV simulated microcontroller employs vector tables to enter interrupt service routines.  Two vector tables are required: one representing the simulated target microcontroller and a second vector table for the remote system that the simulated microcontroller is communicating with.  The target microcontroller is handled by the SUT vector table and the remote system is handled by the MxV vector table.

Any vector entry set to NULL or 0 is ignored.  At a minimum, these two vector tables must be added to the AppIF.c (or perhaps to a dedicated vector.c source file).

//Ignore all simulated interrupts

MxVuC_ISR MxVuC_VectorTableSUT[MxVuC_Vector_Count] = {0};

MxVuC_ISR MxVuC_VectorTableMxV[MxVuC_Vector_Count] = {0};

Refer to #include "MxVuC/uC_vector.h" for a definitive list of the simulated vectors.

Related Topics:

EEPROM

CAN Sub-System

DIO Sub-System

OSEK Library

Setting up your code

Linking your code with the MxVMC

Choosing a Tick Period for Transforms

Programmer's Reference–MxVMC C/C++ API