RTL Design Interview Prep
RTL design interview prep for ASIC and FPGA: Verilog/SystemVerilog, FSMs, clock domain crossing, async FIFOs, blocking vs non-blocking, timing closure, and synthesizable coding.
Quick answer
RTL design (register-transfer-level design) is the discipline of describing digital hardware in Verilog or SystemVerilog at the level where every register, every clock edge, and every combinational transformation between cycles is explicit.
RTL interviews drill into the gap between writing Verilog that simulates and writing Verilog that ships at 800 MHz on a 5nm ASIC or 400 MHz on an FPGA target.
Editorial review
Written by
CompoundLearn editorial team
Wireless / RF / hardware engineering
Reviewed by
CompoundLearn editorial team
Wireless / RF / hardware engineering
Last reviewed
Built from curated topic maps, editorial validation, and subject-matter review so the page stays aligned with the interview intent and the current content pipeline.
What it is
RTL design (register-transfer-level design) is the discipline of describing digital hardware in Verilog or SystemVerilog at the level where every register, every clock edge, and every combinational transformation between cycles is explicit. The RTL description is then synthesized into a gate-level netlist that targets either an ASIC standard-cell library (for chip tape-out) or an FPGA fabric (for reconfigurable deployment). Across both targets the RTL designer owns the same core problem set: writing synthesizable code that meets functional intent, closes timing at the target frequency across process-voltage-temperature corners, fits the area budget, and handles every clock and reset domain correctly. The typical block-level RTL engineer writes finite state machines, datapaths, arbiters, control logic, and standard interfaces (AXI, AHB, APB, AvalonMM, custom). The work intersects with computer architecture (interpreting the spec and partitioning into blocks), verification (writing testbench-friendly RTL, exposing observable state), DFT (scan insertion, ATPG-friendly coding), physical design (registered hierarchy boundaries, balanced pipelining, clock-tree-friendly structures), and low-power design (clock gating, power gating, UPF awareness on ASIC). The same RTL skills apply across roles called RTL design engineer, ASIC design engineer, chip design engineer, FPGA RTL engineer, and digital design engineer — the title varies by company more than the work.
Why interviewers ask
RTL interviews drill into the gap between writing Verilog that simulates and writing Verilog that ships at 800 MHz on a 5nm ASIC or 400 MHz on an FPGA target. A candidate who can describe a state machine on a whiteboard but cannot reason about FSM encoding, latch inference, clock domain crossing for multi-bit buses, or pipeline tradeoffs will produce blocks that fail timing or corrupt data the moment a verification or physical-design corner case fires. Interviewers probe across four axes. Language and synthesizability literacy: do you know when blocking versus non-blocking assignment is correct, why latches are a smell, what the synthesizable subset of SystemVerilog actually covers, when generate blocks and parameterized modules belong in the design. Microarchitecture reasoning: how do you partition a datapath, when do you pipeline, when do you replicate versus share, how do you choose between one-hot and binary FSM encoding. Clock and reset discipline: how do you cross domains safely, when do you need an asynchronous FIFO versus a handshake, how do synchronous and asynchronous resets interact with deassertion synchronization and DFT requirements. Implementation awareness: can you read a static-timing report, identify the critical path, distinguish setup from hold violations, choose between pipelining and retiming, and predict how a coding choice will affect downstream synthesis, DFT coverage, and physical design. The signal that strong candidates show is connecting every RTL decision to a measurable consequence — Fmax, area, dynamic power, leakage, verification cost, DFT coverage, or downstream physical-design risk — instead of stopping at "this is the way I have seen it written."
Common mistakes
The first mistake is treating Verilog like a software language. Loops, conditionals, and assignments do not behave like their C counterparts; they describe hardware. Candidates write for loops expecting sequential execution and produce massive combinational fan-out, or mix blocking and non-blocking assignments inside the same always block and create simulation-synthesis race conditions. The second is naive clock domain crossing: synchronizing a multi-bit bus with one two-flop synchronizer per bit, missing that bits will be sampled across multiple destination cycles and produce torn values absent from the source. Safe multi-bit crossings use gray-coded counters, request-acknowledge handshakes, or asynchronous FIFOs with gray-coded pointers. The third is inferred latches in always_comb blocks. Candidates leave one branch of a conditional without an assignment, the synthesis tool infers a latch, and the design closes timing in simulation but fails post-synthesis static timing. The fix is default assignments at the top of the block, complete case statements with a default arm, or explicit else clauses on every if. The fourth is FSM encoding by habit instead of by analysis. One-hot wins for small high-performance FSMs because next-state logic stays shallow; binary wins for larger area-critical FSMs because it uses fewer flops. Picking one without thinking about the FSM size and the target frequency leaves performance or area on the table. The fifth is treating reset as an afterthought. Candidates pick synchronous or asynchronous reset based on personal habit, forget about deassertion synchronization, and end up with metastability on reset release or with DFT scan-chain violations. The standard pattern is asynchronous assertion and synchronous deassertion, but the right answer depends on the project conventions, the target technology, and the DFT requirements. The sixth is testbench-only constructs leaking into synthesizable RTL: initial blocks, time delays, force/release, dynamic arrays, classes. The simulator runs them; the synthesis tool either ignores them silently or treats them as constants, and the resulting netlist diverges from the simulated behavior. The disciplined rule is to restrict synthesizable RTL to the synthesizable subset of the language and reserve the rest for testbench code.
RTL Design vs Verification vs Physical Design vs Architecture — who owns what
| Dimension | RTL Design | Verification | Physical Design | Architecture |
|---|---|---|---|---|
| Primary deliverable | Synthesizable Verilog / SystemVerilog | Testbenches, assertions, coverage, regressions | Floorplan, placement, routing, signoff | Spec, block diagram, interfaces, performance model |
| Core skills | FSMs, datapaths, CDC, timing intuition | SystemVerilog / UVM, constrained-random, coverage | Place-and-route tools, CTS, DRC/LVS, signoff | Performance modeling, tradeoff analysis, spec writing |
| Failure they catch | Coding errors, latch inference, CDC bugs | Functional bugs, corner cases, spec violations | Timing, IR drop, congestion, manufacturability | Wrong-feature, wrong-interface, infeasible budgets |
| Typical tools | VCS / Xcelium / Questa, Design Compiler, Genus, Spyglass | VCS / Xcelium / Questa, UVM, Verdi | Innovus, ICC2, Tempus, Calibre | Cycle-accurate models, spreadsheets, internal architecture tools |
| Hands-on with RTL? | Writes it | Reads and stresses it | Receives a netlist (after synthesis) | Defines what RTL must implement |
| Tapeout exposure | Owns block-level signoff | Owns functional signoff | Owns physical signoff | Owns architectural correctness |
Sample interview questions
- In a synthesizable always block, when do you use blocking (=) vs non-blocking (<=) assignment, and why does mixing them cause simulation-synthesis mismatch?
- A. Use non-blocking in sequential always_ff (registers update at clock edge with last-sampled values); use blocking in combinational always_comb (immediate left-to-right evaluation); mixing in one block creates race conditions where simulation order differs from synthesized hardware. ✓
- B. Use blocking in always_ff because it is faster to simulate; non-blocking is for testbench code only and synthesis treats both identically.
- C. Always use non-blocking everywhere because it is the safer default and modern synthesis tools no longer support blocking assignment in synthesizable code.
- D. The choice is purely stylistic; both compile to the same gate-level netlist and only differ in waveform readability during debug.
Option A is correct because the two operators model different hardware. Non-blocking (<=) in a clocked always_ff captures every right-hand side at the clock edge and updates all left-hand sides simultaneously, which matches how a real flop bank behaves. Blocking (=) in always_comb evaluates immediately and in source order, which matches combinational logic. Mixing them in the same block — for example, updating one register with = and another with <= in the same always_ff — creates an ordering dependency in simulation that does not exist in synthesized hardware, so the post-synthesis netlist behaves differently from the pre-synthesis simulation. The standard rule is one operator per block, tied to the always-block flavor. Option B is incorrect because simulation speed is not the criterion, and synthesis and simulation diverge in both directions: a non-blocking assignment in combinational logic makes simulation read stale (delta-delayed) values that the synthesized gates never produce, and a blocking assignment in a clocked block can introduce a read-before-write race. (Latch inference, by contrast, comes from leaving a combinational signal unassigned on some branch — not from the choice of operator.) Option C is incorrect because blocking is the correct choice for combinational logic; banning it would force everything through clocked blocks and inflate area unnecessarily. Option D is incorrect because the netlist and the simulation order are exactly where the two operators diverge — that is the failure mode the rule exists to prevent.
- When does one-hot FSM state encoding beat binary encoding, and what is the cost?
- A. One-hot wins for small-to-medium FSMs targeting high frequency: next-state logic becomes a wide OR of single-bit checks, which is shallow and fast, at the cost of N flops for N states instead of ceil(log2(N)). ✓
- B. One-hot is always better because it eliminates state-decoding logic, and modern synthesis tools transparently convert binary encoding to one-hot during optimization.
- C. Binary encoding is faster because fewer flops mean less clock-tree fanout, so binary always closes timing at higher frequency than one-hot for the same FSM.
- D. One-hot only helps on FPGAs because LUT-based decoding is inefficient; on ASICs the synthesis tool ignores the coded encoding and re-encodes for area.
Option A is correct because one-hot encoding represents each state as a single asserted flop. Next-state logic becomes a flat OR of single-bit checks rather than a multi-bit comparator, which keeps logic depth shallow and supports higher Fmax. The tradeoff is flop count: an FSM with 16 states needs 16 flops one-hot vs 4 flops binary, and the gap widens for larger FSMs. The standard guidance is one-hot for small FSMs (under ~20 states) targeting performance, binary or gray for larger FSMs or area-critical paths. Option B is incorrect because synthesis tools do not transparently rewrite encodings unless given explicit pragmas, and forcing one-hot on a 64-state FSM wastes considerable area. Option C is incorrect because next-state logic depth is usually the timing bottleneck, not flop count. Binary encoding requires a comparator on the state register, which adds depth and often loses on Fmax even though it uses fewer flops. Option D is incorrect because one-hot vs binary is a general digital-design tradeoff that applies to both ASIC and FPGA. ASIC synthesis tools respect the encoded representation unless explicitly told to re-encode, and one-hot remains a valid performance choice on ASIC.
- You need an arbiter that grants one of N requesters per cycle and prevents starvation. What is the tradeoff between priority and round-robin?
- A. Priority arbiters grant to a fixed-rank requester first, so a high-rank requester starves low-rank ones; round-robin rotates the grant origin each cycle so every requester gets serviced within N cycles, at the cost of more state and slightly wider next-state logic. ✓
- B. Round-robin arbiters are unfair because the rotation is pseudo-random, while priority arbiters guarantee fair sharing by definition.
- C. Priority arbiters use fewer gates because they require no state, so they always close timing at higher frequency than round-robin and starvation is rarely a real concern in practice.
- D. The two are functionally identical when all requesters are active; the difference only matters when fewer than N requesters are asserted at the same time.
Option A is correct because a static-priority arbiter ranks requesters in fixed order — say req[0] wins over req[1], which wins over req[2], and so on. If req[0] is always asserted, no other requester ever gets the grant. A round-robin arbiter holds a pointer to the next-priority origin and rotates it past the granted requester each cycle, so every active requester is guaranteed service within N cycles. The cost is the pointer register, the rotated priority encoder, and slightly deeper next-state logic. Option B is incorrect because round-robin is deterministic — the rotation pointer is a counter, not pseudo-random — and is the canonical fair arbiter in textbooks and standard cells. Option C is incorrect because starvation is a real concern in most multi-master systems (DMA engines, processor cores, peripherals), and priority arbiters are normally limited to cases where the priority ordering is itself the design intent. Option D is incorrect because the difference shows up exactly when multiple requesters are active simultaneously. With only one requester active the grant is unambiguous and both schemes behave the same.
- Which SystemVerilog constructs are unsafe in synthesizable RTL even though they simulate correctly?
- A. Initial blocks, delays (#n), force/release, dynamic arrays, classes, and most assertion immediate constructs are testbench-only; synthesis tools either ignore them silently or treat them as constants, producing a netlist that diverges from simulation. ✓
- B. Generate blocks and parameterized modules should be avoided in synthesis because tools have inconsistent support and emit different netlists across vendors.
- C. Anything inside an always block is synthesizable by definition, so the safe rule is to put every line inside an always block, including testbench-only constructs.
- D. Tasks and functions are not synthesizable; replacing them with macros (`define) is the only portable way to reuse code across modules.
Option A is correct because synthesizable RTL is a subset of the full SystemVerilog language. Initial blocks model power-on simulation state but synthesis tools typically only honor them for ROM initialization on FPGAs; delays (#5) have no hardware meaning; force/release manipulate signal values from outside the hierarchy with no synthesizable counterpart; dynamic arrays and classes are object-oriented constructs that require a runtime heap; and most immediate-assertion forms exist for simulation-time checking only. A safe RTL coding standard restricts the language to the synthesizable subset and reserves the rest for testbench code. Option B is incorrect because generate blocks and parameterized modules are core synthesis features supported by every commercial tool and central to reusable IP design. Option C is incorrect because always blocks happily accept unsynthesizable constructs that simulators evaluate; the divergence at synthesis is exactly the failure mode the rule guards against. Option D is incorrect because automatic functions and tasks are synthesizable when used correctly (no static state, no timing controls). Macros are a separate textual-substitution mechanism with different tradeoffs.
- You are writing an N-deep, W-wide synchronous FIFO that needs to be reused across multiple blocks. How do you make it cleanly parameterizable in Verilog or SystemVerilog?
- A. Use module parameters for DEPTH and WIDTH, derive pointer widths with $clog2(DEPTH), use a generate block or a packed array for the storage, and write interface signals in terms of WIDTH so the same module instantiates cleanly at any size. ✓
- B. Hard-code DEPTH and WIDTH inside the module and instantiate it once per size needed, since module parameters add synthesis overhead and are inconsistently supported.
- C. Use SystemVerilog interfaces for all FIFO ports because interfaces always synthesize more efficiently than parameterized scalar ports.
- D. Use `define macros for DEPTH and WIDTH so they can be globally overridden at compile time without modifying the FIFO source.
Option A is correct because parameterized modules are the standard reusability mechanism in synthesizable RTL. Declaring `parameter DEPTH = 16, WIDTH = 32` lets each instance set its own size at elaboration. `$clog2(DEPTH)` computes pointer widths automatically, a packed array or a generate block instantiates the storage cleanly, and writing interface signals as `[WIDTH-1:0]` keeps the port widths consistent. Every commercial synthesis tool supports this pattern, and major IP libraries are built on it. Option B is incorrect because hard-coded modules force a per-size copy that bloats the codebase and creates drift between near-duplicates. Module parameters have no runtime cost; they are resolved at elaboration before synthesis. Option C is incorrect because SystemVerilog interfaces have value (grouping related signals, enforcing modports) but they do not magically synthesize more efficiently and are orthogonal to parameterization. Many tools historically had quirks with interface synthesis. Option D is incorrect because `define macros are global text-substitution and break encapsulation: one block cannot use two FIFOs of different sizes if the size comes from a `define. Module parameters scope the size to each instance.
Frequently asked questions
- What is RTL design?
- RTL design (register-transfer-level design) is the discipline of describing digital hardware in terms of registers, the combinational logic between them, and the clock that advances state. The designer writes Verilog or SystemVerilog at a level where every storage element (flop, register file, memory) and every combinational transformation between cycles is explicit. The RTL description is then fed to a synthesis tool that maps it to a gate-level netlist for an ASIC standard-cell library or an FPGA fabric. RTL sits between architecture (which decides what blocks exist and how they connect at a high level) and physical implementation (which places and routes the gates), and it is the layer at which functional correctness and per-cycle timing are both specified.
- How is RTL design different from ASIC design and from FPGA design?
- The terms overlap and are often used loosely. RTL design is the universal name for writing synthesizable digital logic in Verilog or SystemVerilog. ASIC design extends RTL with the back-end concerns of fixed-mask silicon: tapeout discipline, DFT (scan insertion, ATPG, MBIST), low-power techniques (clock gating, power gating, UPF, multi-Vt cells), and signoff across process-voltage-temperature corners. FPGA design also uses RTL but targets a reconfigurable device with vendor-specific resources (LUTs, BRAMs, DSP slices, transceivers) and iterates faster because there is no mask cost. The day-one RTL skills — FSMs, datapaths, CDC, timing closure, synthesizable coding — transfer directly between ASIC and FPGA roles.
- What do interviewers actually test in an RTL design interview?
- Most chip companies run an RTL screen and one or two on-site rounds. The screen typically asks you to write a small synthesizable block on a whiteboard or shared editor — a state machine that detects a pattern, a synchronous FIFO, a small arbiter, a parity or CRC unit — under realistic constraints (reset behavior, registered outputs, no inferred latches). On-site rounds go deeper: clock domain crossing for multi-bit buses, pipelining and retiming choices, timing-closure debug from a static-timing report, FSM encoding tradeoffs, low-power and DFT awareness. The common thread is whether you can connect a coding choice to a concrete consequence — Fmax, area, power, verification effort, DFT coverage, or downstream physical design.
- Why do interviewers care so much about clock domain crossing (CDC)?
- CDC bugs almost never show up in simulation because Verilog event scheduling does not model setup-window metastability — the simulator just samples whatever value happens to be on the wire. In real silicon, a signal that transitions inside the destination flop's setup or hold window can leave the flop in a metastable state where it hovers between 0 and 1 for an unbounded time before resolving. The standard mitigations are two- or three-flop synchronizers for single-bit signals, gray-coded counters for crossings where only one bit changes per increment, and asynchronous FIFOs with gray-coded pointers for multi-bit data. Multi-bit synchronizers built bit-by-bit are the canonical RTL bug because bits can be sampled across multiple destination cycles, producing torn values that the source never asserted.
- When should I use blocking vs non-blocking assignment?
- The rule is simple: blocking (=) inside always_comb for combinational logic, non-blocking (<=) inside always_ff for sequential logic. Non-blocking samples every right-hand side at the clock edge and updates all left-hand sides simultaneously, which matches how a real flop bank behaves. Blocking evaluates immediately in source order, which matches combinational logic. Mixing them in the same always block creates an ordering race where simulation order diverges from synthesized hardware. Stick to one operator per block, scope the always block to one logic flavor, and lint tools will catch most violations early. Sample question aB1cD2Ee walks through the simulation-synthesis mismatch in detail.
- What is FSM encoding, and how do I pick between one-hot, binary, and gray?
- An FSM stores its current state in a state register; the encoding decides how state values are represented in that register. One-hot uses N flops for N states (only one flop asserted at a time), giving shallow next-state logic and high Fmax at the cost of more flops. Binary uses log2(N) flops with a comparator on the state register, saving area but adding logic depth. Gray encoding lets only one bit change per transition, which is mainly useful when the state register crosses a clock domain because it is metastability-friendly. Default to one-hot for small performance-critical FSMs (under ~20 states), binary for larger or area-critical ones, and gray when the state value itself crosses domains.
- How do RTL designers avoid inferring unintended latches?
- A latch is inferred whenever a combinational always block has a path where an output is not assigned. The fix is one of: default assignments at the top of the block (assign every output to a safe value before the conditional logic), complete case statements with a default arm, explicit else clauses on every if, or always_comb (SystemVerilog) so the compiler emits a warning. Lint tools such as Spyglass catch most cases, but the discipline starts at coding time: every combinational output gets assigned on every path through the block. Latches inside what should be combinational logic create timing hazards, complicate verification, and confuse the synthesis and physical-design flows.
- What tools do RTL designers use day to day?
- The core toolchain is: simulators (Synopsys VCS, Cadence Xcelium, Siemens Questa, or open-source Verilator), synthesis tools (Synopsys Design Compiler or Cadence Genus for ASIC; AMD/Xilinx Vivado, Intel/Altera Quartus for FPGA), static-timing analysis (Synopsys PrimeTime), lint and CDC checkers (Synopsys Spyglass, Real Intent Meridian), waveform viewers (Verdi, GTKWave, SimVision), and version control (git). Verification engineers also use UVM frameworks and constraint solvers. The lint tool catches surprising bugs (latches, blocking-non-blocking mixes, simulation-synthesis mismatches) before they reach synthesis, and learning to read the synthesis and timing reports critically is what separates senior RTL designers from juniors.
- What is the typical career path for an RTL design engineer?
- New graduates usually start as block-level RTL designers responsible for small datapath or control blocks under the guidance of a senior. After two or three tapeouts they take ownership of larger blocks (a memory controller, a serial interface, an interconnect bridge). Senior engineers progress into either RTL architect roles (microarchitecture, block partitioning, interface contracts across the chip) or specialist tracks (low-power, DFT, formal verification). Many companies also have a parallel principal-engineer track for deep technical experts who do not want to manage. The lateral moves into ASIC design, FPGA prototyping, or verification engineering all build on the same RTL foundation. See /topics/rtl-design-academia-to-industry for the new-graduate framing and /topics/rtl-design-engineer-signals for the senior-interview signal model.
Related topics
Essential AI-Native Skills for RTL Design
Modern engineering work increasingly uses AI tools for design and code review, debugging, documentation, test and testbench generation, and workflow automation. The goal is not to let AI replace engineering judgment — it is to move faster while keeping verification discipline.
- Use AI to explain unfamiliar code, logs, waveforms, datasheets, or test failures.
- Break large problems into small, reviewable steps you can verify independently.
- Ask AI for hypotheses, then validate them against tests, measurements, simulations, or lab data.
- Version-control your analysis scripts, testbenches, and configs — keep changes small and reviewable.
- Document your assumptions, design tradeoffs, and debugging decisions.
- Verify AI output before trusting it: run the checks that fit the domain — unit tests, linters, simulations, or bench/lab measurements.
- Review AI output for correctness, edge cases, and real-world consequences.