HDL Coder generated Verilog code for 2-D LUT block propogates X in Vivado Simulator

When generating HDL code with HDL Coder for a 2-D Look Up Table block, I observed different behavior between VHDL and Verilog for the same lookup table access:
  • In VHDL, the generated code uses
to_integer(add_cast + resize(mul_temp, 32))
which ensures that everything is resized into a 32-bit signed domain, producing a deterministic index.
  • In Verilog, however, the generated code was:
$signed({1'b0, prelookup_idx}) + alpha2_D_Lookup_Table_mul_temp_2
Here, prelookup_idx is only 2 bits, while mul_temp_2 is 35 bits signed. The result is a 35-bit signed expression, and if any bit in the operands propagates X, the whole lookup table output becomes X in Vivado simulator.
Solution
To mimic the VHDL resize behavior, the Verilog code needs explicit truncation and casting:
wire signed [31:0] add_cast_2 = $signed({{30{1'b0}}, prelookup_idx});
wire signed [31:0] mul_temp_2_resized = alpha2_D_Lookup_Table_mul_temp_2[31:0];
wire signed [31:0] idx2 = add_cast_2 + mul_temp_2_resized;
assign alpha2_D_Lookup_Table_tableout3 = alpha2_D_Lookup_Table_7[idx2[5:0]];
With this change:
  • The 35-bit value is truncated to 32 bits (matching the VHDL resize).
  • The addition is performed in a 32-bit signed domain.
  • The X propagation issue in Verilog simulation disappears.
Conclusion
There is a subtle difference between HDL Coder’s VHDL and Verilog backends regarding resize handling.
  • VHDL always produces a deterministic integer with resize + to_integer.
  • Verilog can leave the expression at a wider signed width, which increases the chance of X propagation.
Explicit truncation/casting in Verilog aligns its behavior with VHDL and resolves the simulation mismatch.
HDL Coder version is 25.1.

Answers (3)

Could you please share the sample model?
The input types and block parameters are essential for generating HDL code.
Additionally, the testbench surrounding the subsystem shown in the image helps reproduce the simulation behavior, making it easier to validate the design.

1 Comment

Hi. The code is generated from Jacobi SVD Optimized for HDL example. I will attach the generated original code and updated version by me. Relative hierarchy under example model is included in the codes. Thanks.

Sign in to comment.

Could you please share your test model along with the version of MATLAB you are using?
We tested with R2025b using the attached model (types selected based on your HDL code), but we did not observe any 'X' propagation in the generated code or testbench using HDL Coder.

4 Comments

Hi. I generated design and testbench Verilog codes with provided model with MATLAB R2025b. Exact same problem can be seen during Vivado behavioral simulation. The below figure original codes shows X propagation situation. Figure 2 shows updated code's results. X's are gone. Maybe a simulator issue. Did you do your simulation in Vivado?
Figure 1: original code, X propagation in Out1_2
Figure 2: updated code, X propagation gone in Out1_2
I'm attaching 2 Vivado projects folders that include design and simulation code files. Vivado version is v2025.1.
We have used QuestaSim/ModelSim to verify the standalone functionality of the 2D LUT block. The generated HDL code and testbench from HDL Coder are consistent and behave as expected. Next, we will evaluate the design using Vivado Simulator and share our findings if we find any issues.
Thank you for the follow-up. We’ve confirmed that a technical support case has been raised regarding this issue. The MathWorks Support Team will respond directly, and we’ll continue to follow up with you to ensure it’s resolved. The issues here involves AMD Vivado Simulator that needs additional investigation.

Sign in to comment.

Confirming this is still present in MATLAB 26.1 / HDL Coder 26.1 / Simulink 26.1 — one release newer than the R2025b / HDL Coder 25.1 reported above, so it hasn't been addressed in the interim release either.
We hit the identical pattern in Verilog generated for several 2-D T(u) (2-D Lookup Table) blocks inside a custom FEC IP core. The generated code looks like:
assign shiftLUT2_out1 = shiftLUT2_1[$signed({1'b0, prelookup_idx_3}) + shiftLUT2_mul_temp];
with shiftLUT2_mul_temp declared signed [36:0] even though the LUT's actual address range only needs 13 bits. We confirmed via waveform that both operands held fully-defined, in-range values at the exact cycle of failure, yet the array read still returned X in Vivado 24.1 — consistent with the wide signed expression propagating X described above.
Workaround that resolved it for us: rather than resizing while keeping the expression signed, we replaced the index with plain unsigned arithmetic sized to the LUT's actual address range:
assign shiftLUT2_out1 = shiftLUT2_1[{4'b0000, prelookup_idx_3} + shiftLUT2_mul_temp[12:0]];
This is value-equivalent — both operands are structurally non-negative given the model's clamped index ranges — and it eliminated the X for us across all four affected 2-D T(u) LUT reads in this subsystem (two "shift" address LUTs, two "RAM address" LUTs, same pattern in each).
Since this affects every 2-D T(u) block lowered to Verilog via this codegen path, it would help to get either a fix or a documented HDL Coder setting to avoid the wide/signed intermediate, rather than needing to hand-patch generated code after every regeneration.

Asked:

on 12 Sep 2025

Answered:

on 9 Jul 2026 at 7:06

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!