Help us with your details

Oops! Something went wrong while submitting the form.
Our June 2021 cohorts are filling up quickly. Join our free webinar to Uplevel your career

Top Embedded Software Engineer Interview Questions You Should Learn

by Interview Kickstart Team in Interview Questions
November 9, 2022
Get trained by FAANG Embedded SWEs and nail your next interview!

Top Embedded Software Engineer Interview Questions You Should Learn

Attend our Free Webinar on How to Nail Your Next Technical Interview

WEBINAR +LIVE Q&A

How To Nail Your Next Tech Interview

Hosted By
Ryan Valles
Founder, Interview Kickstart
Our tried & tested strategy for cracking interviews
How FAANG hiring process works
The 4 areas you must prepare for
How you can accelerate your learnings
You can download a PDF version of  
Download PDF

While there are many different types of software engineer positions, the job responsibilities and interview questions are generally consistent across the field.

If you're interviewing for a position as an embedded software engineer, expect to be asked about your experience with microcontrollers, firmware development, and real-time operating systems.

You'll also likely be asked questions about your problem-solving skills and how you would handle certain development scenarios. The best way to prepare for a FAANG software engineer interview is to upskill and become the kind of embedded software engineer leading tech companies would love to hire.

By preparing for these embedded software engineer interview questions in advance, you can ensure that you're ready to answer any question thrown your way.

If you are preparing for a tech interview, check out our technical interview checklist, interview questions page, and salary negotiation e-book to get interview-ready!

Having trained over 13,500 software engineers, we know what it takes to crack the most challenging tech interviews. Our alums consistently land offers from FAANG+ companies. The highest-ever offer received by an IK alum is a whopping $1.267 Million!

At IK, you get the unique opportunity to learn from expert instructors who are hiring managers and tech leads at Google, Facebook, Apple, and other top Silicon Valley tech companies.

Want to nail your next tech interview? Sign up for our FREE Webinar.

In this article, we’ll cover:

  1. What Interviewers Look for in an Embedded Software Engineer 
  2. Technical Embedded Software Engineer Interview Questions and Answers
  3. Behavioral Embedded Software Engineer Interview Questions
  4. Google Embedded Software Engineer Interview Questions
  5. FAQs on Embedded Software Engineer Interviews

What Interviewers Look for in an Embedded Software Engineer

If you're an experienced embedded software engineer, you know that interviews can be nerve-wracking. You want to make sure that you impress your interviewer and demonstrate your skill set. But what exactly are they looking for?

Embedded software engineer interview questions will depend on what the interviewer is looking for based on the role and company requirements. Interviewers will primarily try to uncover if you have the required skills to function as an embedded software engineer. Additionally, they will assess your skill level and whether you are a good cultural fit for the company.

Role of an Embedded Software Engineer

An embedded software engineer develops code optimized for an embedded system to manage a hardware device. For example, embedded software engineers are responsible for the development and maintenance of software that is embedded in devices such as pacemakers, cars, and thermostats.

Embedded software is developed and used for a particular hardware system or platform, keeping end-user needs in mind. Because this software controls critical functions, embedded software engineers must have a strong understanding of both hardware and software design principles. They must also be able to write code that is both efficient and reliable. The role of an embedded software engineer is challenging but also very rewarding.

Learn more about the roles and responsibilities of an Embedded systems engineer here.

Skill Needed to Become an Embedded Software Engineer

Embedded software engineer interview questions will be largely based on embedded software engineering skills. Here are some key skills requirements:

  • Programming language expertise: C, C++, C#, Python
  • Assembly Programming Language
  • Real-time Operating Systems (RTOS)
  • MATLAB
  • ARM
  • Software and Hardware programming at a low level
  • Tensorflow, Linux, RISC-V, Zephyr; other open source technologies
  • System of Chip (SoC) level software optimization - how to optimize memory, performance, and power
  • Microcontrollers, latest MCUs, and Microprocessors
  • System-level designing and software development
  • Communication Protocols: SPI (Serial Peripheral Interface), 12C, GPIO (General Purpose Input Output), USB, Controller Area Network (CAN)
  • Device Drivers
  • Debugging 
  • Interrupt-driven design
  • IoT technologies
  • Data Structures
  • Project Management and trouble-shooting
  • Creativity, problem-solving, and analytical thinking

Embedded software engineer interview questions will be devised to discover your expertise in one or more key skills. Interviewers will also look for knowledge of both hardware and software design and embedded systems. You can take an in-depth look into the skills required to become an Embedded systems engineer.

Embedded systems are being adopted at an increasing rate in various industries. As such, embedded software technologies are ever-evolving. Ensure you stay updated on the latest skills and developments in this field to gain a competitive edge.

Take a look at some Embedded Software Engineer Interview Prep Tips here.

Technical Embedded Software Engineer Interview Questions and Answers

