//
// ESL Modelling : Test application or device driver fragment: illustrating c-preprocessor calls to implement direct calling of behavioural model of a device.
// The device in this case is a co-processor for Ethernet CRC generation.
//

  testapp.c                - Test program including test data. 
                           - Can be compiled natively and run without SystemC or else compiled for OR1K and run on that.
                           - Produces easy to check console output when run.

  ethercrc_bev_core.cpp/.h - The behavioural model of the device

  ethercrc_tlm.cpp/.h      - The TLM wrapper for instantiating the behavioural model in a SoC model.




The .a archive file generated by the Makefile is a TLM model of the
device that can be instantiated in a larger SoC model, such as the
btlm-ref-design ... it just needs to be connected to a spare busmux
port.  For conciseness, I did not include a complete copy of any such
ref design in the example folder, but such as system will call
sc_start.


------------------------

Matters arising: the points below arose in email discussions and are not necessarily specific to this example.

Be clear over the terms testbench and test application:
  testbench : a wrapper put around a hardware model to provide
stimulus, such as clocks and resets and test input data, as well as to
monitor or log outputs.

 test application: a program compiled to run on the embedded system
that performs tests by behaving in roughly the same way as the real
application.

In simple cases, like the one in this folder, we can run a test
application by compiling it for the modelling workstation and allowing
to to make direct calls to the behavioural models of devices.  In more
complex cases a threading library is needed owing to concurrency in
the various components or owing to the code structure.  It is sensible
to use SystemC threads in that situation.  (In none of these situations
are we compiling SystemC libraries or primitives to run on the ISS.)


SystemC provides non-preemptive schedulling, whereas firmware and
device drivers are likely to use spinlocks and rely on true
concurrency.  This difference needs fixing with further preprocessor
conditional compilation. A spinlock is where one thread does nothing
while polling for a change made by another thread or piece of hardware.

So to run the embedded firmware natively as a SystemC component:
  1. in the firmware C code, modify all device operations to use direct calls that are enabled by the C preprocessor,
  2. also in the firmware C code, modify all spinlocks or other outer loops to contain
yields (and also preferably instruction count annotations) that are enabled via the C preprocessor.  
 3. Create an SC_MODULE called something appropriate, such as 'test_application_directbev' that starts
one SystemC thread.  The SystemC thread should call the firmware by simply giving the entry point of the firmware
as the argument to SC_THREAD or else naming a small shim C++ function that first does any needed set up work before
calling the native firmware.

In this way, the embedded software is run natively as a SystemC thread
in parallel with the complete rest of the system model, or as much of
it as you care to include, but certainly missing out the ISS that it
would normally be running on.  At the testbench level, you can make
the swap between the ISS and the native code itself depend on the same
preprocessor conditional compilation flag as is used inside the native
code to adapt it.

The spinlocks in the firmware should be modified roughly as follows:

#ifndef TEST_DIRECT_BEVMODEL
   while (!(TEST_STATUS_REG() & POLLED_BIT)) continue;
#else
   while (!(TEST_STATUS_REG() & POLLED_BIT)) wait(10,SC_NS); // one clock cycle
#endif


In the case where approximate times of execution for basic blocks is
being logged in a 'delay' variable, the body of the while loop would
be { wait(delay); delay=SC_ZERO_TIME; }.  However, inserting detailed execution
times is preferably done by an automatic tool, such as a modified
backend to gcc.  Custom backends to gcc are quite commonly used 
because custom processors are quite commonly used in SoC design.


You may find that the unmodified code, instead of containing a
'continue' statement, contains some special power saving assembly
language such as 'asm("pause")' or 'asm("halt")' which can safely be
disregarded.  On more-advanced embedded systems, the spinlocks may
contain calls to the embedded system's own coroutine or thread
package, in which case there are two ways forward:
  1. also compile its thread package for native operation and put the SystemC yeilds in the places where it halts or spins
  2. or leave it out and replace the calls to it with the above SystemC yeilds.

  





// END
