The TL;DR explanation
Multiple drivers require resolution to determine the effective value of a signal.
IEEE Std 1076-2008 6.4.2.3 Signal declarations, paragraph 8:
A signal may have one or more sources. For a signal of a scalar type, each source is either a driver (see 14.7.2) or an out, inout, buffer, or linkage port of a component instance or of a block statement with which the signal is associated. For a signal of a composite type, each composite source is a collection of scalar sources, one for each scalar subelement of the signal. It is an error if, after the elaboration of a description, a signal has multiple sources and it is not a resolved signal. It is also an error if, after the elaboration of a description, a resolved signal has more sources than the number of elements in the index range of the type of the formal parameter of the resolution function associated with the resolved signal.
sources makes resolution hierarchical -
13 Design units and their analysis, 13.1 Design units, paragraph 1:
Certain constructs are independently analyzed and inserted into a design library; these constructs are called design units. One or more design units in sequence comprise a design file.
A component that is instantiated represents an external block (3.1), a design unit is comprised of a a context clause (which may be empty) and a library unit, either a primary unit (e.g. entity declaration) or secondary unit (e.g. a matching architecture).
You can't determine all the actual drivers until elaboration. Ports are signals for this reason.
14.7.3 Propagation of signal values, 14.7.3.1 General, paragraph 5:
The kernel process determines two values for certain signals during certain simulation cycles. The driving value of a given signal is the value that signal provides as a source of other signals. The effective value of a given signal is the value obtainable by evaluating a reference to the signal within an expression. The driving value and the effective value of a signal are not always the same, especially when resolution functions and conversion functions or type conversions are involved in the propagation of signal values.
A port signal provides the effective value for all the sources (drivers and ports of mode out, inout, buffer or linkage) contributing to the effective value of that port signal. Ports can be read or evaluated when resolving the value of an elaborated net:
6.5.2 Interface objects, paragraph 9 and 10 (in part):
An interface object provides a channel of communication between the environment and a particular portion of a description. The value of an interface object may be determined by the value of an associated object or expression in the environment; similarly, the value of an object in the environment may be determined by the value of an associated interface object. The manner in which such associations are made is described in 6.5.7.
The value of an object is said to be read when one of the following conditions is satisfied:
— When the object is evaluated, and also (indirectly) when the object is associated with an interface object of the modes in, inout, or linkage.
...
The key here is that there are multiple drivers (multiple sources) for data
in your testbench -
14.7.2 Drivers, paragraph 1:
Every signal assignment statement in a process statement defines a set of drivers for certain scalar signals. There is a single driver for a given scalar signal S in a process statement, provided that there is at least one signal assignment statement in that process statement and that the longest static prefix of the target signal of that signal assignment statement denotes S or denotes a composite signal of which S is a subelement. Each such signal assignment statement is said to be associated with that driver. Execution of a signal assignment statement affects only the associated driver(s).
14.2 Elaboration of a design hierarchy, paragraph1:
The elaboration of a design hierarchy creates a collection of processes interconnected by nets; this collection of processes and nets can then be executed to simulate the behavior of the design.
and concurrent statements (including internal and external blocks) are elaborated as block statements and/or block statements and processes (11. Concurrent statements). Signal assignment is guaranteed to take place in a process statement, resolution is required with multiple sources.
That requires resolved data types with resolution functions.
4.6 Resolution functions, paragraph 1:
A resolution function is a function that defines how the values of multiple sources of a given signal are to be resolved into a single value for that signal. Resolution functions are associated with signals that require resolution by including the name of the resolution function in the declaration of the signal or in the declaration of the subtype of the signal. A signal with an associated resolution function is called a resolved signal (see 6.4.2.3).
6.4.2.3 Signal declarations, paragraph 3:
If a resolution indication appears in the subtype indication in the declaration of a signal or in the declaration of the subtype used to declare the signal, then each resolution function in the subtype is associated correspondingly with the declared signal or with a subelement of the declared signal. Such a signal or subelement is called a resolved signal.
For std_logic or std_logic elements the resolution function is provided in package std_logic_1164 in the std_logic subtype declaration. std_ulogic (the element type of std_ulogic_vector) is not a resolved type.
All this boils down to not being able to use std_ulogic_vector as the type for data
which requires a resolved element type.
You're also missing declarations for signals convst
, cs
, wr
, rd
, and shdn
.
data
. \$\endgroup\$ – Dave Tweed Jun 9 '17 at 11:20