Technical interview questions for embedded software engineers will vary depending on the role and the interviewer. Some common questions are:

Q1. What is an embedded system?

An embedded system is a computer system with hardware and software components designed to serve a dedicated function. For example, a smoke detector (fire alarm) can be considered an embedded system. It is made of hardware (sensors, speaker, etc.) and software that starts the alarm when the smoke is detected via sensors. 

An embedded system could be designed to work as an independent system or as part of a large system. The smoke detector could be an example of an independent system. We can find several embedded systems in consumer electronics, automobiles, smart watches, household appliances, industrial equipment, and medical equipment.

Let’s understand the basic structure of an embedded system using a fire alarm: 

  • Sensor: The sensors (heat, smoke, or carbon monoxide sensors) measure and convert the physical quantity to an electrical signal.
  • A-D Converter: To convert the analog signal sent by the sensor into a digital signal.
  • Processor & ASICs: Processor and other ASICs run the instructions programmed to the fire alarm and process the sensor data by checking the values with the reference/threshold value to produce a suitable output (or no output). 
  • D-A Converter: To change the digital data fed by the processor to an analog signal. The actuators often require analog signals. 
  • Actuator: As directed by the output data from the processor, the actuator produces a physical signal, such as setting off the alarm, blinking LEDs, etc.

Q2. What is a spinlock?

Spinlock is a synchronization mechanism used to access shared resources orderly by locking resources for a short amount. At the same time, other consumers wait in a spin (loop) until the shared resource is released/unlocked. For example, two threads named Thread-A and Thread-B would like to access a common resource named Resource-X.  

Let's say Thread-A requests spinlock to access a Resource-X:

If Thread-B is using Resource-X, Thread-A will be waiting in a loop until Thread-B releases/unlocks Resource-X. 

If any thread does not use Resource-X, Thread-A will be allowed to acquire the spinlock, and all other threads will wait until Thread-A releases the lock. 

A spinlock is a lock that causes a thread trying to acquire it to simply wait in a loop ("spin") while repeatedly checking whether the lock is available. 

Because the thread is not performing any task despite being active, the use of this lock puts the thread in a busy waiting state. Spinlock held until explicitly released. However, in some implementations, spinlocks may be automatically released if the thread being waited on “goes to sleep” or blocks. Spinlock is useful in a multiprocessor system.

Q3. What do we mean by virtual memory?

Virtual memory is a memory addressing scheme that offers the illusion of having one large continuous addressing space for an application. Physically, the main memory can be smaller than the virtual memory address. Some virtual memory addresses physically correspond to the main memory, and other addresses physically correspond to the secondary memory. 

There will be address translator implementation for the application to utilize one large contiguous address space. The address translation introduces an uncertain delay in the memory response. As a result, many embedded system microcontrollers do not implement virtual memory. 

Virtual memory allows users to load multiple bigger-size processors than in the main memory. Now the operating system does not load complete processes into the memory; rather, it loads the different relevant parts of multiple processors into the main memory. Thus effectively using the memory and also increasing the CPU utilization between processes. The size of the virtual memory is determined by the addressing scheme of the given CPU instruction architecture and not by the size of the main memory.

Virtual memory is implemented using both software as well as hardware. It maps memory addresses that a program uses, known as virtual addresses, to physical addresses in the computer's memory. All of a process's memory references are virtual or logical addresses. These logical addresses are then dynamically translated to physical addresses during run time.

Q4. What is your approach for debugging any issue in an embedded system?

Debugging techniques are very specific to the issue and the system. However, the following is a generic step-by-step approach that can be applied to most embedded systems. 

1. Understand the system

Understanding the system is always a good idea as it helps a lot in narrowing down the issue. You can start by asking a few basic questions, such as:

  • What is the input to the system? 
  • What is the hardware underneath? 
  • What is the expected behavior from the system?
  • What are the different states of the system (at a very high level)?

Questions such as these can save a lot of time at various stages during debugging. 

2. Reproduce the issue

Once you have a basic understanding of the system, you should try reproducing the issue. Hopefully, it is an issue that can be easily reproduced. If you can do these first two steps properly, finding the issue or debugging it becomes much easier. 

3. Add instrumentation/logging

Once you can reduce the issue, the next step is to see if it's easy or hard to reproduce the issue. If it's an easy-to-reproduce issue, then you don't worry too much about what are certain scenarios during which you can reproduce the issue. But if it is hard to reproduce the issue, then sometimes just seeing when the issue reproduces usually gives you a clue. 

In either of these cases, it is essential to add logging, some debug information, or add instrumentation (Oscilloscope, Signal Generator, Multimeter, etc.), depending on your system. 

Now, tracking through the sequences of steps becomes much easier until the issue is reproduced. 

In some systems, it's possible that you don't even have access to the signals. In such scenarios, you can try to look through the crash logs. You can then analyze the signals from the instruments or logs for a better understanding of the issue. 

