Resolving record with enumerated type

Discussion in 'VHDL' started by Analog_Guy, Sep 15, 2006.

  1. Analog_Guy

    Analog_Guy Guest

    I am trying to implement a transaction based client/server testbench
    using records, but am having difficulty getting to the bottom of my
    compilation errors regarding my transaction records (which contain an
    enumerated type).

    ******Signal 'cpu_cmd' has multiple drivers but is not a resolved
    signal.

    I am trying to base the testbench on the approaches taken by Janick
    Bergeron (code snippets), Jim Lewis (records of INOUT with only
    STD_LOGIC) and Ben Cohen (records of either IN or OUT with various
    element types). Each presents a slightly different case, but I am
    trying to come to my own understanding of the testbench structure.

    I am wanting to use records of INOUT with all STD_LOGIC elements,
    except for one element which is an enumerated type.

    In a CPU BFM package, I have defined the following:

    TYPE CPU_INSTR_TYPE IS (CPU_READ, CPU_WRITE);
    --
    TYPE CPU_CMD_TYPE IS RECORD
    instr : CPU_INSTR_TYPE;
    addr : STD_LOGIC_VECTOR(0 TO 15);
    data : STD_LOGIC_VECTOR(0 TO 15);
    END RECORD CPU_CMD_TYPE;
    --
    SIGNAL cpu_cmd : CPU_CMD_TYPE;

    Essentially, this record communicates between a test control entity and
    a CPU BFM, and is defined as INOUT at the top-level (i.e. for both of
    the modules described). With the above method, the code compiles, but
    when I load the testbench in ModelSim I receive the "multiple drivers"
    error.

    I have tried eliminating the global signal declaration that appears
    above, and defining cpu_cmd at the top-level, but that doesn't make
    much of a difference. Instead, I get the same error during
    compilation.

    I am assuming the fact that CPU_INSTR_TYPE is unresolved and the
    records are INOUT is leading to the 'multiple drivers' error.

    Are there any suggestions to get to the bottom of this issue? Do I
    have to separate the 'instr' element into a separate record (doesn't
    seem to be as clean)? If I want mixed elements in a record am I going
    to have to separate the record elements into an IN record and an OUT
    record?
     
    Analog_Guy, Sep 15, 2006
    #1
    1. Advertising

  2. Analog_Guy

    KJ Guest

    Analog_Guy wrote:
    > I am trying to implement a transaction based client/server testbench
    > using records, but am having difficulty getting to the bottom of my
    > compilation errors regarding my transaction records (which contain an
    > enumerated type).
    >
    > In a CPU BFM package, I have defined the following:
    >
    > TYPE CPU_INSTR_TYPE IS (CPU_READ, CPU_WRITE);
    > --
    > TYPE CPU_CMD_TYPE IS RECORD
    > instr : CPU_INSTR_TYPE;
    > addr : STD_LOGIC_VECTOR(0 TO 15);
    > data : STD_LOGIC_VECTOR(0 TO 15);
    > END RECORD CPU_CMD_TYPE;
    > --
    > SIGNAL cpu_cmd : CPU_CMD_TYPE;

    <snip>
    > Essentially, this record communicates between a test control entity and
    > a CPU BFM, and is defined as INOUT at the top-level (i.e. for both of
    > the modules described). With the above method, the code compiles, but
    > when I load the testbench in ModelSim I receive the "multiple drivers"
    > error.


    The cpu_cmd signal can not be an inout of more than one entity....and
    what you've said (I think) is that it is an 'inout' of two
    modules....and even if that's not what you meant, that is the complaint
    that the simulator is giving you...hence there are multiple
    drivers....even if only certain elements of 'cpu_cmd' are driven from
    only one entity. For example, cpu_cmd.instr comes out of entity #1;
    cpu_cmd.addr and cpu_cmd.data come out of entity #2. Doesn't matter.
    It's an error because
    - cpu_cmd is the signal
    - cpu_cmd is not a resolved signal
    - cpu_cmd is an inout (therefore an output) of more than one entity.

    You don't want to lump what will be inputs and outputs of various
    entities into a single record and try to make what appears to be a
    'cleaner' implementation. Things start to choke as soon as you need to
    type in the VHDL mode and realize that's it's not an 'in', it's not an
    'out'....hmmm, must be an 'inout' then. The day of reckoning comes
    when you stitch those components together and can't simulate because of
    the multiple drivers.

    KJ
     
    KJ, Sep 15, 2006
    #2
    1. Advertising

  3. Analog_Guy

    Amal Guest

    Until we get record element directions (something similar to
    SystemVerilog interfaces), I suggest you to separate the elements and
    bundle them into input elements and output elements. This is not as
    clean, but it will give you less headache.

    -- Amal
     
    Amal, Sep 15, 2006
    #3
  4. Analog_Guy wrote:

    > I am trying to implement a transaction based client/server testbench
    > using records, but am having difficulty getting to the bottom of my
    > compilation errors regarding my transaction records (which contain
    > an enumerated type).
    >
    > ******Signal 'cpu_cmd' has multiple drivers but is not a resolved
    > signal.
    >
    > I am trying to base the testbench on the approaches taken by Janick
    > Bergeron (code snippets), Jim Lewis (records of INOUT with only
    > STD_LOGIC) and Ben Cohen (records of either IN or OUT with various
    > element types). Each presents a slightly different case, but I am
    > trying to come to my own understanding of the testbench structure.
    >
    > I am wanting to use records of INOUT with all STD_LOGIC elements,
    > except for one element which is an enumerated type.
    >
    > In a CPU BFM package, I have defined the following:
    >
    > TYPE CPU_INSTR_TYPE IS (CPU_READ, CPU_WRITE);
    > --
    > TYPE CPU_CMD_TYPE IS RECORD
    > instr : CPU_INSTR_TYPE;
    > addr : STD_LOGIC_VECTOR(0 TO 15);
    > data : STD_LOGIC_VECTOR(0 TO 15);
    > END RECORD CPU_CMD_TYPE;
    > --
    > SIGNAL cpu_cmd : CPU_CMD_TYPE;
    >
    > Essentially, this record communicates between a test control entity
    > and a CPU BFM, and is defined as INOUT at the top-level (i.e. for
    > both of
    > the modules described). With the above method, the code compiles,
    > but when I load the testbench in ModelSim I receive the "multiple
    > drivers" error.
    >
    > I have tried eliminating the global signal declaration that appears
    > above, and defining cpu_cmd at the top-level, but that doesn't make
    > much of a difference. Instead, I get the same error during
    > compilation.
    >
    > I am assuming the fact that CPU_INSTR_TYPE is unresolved and the
    > records are INOUT is leading to the 'multiple drivers' error.


    No, CPU_CMD_TYPE is unresolved, that's the problem. So it needs to be
    a resolved type.

    Furthermore, because data goes in two directions (request from the
    client (testbench) to the server (BFM), acknowledge and/or results
    from the server to the client), you'll need some way to orchestrate
    this.

    I have done that with two aditional record fields: req and ack.
    Ack is a boolean, req is an enumeration type, conveying the required
    action/request. It is similar to your CPU_INSTR_TYPE, with an added
    member req_none (idle state, no request).

    With req and ack a handshake can be implemented between the
    client and the server. Req and ack are also used by the resolution
    function to determine the actual value the cpu_cmd signal is going to
    get, thus the direction of the data flow.

    I've created an extra level of record elements in cpu_cmd_type:
    data and control. The latter contains the req and ack field, the
    first the data needed for the commands. Just to make the distinction
    between the purpose of the various fields. This also makes the
    implementation of this signal type for various BFMs quite similar.
    There is always a data field, but it contents varies between the
    different models.

    Some code.

    In the package:

    ----------------------------------------------------------------------
    -- Type definitions for cmd channel
    ----------------------------------------------------------------------

    -- Action to be performed
    --
    TYPE cpu_req_type IS
    (
    req_none,

    req_cpu_read,
    req_cpu_write
    );

    -- Type definition to hold the data for a request of a setting.
    -- Used in cpu_cmd_utype.
    --
    TYPE cpu_data_type IS
    RECORD
    addr : std_logic_vector(15 DOWNTO 0);
    data : std_logic_vector(15 DOWNTO 0);
    END RECORD cpu_data_type;

    -- Type definition to hold the control fields of the
    -- communication channel between the server and client.
    -- Used in cpu_cmd_utype.
    --
    TYPE cpu_control_type IS
    RECORD
    req : cpu_req_type;
    ack : boolean;
    END RECORD cpu_control_type;

    -- Type definition for the signal wich will serve as the
    -- communication channel to and from the server
    --
    TYPE cpu_cmd_utype IS
    RECORD
    data : cpu_data_type;
    control : cpu_control_type;
    END RECORD cpu_cmd_utype;

    -- Type must be a resolved type, because client end server will both
    -- drive the communication channel. This also makes it possible to
    -- have more than one client (an extra prio field in
    -- cpu_control_type could be needed though).
    --
    TYPE cpu_cmd_arr_utype IS ARRAY(integer RANGE <>) OF cpu_cmd_utype;
    FUNCTION resolve_cpu_cmd
    (
    s : cpu_cmd_arr_utype
    ) RETURN cpu_cmd_utype;
    SUBTYPE cpu_cmd_type IS resolve_cpu_cmd cpu_cmd_utype;


    In the package body:

    ----------------------------------------------------------------------
    -- Resolution function for cpu_cmd_utype
    ----------------------------------------------------------------------
    --
    -- This function makes it possible to use a single signal for
    -- sending requests from the client to the server, and sending data
    -- from the server to the client. Having more than one client is
    -- also made possible by this function.
    --
    FUNCTION resolve_cpu_cmd
    (
    s : cpu_cmd_arr_utype
    ) RETURN cpu_cmd_utype IS
    VARIABLE result : cpu_cmd_utype;
    VARIABLE active_req_drivers : natural;
    VARIABLE active_ack_drivers : natural;
    BEGIN
    -- Evaluate all signal drivers. Data from the client (req)
    -- takes precedence.
    --
    FOR i IN s'RANGE LOOP
    ASSERT s(i).control.req = req_none OR NOT s(i).control.ack
    REPORT "resolve_com: What are you, a client or a server?"
    SEVERITY failure;

    IF s(i).control.req /= req_none THEN
    inc(active_req_drivers);
    result := s(i); -- Data direction: from client to server
    END IF;

    IF s(i).control.ack THEN
    inc(active_ack_drivers);
    IF active_req_drivers = 0 THEN
    result := s(i); -- Data direction: from server to client
    END IF;
    END IF;
    END LOOP;

    -- Concurrent requests or acknowledges should not occur
    --
    ASSERT active_req_drivers <= 1
    REPORT "resolve_com: More than one request active!"
    SEVERITY failure;
    ASSERT active_ack_drivers <= 1
    REPORT "resolve_com: More than one acknowlegde active!"
    SEVERITY failure;

    -- More than one simultaneous request (or acknowledge) should
    -- result in no request (or acknowledge) at all. That way,
    -- undeterministic results are avoided.
    --
    IF active_req_drivers > 1 THEN
    result.control.req := req_none;
    END IF;
    result.control.ack := active_ack_drivers = 1;

    RETURN result;
    END FUNCTION resolve_cpu_cmd;


    The hanshake procedure between the client and server is as follows:

    The client makes a request by assigning the appropriate data fields
    and setting control.req to the required action. It then waits on the
    ack of the server. After receiving the ack, the client sets
    control.req to req_none. This can be wrapped in a general procedure.

    PROCEDURE do_req
    (
    CONSTANT req : IN cpu_req_type;
    SIGNAL control : INOUT cpu_control_type
    ) IS
    BEGIN
    control.req <= req;
    WAIT UNTIL control.ack;
    control.req <= req_none;

    -- Allow back-to back calls of commands. Also needed so data
    -- from the server is not overruled by data from the client.
    --
    WAIT FOR 0 ns;
    END PROCEDURE do_req;


    The server process waits on a request from the client. On receiving
    the request, it removes its own acknowledge (from a previous cycle).
    This allows the data to go from the client to the server. Then it
    executes the request (or as I usually do: hand the request to the
    actual process that implements the BFM). If the request has been
    executed, the server places the data to be sent back (if any) on the
    data field of the cpu_cmd_type signal, and sets the control.ack field
    to true, so data actually goes from the server to the client.

    So the server process looks like:

    server: PROCESS IS
    BEGIN
    -- Wait for command from client. Only then, remove the ack.
    -- Ack works as a "data from server is valid" indication, if
    -- any data is tranferred back to the client.
    --
    WAIT UNTIL cpu_cmd.control.req /= req_none;
    cpu_cmd.control.ack <= false;
    WAIT FOR 0 ns; -- For requests that do not consume time

    -- Determine the kind of the request and handle it
    --
    CASE cpu_cmd.control.req IS
    WHEN req_cpu_write =>
    trigger the BFM process with a write command and wait until
    it has finished

    WHEN req_cpu_read =>
    trigger the BFM process with a read command and wait until
    it has finished

    cpu_cmd.data.data <= read_value; -- from BFM

    WHEN req_none =>
    REPORT "Impossible choice"
    SEVERITY failure;

    END CASE;

    -- Notify client that the request has been handled and that
    -- data from the server is valid (until a new request is
    -- received).
    --
    cpu_cmd.control.ack <= true;
    END PROCESS server;


    Now you need to create the cpu_read and cpu_write procedures that will
    be actually be called from the testbench. These procedure together
    form the procedural interface of your BFM. These procedure use the
    do_req procedure. They all have a signal inout parameter of type
    cpu_cmd_type.

    The BFM (instantiated in the testbench) has an inout port of type
    cpu_cmd_type. A signal connected to this port acts as the single
    communication channel with the BFM (which was the ultimate goal).

    The cpu_read and write procedures would look like:

    PROCEDURE cpu_read
    (
    SIGNAL cmd : INOUT cpu_cmd_type;
    CONSTANT addr : IN std_logic_vector(15 DOWNTO 0);
    VARIABLE data : OUT std_logic_vector(15 DOWNTO 0)
    ) IS
    BEGIN
    cmd.data.addr <= addr;
    do_req(req_cpu_read, cmd.control);
    data := cmd.data.data;
    END PROCEDURE cpu_read;

    PROCEDURE cpu_write
    (
    SIGNAL cmd : INOUT cpu_cmd_type;
    CONSTANT addr : IN std_logic_vector(15 DOWNTO 0);
    CONSTANT data : IN std_logic_vector(15 DOWNTO 0)
    ) IS
    BEGIN
    cmd.data.addr <= addr;
    cmd.data.data <= data;
    do_req(req_cpu_write, cmd.control);
    END PROCEDURE cpu_read;

    > Are there any suggestions to get to the bottom of this issue? Do I
    > have to separate the 'instr' element into a separate record (doesn't
    > seem to be as clean)? If I want mixed elements in a record am I
    > going to have to separate the record elements into an IN record and
    > an OUT record?


    No, the resolution function takes care of all that, as you hopefully
    can see now.

    --
    Paul.
     
    Paul Uiterlinden, Sep 16, 2006
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Marek Ponca

    Enumerated Type in assertion ?

    Marek Ponca, Jan 10, 2005, in forum: VHDL
    Replies:
    2
    Views:
    3,276
    Jonathan Bromley
    Jan 10, 2005
  2. Nick Bassiliades

    Redefining an enumerated attribute type

    Nick Bassiliades, Dec 9, 2005, in forum: XML
    Replies:
    1
    Views:
    446
    Henry S. Thompson
    Dec 12, 2005
  3. Replies:
    1
    Views:
    384
    Henry S. Thompson
    Mar 6, 2006
  4. David
    Replies:
    0
    Views:
    373
    David
    Mar 1, 2006
  5. Andrew FPGA
    Replies:
    1
    Views:
    726
    amakyonin
    Jun 26, 2006
Loading...

Share This Page