200 Embedded and IoT Software Engineering Interview Questions – Part 6 Software Engineering & Design Patterns.

So far in part-1 of this interview questions series, we saw about

  • Questions about yourself
  • Questions about the projects you have done and
  • Questions from C programming language

Then in part-2, we saw some questions about the Basics of electronics. In part-3 we same some questions about Microcontrollers and peripherals. Then in Part 4, we saw about operating systems and in the last part (part 5) we saw questions on Networking

You can find parts 1, 2, 3, 4 and 5 in the links below.

200 Embedded and IoT Software Engineering Interview Questions – Part 1

200 Embedded and IoT Software Engineering Interview Questions – Part 2 Basics of Electronics

200 Embedded and IoT Software Engineering Interview Questions – Part 3 Microcontrollers

200 Embedded and IoT Software Engineering Interview Questions – Part 4 Operating Systems

200 Embedded and IoT Software Engineering Interview Questions – Part 5 Networking

Now lets head onto the next part and in this part, we will look at some questions about Software Engineering Principle and Design patterns that apply to embedded software development. Good knowledge of these topics is essential as it will help us write good maintainable source code.

As usual, I have divided the questions into 3 categories of easy medium and hard. So let’s begin!

Difficulty level: Easy

Question #1: Give 3 software development models

Answer #1: Some software development models are

  • waterfall model: Here the various stages of software development like requirements, design, implementation, testing, and deployment are completed one after another in a sequential manner.
  • agile model: Here the requirements are not fixed and it is assumed that the end customer does not know what he needs. The entire development is divided into iterations and during each iteration, a feature is made and presented to the end customer. The communication with the customer is considered key to the agile model.
  • prototyping model: Here first a simple prototype is made and given to the customer, the customer will play with it and give us feedback about what features need to be added and what should be improved. Then we continuously refine the product till we reach a much more mature version of the product and then do the release.

Question #2: Explain the concept of test-driven development

Answer #2: In test-driven development, the tests are written even before the software is written. For example assume we wish to add a small feature to our code. Let’s say we need to add a % button to our calculator app. So we first write a test to test the functionality of this button. Then we run the test and obviously it will fail. Then we can make the code to do this functionality and make sure it passes the test. Once it can pass the test, we simply refactor and refine the code to meet some acceptable standards.

You can read more about test-driven development in this link at technologyconversations.com

Question #3: What are version control systems?

Answer #3: Version Control Systems are used to track changes in the software as it goes through various versions and revisions during the software development process. A famous example is Git.

Question #4: Give 3 commands that you will use frequently in Git

Answer #4: They can include

  • git fetch
  • git commit and
  • git push

“fetch” command fetches the software from the remote repository, “commit” commands saves the changes in the repository and “push” command uploads the changes made to the remote repository.

Question #5: What are distributed embedded systems?

Answer #5: These are systems that are physically divided into modules and communicate with each other through some medium. One module can do general-purpose processing, another module can do signal processing and thus the processing load is shared.

Question #6: What are design patterns?

Answer #6: There are some problems that occur frequently in software design with a few small variations as it occurs each time. These problems can be solved by implementing the same patterns to the design. A collection of the patterns to solve commonly occurring software design problems are called design patterns.

Question #7: Explain the concept of modularity in software

Answer #7: Modularity is a design principle with the primary goal of having a loosely coupled system. For example, consider an indoor temperature monitoring system. Now if you initially designed the code with modularity in mind, you would have divided the code responsible for getting the temperature into these modules below instead of having just a single C file that does everything.

  • UART driver
  • Sensor driver

Now since we have 2 different parts, we can change them individually as we see fit. Here let’s say you decide to change the hardware design so that the microcontroller and the temperature sensor are now talking with an SPI interface instead of UART in the previous version you can still use the sensor driver code with no issues and just change the UART driver. Similarly, if you need to the sensor chip, then you can still keep the UART driver as it is and just change the chip part. Thus we need to write code that is independent of each other so that we can easily change things as we go. This type of code is called a modular code.

Question #8: Explain Backwards Compatibility

Answer #8: A system usually comprises of several components and each can be updated individually. For example, consider windows operating systems and apps written on them. Here an application originally designed to be run on Windows XP can still run on windows 10. But the same is not true the other way around i.e a windows 10 app will not work on Windows XP.

This concept of maintaining the compatibility to the older versions as we update the system is called Backwards compatibility

Question #9: What is Unit Testing?