4. Isolate the bug/system 

After finding where the issue is coming from, you can try to isolate the system (sub-system). You can then use divide-and-conquer techniques such as binary search to figure out the point where the bug is getting introduced.

5. Apply quick fixes

With all the information you gathered, try to form some hypotheses: 

  • This is the bug
  • This is the module (sub-system) or line of code the bug is coming from
  • Under these conditions; I can reproduce the issue (these conditions are causing the issue)

So now you can try applying quick fixes to counter the bug and iteratively see if the issue is reproducing again with the conditions identified earlier. If everything is working correctly, you can commit the changes and consider the bug fixed.

Q5. Describe the role of the watchdog timer.

Many microcontrollers have the watchdog timer feature (usually implemented with specific dedicated hardware). It can be used to check if the software running on the microcontroller is hung. Microcontrollers are designed to be stable and durable. 

However, there are still many issues that can affect the stability of the hardware. An unhandled combination of software-side events may also occur. Both can cause microcontrollers to get "stuck or hang" either electrically or in an infinite loop in software.

A watchdog timer is a subsystem that must explicitly notify software that everything is running as expected at a certain time. If the watchdog is not receiving the expected notification, it will take some action, such as resetting the microcontroller or even the entire device. This is usually done periodically, and the watchdog timer will take action if it does not receive a notification after a configurable amount of time since the last notification.

Q6. What is a livelock, and how is it different from a deadlock?

First, let’s understand what a deadlock is. In a deadlock, you have two or more concurrent processes waiting for a resource, and one of the processes may be holding the resource. Assuming it's a unitary resource, one process is holding one resource, and the other is holding another resource, so they are both waiting for the other to release the resources while not letting the resources go from themself.

So, basically, in a deadlock, we have a ‘hold and wait’ type of situation between the two processes. Both processes are doing this; therefore, they are in an embrace in which neither will let go so that the wait will be forever - this is what we define as a deadlock situation.

A live lock is the opposite of that. Instead of waiting, the processes try to get a resource, but the process is courteous, so it differs from taking the resource. These processes are not waiting and are running parallel. It means you check if any other process is active, and you assume it will also ask for the same resource, so you differ and back off from using (getting hold of) the resources. So when two processes differ from using the resources out of courtesy that the other process may need, it puts them in a livelock situation.

The loop here is that both processes keep checking for other activity and backing off. Now because they are concurrent, this thing can go on for a long time. None of the threads makes any progress which means they are locked.

To summarize, a deadlock is you're holding and waiting for the resources. But in a livelock, you differ forever from the resource, assuming some other active process will need it.

We can imagine a real-life example of livelock is when two people meet in a narrow hallway, and each of them tries to be polite by moving aside to let the other person pass. Still, they end up swaying from side to side without actually passing the hallway as they repeatedly move the same way at the same time.

To learn more about livelocks, check out our tutorial:

Q7. What are the causes of the interrupt legacy?

The first delay is usually caused by hardware: the interrupt request signal must be synchronous with the CPU clock. Depending on the synchronization logic, up to 3 CPU cycles may elapse before an interrupt request reaches the CPU core.

The CPU usually completes the current instruction, which may take several cycles. On most systems, the time-consuming instructions to get executed are divide, push-multiple, or memory-copy. In addition to the cycles required by the CPU, additional cycles are often required for memory accesses. On an ARM7 system, the STMDB SP!,{R0-R11, LR} instruction is typically a worst-case instruction consisting of 13 32-bit registers and takes 15 clock cycles.

The memory system might require additional cycles for wait states.

Upon completion of the current instruction, the CPU performs a mode switch or pushes registers on the stack. Modern processors such as ARM generally perform mode switching, which takes fewer CPU cycles than saving registers.

Pipeline fill: Most modern CPUs are pipelined. Instruction execution occurs at different stages of the pipeline. The instruction is executed when it reaches its final stage of the pipeline. Since the mode switch has flushed the pipeline, several more cycles are required to refill the pipeline.

Q8. What are static memory allocation and dynamic memory allocation?

Memory Allocation: Memory allocation is the process by which physical or virtual memory space is allocated to computer programs and services. It is done either before or during program execution. There are two types of memory allocation:

  • Static Memory Allocation: Static allocation means that the object is declared with a static storage class or has an external or internal reference. It is initialized only once before the program starts, and its lifetime is for the duration of the program's execution. An example of static memory allocation is a global and static variable.
  • Dynamic Memory Allocation: In C, many library functions, such as malloc, calloc or realloc, etc., are used for dynamic memory allocation. One problem with dynamically allocated memory is that the compiler doesn't destroy it itself; the user is responsible for freeing the allocated memory.

These memory management functions return a pointer to the allocated memory block, and the returned pointer points to the starting address of the allocated memory block. If no space is available, a null pointer is returned.

