pico
pico

Reputation: 1910

emulating systemverilog interfaces in VHDL using record types?

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

Upvotes: 3

Views: 787

Answers (2)

Jim Lewis
Jim Lewis

Reputation: 3973

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.

Upvotes: 0

Renaud Pacalet
Renaud Pacalet

Reputation: 29050

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'.

Upvotes: 3

Related Questions