Digital Design & Verification (Verilog, SystemVerilog, VLSI) Module 2
Verilog and SystemVerilog for design: the language core interview questions
Interview questions on “Verilog and SystemVerilog for design: the language core”: warm-ups, then what each harder question tests and how a good answer starts.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher
What is the difference between a wire and a logic (or reg) signal?
Answered in the lesson Modules, ports, parameters and hierarchy
-
Fresher
What is the difference between logic, bit, int and integer?
Answered in the lesson Data types, literals and part-selects
-
Fresher
What is the difference between == and ===?
Answered in the lesson Operators, expression width and signedness
-
Fresher
Why write always_comb rather than always @* or a sensitivity list?
Answered in the lesson Combinational logic with assign and always_comb
-
Fresher
What is the difference between blocking and nonblocking assignments?
Answered in the lesson Sequential logic, always_ff and nonblocking
-
Fresher
What is the difference between $display, $strobe and $monitor?
Answered in the lesson The event scheduler and race conditions
-
Fresher
Why should every generate block have a name?
Answered in the lesson Parameters, generate blocks and packages
-
Fresher
Why use a typed enum for the states of a state machine instead of localparam constants?
Answered in the lesson Structs, enums, unions and interfaces
-
Fresher
What is the difference between a packed and an unpacked array?
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Fresher to mid
A top-level module compiles and simulates, but the 16-bit bus between two of its instances only ever shows a changing bit 0 and zeros in bits 15 to 1. What do you suspect, how do you confirm it, and how do you stop it from happening again?
- What it tests
- Recognising an implicit net from its symptoms, the warnings each tool gives for it, and the rules that keep implicit nets out of a design.
- First step
- Look at how the bus is declared in the parent. A name used in two port connections but never declared becomes one implicit 1-bit wire: the driving instance's bit 0 reaches it, and the receiving port reads that bit with 15 zeros above it. Then look for port-width warnings on both instances in the compile log.
- Follow-ups
-
- The compile log has no warnings at all. Can it still be an implicit net?
- How would you find every implicit net in a large design?
- Why not simply turn on -Wall and leave the files as they are?
From the lesson Modules, ports, parameters and hierarchy
Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.Opening the model answers with your pass…
Model answers open with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Fresher to mid
Your team is writing its RTL style guide. Instances may be connected by position, by name, with .name or with .*. What would you allow in design files and in testbenches, and why?
- What it tests
- Weighing readability, code review, renaming and the tools' own checks for each connection style, and turning the choice into a rule a team can check automatically.
- First step
- Start from what goes wrong with each style. Positional connections depend on the order of the module's ports, .* hides which signals an instance uses, and named connections show every wire at the cost of typing. Then ask who reads the code most: the people who check design files in code review need every connection in front of them.
- Follow-ups
-
- A module has 60 ports. Is .* acceptable then?
- How would you enforce the rule automatically?
- Does the choice change what synthesis builds?
From the lesson Modules, ports, parameters and hierarchy
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A parameterised block needs a mask of all ones as wide as its parameter W, which may be 8, 32 or 64. Which of 'hFFFF_FFFF, -1, ~0, '1 and {W{1'b1}} give all ones for every W, and which would you write in RTL?
- What it tests
- The width and sign rules of literals, what an assignment does to a narrower value, fill literals and replication, and writing RTL that stays right when a parameter changes.
- First step
- Work out each literal's own width and sign before the assignment: an unsized based literal is 32 bits and unsigned, a plain number is 32 bits and signed, and a fill literal has no width of its own. Then follow each into 8 bits and into 64 bits.
- Follow-ups
-
- What does each of them give when W is 8?
- Why does ~0 work, and where would it stop working?
- How would lint have pointed at the wrong one?
From the lesson Data types, literals and part-selects
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A testbench copies a design's 8-bit output into a bit [7:0] variable, compares it with the expected value and passes. A colleague then finds that the output is x for the first ten cycles after reset. How did the testbench miss it, and how would you change it?
- What it tests
- What two-state types do to unknown values, which comparisons can see an x at all, and where a testbench has to keep four states.
- First step
- Follow one x from the design's output into the testbench variable. A bit can hold only 0 or 1, so the copy turns the x into 0, and if the expected value at that moment is also 0, the comparison passes and the unknown is gone before any check sees it.
- Follow-ups
-
- Would declaring the variable as logic be enough on its own?
- How do you check for x without failing during reset?
- What changes if the regression runs on Verilator?
From the lesson Data types, literals and part-selects
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
An image filter averages two 8-bit pixels with avg = (p + q) >> 1, where avg is 8 bits as well. Bright pixels come out dark. Explain what the hardware computes, fix it in two ways, and say how you would catch the bug before silicon.
- What it tests
- Context-determined widths and where a carry goes, widening with a cast or a wider intermediate, what lint can and cannot see, and choosing test values at the corners.
- First step
- Size the expression before evaluating it. p, q and avg are all 8 bits, so the context is 8 bits and p + q has no ninth bit for its carry. For p = q = 200 the sum 400 keeps only its low 8 bits, 144, and the average comes out as 72 instead of 200.
- Follow-ups
-
- Why does lint not warn about this line?
- Which fix would you choose for the average of 64 pixels?
- Which test values would have caught the bug at once?
From the lesson Operators, expression width and signedness
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A temperature sensor gives a signed 8-bit reading, temp. The RTL raises an alarm with if (temp < LIMIT), where LIMIT is a logic [7:0] constant set to 10. At -5 degrees the alarm stays off. Why, and how do you fix it so that the bug cannot come back?
- What it tests
- The signedness rules of comparisons, casts, typing constants and parameters, and testing a comparison over every input value.
- First step
- Look at the signedness of both operands, not only of the reading. LIMIT is unsigned, so the comparison is unsigned, and the pattern of -5, 11111011, counts as 251, which is not below 10.
- Follow-ups
-
- Would declaring LIMIT as an int parameter fix it?
- What changes if temp grows to 12 bits and LIMIT stays 8?
- How would you test the comparison over every reading?
From the lesson Operators, expression width and signedness
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
Reviewing RTL, you find always_comb begin if (load) next_q = data; end feeding a register. What does it build, what goes wrong, and how do you fix it? How would your flow catch every such case automatically?
- What it tests
- Recognising a latch from a path that writes nothing, why latches cause timing, test and reset trouble, the default-first fix, and the lint and synthesis checks that catch latches.
- First step
- Follow the path where load is 0: nothing writes next_q, so it must keep its value, which is a latch with load as its enable. Then ask what next_q should be when load is 0, usually the register's current value, and write that first in the block.
- Follow-ups
-
- Why is a latch worse than a flip-flop in an FPGA or a chip?
- The latch is wanted. How do you write it?
- Which checks in the flow would have caught it?
From the lesson Combinational logic with assign and always_comb
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A designer marks a case over an 8-bit select, case (1'b1) with one item per bit, as unique, to get a faster multiplexer. What do you check first, what happens if the select is not one-hot after all, and when would priority, or no keyword at all, be the better choice?
- What it tests
- What unique promises and what synthesis may do with the promise, what happens when it is broken in simulation and in hardware, and choosing between unique, unique0, priority and a plain case.
- First step
- Check the claim behind the keyword: can the select ever have two bits set, or none? Find what drives it and whether reset, idle cycles or a bug elsewhere can make it zero or multi-hot, because unique tells synthesis that it never will.
- Follow-ups
-
- What may synthesis do with the promise, and why is that faster?
- How would you show that the select really is one-hot?
- The select is all zeros when the block is idle. Which keyword fits then?
From the lesson Combinational logic with assign and always_comb
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A counter must clear even while its clock is stopped, for example before the clock source has started. Which reset style does that need, how do you write it, and what new problem does releasing the reset bring?
- What it tests
- Asynchronous resets in RTL, why the reset is tested first, why the release is the hard part, and the synchronizer that makes the release safe.
- First step
- A synchronous reset needs a clock edge to act, so with no clock it never clears anything. The requirement calls for an asynchronous reset, named in the event control, always_ff @(posedge clk or negedge rst_n), and tested first in the block.
- Follow-ups
-
- Why is the release of an asynchronous reset the dangerous moment?
- How does a reset synchronizer help, and what does it look like?
- What changes on an FPGA that loads start values from its bitstream?
From the lesson Sequential logic, always_ff and nonblocking
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A 3-stage pipeline written in one always_ff block with blocking assignments passes a test that holds its input steady, then fails in the system, where results arrive two cycles early. Explain what was built, why the test missed it, and how you fix it and guard against it.
- What it tests
- What blocking assignments do in a clocked block, why a steady input hides it, how to read what synthesis built, and the lint rule that catches it.
- First step
- Follow one value through the block at one edge. With =, the second stage reads the first stage's new value at once, and the third reads the second's, so the value passes all three lines in one edge: the three stages are one flip-flop, and results arrive after one cycle instead of three.
- Follow-ups
-
- Why did the test with a steady input pass?
- How would you show what synthesis built?
- Is there an order of blocking lines that works, and should you use it?
From the lesson Sequential logic, always_ff and nonblocking
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A testbench passes on Icarus Verilog, but on Verilator the design's registered outputs seem to change one cycle early. The testbench changes the design's inputs with = right after @(posedge clk). What is going on, how do you prove it, and how do you change the testbench so that every simulator gives the same result?
- What it tests
- Recognising a testbench-to-design race from its symptoms, the scheduling rule behind it, proving it by changing only what the standard leaves open, and fixes that hold on any simulator.
- First step
- Look at the rising edge. The testbench's block and the design's flip-flops wake together in the Active region, and the testbench writes the inputs at once with =. A flip-flop that runs after that write reads the new value and seems to load it a cycle early; one that runs before reads the old value. Which happens is the simulator's choice.
- Follow-ups
-
- How can you show it is a race and not a bug in one simulator?
- Why did the test pass on one simulator at all?
- Which fix would you choose for a large testbench, and why?
From the lesson The event scheduler and race conditions
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
To stop failures that come and go, a colleague puts #0 in front of every input assignment the testbench makes at the clock edge, and the failures disappear. Is the testbench now correct? Explain it with the scheduling regions, and say what you would do instead.
- What it tests
- What #0 and the Inactive region do, why a race moves instead of disappearing, and choosing fixes that hold for every reader of a signal.
- First step
- Place the write in the regions. #0 suspends the block until the Active region is empty, so blocks that read in the Active region, such as the design's flip-flops, now see the old value in every order. Then ask who else reads those inputs after waiting #0 themselves: blocks resumed from the Inactive region run in an order the standard leaves open.
- Follow-ups
-
- Which readers still race with a #0 write?
- Why is a nonblocking write in the testbench enough?
- How would you stop new #0 fixes from entering the testbench?
From the lesson The event scheduler and race conditions
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Fresher to mid
A FIFO sizes its read and write pointers as logic [$clog2(DEPTH)-1:0] and passes its tests for DEPTH = 16 and 32. A team instantiates it with DEPTH = 1 as a one-entry buffer, and it misbehaves. What went wrong, and how would you fix the FIFO and its tests?
- What it tests
- What $clog2 returns at its edge cases, what a negative range declares, guarding a width in the parameter list, and testing every value a parameter allows.
- First step
- Work the declaration out for DEPTH = 1. $clog2(1) is 0, so the pointers become [-1:0], a legal range two bits wide that some tools accept without a warning. Pointers that count 0, 1, 2 and 3 in a one-entry buffer read and write outside its single word. Then ask what a one-entry FIFO should build.
- Follow-ups
-
- Why did neither simulator stop on the declaration?
- How would you guard the width, and what should DEPTH = 1 build?
- Which parameter values should the regression of a FIFO cover?
From the lesson Parameters, generate blocks and packages
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A block's WIDTH parameter must be a multiple of 8 and at most 512. How do you make a wrong value fail as early as possible, on every tool your team uses, and what can stop such a check from firing?
- What it tests
- Elaboration-time checks with a generate if and $error, how each tool reports them, flows that turn them into warnings, and testing the check itself.
- First step
- Put the rule where elaboration evaluates it: a generate if on the parameter, with an $error that states the rule, so no simulation or synthesis starts from a bad value. Then check how each tool in the flow reports it: Icarus Verilog stops with an elaboration error, while Verilator reports USERERROR as a warning, which -Wno-fatal lets through.
- Follow-ups
-
- Why a generate if rather than an assertion at time 0?
- How do you know the check works?
- What about synthesis tools?
From the lesson Parameters, generate blocks and packages
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
The RTL of a design that joins its blocks through a SystemVerilog interface passes every test, but its synthesised netlist does nothing in gate-level simulation, although synthesis finished without an error. What do you suspect, how do you confirm it, and how do you stop it from reaching the chip?
- What it tests
- Knowing that synthesis front ends support different parts of SystemVerilog, reading a synthesis log, comparing front ends, and checking a netlist against its RTL.
- First step
- Suspect the front end before the design: the RTL works, so the netlist is not what the RTL describes, and interfaces are where front ends differ most. Read the synthesis warnings for wires with no driver or implicitly declared wires named after the interface's signals, and compare the cell and flip-flop counts with a second front end.
- Follow-ups
-
- Why did synthesis not stop with an error?
- How would you find the construct that caused it?
- Which check in the flow catches this class of bug best?
From the lesson Structs, enums, unions and interfaces
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
Firmware writes an RGB565 pixel register as (b << 11) | (g << 5) | r, while the RTL declares typedef struct packed { logic [4:0] r; logic [5:0] g; logic [4:0] b; } rgb565_t. Red and blue come out swapped. Explain the rule behind it, fix it, and keep the two sides from drifting apart.
- What it tests
- The bit layout of a packed struct, working a register value through both layouts, choosing which side to fix, and test values that can see a swap.
- First step
- Lay out both words. In a packed struct the first field is the most significant, so the RTL expects red in bits 15 to 11 and blue in 4 to 0. The firmware puts blue at the top and red at the bottom, so a pure red, 001f in its layout, reaches the hardware as blue 31.
- Follow-ups
-
- Why did only red and blue swap, and not green?
- How would a union help, or not?
- Which test values would have caught it?
From the lesson Structs, enums, unions and interfaces
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A 1K x 32 lookup table on an iCE40 synthesises into thousands of flip-flops and LUTs instead of block RAM, and the design no longer fits. What do you look for in the RTL, and how do you fix it without changing what the logic computes?
- What it tests
- How synthesis maps a memory by its read style and the target family, reading a synthesis report, and pipelining a read so that it fits a block RAM.
- First step
- Look at how the table is read. A block RAM reads only on a clock edge, so a read whose data is used in the same cycle, through an assign or an always_comb, forces synthesis into LUT RAM, and the iCE40 has none, which leaves flip-flops. In the lesson the same 256 x 8 RAM took one SB_RAM40_4K with a registered read and 4,150 cells with a combinational one.
- Follow-ups
-
- How do you confirm what synthesis built?
- The logic needs the data in the same cycle as the address. What now?
- Would a reset on the read register change the result?
From the lesson Arrays, memories and $readmemh
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A design's ROM is loaded by $readmemh in an initial block. It works in simulation and on the FPGA, and the team now ports the design to a chip built from standard cells. What changes, and how do you keep one source of the contents for both targets?
- What it tests
- Knowing that an initial block and $readmemh give a memory its contents only where the target can load them, how ROMs are built on a chip, and keeping the FPGA and chip versions consistent.
- First step
- Ask where the contents come from at power-up. On an FPGA, synthesis reads the file and the configuration writes the words into the block RAM; a chip has no configuration step, so an initial block builds nothing. The contents must become hardware: logic generated from the file, or a ROM macro from the foundry's memory compiler.
- Follow-ups
-
- How would you turn the hex file into logic for the chip?
- How do you prove both versions hold the same words?
- What about RAM that the FPGA initialises to zero?
From the lesson Arrays, memories and $readmemh
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
All Digital Design & Verification (Verilog, SystemVerilog, VLSI) interview questions · The Digital Design & Verification (Verilog, SystemVerilog, VLSI) track