Two trains on tracks near a building.

Benefits and challenges of reusing safety-critical software.

Safety-critical systems and software not only have long approval processes, but a long life cycle as well. Electronics developed 20 years ago sometimes need support or maintenance so that they can be used in a new environment. It is a reasonable question to ask what challenges the use and maintenance of such software hold. Our electronics development engineers in Budapest have explored this topic in depth.

Contact

Knorr-Bremse Vasúti Jármű Rendszerek Hungária Kft.

1238 Budapest
Helsinki út 105.
Magyarország - Hungary

Phone: +36 1 289-4100
info@knorr-bremse.com

Introduction

Safety-critical systems, such as rail brake control electronics (including the software that runs on them) developed by Knorr-Bremse, are subject to a thorough and lengthy qualification and external, standards-based, regulatory approval process before they are allowed to serve transportation or the travelling public on a daily basis.

Not only product development is long, but the life cycle of these devices, too. Therefore, electronics developed in some cases even 20 years ago need support and maintenance. In addition, new customer needs have to be fulfilled, meaning that new interfaces have to be provided so that an existing product or one of its components can be used further on in a new environment (e.g. as part of a new brake control platform).

Take a brake with us

At Knorr-Bremse Budapest, we work every day for a safer and greener future. In our Brake with us series, we not only show you how railway brake controllers and related services and systems work, but also give you a glimpse behind the scenes. Our engineers tell us personally how development and testing is carried out, how the various components combine to form a complete whole at the end of the process, and how passengers only notice that the vehicle stops exactly where and how it needs to.

Two trains on railway tracks in urbanTwo trains on railway tracks in urban

The controllers running on trains collect experience from hundreds of thousands of operating hours, confirming the merits of the product and the validity of the testing and qualification process.

Besides, taking into account all the work and costs invested, there is no doubt we want to reuse our existing and well-performing equipment and the software that controls them. With regards to all these aspects, the question arises what challenges we must face in maintaining and using this software.

Challenges

Changing standards

Generally speaking, newer versions of the standards usually contain more stringent requirements, or at least they are described more precisely. While it is true that the codes developed according to a previous standard do not have to be validated by a newer one, certain conditions must be met.

For bugs to be fixed in a software, the standard environment of the original project should be sufficient, and so only the change needs to be documented. If, however, we want to extend the functionality of a component, an add-on module might have to be developed. In this case, the original source files can remain intact, and new interfaces can be developed within new files. Obviusly, the extra component must comply with the new standards.

A real-world example is a CAN (Controller Area Network) peripheral control component, where, for example, functionality needs to be expanded by using ‘extended frame’ support. However, it is important to note that both the old and the new add-on module access the same peripheral and use the same registers. Consequently, they should be prevented from accessing the shared resources at the same time. The old component can then be left untouched, while the new component can be built purely in accordance with today's development requirements.

Changing technologies and ‘best practice’

Today's more advanced and faster microcontrollers and smarter compilers enable code to be organised into very small but testable modules connected via well-defined interfaces. Furthermore, we can now expect a compiler to generate highly optimised machine codes, meaning that we can write source codes that are bulkier, but also more readable, maintainable and testable.

In contrast, 20 years ago, developers had to put up with much more limited resources, so the use of bulkier functions and many global variables may have been necessary to ensure faster runtimes. Because of earlier rudimentary operating systems or schedulers, each problem arising from the asynchronicity of varioustasks was managed in its own way (e.g. using local access control variables instead of semaphores or mutexes). All these solutions, however, reduced code transparency, maintainability and testability.

To redesign the entire code in such a situation is tempting, but the original ‘set of rules’ by which the code was written must still be accepted and appropriate solutions must be applied to renew the module, even if they may seem outdated by today's standards. When using such a code, we should keep changes to the minimum on the one hand, but on the other, we must not forget that the architectural coherence of the software can be disrupted, which can lead to errors.

Informality

In spite of all the formalism (documentation, in-code comments, meaningful variant names), there are still elements of the development that seem self-evident in the given environment, and hence are not included in the documentation. For example, the empirical behaviour of the microcontroller used, or C code adapted to the optimal assembly code generation of the old and therefore not very sophisticated compiler.

When examining software for fixing bugs or further development, it is useful to know whether the code was originally developed for this microcontroller or it has been incorporated by porting. Failures of such software, which has been running for a long time and can be considered robust, are usually due to changes in the conditions and the integrated environment. In such cases, modifying them to correct the error can be obvious.

Conclusion

When using an inherited system, it is important to keep changes to a necessary minimum. If possible, you should try developing everything as a new module, even if it is logically integral to the existing one. If you see a ‘strange’ or apparently impractical solution in the code, always explore it carefully, as it may have been developed by experience. If you want to reuse an existing code, it may be useful to respect the logic and ‘rules’ of the previous development, as it can prevent systemic errors.

Man coding with multiple computer screens displayingMan coding with multiple computer screens displaying

When developing new software, you need to try and keep the code parts you implement fairly (but still to a reasonable extent) generic for easier future extensibility. Any constraints or contingency solutions that may emerge during development always need to be documented. It will help ensure that the completed software components can still be reused without problems for another 20 years.

Back to overview: Local Activities