In general we use a SIL Test Harness to develop and test C or C++ code on a PC. The code might be generated, hand crafted, or a combination. It is also possible to build a SIL Test Harness which executes the test cases on the target microcontroller. Note that for testing on a PC, although we are using C++ compiler, we compile and build ANSI C code. It is advisable to use a Static Analysis tool for many reasons, here are a couple that apply in the PC-based testing of embedded C code:
•To ensure that you do not include any C++ constructs (if your target compiler is a C compiler)
•To ensure that your code is portable (See our C Portability Guidelines.)
•For a simple SIL project with one VMC, select SIL when creating a new project.
•For a more flexible project, use MxTransIt to create a Custom Harness with any number of VMCs and other transforms. For an example, see Using VMCs.
Customize the MSVC project to match the target micro-controller and build environment.
•If you don't have an existing MSVC project, your best strategy for setting up an MxVDev/MSVC test environment is to:
oCreate a new MxVDev project, including a new MSVC project. If you have multiple VMCs, create a separate folder for each MSVC project associated with a VMC.
oAdd the source file to the new MSVC project.
•You should not need to make many changes to the MSVC project settings. However you should check the following:
oStructure Member Alignment – It is worth setting the MSVC project to match that of your embedded build environment.
oYou may need to add or modify #include directories (under C/C++ Preprocessor Options).
•Locate the file called Types.h in your new Visual C++ project. Either modify this file, or replace it with your standard version. Either way, ensure that variable lengths in the MxVDev/Visual C++ build environment are the same as in the Target compiler build environment.
•Set up Just-in-time Debugging. This will help you find run-time errors in your code.
You have to decide which parts of your system to stub and which parts to include in the MSVC build. Consider the CAN driver layers. The table below shows that you can choose to stub at different levels. There are pros and cons to both approaches:
Here are some general rules:
•If the architecture of a system is not well partitioned, then it is usually easier to stub close to the I/O interface of the microcontroller.
•If some of the driver code is trusted and well partitioned (such as EEPROM code, or CAN code that you have used many times in the past), then stubbing it out may save you some time.
Follow these guidelines when defining ports manually:
•In the AppIF.c file, in the section called Interface to SUT add #include statements for all your standard application include files (files like Types.h, Defines.h, and any others that you normally include in all your application source files).
•Code that you add to AppIF.c should make references to many variables and functions in your application code. #include other header files as needed (in some cases, for existing code, it may be easier to just paste individual definitions or prototypes).
oAttach to functions/tasks that are the main scheduling entry points of the software.
oAttach to other functions/task entry points that you want to invoke.
oSet up a way to mimic the arrival of interrupts. There are a couple of approaches:
▪Attach directly to the Interrupt Handler function by creating a new Task in the Signal Dictionary.
▪Use the change in an input Signal to trigger a call to an event handler function. The Interrupt Handler function name can be entered directly in the ‘‘C’ function Name’ field for the Signal in the Signal Dictionary.
For more information, see Port Registration.
During compiling and linking, you formalize the design of your stubs and resolve portability issues that arise. Refer to:
•Harnessing Application Code for Testing when working with existing code
•Tips for Stubbing for advice on how to build effective stubs
•Tips for Porting for help harnessing existing code that has portability issues
•Portability Guidelines for advice on developing portable C code. Focusing on portability is a strongly recommended practice, improving the reliability and re-usability of your code.
•Visual Studio and MxVDev for information on Visual Studio properties.
Export the ports you created and use the Connecting Signals dialog to import them into the MxVDev Signal Dictionary. Once the Signals are in the Signal Dictionary, they are available for use in TestCases.
There are some basic building block test cases that need to be created, and again there is opportunity for TestCase reuse with some forethought. The first couple of test cases should include:
•Basic Initialization – This TestCase initializes the I/O to the SUT, and invokes the initialization functions for the SUT. It may also cause the SUT to reload so that its RAM is re-initialized.
•Execution Model – This TestCase behaves like the basic scheduling mechanism of the SUT. It should invoke the periodic tasks for a cyclic scheduler. It should invoke the Clock ISR for an OS.
Other re-usable test cases include tests that put the system in a known state, without causing a reset. For example, we may want a TestCase that initiates a diagnostics session.
To Harness a project is to set up the Virtual Micro Controller environment for your source code set. You need to follow all the steps above for harnessing a new project, but before you start you should read the notes below.
Harnessing addresses Scheduling and the I/O interface.
For harnessing we can classify the scheduling strategy for a controller into Simple Scheduler, OSEK, and Other.
•Simple Scheduler - Many real-time control systems have a Simple Scheduler. By simple Scheduler we mean that there is no concept of Tasks, and the scheduler therefore is not responsible for switching from one task to another in a preemptive way. There may be interrupt handlers, but the Scheduler does not handle the interrupts.
•OSEK - Many ECUs employ an OSEK operating system. MxVDev has an OSEK that runs on the PC. When testing in this environment you use the MxVDev OSEK.
•Other - For other scheduling approaches, such as a preemptive OS, we essentially adapt/extend what we described above. It may be necessary to port the OS to run in a PC environment.
Identify the I/O interface to which you plan to connect. The choices are usually MicroController Abstraction Interface, or Application Component Interface. For an existing code set, the MicroController abstraction is best.
There are five steps to building your harness. This may take half a day or so to complete.
1.Add Source Code to project - Check out a complete copy of the Source Code, including all included header files. Based on the I/O interface chosen, add the appropriate files to the MSVC project.
2.Compile - Pick one of the simple C files and click the compile button. You will probably get some errors which you will have to address. Once you get one file compiling, the second one is easier, and the third is easier still. See the Tips on Portability to help you get the compilations working.
3.Link - During the linking phase we identify all the unresolved variables and functions. This list usually corresponds closely to the Input and Outputs to the SUT. In general, each unresolved symbol results in a definition in the AppIF.c file. See the Tips on Stubbing for help resolving problems with linking.
4.Init Function - Identify the functions that should be called to Initialize the SUT code. Cause them to be invoked from an initialization test case.
5.Tick Function - Identify the Step or Tick Function(s). Cause them to be invoked at the appropriate periodic rate from Execution Model test cases.
•Generated Code - Code that has been generated is usually easy to harness. It is generally very portable.
•Execution Issues - There are sometimes while() loops in device driver code which can cause the software to hang in a SIL environment. If the execution hangs, break using MSVC and look for an infinite loop. Use conditional compilation as needed to prevent the software from staying in the loop.