Answer #9: Each section of a given software needs to be tested individually to make sure that they meet the design requirements. These sections can be single functions or the entire C source file. They are written as the functionalities are being developed so that during each iteration of the software, these tests can be automated to make sure that the added features do not affect the already existing ones.

Question #10: Why software are constantly updated? Why not give a finished version to the customer?

Answer #10: The reason is lack of knowledge. No one could possibly know during the design and development phase what the end product’s use cases are and what features are essential and what are redundant and not necessary. Hence we give software updates to make sure that the customer gets the features he needs in case they were originally not included in the initial version of the product.

Also, it is not possible to produce a test environment that can simulate all possible variations of the actual working environment and hence it is impossible to catch all the bugs. Thus bug fixes are also released onto the software releases.

Difficulty level: Medium

Question #1: Explain why prototype software development model is used widely in embedded industry

Answer #1: It is because of the nature of embedded development. Since we are designing hardware, our system is basically constrained by the limitations of the hardware. Say the customer needs a feature that requires a particular sensor then it cannot be done unless the hardware already has the sensor required. So in these scenarios, there is no point making a perfect complete version until we are sure we meet the customer requirements. Hence we go for simple non-optimised yet modular software, and some simple hardware till we reach a point where the customers are actually happy with the prototype then we can refine and optimize that version.

Question #2: What command would you use in git to take changes from one branch and add it to another branch?

Answer #2: git merge or git rebase can be used.

Question #3: Explain git conflict resolving

Answer #3: When we are merging 2 branches/versions together, the git software will automatically make additions and deletions of lines wherever possible. But say line 10 of file test.c has 2 slightly different versions, then git will get confused about which one to use, and in these situations, it will need the user to interfere and tell the software what to keep in the merged version. This is called git conflict resolving.

Question #4: What is Bricking? Explain how to avoid bricking the device during the update process

Answer #4: Bricking refers to rendering the product unusable, and if that happens the product basically behaves like a brick or a rock. It basically has no use other that like a paperweight!

If the uploaded firmware is corrupted or if the uploading process stops in the middle resulting in an incomplete software try to run, bricking happens.

To avoid this what we must do is to keep the original software undisturbed and copying the new firmware to an unused memory location, often referred to as scratch space in the system. This space can either be RAM or unused flash space. Once the entire software has been uploaded and verified, we that then copy this software to the old software’s location and start running the new software.

Some implementations even have 2 storage spaces, one for the old code and the other for the new code so that in case the new software is found too buggy to use or if the copying failed due to power failure or insufficient space, the user can revert back to the old one and still have a functioning system.

Question #5: Give 2 most commonly used design patterns used in embedded systems

Answer #5: They include

  • Super-loop architecture and
  • Multitasking architecture

Super-loop architecture is used on a simple system, but when the system gets complex, we can go for multitasking architecture instead to keep the code cleaner.

If there are too many tasks to maintain then its a good idea to make the architecture operating system dependent.

Question #6: Explain the role of bootloader in the updating process

Answer #6: A Bootloader is a software permanently placed in the flash and its sole job is to copy the code coming in from the PC to the Flash or RAM. This software is primarily used on space constraint systems to do the updating process where there is no scratch space to copy the updated firmware.

The device can usually be booted to run this bootloader code by setting an appropriate GPIO pin externally during power-up or by writing a special value on a special location in the flash.

Question #7:  Explain superloop architecture pattern

Answer #7: This is a simple pattern,I am sure you should have used it, but haven’t heard of this”super-loop” jargon before.

Here the tasks that the system should do regularly are placed inside an infinite while loop called the super-loop. All the work that is to be done just once are placed outside this loop and this section is usually called the initialization section.

The setup and loop functions in Arduino is a good example of this super-loop architecture.

Question #8: Explain multitasking architecture pattern

Answer #8: Here there are several tasks that need to take turns executing on the CPU.

Unlike super-loop architecture, the tasks cannot be executed one after another, but instead, they are executed parallelly with the help of a scheduler.

Question #9:  Give 2 ways you can optimize your code for space

Answer #9: It can be done by

  • hand-coded assembly
  • disabling function inlining and loop unrolling on compilers
  • avoid big standard library routines like sprintf.

Question #10: Give 2 ways you can optimize your code for speed

Answer #10: To optimize for speed the following can be done.

  • table lookups instead of complex computations
  • fixed-point arithmetic
  • hand-coded assembly on frequently called routines

Difficulty level: Hard

Question #1: what is the git command you would use to squash previous 3 commits into one

