nvc icon indicating copy to clipboard operation
nvc copied to clipboard

Bug: Constant value for record types (NVC Simulator v1.15.2)

Open yangyt96 opened this issue 5 months ago • 1 comments


LIBRARY ieee;
USE ieee.std_logic_1164.ALL;

PACKAGE test_pkg IS

    CONSTANT C_NUM_1 : integer := 4;
    CONSTANT C_NUM_2 : integer := 2;

    TYPE t_slv_vector IS ARRAY (natural RANGE <>) OF std_logic_vector;

    TYPE rec_1 IS RECORD
        a : std_logic_vector(C_NUM_2 - 1 DOWNTO 0);
        b : std_logic_vector(1 DOWNTO 0);
    END RECORD;

    CONSTANT RST_REC_1 : rec_1 := (OTHERS => (OTHERS => '0'));

    TYPE rec_2 IS RECORD
        a : std_logic_vector(12 DOWNTO 0);
        b : std_logic_vector(31 DOWNTO 0);
        c : std_logic_vector(31 DOWNTO 0);
        d : t_slv_vector(C_NUM_1 - 1 DOWNTO 0)(31 DOWNTO 0);
        e : std_logic_vector(C_NUM_1 - 1 DOWNTO 0);
        f : t_slv_vector(C_NUM_1 - 1 DOWNTO 0)(9 DOWNTO 0);
        g : t_slv_vector(C_NUM_1 - 1 DOWNTO 0)(31 DOWNTO 0);
        h : std_logic_vector(10 DOWNTO 0);
        i : std_logic_vector(C_NUM_1 - 1 DOWNTO 0);
        j : t_slv_vector(C_NUM_1 - 1 DOWNTO 0)(31 DOWNTO 0);
        k : t_slv_vector(C_NUM_1 - 1 DOWNTO 0)(9 DOWNTO 0);
        l : t_slv_vector(C_NUM_1 - 1 DOWNTO 0)(15 DOWNTO 0);
        m : rec_1;
    END RECORD;

    CONSTANT RST_REC_2 : rec_2 := (
        d      => (OTHERS => (OTHERS => '0')),
        f      => (OTHERS => (OTHERS => '0')),
        g      => (OTHERS => (OTHERS => '0')),
        j      => (OTHERS => (OTHERS => '0')),
        k      => (OTHERS => (OTHERS => '0')),
        l      => (OTHERS => (OTHERS => '0')),
        m      => RST_REC_1,
        OTHERS => (OTHERS => '0')
    );

END test_pkg;

PACKAGE BODY test_pkg IS

END test_pkg;


Output file: /var/local/test/vunit_out/test_output/sys.test_tb.test-alive_23a14e315c62cefe3fd086f18fa92d5ddb35b2c4/output.txt
Seed for sys.test_tb.test-alive: 79a44ec5c06174dc
/opt/nvc/bin/nvc --work=sys:/var/local/test/vunit_out/nvc/libraries/sys --std=2008 --map=vunit_lib:/var/local/test/vunit_out/nvc/libraries/vunit_lib --map=sys:/var/local/test/vunit_out/nvc/libraries/sys -H 64m -e test_tb-bench '-grunner_cfg=active python runner : true,enabled_test_cases : test-alive,output path : /var/local/test/vunit_out/test_output/sys.test_tb.test-alive_23a14e315c62cefe3fd086f18fa92d5ddb35b2c4/,seed : 79a44ec5c06174dc,tb path : /var/local/test/,use_color : true' --no-save --jit -r --exit-severity=error
** Fatal: value length 13 does not match field B length 32
    > /var/local/test/test_pkg.vhd:21
    |
 21 |         b : std_logic_vector(31 DOWNTO 0);
    |         ^
   Package SYS.TEST_PKG at /var/local/test/test_pkg.vhd:5
   Process (init) at /var/local/test/test_tb.vhd:17
fail (P=0 S=0 F=1 T=1) sys.test_tb.test-alive (0.3 s)

==== Summary ==================================
fail sys.test_tb.test-alive (0.3 s)
===============================================
pass 0 of 1
fail 1 of 1
===============================================
Total time was 0.3 s
Elapsed time was 0.3 s
===============================================
Some failed!

NVC version: NVC Simulator v1.15.2

GHDL has no issue with this but NVC triggers this error.

yangyt96 avatar Jul 22 '25 09:07 yangyt96

Hi,

this occurs under the following conditions:

  1. NVC thinks the value assigned is not constant. For some reason, assigning RST_REF_1 is analysed as non-constant.
  2. The record others choice covers multiple record fields of the same array type, but of different width.

A simpler reproducer for this is:

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;

entity issue1248 is
end entity;

architecture test of issue1248 is

    TYPE rec_1 IS RECORD
        x : std_logic_vector(1 DOWNTO 0);
    END RECORD;

    TYPE rec_2 IS RECORD
        a : std_logic_vector(12 DOWNTO 0);
        b : std_logic_vector(31 DOWNTO 0);
        m : rec_1;
    END RECORD;

    signal RST_REC_2 : rec_2;
    signal B : std_logic := '1';

begin
    process
    begin
        RST_REC_2 <= (
            m      => (OTHERS => (OTHERS => '0')),

            OTHERS => (OTHERS => B)
            -- If we replace the above assign with the following, it works
            --OTHERS => (OTHERS => '0')
        );
        wait for 0 ns;

        report "A: " & to_string(RST_REC_2.a);
        report "B: " & to_string(RST_REC_2.b);

        wait;
    end process;
end architecture;

The call in lower_record_sub_aggregate taking the else if (type_is_array(ftype)) { branch ends in lower_array_aggregate, but for each field covered by others, it boils down to the same type. This is since there is only single tree_t for all record fields covered by others. It gets the type (range) solved for the first field, and therefore all the follow-up fields "inherit it".

I am not sure on what it would take to fix it. Maybe unroll all the others record aggregates during simp phase, and convert them to A_NAMED ? It seems to be too messy solution...

Blebowski avatar Nov 09 '25 18:11 Blebowski