Here are some sample technical embedded software engineer questions that you can practice:

  1. Explain inline functions
  2. Explain buses in communication
  3. Explain the troubleshooting process for embedded targets
  4. Differentiate between component-based design and object-oriented design
  5. Differentiate between mutexes and semaphores
  6. Name some code testing tools
  7. Name some of the latest technologies
  8. How would you reduce interrupt latency?
  9. What are model transformations?
  10. Why would you need DMAC in ES?
  11. Would you debug ISR using breakpoints?
  12. Why do segmentation fault errors occur?
  13. Write a program using the power-save super loop.
  14. Which software configuration tools are you aware of?

C, C++, Embedded C Software Engineer Interview Questions

In your embedded software engineer interview, you can be asked specific questions on C, C++, and Embedded C, such as:

Q1. What are the type qualifiers in C Programming? 

It is a keyword applied to a data type, resulting in a qualified type. For example, const int is a qualified type representing a constant integer, whereas const is a type qualifier to specify that the value will not be changed. The following code declares a constant integer x and sets its value to 10. Now, if we attempt to change x’s value, the compiler will throw an error.  


const int x = 10;

Similarly, a volatile int is a qualified type representing a volatile integer, meaning its value can change by any external device or hardware. The following code declares a volatile integer y and set’s its initial value to 0, which can be modified.


volatile int y = 0;

As of 2014 and C11, there are the following four type-qualifiers in standard C: 

  • const (C89): The const keyword is compiler-enforced and says that the program cannot change the object’s value, making the object a nonmodifiable type.
  • volatile (C89): A variable with a volatile-qualified type may be modified in ways the implementation is unaware of, or it may have other unanticipated side effects. The volatile keyword indicates that a value may change between different accesses, even if it does not appear to be modified.
  • restrict (C99): This type of qualifier can be used in pointer declarations. By adding this type of qualifier, a programmer hints to the compiler that for the lifetime of the pointer, no other pointer will be used to access the object to which it points.
  • _Atomic (C11):  The latter has a private name to avoid clashing with user-defined names. _Atomic is used to avoid the race condition if more than one thread attempts to update a variable simultaneously.

Q2. What is the difference between a null pointer and a void pointer?

A null pointer is essentially a null value assigned to a pointer of any data type, while a null pointer is a data type that remains void until it is assigned a data type address.

The data type of a pointer is stored in the memory location to which the pointer points. If you are unsure of the type of data stored in a particular memory location, you must create a void pointer.

A null pointer does not contain a reference to any variable/value; it is NULL, making it a null pointer.

a void pointer is always of type void *

These pointers are mainly used for dynamic memory allocation using the malloc(), calloc(), and realloc() functions. Here, the reserved memory block is a pointer to an empty pointer, and when the pointer value is returned, it must be explicitly cast to the specific type required.

Q3. Explain the use of ‘volatile’ in Embedded C.

A volatile keyword is a type qualifier in C applied to a variable when declared. It tells the compiler the variable may be modified in ways unknown to the implementation or have other unknown side effects. As a result, the compiler cannot make any assumptions about the variable's value. The optimizer must reload the variable every time it reads the value rather than keeping a copy in a register.

Let's take a look at the syntax:

To declare a variable as volatile, you must put the volatile keyword before or after the data type while defining the variable. Look at the following two variable declarations for a volatile unsigned 8-bit integer variable:


volatile uint8_t a; 
uint8_t volatile b;

Now, it turns out that pointers to volatile variables are pretty common, 

You can also declare a pointer to a volatile variable. The following declarations declare v_pot to be a pointer to a volatile unsigned 8-bit integer:


volatile uint8_t * v_pot; 
uint8_t volatile * v_pot;

However, using a volatile pointer to non-volatile data is very rare; the following is the syntax for reference:


uint16_t * volatile v_pot;

If you really must have a volatile pointer to a volatile variable, the following is the syntax for your reference:


uint16_t volatile * volatile v_pot;

Q4. Why do we need infinite loops in embedded programming, and how do we code an infinite loop in Embedded C?

As the name suggests, infinite loops repeat indefinitely and never terminate as long as the program is alive. In most other types of programming, infinite loops are created by mistake and don’t have many use cases. However, this is not the case with embedded programming. 

Embedded systems are intended to run forever and serve a given task repeatedly. If the embedded system suddenly exits, it means it no longer serves the task it is designed for. If the embedded system completes a given task, it should go into a sleep mood instead of exiting altogether because the next time the task is needed, it should be able to wake up and resume the task.

This infinite loop contains the main instruction the embedded system should execute. You can take the Arduino program template, for example:


void setup(){
// This function is called once when the program starts. This is a good place to initialize the various hardware and software parameters. 
}
void loop(){
// The loop() function is called over and over and is the heart of most sketches. 
}

Now, let’s see how we create infinite loops in Embedded C. 

