Most people who have developed software have at some point or another used virtualization technology. Software development for PLCs in a virtual environment is often overlooked, since PLC development is so close to the hardware. Nevertheless, there are still advantages. Working for several projects with various requirements, but where a Beckhoff PLC/TwinCAT was the common delimiter, made me ask myself “How much use of virtualization can I do for TwinCAT software development?”
There can be many reasons for wanting to use a virtual machine while doing TwinCAT development, a few of them being:
- Doing a temporary test of a certain functionality without messing up the main PC
- Have different toolchains for development
- Developing software for different operating systems
- General prototyping
During a project a requirement came up in where I needed to develop some software that was supposed to run on a Linux machine, but communicating to a TwinCAT runtime. What I needed was to run a client on a Linux machine, talking to a TwinCAT 3 PLC through the ADS protocol. At this point I had no access to any physical PLC, which meant I needed to run the TwinCAT code on my Windows 10 developer machine. I used VirtualBox, which is a free open-source hypervisor to virtualize x86 computers. For Linux, I had a virtual instance of it in VirtualBox. The problem is, if you try to run a virtual machine and at the same time try to put the TwinCAT 3 runtime in “RUN”-mode, you get a warning:
TwinCAT might fail if I press Continue? Let’s try anyway…
…and my computer crashed. Something needed to be done. The above suggests two solutions:
- Don’t run any VirtualBox machines when TwinCAT is in run mode at the host machine
- Activate a solution using only isolated cores
I didn’t like the first suggestion, which is just another way of saying “just give up”. The other suggestion was however good. This warning message suggests that the problem occurs when you’re trying to run a TwinCAT task on a CPU-core that is also used on the virtual machine. If we could make sure to remove one (or more) CPU-cores from the host operating system, thus allowing only TwinCAT to have access to it. If the host OS did not have access to the CPU core(s), then neither would any virtual machines running in the host have it and all should be fine. Theory is one thing, time to do some experiments.
My development machine is running a Intel Core i7-8550U CPU, and according to the datasheet it has four physical CPU cores (eight running with Hyper-Threading). Doing a “Read from Target” in TwinCAT 3 on my machine confirmed this:
Now the trick here is that with the current configuration, all these logical CPUs are accessible for both TwinCAT and Windows. Because they are accesible to both Windows and TwinCAT they need to be shared (through some magic done in TwinCAT). What we need to make sure is that TwinCAT has 100% access to at least one CPU-core. Now I should mention that TwinCAT generally is not too fond of having hyperthreading enabled, so I would suggest to disable it (possible in the BIOS/UEFI of most computers), though I for this example will not do so. If hyperthreading would be disabled the above would show just four rows, one for each physical core. Change the above settings so that we have 1 isolated core and 7 cores for Windows like this:
After doing this, TwinCAT is going to ask you to reboot the machine. After rebooting you might need to run the batch-file
C:\TwinCAT\3.1\System\win8settick.bat in a command prompt (as administrator), and reboot again.
After the reboot is done, you can see that Windows only “sees” seven of the eight logical CPUs:
Now, enabling configuration in TwinCAT 3 and starting a Linux machine (specifically CentOS 7.5), it works!
So apparently it is possible to run TwinCAT on a hyperthreading CPU. This is great stuff! Even better, thanks to the bridged networking functionality in VirtualBox, I can have the Linux machine do network communications with the TwinCAT runtime on the Windows host. For instance, it’s possible to compile libraries for Linux by using Beckhoffs ADS protocol. Now TwinCAT (The WINdows Control and Automation Technology) got a little bit flavoured with the sweet taste of Linux.
All of this is amazing, but I want more. Is it possible to run a virtual Windows machine running TwinCAT 3? Probably not, as TwinCAT most likely needs direct access to the hardware and not through any abstraction layers provided by a hypervisor as VirtualBox. But for experiment, I created a Windows 10 x64 Pro machine and installed the latest TwinCAT 3.1.4022.20 XAE (development environment + runtime). I allocated 4 logical CPUs to the Win10-machine in VirtualBox, and verified that this was what TwinCAT was seeing as well.
Now I tried to start the program with TwinCAT using one of the CPUs that are shared with Windows:
This was expected. But TwinCAT says there is a solution. TwinCAT suggests to activate it on an isolated core. We could again isolate a core as I showed you above, but because we are software engineers we want to have at least two ways to do one thing, just in case we forget one of them. Another way to remove a logical CPU from Windows so that TwinCAT can have it all for itself is by using a built-in way in Windows. Press WindowsKey+R, enter msconfig and click OK. This will give you the System configuration tool. Go to Boot, and click Advanced options. Tick Number of processors and select one less than the virtual machine has. So in this example the virtual machine has four, select three.
Do a reboot. Again, verify both in the task manager and doing a read from target in TwinCAT 3. This is exactly how I want it to look. Just as with the previous example, we have an isolated core just for the TwinCAT 3 program in the virtual machine.
And finally, write a quick little structured-text program that only increments a number, activate configuration and put the system in RUN:
… it works!
Some final thoughts. Even though this worked, I’m still expecting some issues. I haven’t done any deep analysis, but the real-time properties (determinism/jitter) were far from optimal. The jitter for instance topped at a whopping 2000µs+ (i.e. more than 2ms). Even though my program only consisted of a simple counter running all by itself without any fieldbuses or external communication, making it useless for critical tasks (especially involving fast fieldbuses as EtherCAT). Though this is of course less than optimal, for most of my purposes it doesn’t matter! If I want high-determinstic/low jitter performance I’d run this on a dedicated PLC optimized for this very purpose. But being able to virtualize a TwinCAT runtime gives me a possibility to do certain tests that would require a large amount of physical PLCs. This is a huge benefit with running PC-based control in generic and TwinCAT in specific. The possibilites to run many instances of a real-time controller (even only for doing certain tests), mixed with standard user-space programs are huge, and is definitely something I will elaborate further on. Beckhoff should without any doubt emphasize this possibility more!
Also, I have not tested what combinations of hardware and versions of TwinCAT/Windows this works with. This might or might not work with your specific configuration of the hardware and software. For reference I used:
- Lenovo ThinkPad X1 carbon (gen6) with Intel Core i7-8550U CPU, BIOS 1.22
- TwinCAT 3.1.4022.20
- VirtualBox 5.2.12
- Windows 10 Pro version 1803 (in both host and virtual machine)
- CentOS 7.5.1804
As a grand finale I started a TwinCAT 3 runtime/program on my (real) Windows host, and simultaneously I started another TwinCAT 3 runtime/program as a virtual machine, and simultaneously with this I started the CentOS linux machine. Now I had two TwinCAT machines (on the same physical machine, one real and one virtual) each with their own separate AMS NetID. On the CentOS linux machine I downloaded the ADS-client sources from Beckhoff GitHub, compiled them and wrote a small little C++ test-program that retrieved a PLC-variable from the virtual ADS-host, and the result being:
This is PC-based control at its best!