Answer #1: This question will reveal your experience level to the interviewer. The command to use is

git rebase -i HEAD~3

Question #2: Give the basic components of a driver source code

Answer #2: The following components are needed to make a device driver

  • macros and structs representing the register structure of the device
  • initialization routine
  • API interface and
  • Interrupt handlers

Question #3: give 3 best practices to make code easily readable

Answer #3: They can include

  • making variable names and function names meaningful
  • making functions short and make them specific
  • adding comments to explain the hard, unusual parts
  • keeping the code clean and nicely formatted

Question #4: What are Version naming conventions? Explain using an example

Answer #4: Version naming conventions are used to tell the user some details about a software version just by looking at the version name

For example in Semantic Naming Convention, the format to use is

MAJOR. MINOR. PATCH

Here PATCH refers to simple bug fixes, MINOR refers to features added in backward-compatible manner and MAJOR are changes that are not backward-compatible.

For example, the version v1.2.5 means first major version, 2 backward-compatible features have been added on the version1 and 5 bug fixes have been made to these added features so far.

You can read more about version naming in this link.

Question #5: How OTA updates work?

Answer #5: OTA stands for Over The Air updates. Basically all kinds of updates we receive for our smartphones an PCs are of this type.

Here the updated firmware is placed on the internet and delivered to the device through the network. In olden days updates were done by either sending the device back to the manufacturer or by the manufacturer sending storage media like a CD or USB flash drive to the user to perform the update. Imagine the hassle we have to go through if we are still in that era! These days OTA has become the standard for updating any device that has networking capabilities, which include all of the IoT devices!

Question #6: Explain HAL layers. What is the benefit of having a HAL layer?

Answer #6: HAL stands for Hardware Abstraction Layer.

Question #7: How to optimize an embedded software for power consumption

Answer #7: Microcontrollers usually have several options for optimizing the power consumption using idle and sleep states.

As an embedded software engineer we must be familiar with the possibilities that the processor provides and use them effectively.

One main way we can optimize for the system is by using an interrupt based architecture and letting the CPU sleep as much as possible.

Question #8: Explain reentrancy

Answer #8: If a function/method/routine can be safely called even before the previous version has completed its execution then its called a reentrant function.

Question #9: Explain race conditions

Answer #9: Race conditions occur when the tasks are executed in the wrong order.

Assume a system having many interrupts and several tasks in the main loop. Then care must be taken to make sure these routines do not mess up each other’s states to avoid race conditions.

Have a look at the 2 functions below

int x, y;

void Task1()
{
    if (x == 5) {
       y = x++;
    }
}

void Task2()
{
    x  = 20;
}

Here say the scheduler is running Task1 and just before line 4 is executed the scheduler gives Task2 the CPU. Now the CPU executes Task2 and changes the value of the global variable x to 20. So instead of getting y = 6, we are getting y = 21.

These kinds of problems are called race conditions.

Question #10: Explain priority inversion problem in multitasking systems

Answer #10: It happens when a lower priority task is holding a resource that is needed by a higher priority task.

Assume a low priority task named Task-L is executing and it needs file-A and hence it opens it and continues execution. Then at a scheduling point, the compiler comes over and sees that another task named Task-M which has higher priority is waiting on the queue and so it stops Task-L and lets Task-M run.

Now assume another task Task-H come to the queue and it has even higher priority than Task-M. So Task-M is also stopped to let Task-H run. Let’s say Task-H needs File-A which is held by Task-L to continue its execution. So Task-H will go into waiting again letting Task-M to run and complete. Once M completes, L can also complete giving the File-A finally to Task-H.

Thus priority is Task-H -> Task-M -> Task-L

But the order of completion is Task-M -> Task-L -> Task-H. This is the priority inversion problem.

The only way around this is to promote Task-L’s priority to same as Task-H as soon as we realize that it’s holding a resource needed by H. This way at least Task-L can run before Task-M

Apart from these questions, you can be asked to

  • draw a simple architectural diagram of a system they define.
  • make a sample interface for specific subsystems/modules.

Make sure you can do these! I will cover these in detail in a future article.

Okay, let me stop here for now. I hope you got some value out of this! Make sure to learn and practice your weak areas.

Feel free to share it with your friends and colleagues!

If you have any questions or sugessions you can also email us or contact us through this link!

EI

We’re passionate about inventing embedded devices and we hope you are too! This blog deals with a wide variety of topics from C programming to IOT to networking certifications and more. Hope you enjoy your time spent here and hope you get some value out of our blog!

You may also like...