The following is the definition of the infinite for loop. Here we don’t mention any of the parameters for initialization, condition, or increment/decrement for the loop.

Now, we'll look at how to use a while loop to create an infinite loop. The infinite while loop is defined as shown below. Here we hard code the while condition to true.


while(true)  
{  
   // body of the loop..  
}

The do-while loop can also be used to create the infinite loop. The following is the syntax to create the infinite do-while loop. Here again, we hard code the while condition to true


do  
{  
    // body of the loop..  
}while(true);

Q5. What do sizeof(int) return on 32-bit and 64-bit machines?

According to the C-spec, an integer only needs to be able to represent this (-32767,32767) range of numbers. Essentially the C-spec only says that an integer has to be 16 bits or larger. But in practice, sizeof(int) will return you 4 bytes on both 32-bit and 64-bit machines.

In other words, The C standard only specifies a minimum range for integer types guaranteed to be supported. An int is guaranteed to be able to hold (-32767,32767), which requires 16 bits (2 bytes). However, implementations can go beyond that minimum, as we see that many modern compilers make int 32-bit, i.e., 4 bytes.

However, a long int might have a different size on a 32-bit or 64-bit machine. It is also possible that a long int might be different on Windows versus Linux machines. But on the other hand, long long int is consistently 64 bits but could be implemented as more.

 To learn more about it, watch our tutorial:

These are some sample C, C++, and Embedded C software engineer questions that you can practice:

  1. How many storage classes are there in C?
  2. How do you write Function Itoa() in C?
  3. Why is C++ popularly used in embedded firmware development?

Coding, Data Structure, Algorithm Embedded Software Engineer Interview Questions

These questions stated below are similar to the software engineer interview questions on coding, algorithms, and data structures. You can practice these for your embedded software engineering interview.

Q1. How would you use a linked list to test for a loop?

A linked list is one of the most important data structures to learn and understand while preparing for interviews. Having a good grasp of linked lists can be a plus point during interviews.

In the given problem, we are given a singly linked list that may contain a loop. We have to return true if the linked list contains a loop and vice-versa.

If the linked list contains a loop, the last node in the list will not be pointing to the NULL; instead, it will be pointing to another node in the list (it can also point to itself).

Given problem can be approached in multiple ways.

Hashing Approach:

This approach is very simple. In this approach, we use a hashtable and keep inserting nodes into it while traversing the linked list. If NULL is reached at any point, we return false, and the linked list does not contain a loop.

And if the following node in the chain of current nodes points to any of the previously stored nodes in the hashtable, then return true.


bool hasCycle(ListNode *head) {
        ListNode *temp=head; 
        sethashtable;
        while(temp!=NULL){
       //If this node is already present
 //in hashmap, you will encounter it for the second //time and it means there is a cycle
            if(hashtable.find(temp->next)!=hashtable.end()){
                return true;
            }
            else{
          // If we are seeing the node for
          // the first time, insert it in hash
                hashtable.insert(temp->next);
            }
            temp=temp->next;
        }
        return false;      
    }

Complexity Analysis: 

  • Time complexity: O(n), where n is the number of nodes in the linked list. Only one pass through the loop is needed.
  • Auxiliary space: O(n) since a hash table is used.

Floyd's Cycle Search Algorithm:

This fastest method uses two pointers - a fast pointer and a slow pointer to check if there is a loop in the linked list. A slow pointer advances one node, while a fast pointer advances two nodes at a time. If there is a loop in a linked list, the fast and slow pointers must meet at some point.


class Solution {
public:
    bool hasCycle(ListNode *head) {	
		// if head is NULL then return false;
        if(head == NULL)
            return false;        
		// making two pointers fast and slow and assigning them to head
        ListNode *fast = head;
        ListNode *slow = head;        
		// till fast and fast-> next not reaches NULL
		// we will increment fast by 2 step and slow by 1 step
        while(fast != NULL && fast ->next != NULL)
        {
            fast = fast->next->next;
            slow = slow->next;            			
			// At the point if fast and slow are at same address
			// this means a linked list has a cycle in it.
            if(fast == slow)
                return true;
        }        
		// if traversal reaches to NULL this means no cycle.
        return false;
    }

Q2. Reverse the given string S.

Solution:

Approach 1: A simple approach to solving this problem is to use two-pointers. Initiate one pointer at the beginning of the string and one at the end. Continue the swap operation until both pointers coincide.

void reverseString(string &s) { int left = 0, right = s.length() - 1; while (left < right) { char tmp = s[left]; s[left++] = s[right]; s[right--] = tmp; } }

This problem can also be solved using a recursive approach, where we return the last letter of the string until the size of the remaining string is greater than zero.


void reverseString(const string& str) {
  // store the size of the string
  size_t numOfChars = str.size();
  if(numOfChars == 1) {
    cout << str << endl;
  }
  else {
    cout << str[numOfChars - 1];
    // function recursion
    reverseString(str.substr(0, numOfChars - 1));
  }
}

Q3. Given an increasing array of unique integers and an integer target. Now the array is rotated clockwise by some unknown number of rotations. Find and return the index of the target in the final rotated array. If it is not present, then return -1.

Example One:


"arr": [3, 4, 5, 1, 2],
"target": 1

Output: 3

Example Two:


"arr": [8, 10, 5, 6, 7],
"target": 2

Output: -1

Solution:

The simplistic solution to this problem is to linearly iterate on the array while determining whether the current element and the target element are equal.


int getElementIndex(vector array, int target) {
   int index = -1;
   for (int i = 0; i < array.size(); i++) {
       if (array[i] == target) {
           index = i;
       }
   }
   return index;
}

Analysis

