3

I've seen people using bidirectional buses of record types to emulate the SystemVerilog Interface language structure by driving either an input or output on each field of the record.... I'm just curious why my attempt to do the same things doesn't work? Any ideas how to do this correctly?

--Attemping to emulate SystemVerilog Interfaces using VHDL Record....

----------------------------------------------
-- DUT
----------------------------------------------
library ieee;
use ieee.std_logic_1164.all;

entity dut is
    port(
        i1 : in  std_logic;
        i2 : in  std_logic;
        o1 : out std_logic;
        o2 : out std_logic
    );
end entity;

architecture beh of dut is
begin
    o1 <= '1';
    o2 <= '0';  
end architecture;   
----------------------------------------------
-- TESTBENCH
----------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_textio.all;
use std.textio.all;
    
entity testbench is
end entity;

architecture beh of testbench is

    type sverilog_interface_t is record
        field0 : std_logic;
        field1 : std_logic;
        field2 : std_logic;
        field3 : std_logic;
    end record;
    
    signal sverilog_interface : sverilog_interface_t;
    
    procedure show_bus(
        signal sverilog_interface :inout sverilog_interface_t
    ) is
        variable m :line;
    begin
        write(m, string'("task0"));
        write(m, string'(" x0:")); write(m, sverilog_interface.field0);
        write(m, string'(" x1:")); write(m, sverilog_interface.field1);
        write(m, string'(" x2:")); write(m, sverilog_interface.field2);
        write(m, string'(" x3:")); write(m, sverilog_interface.field3);
        writeline(output, m);       
    end procedure;
    
    procedure task0(
        signal sverilog_interface :inout sverilog_interface_t
    ) is
    begin
        sverilog_interface.field0 <= '0';
        sverilog_interface.field1 <= '1';   
        wait for 1 ns;      
    end procedure;
    
    procedure task1(
        signal sverilog_interface :inout sverilog_interface_t
    ) is
    begin
        sverilog_interface.field0 <= '1';
        sverilog_interface.field1 <= '0';   
        wait for 1 ns;      
    end procedure;
    
begin

dut1: entity work.dut(beh) 
    port map(
        i1 => sverilog_interface.field0,
        i2 => sverilog_interface.field1,
        o1 => sverilog_interface.field2, -- WHy 'U'? should be '0' or '1?
        o2 => sverilog_interface.field3  -- WHy 'U'? should be '0' or '1?
    );

process begin
   wait for 1 ns;
   
   show_bus(sverilog_interface);
   
   task0(sverilog_interface); 
   show_bus(sverilog_interface);
   
   task1(sverilog_interface); 
   show_bus(sverilog_interface);
      
   report "end of testcase" severity failure;
end process;

end architecture;
F:\Xilinx\Vivado\2021.2\bin\xvhdl.bat --incr --relax --2008 -f test1.vhd
F:\Xilinx\Vivado\2021.2\bin\xelab.bat testbench -snapshot simout -incr -debug all 
F:\Xilinx\Vivado\2021.2\bin\xsim.bat simout --tclbatch simout.sim.tcl --onerror quit --onfinish quit --ieeewarnings

****** xsim v2021.2 (64-bit)
  **** SW Build 3367213 on Tue Oct 19 02:48:09 MDT 2021
  **** IP Build 3369179 on Thu Oct 21 08:25:16 MDT 2021
    ** Copyright 1986-2021 Xilinx, Inc. All Rights Reserved.

## run all

task0 x0:U x1:U x2:U x3:U
task0 x0:0 x1:1 x2:U x3:U  <-- WHy are x1 and x2 'U'?
task0 x0:1 x1:0 x2:U x3:U  <-- WHy are x1 and x2 'U'?

Failure: end of testcase
Time: 3 ns  Iteration: 0  Process: /testbench/line__88  File: test1.vhd
$finish called at time : 3 ns : File "test1.vhd" Line 99
exit 0
INFO: [Common 17-206] Exiting xsim at Tue Feb 15 16:02:17 2022...
code:(0)
ok

2
  • The "old" (pre-2019) VHDL way was to use two records; one in each direction. Commented Feb 16, 2022 at 15:32
  • OSVVM uses records as an interface. Our method is described here: osvvm.org/archives/1668 "VHDL-2019 Interfaces & OSVVM Interfaces" Commented Feb 16, 2022 at 16:17

2 Answers 2

3

There are 2 drivers for the fields of your sverilog_interface: the DUT and the testbench process.

The DUT drives only the two last fields (because the mode of the others is in), to '1' and '0', respectively.

The process drives all fields because the sverilog_interface signal is associated with the inout interface declaration of your 3 procedure calls. LRM 4.2.2.3 Signal parameters:

A process statement contains a driver for each actual signal associated with a formal signal parameter of mode out or inout in a subprogram call.

The four fields are first driven to 'U', the leftmost value of the std_logic enumerated type, which is used as a default value for uninitialized std_logic signals. The two first fields are later driven to '0' or '1' values. But not the two last fields that are still driven to 'U'.

The resolution function attached to the std_logic type resolves the conflict between the DUT ('0' and '1') and the process ('U' and 'U') as 'U'.

One solution could be to explicitly drive the two last fields to 'Z' (high impedance) in your process:

process begin
   sverilog_interface.field2 <= 'Z';
   sverilog_interface.field3 <= 'Z';

   wait for 1 ns;
   ...

Value 'Z', when in conflict with anything (except '-'), resolves as the anything. Or to do this in your procedures. Or, as some do (see OSVVM), to define your own resolution function and your own resolved subtype of std_ulogic such that 'U' and anything else resolves as the anything else.

Note: your show_bus procedure does not need mode inout, better use in. And your task0, task1 procedure do not need mode inout, better use out (principle of least privilege). But these modifications alone will not solve your problem: the process is still driving the 4 fields of the sverilog_interface signal.

Note: as noted by @user16145658 another solution would be to modify the default value of sverilog_interface at declaration:

signal sverilog_interface : sverilog_interface_t := ('U', 'U', 'Z', 'Z');

The process would then drive the 2 last fields to 'Z'.

Sign up to request clarification or add additional context in comments.

3 Comments

I tried your suggestion and it fixes the problem... I guess SystemVerilog must default signals to 'z' state at time zero... but VHDL defaults them to 'U' state.... to get systemverilog behavior in VHDL you need to set singla to ''Z' initially...
Systemverilog defaults logic values to 'X'. But SV interfaces are different to VHDL's record types. They allow ports of either direction, modports and interface methods. VHDL has none of these things. VHDL 2019 introduces interfaces by introducing view, which allows the user to use a record type as a connection between two view port items, where the view can set the direction of each element of a record type.
'Z' will allow you to use std_logic family. OTOH, OSVVM uses maximum as a resolution function (unfortunately indirectly due to simulator issues) and this allows any type to be used. It also allows passing any std_logic family value. Everything by default initializes to type'left which is also the minimum value (which is like above initializing to 'Z' except type'left happens automatically)
0

This is a follow up to what Renard posted.

If you want to have some fun and stress test your simulator, you can define sverilog_interface_t as follows:

type sverilog_interface_t is record
    field0 : maximum std_ulogic;
    field1 : maximum std_ulogic;
    field2 : maximum std_ulogic;
    field3 : maximum std_ulogic;
end record;

What this does is use the function maximum [std_ulogic_vector return std_ulogic] as a resolution function. Resolution functions can be applied any place there the syntax calls for a subtype_indication. In non-LRM terms, this is also any place where you can put an array constraint on a type.

I caution that many vendors were not expecting maximum to be used as a resolution function, so where I use this technique in OSVVM, I unfortunately use a wrapper function named resolved_max to call maximum. Note that the wrapper function is strictly to maximize tool support without having to have code variations. Maximum was introduced in 2008, so it is past time for simulators to support this.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.