The following notes are collected from my notes and previous semesters’ labs. I’ve removed some of
the notes that were rare/specific, if you encounter a warning that is not written in here you can
send me an email (you can find on Unilica) or write below this as a comment.
Most of the warnings in here, starts to appear as your code gets bigger. It is only natural for
you to not understand all of the reasoning in here during the first few labs. If after getting a
warning and reading here you believe that explanation in here is confusing, notify me so I can
update.
Examples are rather simple and quite similar, however they should give a starting point for you to
try on Vivado. I’ve tested the code below with Vivado 2018.3, the ones in the lab are probably
earlier versions. Although highly unlikely, in some of the examples your code might work without
any warnings. This doesn’t mean your code is correct in the earlier versions, instead Vivado
failed to diagnose.
- Multi-driven net: same variable modified in multiple always blocks. Usually either with the
reset signal being in a separate block or by sharing a variable between modules
// Even if it is stated in the assignment that 'up' and 'down' switches will not be on at the same
// time, Vivado has no way of knowing it and thus will not be able to choose which module it should
// use to assign the leds
module counter(input clock, reset, up, down, output[7:0] leds);
logic[7:0] data;
up_counter c1(clock, reset, up, data); // If 'up' switch is on increment
down_counter d1(clock, reset, down, data); // If 'down' switch is on decrement
assign leds = data;
endmodule
// A Better way. Would the below code be correct if the assignment allowed both switches to be
// on. If not, how would you fix it?
module counter(input clock, reset, up, down, output[7:0] leds);
logic[7:0] up_data;
logic [7:0] down_data;
up_counter c1(clock, reset, up, up_data);
down_counter d1(clock, reset, down, down_data);
assign leds = up ? up_data : down_data;
endmodule
- “Inferring Latch” or “XXX did not result in combinational logic”: In always_comb and always_ff, if
ther are multiple if-else paths all of the paths should lead to a case where all the left-hand
side variables are assigned. Quite critical as Vivado can delete the entire logic.
In the below code there are multiple issues:
- state is 2-bits but we are only using 3 states. It isn’t clear what to do if state becomes
2’b11. Add a default case to fix. (Even if we are not using a 4th case, Vivado requires it.
- In S0, if input is ready the next state will be S1, however it is not clear what to do if
input_ready is not true. If you want it to stay in S0 you should add an else statement.
- Similar to the above, process_data signal is only getting assigned in S1, it is not clear what
the values should be in other states.
logic [1:0] state, nextstate;
always_comb
case(state) begin
S0: begin
if (input_ready)
nextstate = S1;
end
S1: begin
process_data = 1;
nextstate = S2;
end
S2: begin
output = result;
if (input_ready)
nextstate = S1;
else
nextstate = S0;
end
end
Converting always_comb to always_latch gets rid of this warning but be cautious since
it can also hide bugs in your design.
// When the 'start' is high, save the current state of the switches, and display on the leds. At
// each clock cycle shift them according to 'direction' (0 for right, 1 for left). If 'reset' is on
// stop the whole process and wait for the next 'start' signal.
// Which variable results in latch?
module sliding_leds(input start, clock, reset, direction, input[4:0] switch, output[4:0] leds);
logic[4:0] data;
logic started;
always_comb begin
if (reset) begin
started = 0;
data = 4'b0;
end
else if (started) begin
if (direction)
data = {data[0], data[4:1]};
else
data = {data[3:0], data[4]};
end
else begin
started = start; // We will record at the first cycle 'start' is on.
data = switch;
end
end
assign leds = data;
endmodule
- always_latch/comb: cannot have a sensitivity list, they use inferred sensitivity list.
- always @*: This is a legacy from Verilog. Depending on the code this can correspond to
either _latch or _comb, but usually it hides the intention and causes bugs.
Use the corresponding SystemVerilog constructs.
- Using non-blocking assignment while expecting blocking
// An another counter module but this time it has a limit, when reached, counter wraps around
//
// This will not give any warnings but will not work as expected.
module counter(input clock, reset, trigger, input[4:0] limit, output[4:0] out);
logic[4:0] data;
always_ff @(posedge clock) begin
if (reset)
data <= 5'b0;
else if (trigger) begin
data <= data + 1'b1;
if (data == limit)
data <= 5'b0;
end
end
assign out = data;
endmodule
- Mixing blocking and non-blocking assignment can result in unpredictable behavior. Usually
always_ff is non-blocking always_comb/latch is blocking
// Same wrapping counter as the above. However, increment is made blocking so that 'if' check will
// be correct. However, mixing blocking and non-blocking code can cause more problems in larger
// designs. Paste this code in Vivado and read the warning
module counter(input clock, reset, trigger, input[4:0] limit, output[4:0] out);
logic[4:0] data;
always_ff @(posedge clock) begin
if (reset)
data <= 5'b0;
else if (trigger) begin
data = data + 1'b1;
if (data == limit)
data <= 5'b0;
end
end
assign out = data;
endmodule
- You do not call a module, it is an electronic circuit. It always runs, either use it only when
you need it (via output temp) or calculate it only when you need it (via enable signal).
// This is the same as the first example but written as if modules are function calls. You should
// get hard errors with this one. Try to fix this code using both methods suggested above
module counter(input clock, reset, up, down, output[7:0] leds);
logic[7:0] data;
always_ff @(posedge clock) begin
if (up)
up_counter(clock, reset, up, leds);
else if (down)
down_counter(clock, reset, up, leds);
end
endmodule
- set_property expects at least one object: Input/Output name mismatch between the top module and
the constraint file. All the input outpu ports should be present in the constraint file and
only variables from the top module should be written in the constraint file. If you need a
switch/led etc. in lower modules. Connect to the top module and propagate below.
- while/for-loops are almost always wrong and doesn’t work as you expect in Java. Learn the
difference since they can be extremely useful on testbenches.
Extras: These are rather specific notes/warnings and generally not encountered.
- Multiple non-blocking assignments: In this case, last one wins
always_ff @(posedge clock) begin
if (reset)
counter <= 0;
if (enable) // Note that there is no else
counter <= counter + 1; // If Both reset and enable is high, this will only increment not reset.
end
- Async logic: multiple variables in the sensitivity list usually causes problems. Since Vivado
expects the always block to be triggered at most once. To check posedge on a variable, use a
buffer and implement a RegNext logic.
- uninitialized logics: Not much of a problem if they are wires but uninitialized registers can
cause errors or worse force the variables to be inferred as wires
- Shift has lower precedence than the arithmetic or comparison operators
- Use assert in testbenches for correctness, instead of trying to figure out from the waveforms.
- It is not possible to add a delay on synthesis, use a clock divider.
- Do not use ‘reg’ or ‘wire’. Using reg does not guarantee that it will be a register.
- Inferred net: In certain cases if you use an undeclared variable (possibly because of a typo),
Xilinx will make an implicit logic declaration and give a warning. Note that inferred type will
be a single bit, so ocassionally it might work.
- Initialize on declaration: This works, but do not forget it only loads up the value upon
configuration. You should re-initialize on reset.
- Enum State declaration: FSM example in the book has an error where it specifies the bit-width
both when defining the enum and instantiating the states, leading to an array of states. Do not
copy paste it directly, it will cause problems.
- Know the difference between packed and unpacked arrays:
logic [3:0][5:0] arr, logic [3:0] arr[5:0], logic arr[3:0][5:0]
- It is always easier to have a synchronous design. Only use clock for explicit sensitivity
lists. Meaning no @(posedge clock or posedge reset)