  • Time Complexity: O(n)
  • Space Complexity: O(1)

Optimal Approach (With Binary Search): 

We can solve this problem optimally with a single recursive Binary Search. 

The array can be treated as two different sorted arrays. All we need to do is to find the point from where the array needs to be broken down into two different arrays.

To find that point, we can perform a binary search to find the only element greater than its next element.

After finding that point, we can apply binary search in both parts of the array to find the index of the key.


int findRotatedIndx (vector arr, int low, int high) {
   if (high >= low) {
       int mid = (high + low) / 2;
       if (mid != 0 && arr[mid - 1] > arr[mid]) {
           return mid - 1;
       } else if (arr[0] >= arr[mid]) {
           return findRotatedIndx (arr, low, mid - 1);
       } else {
           return findRotatedIndx (arr, mid + 1, high);
       }
   }
   return - 1;
}
int binarySearch (vector arr, int low, int high, int target) {
   if(high >= low) {
       int mid = (high + low) / 2;
       if (arr[mid] == target) {
           return mid;
       } else if(arr[mid] > target) {
           return binarySearch (arr, low, mid - 1, target);
       } else {
           return binarySearch (arr, mid + 1, high, target);
       }
   }
   return - 1;
}
int getElementIndex(vector array, int target) {
   int n = array.size();
   int rotatedIndx = findRotatedIndx (array, 0, n - 1); 
   //using the  function to find the pivot element
  
   if (rotatedIndx == -1) {
       return binarySearch (array, 0, n - 1, target); 
   // if the array is in ascending or descending order, 
   //then perform binary search on the entire array
///
   }
   if (array[0] <= target) {
       return binarySearch (array, 0, rotatedIndx, target);
   } else {
       return binarySearch (array, rotatedIndx + 1, n - 1, target);
   }
}

Analysis

  • Time Complexity: O(log(n))
  • Space Complexity: O(1)

You can also practice these sample embedded software engineer interview questions on coding and DSA:

  1. Questions on basic sorting and searching
  2. Differentiate between bubble sort and quicksort
  3. How would you use a binary search algorithm without recursion?
  4. Write code to perform a level order search in a binary tree
  5. Can you use Union in Structure?
  6. Differentiate between Structure and Union

Bit Manipulation Embedded Software Engineer Interview Questions

In your embedded software engineer interview, you can expect questions on bit manipulation. Here are some sample questions:

Q1. Return the integer in the range missing from the given nums array containing n distinct numbers in the range [0, n].

Example 1:

Input: nums = [3,0,1]

Output: 2

Explanation: n = 3 because there are 3 numbers, so all numbers are in the range [0,3]. 2 is the missing number in the range as it does not appear in the array.

Example 2:

Input: nums = [0,1]

Output: 2

Explanation: n = 2 because there are 2 numbers, so all numbers are in the range [0,2]. 2 is the missing number in the range as it does not appear in the array.

Solution:

We are given distinct numbers with only one missing number in the range 1 to n (inclusive).

So we can write, sum of numbers in given array + missing number = (1 + 2 + ... + n - 1 + n) = (n * (n + 1)) / 2. Hence, the missing number = (n * (n + 1)) / 2 - sum of numbers in the given array.

Time complexity: O(N), as we are traversing along the array.


int missingNumber(vector& nums) {
    int n = nums.size();
	// Sum of n natural numbers
    int Nsum = n * (n + 1)/2; 	
    int sumOfnums  = 0;	
    for(int i = 0; i < nums.size(); i++){
        sumOfnums += nums[i];
    }
    return Nsum - sumOfnums;
}

Another approach to solving this problem is using the XoR technique.

In this technique, we take two variables, X1 and X2:

