| Backward | Up | Forward |
|---|
The software interface to SMI is defined by five classes; SMI_Channel, SMI_Model, SMI_RxMessage, SMI_TxMessage and SMI_Prototype. SMI_Channel is a singleton class i.e. one and only one object of this class is created in each test process. The SMI_Channel object is used to initialize and terminate communication between the test process and the hardware simulation. It is also used to advertise the existence of software model instances and to perform model communication. The SMI_Model class is the base class for all SMI software models. The SMI_Model class requires implementers to supply a single process() method to handle the message traffic between the hardware and software model instances. The SMI_RxMessage class encapsulates a list of message values sent from the hardware model instance to the software model instance. The SMI_TxMessage class encapsulates a list of message values sent from the software model instance to the hardware model instance. The SMI_Prototype class extends the SMI_Model class allowing objects of this class the ability to duplicate themselves.
Figure 2.3.1: SMI_Channel class
Objects of this class are used by software model processes to initiate and terminate communication with the hardware simulation process. Derived classes vary by communication method (socket or CORBA). The class uses the singleton design pattern to assure one instance per test process. This instance must be initialized prior to use by invoking the initial() class method. Once initialized, a pointer to the singleton object can be obtained from the instance() class method. Typically, test writers will only need to call initial(), post() and setExiting() methods directly. Model implementers will only need to call instance(), add(), newTxMessage() and wait() methods.
After the channel has been initialized, model instances are constructed. Each model and prototype instances must register with SMI and supply a unique identifier that matches the identifier supplied by the hardware component. All SMI model and prototype instances must be constructed and registered with add(). After all instances have been registered post() links the software models to their hardware counterparts. The test process is now ready to accept messages from the hardware simulation by calling wait().
The SMI_Channel object is destroyed when simulation terminates. The simulation exit procedure can be triggered by the hardware simulation (see $smi_exit()), the test process (see setExiting()) or by the user typing Control-C (see signal()). The simulation continues until the simulation exit procedure completes, at which time the wait() method (called frequently during a simulation) throws an SMI_Shutdown exception. At this point the connection to the hardware simulation will have been terminated and the SMI_Channel singleton destroyed.
IMPORTANT: THE initial() METHOD MUST BE CALLED ONCE, BEFORE ANY OTHER METHOD.
| SMI_Channel::initial() | |||
|---|---|---|---|
| Synopsis: | Initialize SMI communication channel | ||
| Prototype: | SMI_Channel* SMI_Channel::initial(int argc, char** argv); | ||
| Arguments: | Type | Name | Description |
| int | argc | Number of UNIX command line arguments | |
| char** | argv | Arguments from the UNIX command line | |
| Returns: | Type | Description | |
| SMI_Channel* | Pointer to the singleton instance of this class | ||
The connection from the test process to the hardware simulation is initialized by calling initial(), which should only be called once. The method initializes the singleton object representing this connection. The type of communication protocol used is selected via arguments ("argc" and "argv") passed from the UNIX command line. Two communications protocols based on CORBA and UNIX sockets are supported. The method searches the command line arguments for "-corba" and "-socket" and uses subsequent arguments to initialize connection to the hardware simulation process. Additional switches required by the CORBA implementation should be supplied by the user as necessary (see your CORBA manual). The socket implementation requires an argument specifying the UNIX port with which to connect. The argument format is [address:]number, where the optional address is the DNS or IP number of the machine running the hardware simulation, and number is the UNIX port number.
| SMI_Channel::instance() | ||
|---|---|---|
| Synopsis: | Returns a pointer to the singleton instance of this class | |
| Prototype: | SMI_Channel* SMI_Channel::instance(); | |
| Returns: | Type | Description |
| SMI_Channel* | Pointer to the singleton instance of this class after it has been initialized with initial(), otherwise undefined | |
| Note: | Must be called after the SMI_Channel object has been initialized with initial(). | |
| SMI_Channel::add() | |||
|---|---|---|---|
| Synopsis: | Associate a name and type with a model object Associate a type with a prototype object | ||
| Prototype: | void SMI_Channel::add(string name, string type,
SMI_Model* model); void SMI_Channel::add(string type, SMI_Prototype* prototype); | ||
| Arguments: | Type | Name | Description |
| string | name | Instance name of the model | |
| string | type | Name of the model/prototype type | |
| SMI_Model* | model | Pointer to the model instance | |
| SMI_Prototype* | prototype | Pointer to the prototype instance | |
| Note: | Must be called prior to calling post(). | ||
These methods are used to associate SMI_Model objects with pairs of strings that uniquely identify each model instance and to associate SMI_Prototype objects with strings that uniquely identify the type of model each prototype can generate. The strings should match the strings supplied to $smi_initial() in the instantiation of the model in the hardware simulation. All calls to add() must occur before calling post(), which advertises the list of models and prototypes to the hardware simulation.
The add() method is typically called by the model or prototype constructor which has been called from the test program. The model constructor accepts the instance "name" parameter passed in from the test program; the "type" parameter is a unique constant associated with the model/prototype class. For example:
MyModel::MyModel(const string& name)
{
SMI_Channel::instance()->add(name, "MyModel", this);
}
aslo
MyPrototype::MyPrototype()
{
SMI_Channel::instance()->add("MyModel", this);
}
| SMI_Channel::post() | |
|---|---|
| Synopsis: | Advertise software model/prototype instances to their hardware counterparts |
| Prototype: | void SMI_Channel::post(); |
| Note: | Must be invoked only once, after every model has been registered with add(). |
This method must be called once and only once. First, the test process creates a list of all models and prototypes it supports by calling add(). Typically each model and prototype instance runs add() itself during construction. After all the objects have been created and registered with add(), the test process advertises the list of models and prototypes to the hardware simulation by calling post(). The test process should not call add() or post() again. The test process may now wait for incoming messages from the hardware simulation (see wait()).
| SMI_Channel::wait() | |
|---|---|
| Synopsis: | Wait for an incoming message from the hardware simulation |
| Prototype: | void SMI_Channel::wait(); |
| Note: | Must be invoked after all model have been advertised by invoking post(). |
This method waits for an incoming message from the hardware simulation. The incoming message is passed to the process() method of the appropriate model object for processing. After the message has been process, wait() will return. The method does not guarantee that a message will be processed before it returns. However, it does guarantee that no more than one message will be processed.
This method will throw an SMI_Shutdown exception when the simulation exit procedure had completed. At this point the SMI_Channel singleton will have been destroyed, and instance() will return 0.
| SMI_Channel::newTxMessage() | ||
|---|---|---|
| Synopsis: | Allocates new, empty SMI_TxMessage object | |
| Prototype: | SMI_SMI_TxMessage* SMI_Channel::newTxMessage(); | |
| Returns: | Type | Description |
| SMI_TxMessage* | Pointer to a newly created SMI_TxMessage. | |
| Note: | The SMI_Model::process() method must return a pointer to a SMI_TxMessage object. | |
This method must be used to create all new SMI_TxMessage objects. The user is responsible for deleting SMI_TxMessage objects when the object is no longer required, though this happens rarely as SMI_TxMessage objects are reusable.
| SMI_Channel::signal() | |
|---|---|
| Synopsis: | Causes Control-C to initiate the simulation exit procedure |
| Prototype: | void SMI_Channel::signal(); |
This method installs a handler that traps Control-C interrupts. The $setExiting() task is run whenever the user hits Control-C after this method has been called.
| SMI_Channel::setExiting() | |
|---|---|
| Synopsis: | Initiate the simulation exit procedure |
| Prototype: | void SMI_Channel::setExiting(); |
| Note: | isExiting() returns true after setExiting() has been called. |
This method sets a global "exit" flag within the hardware simulation that can be polled using $smi_exiting() or $smi_poll_exiting(). When the flag is set, the simulation exit procedure cleans up the simulation (e.g. closes the waveform file) before calling $smi_finish(), which terminates all the models and test processes, followed by $finish, which ends the simulation.
The test process must continue to respond to the hardware simulation after this method has returned by repeat calls to wait().
| SMI_Channel::isExiting() | ||
|---|---|---|
| Synopsis: | Indicates whether the simulation exit procedure has been initiated | |
| Prototype: | bool SMI_Channel::isExiting(); | |
| Returns: | Type | Description |
| bool | True is the simulation exit procedure has been initiated, otherwise false. | |
| Note: | The method returns true once setExiting() has been called. | |
This method is used internally by SMI to determine the exit status of the simulation.
Figure 2.3.2: SMI_Model class
When the hardware simulation reaches a $smi_write() or a $smi_read() task, all or some of the arguments are converted to a message. This message is transmitted by the hardware model instance to the software model instance for processing. The software model instance may send a reply message back to the hardware model instance. The reply is used to set arguments of the $smi_read() or subsequent $smi_data() tasks. Software model instances are implemented by objects conforming to the SMI_Model interface. The interface specifies a process() method that is called whenever a message is received from the hardware model instance. It is this method that processes the incoming message and forms a reply message whenever appropriate.
Classes implementing SMI models must inherit from this class and implement the process() method. It is also good practice for the model to register each model instance with SMI as it is constructed by calling SMI_Channel::add() in the constructor. The test writer is responsible for creating and deleting instances of this class. Deleting SMI_Model objects before the simulation has terminated is not recommended. The test process must call SMI_Channel::wait() to allow the processing of a pending message.
| SMI_Model::process() | |||
|---|---|---|---|
| Synopsis: | Process SMI message | ||
| Prototype: | SMI_TxMessage* SMI_Model::process(SMI_RxMessage rx); | ||
| Arguments: | Type | Name | Description |
| SMI_RxMessage | rx | Incoming message from the hardware model instance. | |
| Returns: | Type | Description | |
| SMI_TxMessage* | Pointer to a reply message for transmission to the hardware model instance. | ||
| Note: | The test process must call SMI_Channel::wait() to allow the processing of a pending message. | ||
This method is called whenever a message is received from the hardware model instance. The method processes the incoming message and returns pointer to a reply message if necessary. The reply message will not be deleted but may be cleared. If the incoming message was generated by $smi_write(), any reply message returned by this method will be ignored. However if the incoming message was generated by $smi_read(), the reply message will be cleared after transmission to the hardware model instance, i.e. the message length will be set to zero.
Figure 2.3.3: SMI_RxMessage class
When the hardware simulation reaches a $smi_write() or a $smi_read() task, the hardware model instance sends a message to the software model instance. The message contains a representation of the simulation values passed as arguments to the $smi_write() and $smi_read() tasks. If these tasks have arguments numbered 0 through N, then argument 0 is the model instance identifier. For $smi_write(), the message contains the values of the arguments numbered 1 through N and no reply is expected. For $smi_read(), the message contains values of the arguments numbered 1 through N-1. A reply is expected where the first value in the reply will be used to set argument N of the $smi_read() task.
The message received by the software model instance is contained in an SMI_RxMessage object. The SMI_RxMessage object provides a container for the list of values transmitted by the hardware model instance to the software model instance. Messages are processed by the SMI_Model::process() method of the object implementing the software model instance. This method has a single argument, a reference to the SMI_RxMessage object containing the incoming message. The values contained in a SMI_RxMessage object may represent the following Verilog types: real, integer, string, signal (wire and reg). Methods are provided to access these values. A method returns the number of values stored in the message. It is the users responsibility to ensure that the access method used is appropriate for the type of value being read. No mechanism has been provided for determining the type of a value before it is read. It is also the users responsibility to ensure that a reply, in the form of a SMI_TxMessage object, is returned by SMI_Model::process() whenever necessary. No mechanism has been provided for determining whether a reply is required. Typically, the user sets the first value in the SMI_RxMessage message to be of a fixed type for all messages accepted by the model. Its value specifies the types of the values that follow it and, if a reply is expected, the types of values in the reply.
Memory management of SMI_RxMessage objects is performed by SMI. SMI_RxMessage objects must not be constructed or copied by the user.
| SMI_RxMessage::real() | |||
|---|---|---|---|
| Synopsis: | Read a "real" value from the message | ||
| Prototype: | double SMI_RxMessage::real(unsigned long index); | ||
| Arguments: | Type | Name | Description |
| unsigned long | index | Index into list of message values. | |
| Returns: | Type | Description | |
| double | Floating point representation of the "real" value stored at the index. | ||
| Exceptions: | Index out of range, i.e. index >= length(). | ||
| Type of the value is not "real". | |||
Given the Verilog task call:
$smi_write(id, 3.14, "hello", 4'b01zx, 4'b0101);The corresponding software model code:
double value(rx.real(0)); cout << "Argument[0] = " << value << endl;Will produce the following output:
Argument[0] = 3.14
| SMI_RxMessage::text() | |||
|---|---|---|---|
| Synopsis: | Read a "text" value from the message | ||
| Prototype: | string SMI_RxMessage::text(unsigned long index); | ||
| Arguments: | Type | Name | Description |
| unsigned long | index | Index into list of message values. | |
| Returns: | Type | Description | |
| string | String representation of the "text" value stored at the index. | ||
| Exceptions: | Index out of range, i.e. index >= length(). | ||
| Type of the value is not "text". | |||
Given the Verilog task call:
$smi_write(id, 3.14, "hello", 4'b01zx, 4'b0101);The corresponding software model code:
string value(rx.text(1)); cout << "Argument[1] = " << value << endl;Will produce the following output:
Argument[1] = hello
| SMI_RxMessage::signal() | |||
|---|---|---|---|
| Synopsis: | Read a signal value from the message as a string | ||
| Prototype: | string SMI_RxMessage::signal(unsigned long index); | ||
| Arguments: | Type | Name | Description |
| unsigned long | index | Index into list of message values. | |
| Returns: | Type | Description | |
| string | String representation of the signal ("wire", "reg" or "integer") value stored at the index, e.g. "zx01". One character per bit. | ||
| Exceptions: | Index out of range, i.e. index >= length(). | ||
| Type of the value is not "wire", "reg" or "integer". | |||
Given the Verilog task call:
$smi_write(id, 3.14, "hello", 4'b01zx, 4'b0101);The corresponding software model code:
string value(rx.signal(2)); cout << "Argument[2] = " << value << endl;Will produce the following output:
Argument[2] = 01zx
| SMI_RxMessage::integer() | |||
|---|---|---|---|
| Synopsis: | Read a signal value from the message as an integer | ||
| Prototype: | unsigned long SMI_RxMessage::integer(unsigned long index); | ||
| Arguments: | Type | Name | Description |
| unsigned long | index | Index into list of message values. | |
| Returns: | Type | Description | |
| unsigned long | Integer representation of the signal ("wire", "reg" or "integer") value stored at the index. | ||
| Exceptions: | Index out of range, i.e. index >= length(). | ||
| Type of the value is not "wire", "reg" or "integer". | |||
| Bit width of the value > 32, i.e. width() > 32. | |||
| Data contains one or more "X" or "Z" bits. | |||
Given the Verilog task call:
$smi_write(id, 3.14, "hello", 4'b01zx, 4'b0101);The corresponding software model code:
unsigned int value(rx.integer(3)); cout << "Argument[3] = " << value << endl;Will produce the following output:
Argument[3] = 5
| SMI_RxMessage::width() | |||
|---|---|---|---|
| Synopsis: | Read the bit width of a signal value from the message | ||
| Prototype: | unsigned long SMI_RxMessage::width(unsigned long index); | ||
| Arguments: | Type | Name | Description |
| unsigned long | index | Index into list of message values. | |
| Returns: | Type | Description | |
| unsigned long | Bit width of the signal ("wire", "reg" or "integer") value stored at the index. Integers have a width of 32. | ||
| Exceptions: | Index out of range, i.e. index >= length(). | ||
| Type of the value is not "wire", "reg" or "integer". | |||
Given the Verilog task call:
$smi_write(id, 3.14, "hello", 4'b01zx, 4'b0101);The corresponding software model code:
unsigned int width(rx.width(2)); cout << "Width[2] = " << width << endl;Will produce the following output:
Width[2] = 4
| SMI_RxMessage::length() | ||
|---|---|---|
| Synopsis: | Return the message length | |
| Prototype: | unsigned long length(); | |
| Returns: | Type | Description |
| unsigned long | Number of values in the list of values forming the message. | |
Given the Verilog task call:
$smi_write(id, 3.14, "hello", 4'b01zx, 4'b0101);The corresponding software model code:
unsigned int length(rx.length()); cout << "Length = " << length << endl;Will produce the following output:
Length = 4
Figure 2.3.4: SMI_TxMessage class
When the hardware simulation reaches a $smi_read() task, the hardware model instance sends a read message to the software model instance and waits for a reply. The reply of the software model instance is contained in an SMI_TxMessage object. The SMI_TxMessage object provides a container for a list of values to be transmitted from the software model instance back to the corresponding hardware model instance. The software model instance replies to the hardware model instance by returning a pointer to a SMI_TxMessage object containing the reply as the return value from the SMI_Model::process() method. Once the hardware model instance receives the reply, it sets the last argument of the $smi_read() task to the value of the first element in the reply. The remaining list of values are saved, so that subsequent calls to the $smi_data() task can be used to set simulation values from the remaining elements in the list. It is the users responsibility to ensure that the types of the values stored in the reply match the types of simulation values assigned by $smi_read() and $smi_data() task calls. After the contents of a SMI_TxMessage object have been transmitted to the hardware simulation, the contents of the object are cleared, i.e. the length becomes zero. This SMI_TxMessage object may now be used to construct a new message.
The values contained in a SMI_TxMessage object may represent the following Verilog types; real, integer and signal (reg only). Methods are provided to allow values to be stored by appending additional values to the list. A method returns the number of values currently stored in the list. From the software model instances perspective, a SMI_TxMessage object is a write only structure, so values may not be read back or deleted after they have been appended to the object (except by deleting the object). SMI will delete all the values stored within the object after they have been transmitted to the hardware simulation.
Memory management of SMI_TxMessage objects is performed by the user. New SMI_TxMessage objects must be constructed by invoking SMI_Channel::newTxMessage() and may not be copied.
| SMI_TxMessage::append() | |||
|---|---|---|---|
| Synopsis: | Append a value to the message | ||
| Prototype: | void append(double real); void append(string text); void append(unsigned long width, unsigned long data); | ||
| Arguments: | Type | Name | Description |
| double | real | Floating point number to be appended to the end of the message. | |
| string | text | Text representation of the signal to be appended to the end of the message, e.g. "zx01". One character per bit. | |
| unsigned long | width | Bit width of the signal to be appended to the end of the message. | |
| unsigned long | data | Value of the signal to be appended to the end of the message. | |
| Exceptions: | String "text" contains illegal characters, i.e. not in "01ZXzx". | ||
| Bit width "width" > 32 bits. | |||
Given the software model code: ("d_tx" is a pointer to an empty SMI_TxMessage)
d_tx->append(3.14);
d_tx->append("0011zzxx");
d_tx->append(32, 42);
return d_tx;
The corresponding Verilog code ("a_real" is a real, "a_reg" is a reg[7:0],
"an_int" is an integer):
$smi_read(id, ..., a_real);
$smi_data(id, a_reg, an_int);
$display("Real = %d", a_real);
$display("Reg = %b", a_reg);
$display("Integer = %d", an_int);
Will produce the following output:
Real = 3.14 Reg = 0011zzxx Integer = 42
| SMI_TxMessage::length() | ||
|---|---|---|
| Synopsis: | Return the number of values stored in the message | |
| Prototype: | unsigned long length(); | |
| Returns: | Type | Description |
| unsigned long | Number of values in the list of values forming the message. | |
The software model code: ("d_tx" is a pointer to an empty SMI_TxMessage)
d_tx->append("0011zzxx");
d_tx->append(32, 42);
cout << "Length = " << d_tx->length() << endl;
Will produce the following output:
Length = 2
Figure 2.3.5: SMI_Prototype class
This class extends the standard SMI_Model class to create classes of model instances that can duplicate themselves. The class simplifies the task of creating large numbers of instances of the same type. The user creates a single prototype instance, this prototype is registered with SMI using SMI_Channel::add(), and is then cloned by SMI whenever a new software model instance of that model type is required. Using the standard SMI_Model class would require each individual software model instance to be constructed and named by the test code. Using the SMI_Protoype class, only a single instance need by constructed by the test code.
The user of this class needs to implement the duplicate() method, which returns a duplicate model instance. SMI will invoke this method when creating a new model instance. SMI will also add the duplicate to the internal database of model instances, which will be used to destroy the models instances when the simulation terminates. The user will also need to implement two constructors; one used to create the initial prototype instance, the other used to construct duplicates.
For example, this constructor is called to construct the initial prototype and register it with SMI:
MyPrototype::MyPrototype()
{
SMI_Channel::instance()->add("MyModel", this);
}
And this constructor is called to construct the duplicate model instances and is
called by the
duplicate() method:
MyPrototype::MyPrototype(const string& name)
{}
| SMI_Prototype::duplicate() | |||
|---|---|---|---|
| Synopsis: | Create a duplicate model object | ||
| Prototype: | SMI_Prototype* duplicate(string name); | ||
| Arguments: | Type | Name | Description |
| string | name | Instance name of the new model instance | |
| Returns: | Type | Description | |
| SMI_Prototype* | Pointer to a new model instance | ||
This method is called by SMI to create a new model of the same type as the prototype. The new model is created and given the instance name passed by argument.
For example:
SMI_Prototype*
MyPrototype::duplicate(const string& name)
{
return new MyPrototype(name);
}
| Backward | Up | Forward |
|---|