Interview guides · Analog Devices
Analog Devices Software Engineer Interview Guide
Analog Devices Software Engineer interview guide: embedded C, digital timing, circuit and DSP questions with worked answers and a 7-day plan.
The 60-second brief
Analog Devices (ADI) makes semiconductors: data converters, power management parts, RF microelectronics and edge-processing chips. Candidates describe software work that sits right next to the silicon. That means firmware and embedded drivers for ARM Cortex microcontrollers, Linux device drivers, post-silicon validation software, SystemVerilog and UVM verification environments, and real-time control and DSP code. The role also covers evaluation-board APIs, GUI configuration tools for external customers, and register-map and API documentation. Day to day, the reported work involves analog designers, system architects and field application engineers, and debugging uses logic analyzers, oscilloscopes, JTAG probes and emulators. In practice your code is judged against timing, latency, power and memory limits set by the hardware.
This guide is for candidates interviewing for a Software Engineer role at Analog Devices, whether the team leans toward embedded firmware, design verification, applications engineering or DSP. It covers the six stages candidates report, from the recruiter pre-screen to the offer. It gives question-specific guidance for all 23 reported questions: register access and volatile in C, memory layout, ring buffers, Cortex-M interrupts, metastability and Gray-code FIFOs, setup and hold timing, serial protocols, packet-parsing state machines, RC filters, MOSFETs, op-amps, ADC errors, nodal analysis, sampling, FIR and IIR filters, the FFT, fixed-point PID, and four behavioral questions about hardware-software work.
Candidates report six stages over roughly 4-6 weeks: Recruiter Pre-Screen, Online Assessment, Technical Discussions, Technical Sessions (3 to 5 sessions, virtual or onsite), Candidate Evaluation and Offer Delivery.
Focus areas
- Digital Electronics Fundamentals
- Analog Electronics Fundamentals
- Static Timing Analysis (STA)
Overview
Software at Analog Devices controls and validates analog and mixed-signal silicon, so the reported questions go well beyond standard algorithm puzzles. They fall into five groups: embedded C and low-level systems, digital logic and RTL, analog circuit analysis, signals and DSP, and behavioral questions about hardware-software projects. Candidates also name Digital Electronics Fundamentals, Analog Electronics Fundamentals, Static Timing Analysis, Verilog Programming, and FPGA Concepts and Architecture as recurring topics. A software applicant who prepares only C and data structures will meet setup-time, op-amp and aliasing questions unprepared.
On the C side, prepare to explain what the compiler and the processor actually do. Know why a register pointer must be volatile and what volatile still does not guarantee. Know what the Cortex-M hardware pushes onto the stack before your ISR runs, how .data and .bss get initialised before main, and how to build a ring buffer that an ISR and the main loop can share without locks or malloc. The scenario questions candidates report follow the same pattern: reversing the bits of a 32-bit register, and debugging a deadlock between two RTOS tasks sharing an I2C driver.
On the hardware side, the bar candidates describe is fundamentals you can derive at a whiteboard, not IC design. You should be able to get an RC low-pass transfer function and its cutoff, apply KCL to a resistor network, compute a maximum clock frequency from flop and logic delays, explain why async FIFO pointers are Gray-coded, and turn ADC resolution into volts per code. The reported scenarios include 16-bit versus 12-bit step size on a 3.3 V rail and an op-amp gain derivation with a falling load resistance. For each of these, practise saying what it means for the software that reads the signal.
Candidates report that technical rounds mix direct questions, code analysis, logic and schematic evaluation, and deep dives into resume projects, and that follow-ups add new constraints or hints. Prepare every topic in two layers: a crisp first answer, then the follow-up about the edge case, the failure mode or the number. Prepare your own projects down to the register and timing level as well.
The Analog Devices interview process
These six stages are the sequence candidates report. Round counts vary with team domain, seniority and location, so confirm your loop with the recruiter.
Recruiter Pre-Screen
Candidates report a recruiter screen on fit for the role. Questions at Analog Devices differ by team (embedded, verification, applications or DSP), so use this call to find out which domain your loop leans toward. Then weight your preparation across C, digital logic, circuits and DSP to match. Give a short account of your background that leads with any work where your software touched physical hardware: microcontrollers, sensors, FPGAs or test benches.
What to demonstrate
- Fit between your background and the team's domain (embedded, verification, applications or DSP)
- Whether your hands-on experience includes hardware-facing software
- Clear communication about your projects and motivation
How to prepare
- Write a two-minute background summary that names the MCU, bus, RTOS or HDL you actually used in each project.
- Ask which domain the team works in and whether the next step is an online assessment or a technical screening interview.
- List the stack items from the requirements candidates describe (C/C++, SPI/I2C/UART/CAN, JTAG/SWD, oscilloscopes, Python, SystemVerilog) and mark where you have real experience.
Online Assessment
Candidates report either an online assessment or a technical screening interview at this stage. Written or automated tests are reported as common, especially for university hires and specialised teams, and they cover digital electronics fundamentals, C syntax, logic problems and basic aptitude. Expect short questions where precision matters more than design: predicting C output, bit manipulation, Boolean simplification and number-system conversions. If you get a screening interview instead, the same fundamentals come up spoken aloud.
What to demonstrate
- Correct reading and writing of C, including pointers, integer types and bitwise operators
- Digital electronics fundamentals: gates, flip-flops, number systems, Boolean logic
- Logic and aptitude problems solved accurately
How to prepare
- Drill C output-prediction items: pointer arithmetic, operator precedence, integer promotion of uint8_t, signed shift, sizeof of arrays versus pointers.
- Write bit-manipulation routines from memory: set/clear/toggle a field, count set bits, reverse the 32 bits of a register in O(log n) swap steps.
- Practise Karnaugh maps, two's complement, hex/binary/Gray conversion and basic flip-flop truth tables until each takes seconds.
Technical Discussions
Candidates report detailed technical discussions with engineering managers and team members after the initial assessment. Reports describe resume projects being taken apart down to low-level implementation choices: architecture diagrams, why a peripheral or bus was chosen, and how a hard failure was debugged. For each project, prepare the hardware boundary (registers, interrupts, DMA, timing), one real trade-off, and one bug you isolated with instruments, and be ready to draw the system.
What to demonstrate
- Depth of understanding of your own projects at the register and timing level
- Structured debugging of hardware-software failures
- Ability to explain design trade-offs and reason aloud
How to prepare
- For each resume project, sketch the block diagram: MCU, peripherals, buses, interrupt sources, memory budget, and where your code ran.
- Prepare a hardware-software bug story with the evidence that separated code from silicon (scope or logic-analyzer capture, register dump over JTAG, errata).
- Rehearse the volatile, memory-layout and Cortex-M interrupt-entry questions aloud, since they connect directly to firmware projects.
Technical Sessions
Candidates report 3 to 5 separate technical sessions, run virtually or as an onsite day, with most technical rounds lasting 45 to 60 minutes. Reports describe a mix of direct technical questions, code analysis, logic and schematic evaluation, and resume deep dives, with follow-ups that add constraints or offer hints. This is where the full breadth comes in, so practise each area as a spoken answer with a whiteboard sketch: embedded C, digital logic and timing, circuits, and DSP. When you get stuck, state your assumptions and work from first principles, because candidates report that hints are offered.
What to demonstrate
- Working C for embedded problems (buffers, state machines, fixed-point arithmetic)
- Digital logic and timing reasoning: synchronizers, Gray code, setup/hold, max frequency
- Circuit and signal analysis: KCL, RC filters, op-amps, ADC errors, sampling and filtering
- Problem-solving process under new constraints or hints
How to prepare
- Write a lock-free SPSC ring buffer and a byte-driven packet-parser FSM in C on paper, then test both against wraparound and corrupted input.
- Work timing numerically: max clock frequency from t_clk-q, logic delay, setup and skew, and a hold check for the same path.
- Derive the RC low-pass response, solve a three-node KCL network, and convert 12-bit and 16-bit resolutions on a 3.3 V reference into volts per code.
- Explain FIR versus IIR, FFT complexity and windowing, and write a fixed-point PID step with saturation and anti-windup.
Candidate Evaluation
Candidates report an evaluation step after the technical rounds that covers performance across the sessions and fit for the team. Little else is reported about it. Your preparation is already done by this point, but a short note to the recruiter can correct or complete an answer you fumbled, for example a timing calculation you finished after the session.
What to demonstrate
- Overall performance across the technical sessions
- Fit for the specific team
How to prepare
- Right after the loop, write down every question you were asked and how you answered, and flag any you would correct.
- If you worked out a better answer afterwards, send the recruiter a brief, factual correction rather than a general thank-you.
Offer Delivery
Candidates report that the process ends with an offer based on the evaluations. Before you accept, settle the practical questions the technical rounds did not cover. Candidates report that roles needing lab access, bench testing or post-silicon validation are usually on-site or hybrid, while some toolchain or architecture roles are more flexible.
What to demonstrate
- Alignment on team, domain and work arrangement
- Clarity on the role's day-to-day scope
How to prepare
- Ask which domain and products the team works on and how much of the role is lab or bench work versus host-side development.
- Confirm the on-site or hybrid arrangement for the team, since candidates report it depends on lab needs.
Where candidates lose points
Saying volatile makes a variable shared with an ISR safe, then incrementing it with ++ from both sides.
Separate the two properties out loud. Volatile forces each access to happen; atomicity and ordering come from word-sized single writers, critical sections, C11 atomics or barriers. Practise the ring-buffer question with one writer per index so you can show the difference in code.
Treating the circuit questions as out of scope for a software role and stalling on an RC filter, a KCL network or an op-amp gain.
Candidates report basic circuit analysis in software panels. Derive H(s) = 1/(1 + sRC), solve a three-node KCL problem, write the non-inverting gain 1 + R_f/R_in, and convert ADC bits into volts per code until each comes out without notes.
Mixing up the timing fixes: proposing a lower clock frequency for a hold violation, or synchronizing a multi-bit bus bit by bit.
Write the setup and hold inequalities with skew and notice that only setup contains T_clk. For clock-domain crossings, keep a rule: two-flop synchronizers for single-bit levels, Gray-coded pointers or handshakes for multi-bit values.
Describing your own projects only at the library level ("I used the HAL's SPI driver") and failing to explain the registers, interrupts or timing underneath.
Candidates report resume deep dives down to implementation choices. For each project, prepare the block diagram, the peripheral configuration, the interrupt and DMA flow, the memory budget, and one bug you isolated with a scope, logic analyzer or JTAG.
Bluffing through an unfamiliar question, or going silent until you have the whole answer.
Candidates report that hints are offered and that follow-ups add constraints. State what you know from first principles (units, limiting cases, what the hardware must do), say which assumption you are making, and use the hint when it comes.
Questions & practice
How do you configure and interact with hardware registers directly in C using volatile pointers?
Medium · Embedded C & Low-Level Systems Programmi
Approach
- Map the register with a fixed-width volatile type at its address, either as a single register, e.g. #define CTRL (*(volatile uint32_t *)0x40000000u), or as a struct of volatile uint32_t fields overlaid on the peripheral base address and matching the register map's offsets.
- Show the three basic operations: set bits with REG |= mask, clear with REG &= ~mask, and write a field with REG = (REG & ~FIELD_MASK) | ((val << FIELD_SHIFT) & FIELD_MASK). Keep masks and shifts as named constants generated from the register map.
- Call out that read-modify-write is not atomic. If an ISR writes the same register between the read and the write, its update is lost. Use the peripheral's dedicated set/clear registers where they exist, or a short critical section.
- Handle special bit semantics: for write-1-to-clear status flags, write only the mask (REG = FLAG). A |= would also clear every other pending flag. Read-to-clear registers must not be read speculatively by debug code.
- Avoid C bitfields for registers. Their layout and access width are implementation-defined, and many peripherals require full 32-bit accesses. Add a barrier (DSB on Cortex-M) where a write must complete before the next step, for example clearing an interrupt flag before returning.
Follow-up
- Why are bitfield structs risky for register maps?
- How do you keep a read-modify-write safe if an ISR touches the same register?
- How would you unit-test this register code on a host machine with no peripheral?
Explain the memory layout of a standard C program (Stack, Heap, Data, BSS, Text) and describe how stack overfl
Medium · Embedded C & Low-Level Systems Programmi
Explain the memory layout of a standard C program (Stack, Heap, Data, BSS, Text) and describe how stack overflow occurs.
Approach
- Walk the segments from low to high addresses on a typical MCU: .text (code) and .rodata (constants) in flash; .data (initialised globals and statics), stored in flash and copied to RAM by startup code; .bss (zero-initialised), zeroed by startup code; the heap growing up; the stack growing down from the top of RAM on ARM.
- Say what lives where: a static local sits in .data or .bss, not on the stack; a string literal sits in .rodata, while char s[] = "abc" inside a function is a stack copy; malloc returns heap memory.
- Explain stack overflow: deep or unbounded recursion, large local arrays, and nested interrupts each pushing frames until the stack pointer passes its region. Without an MMU it usually corrupts the heap or .bss silently instead of faulting, so the symptom appears far from the cause.
- Give detection and prevention: fill the stack with a known pattern at boot and check the high-water mark, use an MPU guard region or the ARMv8-M stack-limit registers, measure per-function usage with GCC's -fstack-usage plus call-graph analysis, and give each RTOS task a sized stack.
Follow-up
- Where do const char *p = "abc" and char a[] = "abc" each store their bytes?
- How would you size the stack for an RTOS task with an ISR that can preempt it?
- What does the linker script do to place these sections?
How would you write a dynamic queue or circular buffer in C for an embedded system without causing dynamic mem
Medium · Embedded C & Low-Level Systems Programmi
How would you write a dynamic queue or circular buffer in C for an embedded system without causing dynamic memory fragmentation?
Approach
- Use a statically allocated array with fixed capacity and head/tail indices, so no malloc is ever called. Choose a power-of-two capacity so wrap is index & (N-1) instead of a modulo.
- Resolve full versus empty explicitly: keep one slot unused, keep a separate count, or use free-running unsigned indices where count = head - tail. That last option wraps correctly when N is a power of two.
- For an ISR producer and main-loop consumer (single producer, single consumer), it can be lock-free: only the producer writes head and only the consumer writes tail. Write the data before publishing the index, and make indices word-sized and volatile or atomic. Add a barrier when another core or DMA is involved.
- Choose and state the full policy (reject newest, overwrite oldest, or return an error) and keep an overflow counter. Push and pop are O(1).
- If the question needs variable-size items without fragmentation, add a fixed-block memory pool: a free list of equal-size blocks with O(1) alloc and free. Fragmentation cannot occur because every block is the same size.
Follow-up
- What changes if two ISRs can both push into the buffer?
- How would you let a DMA controller fill this buffer directly?
- Which tests prove the wraparound and the full/empty edge cases?
What is the purpose of the volatile keyword in C, and why is it essential when writing interrupt service rou
Medium · Embedded C & Low-Level Systems Programmi
What is the purpose of the volatile keyword in C, and why is it essential when writing interrupt service routines (ISRs) or polling memory-mapped peripheral registers?
Approach
- Define it: volatile tells the compiler that every access is an observable side effect, so it cannot cache the value in a register, remove the access, merge accesses, or reorder it against other volatile accesses.
- Give the classic failure. A flag set in an ISR and polled with while (!flag) {} compiles at -O2 into a single load and an infinite loop if flag is not volatile. A status register polled for a ready bit fails the same way, because the hardware changes it.
- State what volatile does not do. It gives no atomicity (counter++ on a volatile is still load, add, store, and a 64-bit value on a 32-bit core takes two accesses). It does not order non-volatile memory, keep caches coherent with DMA, or replace C11 atomics, critical sections or memory barriers.
- Distinguish the declarations: volatile uint32_t *p (the pointee is volatile, the usual register case) versus uint32_t * volatile p (the pointer itself is volatile). const volatile suits a read-only status register.
Follow-up
- Is volatile alone enough to share a 32-bit counter between an ISR and the main loop?
- Can a variable be both const and volatile, and when would you use that?
- What does volatile cost in performance inside a tight DSP loop?
Walk through the execution sequence when a hardware interrupt is triggered on an ARM Cortex-M processor, expla
Medium · Embedded C & Low-Level Systems Programmi
Walk through the execution sequence when a hardware interrupt is triggered on an ARM Cortex-M processor, explaining register pushing and vector table lookups.
Approach
- The peripheral asserts its IRQ line and the NVIC marks it pending. If the interrupt is enabled, not masked by PRIMASK or BASEPRI, and higher priority than the current execution priority, exception entry starts.
- The hardware stacks an 8-word frame onto the current stack (MSP or PSP): R0-R3, R12, LR, PC and xPSR. With an active FPU context it may add S0-S15 and FPSCR (lazy stacking). Because these are exactly the AAPCS caller-saved registers, the ISR can be an ordinary C function.
- In parallel, the core fetches the handler address from the vector table at VTOR + 4 x exception number, where IRQn is exception 16 + n. It switches to Handler mode on MSP and loads LR with an EXC_RETURN code (0xFFFFFFF9 for thread mode on MSP, 0xFFFFFFFD for thread mode on PSP, 0xFFFFFFF1 for a nested handler).
- On return, branching to EXC_RETURN triggers unstacking. Mention tail-chaining (a pending interrupt runs without unstacking and restacking) and late arrival (a higher-priority IRQ arriving during stacking takes the vector), and note that entry latency is about a dozen cycles on Cortex-M3/M4 with zero-wait-state memory.
- Close with practical points: clear the peripheral's flag early, and use a DSB if needed, so the IRQ does not re-fire on return. Keep ISRs short and defer work to a task or main loop.
Follow-up
- How does an RTOS use PendSV and the PSP to switch tasks?
- What happens if the peripheral flag is cleared as the very last instruction of the ISR?
- How do priority grouping and BASEPRI let you mask only some interrupts?
Explain the concept of Metastability and how multi-stage synchronizers mitigate Clock Domain Crossing (CDC) is
Medium · Digital Logic, RTL & Firmware Integratio
Explain the concept of Metastability and how multi-stage synchronizers mitigate Clock Domain Crossing (CDC) issues.
Approach
- Define it: if a flip-flop's input changes inside its setup/hold window, the output can sit at an intermediate level and resolve to 0 or 1 after an unbounded time. The probability that it is still unresolved falls exponentially with the time allowed.
- Give the reliability model: MTBF = e^(t_r/tau) / (T_w x f_clk x f_data). Here t_r is the resolution time available, and tau and T_w are flop parameters. More resolution time increases MTBF exponentially.
- A two-flop synchronizer gives the first flop almost a full clock period to resolve before the second samples it. Each extra stage multiplies MTBF by about e^(T_clk/tau), so very fast clocks may need three stages. The cost is 2-3 cycles of latency, and the output is a clean value, not necessarily the latest one.
- Limit the scope: synchronizers are for single-bit, level signals. A multi-bit bus synchronized bit by bit can be sampled mid-transition into an invalid value, so use Gray-coded pointers, a req/ack handshake or an async FIFO. A single-cycle pulse from a fast domain can be missed in a slow domain, so stretch it or use a toggle synchronizer.
Follow-up
- What happens to MTBF if you add a third flop stage?
- How do you safely pass a one-cycle pulse from a 200 MHz domain to a 20 MHz domain?
- Why is reconvergence of separately synchronized bits a bug?
Draw and explain a Gray Code counter, and describe its advantages over a standard binary counter in asynchrono
Medium · Digital Logic, RTL & Firmware Integratio
Draw and explain a Gray Code counter, and describe its advantages over a standard binary counter in asynchronous FIFO designs.
Approach
- Define Gray code: consecutive values differ in exactly one bit. Conversion is g = b ^ (b >> 1). The inverse is a prefix XOR from the MSB down: b[n-1] = g[n-1], then b[i] = b[i+1] ^ g[i].
- Draw the counter as a binary register, an incrementer, an XOR network producing b ^ (b >> 1), and a Gray output register. Registering the Gray value matters because the combinational XOR output can glitch.
- Explain the FIFO advantage. The write pointer crosses into the read clock domain through a two-flop synchronizer. A binary pointer going 0111 to 1000 flips four bits, so a sample taken mid-transition can return any value. A Gray pointer flips one bit, so the synchronized value is either the old or the new pointer, and both give a safe, conservative full or empty decision.
- Cover the details: use N+1-bit pointers for a 2^N-deep FIFO. Empty is when the Gray pointers are equal. Full is when the top two bits of the write pointer are the inverse of the read pointer's and the remaining bits match. Depth must be a power of two so the wrap from the last code to zero is also a single-bit change.
Follow-up
- Why must the async FIFO depth be a power of two with this scheme?
- Is it a problem that the synchronized pointer is a few cycles stale?
- Write the full-flag comparison for a 16-entry FIFO.
Explain the Nyquist-Shannon Sampling Theorem and describe the physical and mathematical consequences of aliasi
Medium · Signals, Systems & DSP Algorithms
Explain the Nyquist-Shannon Sampling Theorem and describe the physical and mathematical consequences of aliasing when sampling a signal.
Approach
- State the theorem: a signal band-limited to B Hz is fully recoverable from samples taken at fs > 2B, ideally by sinc interpolation (an ideal low-pass at fs/2).
- Explain aliasing mathematically. Sampling replicates the spectrum at multiples of fs, so any component above fs/2 folds to |f - k fs|. For example, 7 kHz sampled at 10 kHz appears at 3 kHz, indistinguishable from a real 3 kHz tone, and no software can undo it.
- Describe the physical effects: out-of-band interference and noise folding into the band of interest, and the wagon-wheel effect as a visual analogy.
- Prevention: an analog anti-aliasing filter before the ADC with enough attenuation at fs/2 for the ADC resolution. Or oversample so a gentle analog filter suffices, then decimate with a sharp digital filter.
- Edge cases: sampling a sine at exactly 2f can land on its zero crossings, and bandpass sampling (undersampling) deliberately uses aliasing to bring an RF band down to baseband.
Follow-up
- A 48 kHz system sees a 30 kHz interferer: where does it appear?
- Why can't a digital filter after the ADC remove aliasing?
- How does oversampling relax the analog filter requirements?
What is the fundamental difference between Finite Impulse Response (FIR) and Infinite Impulse Response (IIR) d
Medium · Signals, Systems & DSP Algorithms
What is the fundamental difference between Finite Impulse Response (FIR) and Infinite Impulse Response (IIR) digital filters in terms of stability, phase linearity, and execution efficiency?
Approach
- FIR: y[n] = sum of b_k x[n-k], with no feedback, so it is always stable. Symmetric coefficients give exactly linear phase with a constant group delay of (N-1)/2 samples. Sharp transitions need many taps, at N multiply-accumulates per output.
- IIR: feedback terms a_k create poles that must lie inside the unit circle. It gives a sharp response with few coefficients (a biquad is 5 multiplies), but the phase is nonlinear and the filter can go unstable or show limit cycles when coefficients are quantized.
- Fixed-point implementation: for FIR, keep a circular delay line and accumulate Q15 x Q15 products in a wide accumulator (32/40/64-bit) before one final rounding. For IIR, use cascaded second-order sections (e.g. direct form II transposed) rather than one high-order direct form, to limit coefficient sensitivity and overflow.
- When to choose which: FIR when phase matters or for decimation and interpolation (polyphase). IIR when cycles are scarce or when copying an analog prototype (Butterworth via the bilinear transform).
- Use the moving average as a concrete FIR: N equal taps, an O(1) recursive running-sum implementation, and a sinc-shaped response with nulls at multiples of fs/N.
Follow-up
- How would you implement a moving-average filter in C for streaming sensor data, and what does it do to the frequency response?
- How do you check that a quantized IIR is still stable?
- What is the latency of a 101-tap linear-phase FIR at 48 kHz?
Explain how the Fast Fourier Transform (FFT) reduces computational complexity from to a
Medium · Signals, Systems & DSP Algorithms
Explain how the Fast Fourier Transform (FFT) reduces computational complexity from to and how windowing affects spectral leakage.
Approach
- The DFT computes X[k] = sum over n of x[n] e^(-j 2 pi k n / N): N outputs each needing N terms, so O(N^2).
- Radix-2 Cooley-Tukey splits x into even and odd samples, giving two N/2-point DFTs E and O. Then X[k] = E[k] + W^k O[k] and X[k + N/2] = E[k] - W^k O[k], with W = e^(-j 2 pi / N). Recursing gives log2 N stages of N/2 butterflies, so O(N log N). At N = 1024 that is about 5,120 complex multiplies instead of about 1 million.
- Mention the implementation details: in-place butterflies with bit-reversed input ordering, a precomputed twiddle table, and per-stage scaling by 1/2 in fixed point to avoid overflow.
- Spectral leakage: the FFT treats the block as one period of a periodic signal. A non-integer number of cycles creates an edge discontinuity, so energy spreads across bins, which is the same as convolving with the rectangular window's sinc.
- Windowing trades main-lobe width (resolution) for side-lobe level (leakage): Hann and Hamming for general use, Blackman-Harris for high dynamic range, flat-top for amplitude accuracy. Correct for the window's coherent gain. Bin spacing is fs/N, and coherent sampling avoids the window entirely in ADC testing.
Follow-up
- Does zero-padding improve frequency resolution?
- How would you exploit a purely real input to halve the work?
- Which window would you use to measure THD of an ADC, and why?
How would you implement a discrete-time PID control loop in fixed-point C arithmetic while avoiding integer ov
Medium · Signals, Systems & DSP Algorithms
How would you implement a discrete-time PID control loop in fixed-point C arithmetic while avoiding integer overflow and windup?
Approach
- Discretize at a fixed period Ts driven by a timer interrupt: u[n] = Kp e[n] + Ki Ts sum(e) + (Kd / Ts)(y[n-1] - y[n]). Precompute Ki Ts and Kd / Ts as fixed-point constants. Taking the derivative of the measurement instead of the error avoids a derivative kick when the setpoint steps.
- Pick Q formats from worst-case ranges, e.g. Q15 signals and gains in Q16.16. Multiply into a wider type (Q15 x Q15 into int32 as Q30, or int64 for Q16.16), then shift back with rounding (add 1 << (shift-1)). Note the -1 x -1 corner, which overflows Q15.
- Use saturating arithmetic: clamp sums to the type's limits instead of letting them wrap, because a wrapped control output reverses the actuator.
- Anti-windup: clamp the integrator to the range the actuator can use, stop integrating while the output is saturated and the error has the same sign (conditional integration), or use back-calculation. Low-pass the derivative term to stop it amplifying ADC noise.
- Verify it against a floating-point reference model on the host with step, ramp and saturation scenarios, and compare the fixed-point output error against a tolerance.
Follow-up
- How would you choose the Q format for the integrator state?
- How do you change gains at runtime without a bump in the output?
- What happens if the loop period jitters, and how would you measure it?
Replace offset paging on the resource feed with keyset
Easy · keyset pagination · composite index · sort order
resource holds resource_id, tenant_id, owner_user_id, title, body_ref, version, status ('draft','active','archived','deleted'), created_at, updated_at, deleted_at, with an index on (tenant_id, status, updated_at DESC, resource_id DESC). The listing endpoint returns active resources for one tenant, newest update first, 50 per page, today with LIMIT 50 OFFSET n. Tenants reach page 400 and rows are created while they read. Write the keyset query, define what the cursor carries and how it is encoded, and say which part of the index each predicate uses. Assume PostgreSQL 16.
Approach
- Name the two failures separately. OFFSET 20000 makes the server produce and discard 20,000 rows, so page cost grows with depth rather than with page size. Independently, any write that changes how many rows sort above the offset moves the window between two fetches, and the direction decides which anomaly you get: an insert lands at the head of updated_at DESC and pushes already-returned rows down past the boundary, so they are returned a second time; a delete above the offset, or a row whose updated_at is bumped above the cursor, pulls rows up and one is never returned at all. Nothing in the response reveals either.
- Write the seek: WHERE tenant_id = 2, $3) ORDER BY updated_at DESC, resource_id DESC LIMIT 50. The row-value comparison is one index range rather than a disjunction, and both columns are NOT NULL, which is what makes that comparison well defined.
- Map each predicate onto the index: tenant_id and status are equality on the leading columns, (updated_at, resource_id) is the range, and the ORDER BY matches the index order so no Sort node appears and the scan stops after 50 rows. The DESC in the definition only matters for mixed directions — a plain ascending btree on the same columns is read backwards for this query.
- Put both sort columns in the cursor and nothing the client can tamper with into another tenant: base64 of (updated_at, resource_id), validated server-side, with tenant_id taken from the principal.
Follow-up
- The client asks for 'jump to page 400'. What do you offer instead, and what does the honest version cost?
- Sort order becomes user-selectable across four columns. How many indexes is that, and which would you refuse to add?
Stop tag and share joins from fanning out a page
Medium · join fan-out · semi-joins · many-to-many
resource_tag is (resource_id, tag_id) with PK (resource_id, tag_id); resource_share is (resource_id, shared_with_user_id, permission). The tagged-and-shared listing inner-joins resource to both, filters tenant_id, tag_id = ANY(3, orders by updated_at DESC and takes 50. Pages come back with fewer than 50 distinct resources and the total in the header is far too high. Explain the row multiplication, rewrite both the page query and the count query so each is correct, and name the index each one needs. PostgreSQL 16.
Approach
- Do the arithmetic against the predicates that are actually there. An inner join emits one row per matching child row, and both joins are filtered: tag_id = ANY(3 admits one user's share rows. So a resource holding three of the requested tags and shared with $3 once yields three rows, not one — the multiplier is its count of matching tags times its share rows for that single user, and that second factor is 1 unless the table admits duplicate (resource_id, shared_with_user_id) pairs. LIMIT 50 then limits rows rather than resources, and COUNT(*) counts pairs — the header is the product, not the population.
- Reject DISTINCT as the fix. It deduplicates after the product has been built, so the planner must materialise and sort the fanned-out set before the LIMIT can apply, and it leaves any SUM or AVG in the same select list wrong.
- Rewrite both filters as semi-joins, keeping resource as the only row source: AND EXISTS (SELECT 1 FROM resource_tag rt WHERE rt.resource_id = r.resource_id AND rt.tag_id = ANY($2)) and the same shape against resource_share. A semi-join stops at the first match per resource and preserves the driving index order, so ORDER BY updated_at DESC, resource_id DESC LIMIT 50 still stops after 50 rows.
- Count with the same predicates and no join at all: SELECT count(*) FROM resource r WHERE r.tenant_id = $1 AND r.status = 'active' AND EXISTS (...) AND EXISTS (...). Nothing multiplies a resource, so the number is the population.
Follow-up
- The filter changes from 'any of these tags' to 'all of these tags'. Rewrite it and state what it costs relative to the ANY form.
- A resource can be shared with the same user twice under different permissions. Does your count change, and should it?
What is the difference between Setup Time and Hold Time in digital flip-flops, and how do you resolve a timing
Medium · Digital Logic, RTL & Firmware Integratio
What is the difference between Setup Time and Hold Time in digital flip-flops, and how do you resolve a timing violation in a high-speed design?
Approach
- Define both: setup time is how long data must be stable before the active clock edge, and hold time is how long it must stay stable after the edge. Violating either risks metastability or a wrong captured value.
- Write the checks, with skew = capture clock arrival minus launch clock arrival. Setup: t_clk-q + t_logic,max + t_setup <= T_clk + skew. Hold: t_clk-q + t_logic,min >= t_hold + skew. Max frequency is 1 / (t_clk-q + t_logic,max + t_setup - skew).
- Fix setup violations: shorten the longest path by pipelining, retiming, restructuring logic, upsizing cells or reducing fanout, use useful skew, or lower the clock frequency.
- Fix hold violations by adding delay (buffers) on the short data path or by reducing skew. Stress that the hold check has no T_clk term, so slowing the clock cannot fix it, and a hold failure in silicon is fatal at any frequency.
- Mention how STA reports this as slack per path, and that timing exceptions (multicycle and false paths) must be constrained correctly or the reported slack is meaningless.
Follow-up
- Two flops with t_clk-q 0.5 ns, 3 ns of logic, 0.3 ns setup and 0.2 ns of positive skew: what is the maximum frequency?
- Why does positive skew help setup but hurt hold?
- How would you handle a path that only needs to be valid every second cycle?
Explain the key differences in communication protocols, operational overhead, and signal wiring between SPI, I
Medium · Digital Logic, RTL & Firmware Integratio
Explain the key differences in communication protocols, operational overhead, and signal wiring between SPI, I2C, and UART.
Approach
- UART: asynchronous, with TX, RX and ground and no shared clock. Both ends agree on a baud rate, and each frame is a start bit, 5-9 data bits, optional parity and stop bits. 8N1 spends 10 bit times per byte (80% efficiency). It is point-to-point and full duplex, and it tolerates only a few percent of baud mismatch.
- SPI: synchronous, with SCLK, MOSI, MISO and one chip select per device (or a daisy chain). It is full duplex with push-pull drive and runs into the tens of MHz. It has no addressing or acknowledge, and the four CPOL/CPHA modes must match the device. Pin count grows with the number of devices.
- I2C: two open-drain wires (SDA, SCL) with pull-ups. It uses 7- or 10-bit addressing, start/stop conditions, an ACK/NACK per byte, clock stretching and multi-master arbitration. Speeds are 100 kHz, 400 kHz, 1 MHz and 3.4 MHz. It is half duplex, the address and ACK bits add overhead, and the pull-up value and bus capacitance limit rise time.
- Summarise the choice: I2C for many slow devices on two pins, SPI for throughput to a few devices (ADCs, displays, flash), UART for consoles and links between boards. Add the failure modes: an SPI mode mismatch shifts data by one bit, and an I2C slave reset mid-byte can hold SDA low until you clock SCL up to nine times and issue a STOP.
Follow-up
- How would you trace a periodic corrupted bit in an SPI stream with an oscilloscope?
- Two RTOS tasks deadlock sharing an I2C driver: how do you debug it?
- How do you choose I2C pull-up resistor values?
How do you construct a Finite State Machine (FSM) in C to parse packet data coming from an external serial int
Medium · Digital Logic, RTL & Firmware Integratio
How do you construct a Finite State Machine (FSM) in C to parse packet data coming from an external serial interface?
Approach
- Fix the frame format first, e.g. start byte, length, payload, CRC, and make the states WAIT_START, READ_LEN, READ_PAYLOAD and READ_CRC, held in an enum plus a context struct (state, index, length, running CRC).
- Write a byte-driven function, parser_feed(ctx, byte), using a switch on state. The UART ISR only pushes bytes into a ring buffer, and the main loop or a task drains it into the parser. Each byte is O(1) and nothing is allocated.
- Defend against bad input. Reject a length larger than the payload buffer before writing anything, verify the CRC, and on any error reset to WAIT_START. Add an inter-byte timeout that resets a stalled frame. If the start byte can appear in the payload, use byte stuffing or COBS so it cannot be mistaken for a frame boundary.
- Mention the table-driven alternative ([state][event] -> next state and action) for larger protocols, and keep the parser a pure function so host unit tests can feed fragmented, back-to-back, truncated and corrupted frames.
Follow-up
- What happens if a byte is dropped in the middle of a frame?
- How would you handle a start-byte value appearing inside the payload?
- At a high baud rate, how would DMA with idle-line detection change the design?
Given an RC low-pass filter circuit, derive the transfer function and explain how the cutoff frequency determi
Medium · Analog Principles & Circuit Analysis for
Given an RC low-pass filter circuit, derive the transfer function and explain how the cutoff frequency determines the behavior of sampled continuous signals.
Approach
- Treat it as an impedance divider: H(s) = (1/sC) / (R + 1/sC) = 1 / (1 + sRC). So |H(jw)| = 1 / sqrt(1 + (wRC)^2) with phase -arctan(wRC).
- The cutoff is f_c = 1 / (2 pi RC), where the magnitude is 1/sqrt(2) (-3 dB) and the phase is -45 degrees. Above it, a first-order filter rolls off at 20 dB per decade. For example, 1 kOhm and 100 nF give about 1.59 kHz.
- Tie it to sampling. As an anti-aliasing filter it must attenuate content above fs/2 before the ADC, but a single pole gives only 20 dB per decade. Compute the attenuation at fs/2 against the ADC's LSB and say when you need a higher-order filter or oversampling.
- Add the time domain: the step response is 1 - e^(-t/RC), so settling to half an LSB of an N-bit ADC takes about (N+1) ln 2 time constants (about 9 tau at 12 bits), which matters for ADC input settling.
- Give the software equivalent: y[n] = y[n-1] + alpha (x[n] - y[n-1]) with alpha = Ts / (RC + Ts), a first-order IIR.
Follow-up
- With fs = 10 kHz and f_c = 1 kHz, how much attenuation do you get at 5 kHz, and is that enough for 12 bits?
- What changes if a load resistor is placed across the capacitor?
- How would you get a steeper roll-off?
Walk through the basic operating regions of a MOSFET (Cutoff, Triode, Saturation) and explain how an NMOS tran
Medium · Analog Principles & Circuit Analysis for
Walk through the basic operating regions of a MOSFET (Cutoff, Triode, Saturation) and explain how an NMOS transistor acts as a switch versus an amplifier.
Approach
- Cutoff: V_GS < V_th, so I_D is about zero apart from subthreshold leakage. Triode: V_GS > V_th and V_DS < V_GS - V_th, with I_D = k'(W/L)[(V_GS - V_th)V_DS - V_DS^2/2]. For small V_DS it behaves as a resistor R_on of about 1 / (k'(W/L)(V_GS - V_th)).
- Saturation: V_DS >= V_GS - V_th, with I_D = (1/2)k'(W/L)(V_GS - V_th)^2 (1 + lambda V_DS). The current is set mainly by V_GS, which is what makes amplification possible.
- As a switch, the NMOS alternates between cutoff and deep triode. A typical low-side switch has the load from the supply to the drain and the source at ground. Drive the gate well above V_th so R_DS(on) is low, and add a flyback diode for inductive loads.
- As an amplifier, bias it in saturation. The small-signal gm = 2 I_D / (V_GS - V_th), and a common-source stage gains about -gm (R_D || r_o).
- Make the software link: a 3.3 V GPIO driving a gate needs a logic-level MOSFET whose R_DS(on) is specified at that gate voltage. Otherwise it sits partly in triode and overheats.
Follow-up
- Why is a PMOS usually used for a high-side switch?
- What happens if a MOSFET specified at V_GS = 10 V is driven from a 3.3 V GPIO?
- How does channel-length modulation change the saturation current?
Explain the non-ideal characteristics of an Operational Amplifier (Op-Amp), such as input offset voltage, fini
Medium · Analog Principles & Circuit Analysis for
Explain the non-ideal characteristics of an Operational Amplifier (Op-Amp), such as input offset voltage, finite open-loop gain, and gain-bandwidth product.
Approach
- Input offset voltage: a small differential voltage appears as if applied at the input, and the output error is V_os x noise gain (1 + R_f/R_in). It drifts with temperature. Software can measure it with the input shorted and subtract it, but the drift limits how well that works.
- Finite open-loop gain A: the closed-loop gain becomes G_ideal / (1 + 1/(A beta)), so the gain error is about 1/(A beta). It grows as the closed-loop gain rises, and A itself falls with frequency.
- Gain-bandwidth product: for a single-pole op-amp, closed-loop bandwidth is about GBW / noise gain. For example, 10 MHz GBW at a gain of 100 leaves about 100 kHz.
- Name the rest briefly: input bias current (which flows through the input resistors as an extra offset, so match the resistances), slew rate (full-power bandwidth = SR / (2 pi V_peak)), output swing limits, CMRR, PSRR and noise.
- Link to software: a two-point offset and gain calibration in firmware, and choosing sample rates and digital filters within the signal chain's real bandwidth.
Follow-up
- Derive the gain of a non-inverting amplifier and explain what happens if the load resistance drops too low.
- Why does the inverting configuration have a different signal gain from its noise gain?
- At what frequency does a 0.5 V/us op-amp distort a 5 V peak sine?
How do non-idealities in an Analog-to-Digital Converter (ADC), such as integral non-linearity (INL), different
Medium · Analog Principles & Circuit Analysis for
How do non-idealities in an Analog-to-Digital Converter (ADC), such as integral non-linearity (INL), differential non-linearity (DNL), and quantization noise, impact software signal acquisition?
Approach
- Start with the ideal: LSB = V_ref / 2^N, with quantization error uniform over +/- 1/2 LSB, rms LSB/sqrt(12), and ideal SNR = 6.02N + 1.76 dB for a full-scale sine. On 3.3 V, 12 bits is about 0.81 mV per code and 16 bits about 50 uV.
- DNL is each code width's deviation from 1 LSB. DNL below -1 LSB means a missing code, and large DNL distorts histograms and small-step measurements. INL is the cumulative deviation of the transfer curve from a straight line, so it limits absolute accuracy and creates harmonic distortion (THD).
- Software correction: a two-point calibration removes offset and gain error, value = (code - offset) x gain. Correcting INL needs a multi-point lookup table or polynomial per device, possibly per temperature. DNL generally cannot be calibrated out.
- Oversampling and averaging improve SNR by 10 log10(OSR) dB, one extra bit per 4x, but only when at least about 1 LSB of noise or dither decorrelates the error. Report the result as ENOB = (SINAD - 1.76) / 6.02.
Follow-up
- How would you measure DNL and INL with a histogram (code-density) test?
- When does averaging fail to add resolution?
- A 16-bit ADC measures 86 dB SINAD: what is its ENOB, and what limits it?
Solve a nodal analysis problem using Kirchhoff's Voltage Law (KVL) and Kirchhoff's Current Law (KCL) for a mul
Medium · Analog Principles & Circuit Analysis for
Solve a nodal analysis problem using Kirchhoff's Voltage Law (KVL) and Kirchhoff's Current Law (KCL) for a multi-loop resistor network.
Approach
- Choose a ground reference, label the unknown node voltages, and write KCL at each non-reference node as the sum of currents leaving equals zero, with each branch current (V_a - V_b)/R. A voltage source between nodes forms a supernode with a constraint equation.
- Arrange the equations as G V = I. For a resistor-only network the conductance matrix is symmetric, with each diagonal entry equal to the sum of conductances at that node. Solve two or three unknowns by substitution or elimination and keep the units consistent.
- Know when mesh analysis (KVL around loops) gives fewer equations and say why you picked one or the other. Use Thevenin or source transformations to collapse parts of the network first.
- Check the answer: currents balance at every node, power delivered equals power dissipated, and the limiting cases (a resistor shorted or opened) give the expected values. Mention that circuit simulators automate this with modified nodal analysis and Gaussian elimination.
Follow-up
- Add a dependent current source: how do the equations change?
- Find the Thevenin equivalent seen by one of the resistors.
- How would you write code to solve an arbitrary resistor network?
Describe a project where you encountered an elusive hardware-software bug. How did you isolate whether the roo
Medium · Behavioral, Project Architecture & Execu
Describe a project where you encountered an elusive hardware-software bug. How did you isolate whether the root cause was in code or in silicon?
Approach
- Structure it as STAR, with the situation in two sentences: the board, the part, the symptom, and how intermittent it was (for example, one corrupted SPI frame per few thousand transfers). Say plainly what you personally owned.
- Make the isolation sequence the core of the answer. Reproduce the bug and measure its rate. Capture the bus with a logic analyzer or oscilloscope to check whether the bytes on the wire match what the firmware intended, since the wire is the boundary between code and silicon. Read registers over JTAG or SWD, check the datasheet timing and the errata, and swap boards or parts to separate one bad unit from a design issue.
- Name the variable you changed that settled it: a minimal firmware reproduction, a voltage or temperature sweep, slower clocks, or a different silicon revision. State the evidence that convinced you.
- Finish with the fix and what you left behind: a code change or errata workaround, a regression test or assertion, and documentation shared with the hardware team. The answer falls apart if you claim the hardware was at fault without evidence, or if someone else found the cause.
Follow-up
- What single measurement told you it was not your code?
- How did you present the finding to the hardware designers?
- What would you instrument earlier next time?
Suggested practice checklist
Each day covers one group of the reported questions: embedded C, interrupts and parsing, digital timing, protocols and debugging, circuits, DSP, and finally the behavioral and resume material. Every day ends in written or spoken answers you can check.
Day 1 — Embedded C fundamentals: registers, volatile and memory layout
- Write a register header for a made-up peripheral (control, status, data) as both #defines and a volatile struct overlay, with set, clear and field-write macros and a write-1-to-clear flag.
- Explain aloud why a while(!flag) loop hangs at -O2 without volatile, and list three things volatile does not guarantee.
- Sketch the .text/.rodata/.data/.bss/heap/stack map for a Cortex-M part and place ten variables of different kinds in it, including a static local and a string literal.
- Write a bit-reversal function for a 32-bit value using mask-and-shift swaps, plus three boundary test cases.
Deliverable: A register header file, a written list of volatile's limits, an annotated memory map, and a tested bit-reversal function.
Day 2 — Interrupts, ring buffers and packet parsing
- Write out the Cortex-M exception entry and exit sequence: NVIC pending, the 8-word stack frame, the VTOR lookup, EXC_RETURN, tail-chaining.
- Implement a static, power-of-two SPSC ring buffer with free-running indices and test full, empty and wraparound on the host.
- Implement a byte-driven FSM parser (start, length, payload, CRC) fed from that buffer, and test it with fragmented, corrupted and oversized-length frames.
Deliverable: A one-page interrupt-entry diagram, a tested ring buffer, and a tested packet parser with its failure cases listed.
Day 3 — Digital logic and timing
- Derive the setup and hold inequalities with skew, then solve three max-frequency problems with numbers, including one with negative skew.
- Explain metastability with the MTBF formula and draw a two-flop synchronizer, a toggle synchronizer for pulses, and a req/ack handshake.
- Draw a Gray-code counter and the async FIFO pointer logic, and write the empty and full comparisons for a 16-entry FIFO.
- Write a Verilog rising-edge detector that outputs a one-cycle pulse, since candidates report Verilog and FPGA topics.
Deliverable: Worked timing calculations, three synchronizer sketches, a Gray-pointer FIFO diagram with its flag logic, and an edge-detector module.
Day 4 — Serial protocols and hardware-software debugging
- Build a comparison table for UART, SPI and I2C: wires, clocking, duplex, addressing, acknowledgement, typical speeds and failure modes.
- Talk through tracing a periodic corrupted bit on an SPI bus with a scope: trigger setup, which edges to inspect, how CPOL/CPHA mismatch and signal integrity look.
- Talk through an RTOS deadlock between two tasks sharing an I2C driver: lock ordering, priority inversion, bus-stuck recovery.
- Write your hardware-software bug story in STAR form, with the evidence that separated code from silicon.
Deliverable: A protocol comparison table, two spoken debugging walkthroughs, and a written STAR bug story.
Day 5 — Analog circuits for software engineers
- Derive the RC low-pass transfer function, cutoff and step response, and compute attenuation at fs/2 for a sample design.
- Solve two nodal-analysis problems (one with a supernode) and check each with a power balance.
- Write the MOSFET region equations and explain the switch versus amplifier bias points, including logic-level gate drive.
- List op-amp non-idealities with one formula each (offset x noise gain, gain error 1/(A beta), bandwidth GBW/noise gain, slew-limited frequency) and derive a non-inverting gain.
Deliverable: Four pages of worked circuit derivations with numeric answers and sanity checks.
Day 6 — Signals, ADCs and DSP
- Compute LSB size for 12 and 16 bits on a 3.3 V reference, ideal SNR, and ENOB from a given SINAD, and explain INL, DNL and calibration.
- Work three aliasing problems with |f - k fs| and explain how you would size the anti-aliasing filter.
- Write a fixed-point moving-average FIR and a biquad IIR in C, and explain the FFT butterfly, the complexity count and the window choice.
- Write a fixed-point PID step with saturation and conditional integration, and compare it with a float model on step and saturation inputs.
Deliverable: ADC calculations, aliasing worked examples, FIR and IIR code, an FFT explanation sheet, and a tested fixed-point PID.
Day 7 — Behavioral stories, resume deep dive and a mock loop
- Write STAR answers for modular firmware under changing specs, a speed-versus-readability-or-memory trade-off with measurements, and testing drivers without hardware.
- For each resume project, draw the architecture and rehearse a register-level explanation of one design choice and one failure.
- Run a mock session with a friend or recording that mixes one C question, one timing question, one circuit question and one DSP question, then fix the weakest answer.
Deliverable: Three written STAR stories, one architecture sketch per project, and a recorded mock session with notes on the weakest answer.
Behavioural preparation
The behavioral questions candidates report for this role are all about working at the hardware-software boundary: isolating a bug between code and silicon, designing firmware while specs change, trading speed against memory or readability, and testing drivers before hardware exists. Candidates also report collaboration with hardware designers and validation teams as an evaluated area. Every story needs real technical detail and the measurements behind your decisions.
- Describe a project where you encountered an elusive hardware-software bug. How did you isolate whether the root cause was in code or in silicon?
- Tell me about a time you disagreed with a hardware designer about where a problem should be fixed, in silicon or in firmware.
- Walk me through the architecture of one project on your resume and the hardest timing or memory constraint you hit.
How do you approach designing modular firmware when hardware specifications are continuously evolving during p
Medium · Behavioral, Project Architecture & Execu
How do you approach designing modular firmware when hardware specifications are continuously evolving during product development?
Approach
- Describe the layering: a register-access layer generated from the register map (one source of truth, regenerated when the spec changes), drivers with stable APIs above it, and application code that never touches registers directly.
- Explain how you contain churn: configuration structs instead of magic numbers, a chip-revision ID read at runtime or a compile-time variant switch, and interface tables (structs of function pointers) where implementations must be swappable.
- Cover the process side: agree interfaces early with the hardware designers, track spec revisions against code, and test against simulators, FPGA prototypes or RTL simulation before silicon arrives.
- Anchor it in a real case: a register field that moved between revisions, how many files changed, and what the layering saved. Avoid the opposite failure too, abstraction so heavy it costs cycles in an ISR path.
Follow-up
- How would you support two silicon revisions in one firmware image?
- Where would you refuse to add an abstraction layer, and why?
- How do you find out a spec changed before it breaks your build?
Give an example of a time you had to trade off execution speed for code readability or memory footprint in a r
Medium · Behavioral, Project Architecture & Execu
Give an example of a time you had to trade off execution speed for code readability or memory footprint in a resource-constrained system.
Approach
- Pick a story with concrete budgets: flash and RAM sizes, the cycle budget per sample or per interrupt, and the number you had to hit.
- Show the measurement before and after: the DWT cycle counter on Cortex-M or a GPIO toggle on a scope for timing, and the linker map file for memory. A trade-off made without measurement is the mistake that sinks this answer.
- Name the technique and its cost, for example a sine lookup table (flash for cycles), fixed-point instead of float on a core without an FPU, loop unrolling, or compiler intrinsics instead of portable C.
- Explain how you kept the code maintainable: the optimized code isolated behind one function, the reasoning in comments, and the plain reference implementation kept in the test suite to check the fast version.
Follow-up
- How did you prove the optimized version gave the same results?
- Would you make the same choice on a core with an FPU?
- What did code review say about it?
How do you structure unit tests and regression test benches for low-level device drivers when physical hardwar
Medium · Behavioral, Project Architecture & Execu
How do you structure unit tests and regression test benches for low-level device drivers when physical hardware targets are not yet available?
Approach
- Separate logic from hardware: put register access behind a thin interface so the driver's state machine, calculations and error handling compile and run on the host.
- Fake the hardware: point the driver's base address at a RAM array acting as a register file, and model peripheral behaviour in the fake (a status bit that sets after N polls, an error flag on demand). Run it with a host C unit-test framework such as Unity or CppUTest.
- Move up in fidelity as it becomes available: an instruction-set simulator or emulator, an FPGA prototype, or co-simulation against the RTL with a SystemVerilog testbench. Then hardware-in-the-loop once boards arrive.
- Run the host tests on every commit in CI under Git so regressions surface immediately, and keep golden traces to compare against the first silicon.
- Be explicit about what host tests cannot cover (real timing, DMA, cache effects, analog behaviour), and say which tests you ran first on the new board.
Follow-up
- How do you keep your fakes faithful to the real peripheral?
- Which bugs did this approach miss that only silicon exposed?
- What is the first test you run when a new board arrives?