  • X1 contains the XOR of all the values from 1 to N
  • X2 contains the XOR of all the values in the array

When XOR is applied on X1 and X2, the elements in both X1 and X2 cancel each other out, returning the missing element.


int MissingNo(int arr[], int n) {
  int x1 = arr[0];
  int x2 = 1;
  for (int i = 1; i < n; i++)
    x1 = x1 ^ arr[i];
  for (int i = 2; i <= n + 1; i++)
    x2 = x2 ^ i;
  return (x1 ^ x2);
}

Q2. Reverse bits of a given 32-bit unsigned integer.

Example 1:


Input: n = 00000010100101000001111010011100
Output:    964176192 (00111001011110000010100101000000)

Explanation: The input binary string 00000010100101000001111010011100 represents the unsigned integer 43261596, so return 964176192; its binary representation is 00111001011110000010100101000000.

Example 2:


Input: n = 11111111111111111111111111111101
Output:   3221225471 (10111111111111111111111111111111)

Explanation: The input binary string 11111111111111111111111111111101 represents the unsigned integer 4294967293, so return 3221225471; its binary representation is 10111111111111111111111111111111.

Solution: 

This is a very interesting problem in bit manipulation.

This problem can be approached by first taking a variable result and assigning its value to be 00000000000000000000000000000000 (32-bit integer).

Now, iteratively add the last bit of the given integer n to the result to come up with the reversed bits. Then the left-shift operator can be used on the result, and a right-shift operator on the given integer to continue with the rest of the bits. These steps should continue until all the bits are examined in the given integer, i.e., 32 times, since the given integer is a 32-bit integer.

class Solution { public: uint32_t reverseBits(uint32_t n) { uint32_t result = 0; for (int i = 0; i < 32; i++) { result <<= 1; //Shifts bit to the left to make space for the new bit result |= n & 1; // Adds the last bit of n to res n >>= 1; // Remove the last bit from n, so we can continue with the rest of the bits } return result; } };

Q3. Given array nums with n objects colored red, white, or blue. Sort them in the order of red, white, and blue so that objects of the same color are adjacent. 

Let’s use the integers 0, 1, and 2 to represent red, white, and blue, respectively. This problem must be solved without using the library's sort function.

Examples :


Input: nums = [2,0,2,1,1,0]
Output: [0,0,1,1,2,2]

Input: nums = [2,0,1]
Output: [0,1,2]

Input: nums = [0]
Output: nums = [0]

This problem is a variation of the famous Dutch National Flag problem and is one of the most popular programming problems. This problem can be solved in multiple ways.

Solution 1: Sorting. Since the array contains only 3 integers, 0, 1, and 2, simply sorting the array would arrange the elements in increasing order.

Time Complexity: O(N*logN)
Space Complexity: O(1)

But this is not the expected solution here, and this can be done in one pass.

Solution 2: 3-Pointer Approach

In this approach, we will be using 3 pointers — low, mid, and high — to move around the values. The primary goal is to move all the 0s to the left and all the 2s to the right of the array. At the same time, all the 1s will remain in the middle of the array, and the array will be sorted. 

Algorithm: 

Start by initializing three-pointers.

 

These three steps repeat until mid<=high. By the end of this operation, all the 0s will be to the left of low, and all the 2s will be to the right of high.


class Solution {
public:    
    void sortColors(vector& nums) {
        int n = nums.size();
        int low=0;
        int mid = 0;
        int high = n-1;
        while(mid<=high){
            switch(nums[mid]){
                case 0: swap(nums[low++],nums[mid++]);
                        break;
                case 1: mid++;
                        break;
                case 2: swap(nums[high--],nums[mid]);
                        break;                
            }
        }
    }
};

Time Complexity: O(N)
Space Complexity: O(1)

Q4. How do you do a big endian to little endian conversion on the 1024-bit system?

The endianness applies to a variable or a field in memory. So you have an endianness for a short or an endianness for an int or an endianness for a long int. The endianness is always based on how large that data type or variable is in memory. 

So if you have a 1024-bit system, the code you would need to write to convert from little to big-endian is the same code that one would write to convert big to little on a 32-bit system. Nothing logically changes; the only difference is now we have 128 bytes as opposed to just 4 or 8 bytes that we were trying to reorder. The bit size (32, 64, or 1024) of our system is irrelevant regarding big and little endian conversion.

You could have a 32-bit system with a 64-bit variable, and that variable might get swapped back and forth between big-endian and little-endian. 

Now you take any approach to reserve the bit to convert from big to little endian. A naive solution code is presented below:


// uint1024_t - hypothetical byte in a 1024-bit system
uint1024_t reverse(uint32_t input){
      // 
	uint1024_t rv;
      //
	uint8_t *inp = (uint8_t*) &input;
	uint16_t *out = (uint8_t*) &rv;
	// reversing
	for(int i=0;i<128;i++){
		out[i] = inp[128-i];
	}
	return rv;
}

Here are some sample embedded software engineer questions on Bit Manipulation that you can practice:

  1. Add two integers using & and ^
  2. Find the single element that does not appear thrice in a given array of integers
  3. For a given number, find the number of ones in its binary representation

Microcontroller Embedded Software Engineer Interview Questions

Microcontrollers are an important topic that you can expect questions on at your embedded software engineer interview. Take a look at the sample questions below:

  1. Can microcontrollers benefit from generic RTOS?
  2. Can a microcontroller be more advantageous than a microprocessor?
  3. How will you ensure the safety of microcontroller embedded data? 
  4. What is an ARM microcontroller?
  5. What is an 8501 microcontroller?

RTOS Embedded Software Engineer Interview Questions

In your embedded software engineering interview, you may be asked questions specifically on real-time operating systems. Learn the concepts, types, and features of RTOS used in embedded systems. Some sample questions are:

  1. Differentiate between RTOS and other OS
  2. Explain non-reentrant code
  3. Name some RTOS used in embedded systems
  4. What are the main features of Real-Time Operating Systems (RTOS)?
  5. What kind of scheduling does RTOS use?

Behavioral Embedded Software Engineer Interview Questions

Behavioral questions are an essential part of embedded software engineering interviews. Behavioral questions will test your soft skills. They are usually situation-based to check your response to critical situations. These are some questions you can practice:

  1. Describe an embedded software project you are proud of
  2. How do you stay abreast of the latest embedded software engineering technologies and trends?
  3. Why did you choose to become an embedded software engineer?
  4. What is your approach to software design?
  5. What is your take on the company’s future and the embedded software market?
  6. Tell me about a time your colleagues disagreed with your solution to a problem. How did you resolve it?
  7. Describe when you used a creative, innovative approach to devise a solution.

Here’s a more detailed list of Behavioral Interview Questions for software engineers.

Google Embedded Software Engineer Interview Questions

The embedded engineer interview at Google comprises multiple on-site interviews, about four rounds of interviews. Below are the types of embedded software engineer interview questions that may be asked at Google. This may vary depending on your overall years of experience.

  • Round 1: Embedding programming concepts, architectural-level problem-solving
  • Round 2: Assembly language programming, computer memory hierarchy, and architecture, coding in C
  • Round 3: Profile discussion, Algorithm development concepts, dynamic programming
  • Round 4: Algorithm development, dynamic programming

Embedded software engineer interview questions can vary in complexity and difficulty depending on whether you’re applying for an entry-level embedded software engineer position or a senior embedded software engineer position. Cover all key topics related to the position, ensuring you have strong conceptual knowledge and the latest technologies.

Learn more about Google’s Embedded System Engineer Interview, including sample questions.

FAQs on Embedded Software Engineer Interview Questions

Q1. How hard are embedded software engineer interview questions at FAANG?

FAANG interviews are highly competitive, and the embedded software engineer interview is no exception. Questions asked will aim to identify software engineers with deep knowledge and strong skills. You should be very clear about which position you are interviewing for at FAANG for an embedded software engineering role.

Q2. What should I consider for the coding embedded software engineer interview questions?

Some aspects to consider are proper compiler optimization, time and space requirements, the underlying hardware, code resilience, bit manipulation, efficient memory references, and data size optimization.

Q3. How can I prepare to answer embedded software engineer interview questions?

Begin your preparation well in advance and understand the role you’re applying for, as well as the company and its products. Develop your resume to showcase the right skills. Develop the right embedded software engineering tech skills by following a structured learning methodology. By enhancing your knowledge and upskilling, you can become a confident engineer who can easily answer embedded software engineering interview questions. Mock interviews are also a great way to prepare effectively for the interview.

Q4. How different are embedded software engineer interview questions from software engineer interview questions?

Embedded software engineer interview questions assess your understanding of software and hardware topics. Software engineer interview questions focus on software topics. However, there is an overlap between the two since software engineer interview questions on coding, algorithms, data structures, etc., are also asked during the embedded engineering interview.

Q5. Which programming languages do embedded systems use?

C and C++ are generally used to develop code for embedded software. Other languages used are JavaScript and Python for microcontrollers. Assembly languages are also used.

Prepare for Your Next Embedded Software Engineer Interview

At Interview Kickstart, we have helped thousands of engineers upskill and uplevel their careers. Our technical interview prep courses, designed and taught by FAANG+ hiring managers and tech leads, have delivered stellar results to our alums, helping many of them land lucrative offers from FAANG+ companies.

Check out our Embedded Software Engineering Interview Course to learn how to prepare for and nail software engineering interviews at FAANG and Tier-1 tech companies.

Learn all about our interview prep courses and learning methodology by signing up for our FREE webinar now!


Worried About Failing Tech Interviews?

Worried about failing Embedded Systems Software Engineering Interviews?

Attend our webinar on
"How to nail your next tech interview" and learn

Hosted By
Ryan Valles
Founder, Interview Kickstart
Our tried & tested strategy for cracking interviews
How FAANG hiring process works
The 4 areas you must prepare for
How you can accelerate your learnings
Register for Webinar

Recommended Posts

About usWhy usInstructorsReviewsCostFAQContactBlogRegister for Webinar