DirectC API Donation
DirectC API Donation
Copyright 2002 Synopsys, Inc. Synopsys. All rights reserved. This documentation contains proprietary information that is the property of Synopsys, Inc. The Synopsys logo and VERA are registered trademarks of Synopsys, Inc. OpenVera is a trademark of Synopsys Inc. All other brands or products are trademarks of their respective owners and should be treated as such.
ii
1
The DirectC Interface 1
DirectC is an extended interface between the Verilog HDL and the C/ C++ programming languages. It is an alternative to the PLI that, unlike the PLI, enables you to do the following: More efficiently pass values between Verilog module instances and C/C++ functions by calling the functions directly, along with actual parameters, in your Verilog code. Pass more kinds of data between Verilog and C/C++. With the PLI you can only pass Verilog information to and from a C/C++ application. With DirectC you do not have this limitation.
With DirectC, for example, you can model a simulation environment for your design in C/C++ in which you can pass pointers from the environment to your design and store them in Verilog signals, then at a later simulation time pass these pointers to the simulation environment.
Similarly you can use DirectC to develop applications to run with the simulator to which you can pass pointers to the location of simulation values for your design. DirectC is an alternative to, but not a replacement for, the PLI. You can do things with the PLI that you cannot do with DirectC. For example there are PLI tf and acc routines to implement a callback to start a C/C++ function when a Verilog signal changes value. DirectC has not been implemented to also do this. You call C/C++ functions like you call (or enable) a Verilog function or Verilog task.
The DirectC Interface Making a Direct Call to a C/C++ Function 1-2 Part IV
There are two access modes for C/C++ function calls. They pertain to the development only of the C/C++ function. They are as follows: The slightly more efficient direct access mode This mode has rules for how values of different types and sizes are passed to and from Verilog and C/C++. This mode is explained in detail in Using Direct Access on page 1-10. The slightly less efficient but with better error handling abstract access mode In this implementation there is a descriptor for each actual parameter of the C/C++ function. You access these descriptors using a specially defined pointer called a handle. All formal arguments are handles. DirectC comes with a library of accessory functions for using these handles. This mode is explained in detail in Using Abstract Access on page 1-14.
The abstract access library of accessory functions contains operations for reading and writing values and for querying about argument types, sizes, etc. An alternative library, with perhaps different levels of security or efficiency, can be developed and used in abstract access without changing your Verilog or C/C++ code. Using abstract access is safer in that the library of accessory functions for abstract access have error messages to help you to debug the interface between the C/C++ and Verilog. With direct access errors simply result in segmentation faults memory corruption, etc. Abstract access is more generalizable for your C/C++ function. For example with open arrays you can call the function with eight bit arguments at one point in your Verilog design and call it again some place else with 32 bit arguments. The accessory functions can
The DirectC Interface Making a Direct Call to a C/C++ Function Part IV 1-3
manage the differences in size. With abstract access you can have the size of a parameter returned to you. With direct access you must know the size.
So C/C++ functions cannot return the value of a four state vector reg, long (longer than 32 bits) vector bit, or Verilog integer, real, realtime, or time data type. You can pass these type of
The DirectC Interface Making a Direct Call to a C/C++ Function 1-4 Part IV
values out of the C/C++ function using a parameter that you declare to be inout or output in the declaration of the function in your Verilog code.
The DirectC Interface Making a Direct Call to a C/C++ Function Part IV 1-5
Where:
extern
access_mode
Is the keyword that begins the declaration of the C/C++ function declaration. Specifies the mode of access in the declaration. Enter C for direct access, A for abstract access. Using this entry enables some functions to use direct access while others use abstract access. You typically use these entries when some functions use direct access and others use abstract access. An optional attribute for the function. The pure attribute enables some optimizations. Enter this attribute if the function has no side effects and is dependent only on the values of its input parameters. The valid return types are int, bit, reg, string, pointer, and void. See Table 1-1 for a description of what these types specify. Specifies a bit-width of a returned vector bit. A C/C++ function cannot return a four state vector reg but it can return a vector bit if its bit-width is 32 bits or less. The name of the C/C++ function. One of the following keywords: input, output, inout. These keywords specify in a C/C++ function the same thing that they specify in a Verilog task, see Table 1-2.
attribute
return_type
small_bit_vector
function_id direction
The DirectC Interface Making a Direct Call to a C/C++ Function 1-6 Part IV
arg_type [bit_width]
The valid argument types are real, reg, bit, int, pointer, string. Specifies the bit-width of a vector reg or bit that is an argument to the C/C++ function. You can leave the bit-width open by entering []. Specifies that the argument is a Verilog memory. Specifies a range of elements (words, addresses) in the memory. You can leave the range open by entering []. The Verilog register argument to the C/C++ function that becomes the actual parameter to the function.
array [index_range]
arg_id
Note: Argument direction, i.e. input, output, inout applies to all arguments that follow it until next direction occurs; the default direction is input. Table 1-1 C/C++ Function Return Types
What it specifies The C/C++ function returns a value for type int. The C/C++ function returns the value of a bit, which is a Verilog reg in two-state simulation, if it is 32 bits or less. The C/C++ function returns the value of a Verilog scalar reg. The C/C++ function returns a pointer to a character string. The C/C++ function returns a pointer. The C/C++ function does not return a value.
Return Type
The DirectC Interface Making a Direct Call to a C/C++ Function Part IV 1-7
Table 1-2
keyword
input
output inout
Table 1-3
keyword
The DirectC Interface Making a Direct Call to a C/C++ Function 1-8 Part IV
Examples
extern "A" reg return_reg (input reg r1);
This example declares a C/C++ function named return_reg. This function returns the value of a scalar reg. When we call this function the value of a scalar reg named r1 is passed to the function. This function uses abstract access.
extern "C" bit [7:0] return_vector_bit (bit [7:0] r3);
This example declares a C/C++ function named return_vector_bit. This function returns an 8-bit vector bit (a reg in two state simulation). When we call this function the value of an 8-bit bit named r3 is passed to the function. This function uses direct access. The keyword input is omitted. This keyword can be omitted if the first argument specified is an input argument.
extern string return_string();
This example declares a C/C++ function named return_string. This function returns a character string and takes no arguments.
The DirectC Interface Making a Direct Call to a C/C++ Function Part IV 1-9
You call a non-void (returns a value) C/C++ function like a Verilog function call by entering its name and arguments in an expression on the RHS of a procedural assignment statement in an always or initial block or in a Verilog task or function declaration. Examples
r2=return_reg(r1);
The value of scalar reg r1 is passed to C/C++ function return_reg. It returns a value to reg r2.
r4=return_vector_bit(r3);
The value of vector bit r3 is passed to C/C++ function return_vector_bit. It returns a value to vector bit r4.
Some of these type identifiers are standard C/C++ types, the ones that arent were defined with the following typedef statements:
typedef typedef typedef typedef unsigned int U; unsigned char UB; unsigned char scalar; struct {U c; U d;} vec32;
The type identifier you use depends on the corresponding argument direction, type, and bit-width that you specified in the declaration of the function in your Verilog code. The following rules apply: Direct access passes all output and inout arguments by reference, so their corresponding formal parameters in the C/ C++ function must be pointers. Direct access passes by value a Verilog bit only if it is 32 bits or less. Direct access passes by reference a bit if it is larger than 32 bits, so their corresponding formal parameters in the C/C++ function must be pointers if they are larger than 32 bits. Direct access passes by value a scalar reg. A vector reg direct access passes by reference, so the corresponding formal parameter in the C/C++ function for a vector reg must be a pointer. An open bit-width for a reg makes it possible for you to pass a vector reg so the corresponding formal parameter for a reg argument specified with an open bit-width must be a pointer. Similarly an open bit-width for a bit makes it possible for you to pass a bit larger than 32 bits so the corresponding formal parameter for a bit argument specified with an open bit-width must be a pointer. Direct access passes by value the following types of input arguments: int, string, and pointer. Direct access passes input arguments of type real by reference.
The following tables show the mapping from the data types you use in the C/C++ function for arguments you specify in the function declaration in your Verilog code. Table 1-4 For Input Arguments
C/C++ formal parameter data type Passed by value reference value value value value value reference reference reference
argument type
int real pointer string bit reg bit [] - 1-32 bit wide vector bit [] - open vector, any vector wider than 32 bits reg [] - 1-32 bit wide vector array [] - open vector, any vector wider than 32 bits
Table 1-5
argument type
Table 1-5
argument type
reg bit [] - any vector, including open vector reg[] - any vector, including open vector array[] - any array, 2 state or 4 state, including open array
In direct access the return value of the function is always passed by value. The data type of the returned value is the same as the input argument.
The DirectC Interface Passing Class Objects in Verilog/VeraLite and DirectC Part IV 1-13
Note that class defined in VeraLite/Verilog gets used in C as a simple struct. No member functions are available in C. Use of struct vec32 is needed since values in Verilog/VeraLite are 4-state and are represented using control and data bits. For class variables that are declared 2-valued in Verilog/VeraLite, unsigned int could be used instead of vec32.
Using vc_handle
In the function header the vc_handle for a Verilog reg, bit, or memory is based on the order that you declare the vc_handle and the order that you entered its corresponding reg, bit, or memory in the function call in your verilog code, for example, in your verilog code you declared the function and called it like so:
extern "A" void my_function( input bit [31:0] r1, input bit [32:0] r2); module dev1; Declare the function reg [31:0] bit1; reg [32:0] bit2; initial begin . . . my_function(bit1,bit2); . . . end endmodule Enter first bit1 then bit2 as arguments in the function call
There are descriptors for bit1 and bit2. These descriptors contain information about their value, but also other information such as whether they are scalar or vector, and whether they are simulation in two or four state simulation.
After declaring the vc_handles you can use them to pass data to and from these descriptors.
These routines were named to help you to remember their function. Routine names beginning with vc_get are for retrieving data from the descriptor for the Verilog argument. Routine names beginning with vc_put are for passing new values to these descriptors. These routines can convert from Verilog representation of simulation values and strings to string representation in C/C++. Strings can also be created in a C/C++ function and passed to Verilog but you should bear in mind that they can be overwritten in Verilog. So you copy them to local buffers if you want them to persist. The following are the access routines, their arguments, and return values.
int vc_is4stVector(vc_handle) This routine returns a 1 value if the vc_handle is to a vector reg. It returns a 0 value if the vc_handle is to a scalar reg, scalar or vector bit, or to a memory. int vc_is2stVector(vc_handle) This routine returns a 1 value if the vc_handle is to a vector bit. It returns a 0 value if the vc_handle is to a scalar bit, scalar or vector reg, or to a memory. int vc_width(vc_handle) Returns the width of a vc_handle. int vc_arraySize(vc_handle) Returns the number of elements in a memory. scalar vc_getScalar(vc_handle) Returns the value of a scalar reg or bit. void vc_putScalar(vc_handle, scalar) Passes by reference to a vc_handle the value of a scalar reg or bit. char vc_toChar(vc_handle) Returns the 0, 1, x, or z character. int vc_toInteger(vc_handle) Returns and int value for a vc_handle to a scalar bit or a vector bit of 32 bits or less. char *vc_toString(vc_handle) Returns a string that contains the 1, 0, x, and z characters. char *vc_toStringF(vc_handle, char) Returns a string that contains the 1, 0, x, and z characters and allows you to specify the format or radix for the display. The char argument can be b, o, d, or x.
void vc_putReal(vc_handle, double) Passes by reference a real (double) value to a vc_handle. double vc_getReal(vc_handle) Returns a real (double) value from a vc_handle. void vc_putValue(vc_handle, char *) This function passes, by reference through the vc_handle, a value represented as a string containing the 0, 1, x, and z characters. void vc_putValueF(vc_handle, char, char *) This function passes by reference through the vc_handle a value for which you specify a radix with the third parameter. The valid radixes are b, o, d, and x. void vc_putPointer(vc_handle, void*)
void *vc_getPointer(vc_handle) These functions pass by reference to a vc_handle a generic type of pointer or string. Do not use these functions for passing Verilog data (the values of Verilog signals). Use it for passing C/C++ data. vc_putPointer passes this data by reference to Verilog and vc_getPointer receives this data in a pass by reference from Verilog. You can also use these functions for passing Verilog strings. void vc_StringToVector(char *, vc_handle) Converts a C string (a pointer to a sequence of ASCII characters terminated with a null character) into a Verilog string (a vector with 8-bit groups representing characters). void vc_VectorToString(vc_handle, char *) Converts a vector value to a string value. int vc_getInteger(vc_handle) Same as vc_toInteger.
The DirectC Interface Using Abstract Access Part IV 1-19
void vc_putInteger(vc_handle, int) Passes an int value by reference through a vc_handle to a scalar reg or bit or a vector bit that is 32 bits or less. vec32 *vc_4stVectorRef(vc_handle) Returns a vec32 pointer to a four state vector. Returns NULL if the specified vc_handle is not to a four state vector reg. U *vc_2stVectorRef(vc_handle) This routine returns a U pointer to a bit vector that is larger than 32 bits. If you specify a short bit vector (32 bits or fewer) this routine returns a NULL value. vc_get4stVector(vc_handle, vec32 *)
void
void vc_put4stVector(vc_handle, vec32 *) Passes a four state vector by reference to a vc_handle to and from an array in C/C++ function. vc_get4stVector receives the vector from Verilog and passes it to the array. vc_put4stVector passes the array to Verilog. void vc_get2stVector(vc_handle, U *)
void vc_put2stVector(vc_handle, U *) Passes a two state vector by reference to a vc_handle to and from an array in C/C++ function. vc_get2stVector receives the vector from Verilog and passes it to the array. vc_put4stVector passes the array to Verilog. UB *vc_MemoryRef(vc_handle) Returns a pointer of type UB that points to a memory in Verilog. UB *vc_MemoryElemRef(vc_handle, U indx) Returns a pointer to an element (word, address or index) of a Verilog memory. You specify the vc_handle of the memory and the element.
scalar vc_getMemoryScalar(vc_handle, U indx) Returns the value of a one bit memory element. void vc_putMemoryScalar(vc_handle, U indx, scalar) Passes a value, of type scalar, to a Verilog memory element. You specify the memory by vc_handle and the element by the indx argument. int vc_getMemoryInteger(vc_handle, U indx) Returns the integer equivalent of the data bits in a memory element whose bit-width is 32 bits or less. void vc_putMemoryInteger(vc_handle, U indx, int) Passes an integer value to a memory element that is 32 bits or fewer. You specify the memory by vc_handle and the element by the indx argument. void vc_get4stMemoryVector(vc_handle, U indx, vec32 *) Copies the value in an Verilog memory element to an element in an array. This routine copies both the data and control bytes. It copies them into and array of type vec32. void vc_put4stMemoryVector(vc_handle, U indx, vec32 *) Copies Verilog data from a vec32 array to a Verilog memory element. void vc_get2stMemoryVector(vc_handle, U indx, U *) Copies the data bytes, but not the control bytes, from a Verilog memory element to an array in your C/C++ function.
void vc_put2stMemoryVector(vc_handle, U indx, U *) Copies Verilog data from a U array to a Verilog memory element. This routine is used in the previous example. void vc_putMemoryValue(vc_handle, U indx, char *) This routine works like the vc_putValue routine except that is for passing values to a memory element instead of to a reg or bit. You enter an argument to specify the element (index) you want the routine to pass the value to. void vc_putMemoryValueF(vc_handle, U indx, char, char *) This routine works like the vc_putValueF routine except that it is for passing values to a memory element instead of to a reg or bit. You enter an argument to specify the element (index) you want the routine to pass the value to. char *vc_MemoryString(vc_handle, U indx) This routine works like the vc_toString routine except that it is for passing values to from memory element instead of to a reg or bit. You enter an argument to specify the element (index) you want the routine to pass the value of. char *vc_MemoryStringF(vc_handle, U indx, char) This routine works like the vc_MemoryString function except that you specify a radix with the third parameter. The valid radixes are b, o, d, and x. void vc_FillWithScalar(vc_handle, scalar) This routine fills all the bits or a reg, bit, or memory with all 1, 0, x, or z values (you can choose only one of these four values). char *vc_argInfo(vc_handle) Returns a string containing the information about the argument in the function call in your Verilog source code.
When a routine returns Verilog data to a C/C++ function, how that data is stored depends on whether it is from a two or four state value and whether it is from a scalar, a vector, or from an element in a Verilog memory. For a four state vector (denoted by the keyword reg) the Verilog data is stored in type vec32, which for abstract access is defined as follows:
typedef unsigned int U; typedef struct { U c; U d;} vec32;
So type vec32* has two members of type U, member c is for control bits and member d is for data bits. For a two state vector bit the Verilog data is stored in type U*. Vector values are stored in arrays of chunks of 32 bits. For four state vectors there are chunks of 32 bits for data values and 32 bits for control values. For two state vectors there are chunks of 32 bits for data values.
The DirectC Interface Storing Vector Values in Machine Memory Part IV 1-23
Figure 1-1
Long vectors, more than 32 bits, have their value stored in more than one group of 32 bits and can accessed by chunk. Short vectors, 32 bits or less, are stored in a single chunk. For long vectors the chunk for the least significant bits come first, followed by the chunks for the more significant bits. Figure 1-2 Storing Vector Values of More Than 32 Bits
four state
control data control data
two state
data data data data
In an element in a Verilog memory, for each eight bits in the element there is a data byte and a control byte with an additional set of bytes for remainder bit, so if a memory had 9 bits it would need two data bytes and two control bytes. If it had 17 bits it would need three data
The DirectC Interface Storing Vector Values in Machine Memory 1-24 Part IV
bytes and three control bytes. All the data bytes precede the control bytes. For two state memories there are still data and control bytes but the bits in the control bytes always have a zero value. Figure 1-3 Storing Verilog Memory Elements in Machine Memory
data data data control control control
Converting Strings
There are no true strings in Verilog and a string literal, like "some_text," is just a notation for vectors of bits, based on the same principle as binary, octal, decimal, hexadecimal numbers. So there is a need for a conversion between the two representations of strings: the C/C++ representation (which actually is a pointer to the sequence of bytes terminated with null byte) and the Verilog vector encoding a string. DirectC comes with the following routines for string conversion:
void vc_ConvertToString(vec32 *, int, char *) Converts a Verilog string to a C string. void vc_VectorToString(vc_handle, char *) Converts a vector to a C string. void vc_StringToVector(char *, vc_handle) Converts a string to a vector.
2
Cmodules
Introducing Cmodules Cmodule Example Cmodule Instantiation Cmodule Always and Initial Blocks User-Defined Functions Scope Timescale Specification Stack Size Specification Named Events Event Control Statements Delay Statements
Introducing Cmodules
A cmodule is a way to model hardware using the significant advantages of C/C++ to model at a high level, using sophisticated constructs like multi-dimensional arrays and data structures, while still having what would be the advantages of Verilog in hardware modeling, specifically parallel processes and timing. Cmodules are another way to use C/C++ in your design or the modeled environment for your design. They also borrow a few concepts from Verilog. These borrowed concepts are: They have a Verilog-like outer appearance they have a header and ports like Verilog modules. You instantiate them using a port connection list instead of calling C/C++ functions with arguments for their formal parameters. They can contain initial and always blocks that work like Verilog initial and always blocks. These blocks are an additional level of hierarchy within the cmodule and like their Verilog counterparts are separate activity flows that execute in parallel. They can have named events and you can use them to halt activity in an initial or always block, or a function defined in a cmodule, until you trigger the named event.
They can contain event control statements that halt activity in an initial or always block, or a function defined in a cmodule, until the event expression is true. They can contain delay statements that halt activity in an initial or always block, or a function defined in a cmodule, for a certain amount of simulation time.
These are all the concepts that are borrowed from Verilog, all other actions specified in a cmodule are specified in C/C++. Most of the statements in a cmodule should be C/C++. Except for port declarations, named events, and always and initial block specifications, all declarations in the cmodule are C/C++ declarations. A cmodule is not intended to be a way to use all of Verilog and C/C++ interchangeably. When you use cmodules, just like when you call C/C++ functions using direct or abstract access, you set up a simulation in which there are two domains, one for Verilog values and one for C/C++ values. Each domain is largely invisible to the other except through the portals where values can pass back and forth between these domains: the parameters of the C/C++ functions and the ports of the cmodules. A cmodule, like a C/C++ function in direct or abstract access, is like a black box to the Verilog domain. The Verilog design has no access to the values of the variables inside the cmodule unless these values propagate out through the cmodule ports. A cmodule is intended to model hardware that has the following requirements:
The model needs to be sensitive to Verilog four-state values (0, 1, x, and z) or Verilog two-state values (0 and 1). The model is instantiated in a Verilog design, Verilog simulation values propagate into it, changing its behavior, and Verilog values propagate out of it. The model needs to use the sophisticated construct in C/C++ like multi-dimensional arrays and data structures. The model needs a certain amount of simulation time to perform its function in your design and some of its operations need to be performed in parallel. The model needs to do an extensive amount of processing of input values before it can output resulting values.
If the hardware you are modeling has these requirements, then modeling it with a cmodule is something that you should consider. Note: VCS can monitor or dump the values of ports of cmodules from the Verilog side. C/C++ variables defined in a cmodule cannot be monitored or dumped. Cmodules require a C++ compiler, since VCS will translate cmodules into C++ code. All C-compiler compile and link options can be specified under the existing -CFLAGS and -LDFLAGS options of VCS. All standard C/C++ debuggers (gdb, dbx, etc.) will be supported for debugging code in DirectC and in cmodules. The C/C++ debugger of choice will be invoked along with VirSim for concurrent debugging of Verilog, Cmodule, and C/C++ source during a debug simulation (Currently not supported).
Existing PLI 1.0 routines may be called from within direct C functions or cmodules. TF routines that handle parameters to PLI task/function are not relevant here. TF routines that handle delays, events and callbacks are disallowed (since there is better mechanism to achieve this through delay/event constructs in cmodule). Use of these functions will produce unpredictable results. No explicit error checking will be performed. ACC routines that walk the design hierarchy will treat a cmodule as an empty Verilog wrapper module with visibility only to its instantiation and ports. Internal details of the cmodule are within behavioral scope and so will not be visible to PLI 1.0 routines. You enable the use of cmodules with the +cmod compile-time option.
Cmodule Example
This section describes the parts of a cmodule definition. Lets begin with an example:
stacksize 32k timescale 1ns/1ns #include <stdio.h> #include <iostream.h> #include <string.h> #include "rom.h" #include "display.h" #define AttrLength (RS_ATTR_HI - RS_ATTR_LO ) + 1 int i, j; Rom chRom; UCHAR display_page[DisplayPageSize]; UCHAR attribute [AttrLength]; cmodule osd_test(clk, reset_l, vr_data, pixel_clk, vsync) input reg clk;
output reg reset_l; output reg [7:0] vr_data; input reg pixel_clk; input reg vsync; { vc_event wait_for_vsync; initial { int int_true = 1; printf("Starting osd_test\n"); chRom.load("/cmod-testing/ext-ex/character.rom"); @(posedge clk); reset_l = 0; for(int idx = RESET_LENGTH;idx>0;idx--){ @(posedge clk); } vc_delay(5); reset_l = int_true; @(posedge clk); i = RS_RAM_LO; rs_write rs_write rs_write rs_write (0x53, i++); (0x45, i++); (0x41, i++); (CC_RTN, i);
vc_trigger(wait_for_vsync); compare_display(); } initial { @(wait_for_vsync); @(posedge vsync); @(negedge vsync); for(int idx=40; idx>0; idx--){ @(negedge pixel_clk); } printf("Test completed after 1 video frame\n"); }
/**************************************************/ void rs_write (UCHAR data, UINT addr) { if ((addr >= RS_RAM_LO) && (addr <= RS_RAM_HI)) { display_page[addr] = data; } else if ((addr >= RS_ATTR_LO)&&(addr <= RS_ATTR_HI)) { attribute[addr - RS_ATTR_LO] = data; } vr_data = data; @(posedge clk); } /**************************************************/ void compare_display() { Screen screen; Display display; Pixel p; screen.build(); display.fill(screen, attribute[RS_HZ_DELAY - RS_ATTR_LO], attribute[RS_TOP_MARGIN - RS_ATTR_LO], 0); int x = 0; int y = 0; for (int idx=40; idx>0; idx--) { @(posedge pixel_clk); if (!(vsync.toInteger())) { p = display.get(x, y); x++; if (x >= DisplayPixelWidth) { x = 0; y++; if (y >= DisplayPixelLength) { y = 0; } } } } } } //End of cmodule
The example begins with preprocessor directives, declaring global variables and special compiler directives for cmodules: stacksize (which enables you to change the stacksize of a thread) and timescale (which is similar to the Verilog compiler directive):
stacksize 32k timescale 1ns/1ns #include <stdio.h> #include <iostream.h> #include <string.h> #include "rom.h" #include "display.h" #define AttrLength (RS_ATTR_HI - RS_ATTR_LO ) + 1 int i, j; Rom chRom; UCHAR display_page[DisplayPageSize]; UCHAR attribute [AttrLength];
See Timescale Specification on page 2-26 and Stack Size Specification on page 2-27. Next is the cmodule header:
cmodule osd_test(clk, reset_l, vr_data, pixel_clk, vsync)
The cmodule header begins with the keyword cmodule, followed by the cmodule name or identifier and then a port connection list. Notice that the cmodule header does not end with a semicolon, just like a C/C++ function. The port connection list is optional, however a cmodule is always at the leaf level of the design hierarchy (has no
hierarchy under it) and so a cmodule without ports would only be able to communicate with other cmodules and only by passing values to and from global external variables. In this example the port connection list contains ports named clk, reset_l, vr_data, pixel_clk, and vsync. After the cmodule header are the port declarations:
input reg clk; output reg reset_l; output reg [7:0] vr_data; input reg pixel_clk; input reg vsync;
Like Verilog module ports, cmodule ports have either the input, output, or output direction and the input, output, and inout keywords begin a declaration of one or more ports. Unlike Verilog module ports, cmodule ports are always registers, never nets. The keyword reg specifies a port for four-state simulation values. The keyword bit specifies a port for two-state simulation values. You can declare more than one port in a declaration if all the ports in that declaration have the same direction and size and all of them in the declaration simulate all together in two-state or all together in four-state simulation. Also, unlike Verilog ports, you must declare them in the same order that you list them in the port connection list.
Notice that, like Verilog port declarations, cmodule port declarations end with a semicolon (;). As stated earlier, having ports at all is optional, but if a cmodule header contains a port connection list then, as you might expect, the ports must be declared. After the port declarations is the beginning of the main set of braces:
{ The cmodule body goes in here. } //End of cmodule
Like the body of a C/C++ function, the body of a cmodule is always enclosed in braces. Just inside the main braces is the declaration for a named event in a cmodule.
vc_event wait_for_vsync;
A named event is similar to a named event in a Verilog module. You declare it with the vc_event keyword. You trigger the event with a pre-defined function shown later in this example. Named events are, like in Verilog module definitions, optional. For more information on named events see Named Events on page 2-28. Next is the first of two initial blocks in this example:
initial { int int_true = 1; printf("Starting osd_test\n"); chRom.load("/cmod-testing/ext-ex/character.rom"); @(posedge clk); reset_l = 0; for (int idx = RESET_LENGTH; idx>0; idx--) {
Cmodules Cmodule Example 2-10 Part IV
@(posedge clk); } vc_delay(5); reset_l = int_true; @(posedge clk); i = RS_RAM_LO; rs_write rs_write rs_write rs_write (0x53, i++); (0x45, i++); (0x41, i++); (CC_RTN, i);
vc_trigger(wait_for_vsync); compare_display(); }
Notice that unlike a Verilog initial block, in a cmodule initial block all statements are enclosed in braces {}. The different types of statements in this initial block include: A call to standard C/C++ functions:
printf("Starting osd_test\n");
This is a unique statement in a cmodule. This statement halts execution of the initial block until there is a rising edge on input port clk. This statement is the only statement that is not a C/C++ statement that you can use in a cmodule. See Event Control Statements on page 2-29.
This is another unique statement in a cmodule. This statement halts execution of the initial block for five time units (as specified by the timescale compiler directive). SeeDelay Statements on page 2-30. An assignment to an output port of the value of a local variable:
reset_l = int_true;
An assignment of a definition from a .h file that is used with this .vc file to a global external variable.
i = RS_RAM_LO;
See Pre-Defined Functions on page 2-31. Next is the second initial block:
Cmodules Cmodule Example 2-12 Part IV
initial { @(wait_for_vsync); @(posedge vsync); @(negedge vsync); for (int idx=40; idx>0; idx--) { @(negedge pixel_clk); } printf("Test completed after 1 video frame\n"); }
Notice that the event expression in the event control statement is the named event that was declared earlier with the vc_event keyword. For more information on named events see Named Events on page 2-28. Next is a function defined within the cmodule:
void rs_write (UCHAR data, UINT addr) { if ((addr >= RS_RAM_LO) && (addr <= RS_RAM_HI)) { display_page[addr] = data; } else if ((addr >= RS_ATTR_LO) && (addr <= RS_ATTR_HI)) { attribute[addr - RS_ATTR_LO] = data; } vr_data = data; @(posedge clk); }
Notice that an event control statement is in the function definition. Functions defined within a cmodule can only be called within that cmodule but you can include event control and delay statements in them. Next, and in this example last, is another function defined within the cmodule:
void compare_display() { Screen screen; Display display; Pixel p; screen.build(); display.fill(screen, attribute[RS_HZ_DELAY - RS_ATTR_LO], attribute[RS_TOP_MARGIN - RS_ATTR_LO], 0); int x = 0; int y = 0; for (int idx=40; idx>0; idx--) { @(posedge pixel_clk); if (!(vsync.toInteger())) { p = display.get(x, y); x++; if (x >= DisplayPixelWidth) { x = 0; y++; if (y >= DisplayPixelLength) { y = 0; } } } } }
This function also contains an event control statement. A partial EBNF specification for cmodule definition is as follows:
cmod_defn ::= cmodule cmod_id (port_list?) port_defn_list? { cmod_body } port_list::= port_id, port_list | port_id port_defn_list::= port_defn; port_defn_list | port_defn port_defn::= direction_id port_type port_id direction_id::= input | output | inout port_type::= (reg | bit) ([number:number])?
cmod_body::= (cmod_local_decl | cmod_always_blk | cmod_initial_blk | cmod_func_definition )* cmod_always_blk ::= always (@(event_list))? { cmod_statements } cmod_initial_blk::= initial { cmod_statements } cmod_statements::= (c_statement | @(event_list); * event_list::= simple_event | simple_event or event_list simple_event::= event_id | (posedge | negedge)? input_port_id
Bold-face identifiers and punctuation are terminals in the above productions. (In BNF terminals are entries for which there can be no further substitutions and productions are the formal term for rules.) Plain-face punctuation are the standard meta-symbols for alternation, grouping, etc.
Cmodule Instantiation
You instantiate a cmodule in a Verilog module so that it can pass values back and forth from Verilog through its ports. Cmodule instantiation is just like Verilog module instantiation. The instantiation statement begins with the cmodule identifier, followed by an instance name, followed by an order based connection list or a name based connection list. The following is an example cmodule header followed by two instantiation statements, one with a name based, the other with an order based connection list:
cmodule osd_test(clk, reset_l, vr_data, pixel_clk, vsync) osd_test vshell(.clk(top_clk),.reset_l(top_reset_l), .vr_data(top_vr_data),.pixel_clk(top_pixel_clk), .vsync(top_vsync)); osd_test vshell(top_clk,top_reset_l,top_vr_data, top_pixel_clk,top_vsync);
Also like instantiating Verilog modules you can leave a null port in the instantiation. In the above examples of instantiation statements, signal top_vr_data is connected to the cmodule through its port named vr_data. We could leave out this connection as follows:
osd_test vshell(.clk(top_clk),.reset_l(top_reset_l), .pixel_clk(top_pixel_clk),.vsync(top_vsync)); osd_test vshell(top_clk,top_reset_l, ,top_pixel_clk, top_vsync);
Just like Verilog module instantiation statements, you can include bitselects, part-selects, and concatenations in the port connection list in the cmodule instantiation statement, for example:
osd_test osd1(tclk,rst_top[7],{data1,data2[3:0]},pclk, syncher);
Also just like Verilog module instantiation statements, you can connect a register only to input ports but you can connect a net to an input, inout, or output port. Just like Verilog modules there is no limit to the number of instantiations you can make of a cmodule. Cmodule instances must be leaf-level instances, that is, they are at the bottom of their hierarchical tree and cannot contain cmodule or Verilog module instantiation statements. You can choose not to instantiate a cmodule. If you do there is no way for the cmodule to pass values to the Verilog part of your design because communication to Verilog is only through a cmodules ports. You cannot make a cross-module reference (sometimes called an
upwards name reference) to a Verilog signal from inside a cmodule or a cross module reference to a variable inside a cmodule from a Verilog module. If you choose not to instantiate the cmodule, it can still communicate with other cmodules by passing values to global variables declared outside of a cmodule.
As you can see in this syntax there is a formal sensitivity list for an always block, see The always Block Sensitivity List on page 2-18. This sensitivity list is almost the same as an event control statement (see Event Control Statements on page 2-29) but does not end with a semicolon. Cmodule always and initial blocks are an additional level of hierarchy in a cmodule. The always and initial blocks in cmodules are similar to Verilog always and initial blocks in the following ways: They execute in parallel. The order of their execution is arbitrary and cannot be specified. They also execute in parallel with the always and initial blocks in Verilog modules.
The initial blocks execute at the start of simulation and the always blocks execute continuously through out the simulation. (There are delay statement and event control mechanisms for interrupting the execution of always blocks.) An event at time zero that makes the event expression true in the sensitivity list for an always block, triggers the execution of the always block at time zero.
This is where the similarity ends between Verilog and cmodule always and initial blocks. Their differences include: Cmodule always and initial blocks begin and end with curly braces {}. There are no begin-end or fork-join blocks within them. The statements inside cmodule always and initial blocks are C/C++ statements, they are never Verilog statements. There are also special statements that you can use in these blocks, see Event Control Statements on page 2-29 and Delay Statements on page 2-30.
The sensitivity list contains an event expression that is enclosed in parentheses and can include one or more input or inout port (if more than one, separated by the or keyword) and the negedge or posedge keyword (to specify a falling or rising edge on the port, just like in Verilog). VCS does not begin to execute the always block until the event expression is true. In this example VCS does not begin to execute the always block until there is a rising edge on port inport1 or a falling edge on port inport2. Cmodule sensitivity lists differ from cmodule event control statements in that they do not end with a semicolon (;). See Event Control Statements on page 2-29.
User-Defined Functions
You can define a C/C++ function in a .vc file that contains a cmodule definition. You can define these functions inside the cmodule definition (where they are local to the cmodule) or above a cmodule definition (where they are global to all cmodules that your design uses). These functions, like initial and always blocks can include delay and event control statements, see Event Control Statements on page 2-29 and Delay Statements on page 2-30. Synopsys recommends that you use these types of statements with caution in
Cmodules User-Defined Functions Part IV 2-19
user-defined functions and only in void function because the order of execution of functions with these types of statements cannot be defined. For each function there is a separate stack for each call of the function, provided the different calls come from different concurrent blocks like different initial or always block or another userdefined function. Their local data is private to each call. Variables declared static in such a function will be shared across calls of the same function, for all cmodule instances, as per C/C++ semantics. Two concurrently executing copies of the function can overwrite the values of such shared static variables.
Scope
For cmodules there are three levels of scope:
Global Scope Variables and named events declared, and functions defined, outside and above the cmodule definition but inside the .vc file that contains the cmodule definition. This scope is equivalent to the scope of C++ variables declared globally outside any class. As in C/C++, static variables declared in this scope have visibility restricted to the source file. Cmodule Scope Variables and named events declared inside a cmodule definition but outside an always or initial block or a function defined inside a cmodule definition. Functions defined inside a cmodule definition. always and initial blocks are always at the cmodule scope, they cannot be specified in another always or initial block or a function Block or Function Scope Variables and declared inside an always or initial block or a function. Functions cannot be defined inside an always or initial block.
Global functions can do the following: Call global functions defined above them in the.vc file Access global variables declared above them in the .vc file as well as variables declared inside the global function Trigger global named events declared above them in the .vc file. (You can declare a named event in a global function, but if you do you cannot trigger it from outside the global function or use it in an event control statement in the cmodule.)
Cmodule scope functions can do the following: Call global functions defined above them in the.vc file and cmodule scope functions defined above them in the cmodule Access global variables declared above them in the .vc file, cmodule scope variables declared above them in the cmodule, as well as variables declare inside the cmodule scope function Trigger global named events declared above them in the .vc file, and cmodule scope named events declared above or below them in the cmodule. (You can declare a named event in a cmodule function but if you do you cant trigger it from outside the cmodule function or use is in an event control statement outside the cmodule function.)
always and initial blocks can: Call global functions defined above them in the.vc file and cmodule scope functions defined above or below them in the cmodule
Access global variables declared above them in the .vc file, cmodule scope variables declared above or below them in the cmodule, as well as variables declare inside the always and initial block Trigger global named events declared above them in the .vc file and cmodule scope named events declared above them in the cmodule. (You can declare a named event in an always or initial block but if you do you cannot trigger it from outside the always or initial block or use is in an event control statement outside the always or initial block.)
The following .vc files, used for an example design, show the different levels of scope:
// ex1.vc stacksize 32k timescale 1ns/1ns #include <stdio.h> int i, j; void glob_func1 (char data) { . . . } global function
global variables
global event
cmodule mycmod(clk, flag,indata,outdata) input reg clk; output reg flag; input reg [7:0] indata; output reg [7:0] outdata; cmodule event { vc_event do_it; char karak; cmodule variable
initial block variable accessing a cmodule variable calling a global function triggering a cmodule event triggering a global event
cmodule event in event control statement global event in event control statement call of a cmodule function
cmodule function
// ex2.vc stacksize 32k timescale 1ns/1ns #include <stdio.h> vc_event glob_event1,glob_event2; int j2; char k; void glob_func2(char char1) { . . .
global events in different .vc files cant have the same name
global functions in different .vc files cant have the same name
A global function can call a global function that precedes it in the .vc file
In an initial block, call of a function defined later in the cmodule In an initial block, access of a cmodule variable declared later in the cmodule
Static Variables
You can define static variables in the following scopes: Globally, outside the cmodule definition but inside the .vc file. These static variables are accessible by all functions and always and initial blocks. Global static variables can be access by functions and always and initial blocks in other .vc files if there is an extern declaration for it. At the cmodule level. These static variables are accessible by all functions and always and initial blocks inside the cmodule definition. At the block or function level. These static variables cannot be accessed from another function or always or initial block.
Timescale Specification
The possibility of delay statements in a cmodule require the ability to specify a time scale. You can use a timescale compiler directive in a .vc file, just like to timescale compiler directive in your Verilog source files, to specify the time scale and time precision for these delay statements. There is nothing different about a timescale compiler directive in a .vc file. It takes two arguments time_unit and time_precision just like it is specified in IEEE Standard 13641995 pages 225-227. For example,
timescale 10ns/1ns
This compiler directive specifies that the delay value in a delay statement is multiplied by 10 ns, rounded to the nearest 1 ns. When you enter a timescale compiler directive in a .vc file, the specified time scale and precision apply to the next cmodule definition in the .vc file. It also applies to all cmodule definitions that follow in the .vc file, and all cmodule definitions in subsequent .vc files on the command line, until VCS encounters another a timescale compiler directive. A timescale compiler directive in a Verilog source file has no effect on a cmodule definition, however a timescale compiler directive in a .vc file effects all Verilog module definitions not under a timescale compiler directive in the Verilog source files.
Where n is a decimal number and the optional suffix k specifies multiplying n by 1000. So for example stacksize 4k and stacksize 4000 are equivalent. In case of a highly recursive function, you might need to set the stacksize to a very high value. DirectC attempts to detect stack overflow but is not always successful.
Named Events
Cmodules can contain and use named events just like Verilog modules can. In cmodules they work the same way, they dont have value, they are just triggered to make some other event happen. The details of their declaration, triggering statement, and how you use them to make other events happen are different: In Verilog you declare a named event with a declaration using the event keyword. You can declare a named event inside a module definition or inside a named begin-end or fork-join block. You trigger a Verilog named event with the -> event triggering operator followed by the event name. You can specify a hierarchical name for the event. You can use a Verilog named even in an event control on a statement or block of statements to control when the statement or statements are executed. In cmodules you declare a named event using the vc_event keyword, for example:
vc_event event1, event2;
You can declare a named event at the global scope, in the .vc file but outside the cmodule definition, or at the cmodule scope inside the cmodule definition. (You can also declare them inside a global function, cmodule function, or an initial or always block, but if you do you cannot trigger or use them outside of the function or initial or always block.)
You trigger a cmodule named event with the predefined function vc_trigger(named_event), for example:
vc_trigger(event1);
You can use a named event is an event control statement. In cmodules event controls are separate statements, for example:
@(event1);
This statement halts execution of a function or an initial or always block until the event is triggered by the vc_trigger function.
In this example the execution of the always block stops until there is a change of value on port port1. Cmodule event control statements are similar to Verilog event controls in that they both contain event expressions and they halt execution until the event expression is true.
Cmodule event control statements are different from a Verilog event control in that a Verilog event control applies to the statement or block of statements that follow it, or, when used as an intraassignment timing control, controls when the value of the RHS is assigned to the LHS, and is not in and of itself a statement. In cmodules there are event control statements and you cannot apply an event control to another statement. The event expression is enclosed in parentheses, can include one or more ports or named events (but not other types of local of external variables) and can include, like Verilog, the posedge, negedge, and or keywords. You can use an event control statement in an initial or always block or in a C/C++ function that you define inside a cmodule.
Delay Statements
Delay statements halt the execution of an always or initial block, or a function defined inside a cmodule for a specific amount of simulation time. For example:
always { vc_delay(5); . . . }
The delay statement is designed to look like a predefined function call. It is not the same as a Verilog delay specification. Cmodule delay statements do not apply to other statements or constructs, instead they are individual statements of themselves.
You can use a delay statement in an initial or always block or in a C/C++ function that you define inside a cmodule.
Conditional Compilation
You can use the #ifdef, #ifndef, and #endif preprocessor directives in you cmodule file to specify identifiers for conditional compilation. You define these conditional compilation identifiers with the +cmoddefine+identifier compile-time option.
Pre-Defined Functions
There are predefined functions that enable you to control the simulation or obtain the simulation time. You can call these functions from any initial or always block or from any global or cmodule scope user-defined function.This section describes these functions:
void vc_trigger(named_event)
This function triggers a named event. The named event must be visible to the initial or always block or user-defined function that calls this function. Name events declared in other initial or always blocks or user-defined functions are not visible to a different initial or always block of user-defined function. You declare a named event with the vc_event keyword, for example:
vc_event my_named_event;
void vc_delay(number_of_time_units)
Halts the execution of the statements in an initial or always block for the specified number of time units.
void vc_finish()
Stops simulation just like the $finish system task.
double vc_time()
This function returns a double value for the entire simulation time.
Cmodule Ports
Like Verilog module ports, cmodule ports are listed in the connection list in the cmodule header and then declared separately listing their direction (input, output, or inout) and size. Both Verilog and cmodule ports can be scalar or vector ports. Unlike Verilog module ports, cmodule port declarations also specify their type and they can have two types: reg for four-state simulation bit for two-state simulation
These types are included in the port declaration. The following is an example of a cmodule header and its port declarations: In cmodules all ports are four-state or two-state registers; they are never considered nets. Assignments to them are in cmodule scope functions or in always or initial blocks. Also unlike Verilog module ports, cmodule port declarations must be in the same order as the ports are listed in the port connection list in the cmodule header. The following code shows example port declarations:
#define width 7 cmodule vector (in4,in2,out4,out2) input reg [width:0] in4; input bit in2;
In this example: The ports are listed in the following order in the cmodule header port connection list: in4 in2 out4 out2 So the port declarations for these ports are in the same order: in4 in2 out4 out2 Ports in4 and out 4 can propagate all four simulation states: 1, 0, x, and z. Ports in2 and out 2 can propagate only two simulation states: 1 and 0. Ports in2 and out2 are scalar ports and ports in4 and out4 are eight-bit vector ports.
Cmodule ports of the same direction, type, and size can be declared together, for example:
cmodule des1 (in1,in2,out1,out2) input reg [7:0] in1,in2; output reg [7:0] out1,out2; { .
. . }
There is no such thing as port coercion in cmodules. If you declare a port to be an input port, then the cmodule cannot contain an assignment statement to that port. In Verilog modules you can make an assignment to an input port, VCS will coerce it to an inout port, so that values can propagate both into and out of this port. For example:
module top; reg r1,r2; wire w1,w2; initial begin #10 r1=1; r2=r1; #100 $finish; end inst inst1(r1,w1); cinst cinst1(r2,w2); endmodule port coerced to inout module inst (in1,in2); input in1,in2; reg r2; assign #3 in2 = r2; always @ in1 #5 r2 = ~in1; endmodule Valid assignment to an input port
You can assign the value of a bit-select or part-select of a port to a variable including an inout or output port. The access functions return different data types (like int and char*) depending on what sort of information you are trying to obtain about the port (such as its size or value) and the nature of the port (such as whether it contains two- or four-state simulation values, or whether its a scalar or vector port). When obtaining the values of ports logic values are converted to int, char, and string values. In assignments of values to inout and output ports that can contain four-state simulation values, string and char values such as x/X, z/ Z, 0, and 1 will be converted to logic values X, Z, 0, and 1. In assignment to inout and output ports that contain two-state simulation values, a 1 is converted to 1 and all string and char values that are not 1 are converted to 0. The following line-numbered .vc file shows an example cmodule to show you how values are obtained and assigned:
1 #include <iostream.h> 2 #include <stdio.h> 3 4 cmodule bitselect (a,b,c,d,e,f) 5 input reg a; 6 input reg [7:0] b; 7 inout reg [7:0] c; 8 output reg d; 9 output reg [7:0] e; 10 output reg [1:0] f; 11 { 12 int i; 13 always @(b) { 14 vc_delay(10); 15 c = b.toString(); 16 i = b.toInteger() ; 17 vc_delay(10);
18 19 20 21 22 23 24 25 26 27 28 29 30 31 }
d = a.toChar(); vc_delay(10); d = b[7]; f = b.range(7,6); vc_delay(10); assigner(); vc_delay(10); e = c.toString(); } void assigner(){ d = a.toChar(); }
In the always block, on line 15, inout port c is assigned the value of input port b. The access function toString() that was written for the type of port that b is, returns a string value to inout port c. On line 16 int variable i is assigned the value of input port b, converted to an integer by access function toInteger(). On line 18 output port d is assigned the value of input port a using the access function toChar(). The access function returns a char type, possibly x, z, 1, or 0 and the assignment operator converts these char values to their corresponding logic values. On line 20 output port d is assigned a bit-select of input port b. Using the square brackets [ ] to specify a bit-select also invokes a special access function to return the value of the bit. On line 21 output port f is assigned a part-select of input port b. The range() access function for this type of port returns a string with the values of the specified bits. On line 25 output port e is assigned the value of inout port c using the access function toString() that was written for this type of port.
Cmodules Accessing Port Values 2-38 Part IV
On line 29, in a cmodule function, output port d is assigned the value of input port a using the toChar access function.
Port Classes
Ports, depending on their direction, size, and type (four-state or twostate), are organized into classes. This implementation detail needs to be pointed out because the access functions that have been implemented for each class of port and error messages from these access functions sometimes refer to the classes of ports. The classes of ports are as follows:
vcInputBit vcInputBitSv vcInputBitv vcInputReg vcInputRegSv vcInputRegv vcInOutBit vcInOutBitSv vcInOutBitv vcInOutReg vcInOutRegSv vcInOutRegv vcOutputBit vcOutputBitSv vcOutputBitv
Scalar input port for two-state simulation Vector input port, 32-bits or fewer, for two-state simulation Vector input port, more than 32-bits, for two-state simulation Scalar input port for four-state simulation Vector input port, 32-bits or fewer, for four-state simulation Vector input port, more than 32-bits, for four-state simulation Scalar inout port for two-state simulation Vector inout port, 32-bits or fewer, for two-state simulation Vector inout port, more than 32-bits, for two-state simulation Scalar inout port for four-state simulation Vector inout port, 32-bits or fewer, for four-state simulation Scalar inout port for two-state simulation Scalar output port for two-state simulation Vector output port, 32-bits or fewer, for two-state simulation Vector output port, more than 32-bits, for two-state simulation
Scalar output port for four-state simulation Vector output port, 32-bits or fewer, for four-state simulation Scalar output port for two-state simulation
For each of these classes there is a set of different access functions. Each access function is unique and is implemented for a particular operation for passing data to and from a particular class of port or obtaining other information about that class of port. However access functions in different classes have the same name if they have the same purpose. For this reason, to avoid unnecessary repetitiveness, these access functions are presented in the remainder of this section, by function, instead of by class. The following are the access functions from the various classes of ports:
vcSignal getType()
Type vcSignal is a range of integers from 11 to 28. The getType() functions return a vcSignal that stands for the type of a port specified as the argument. There is, of course, an access function with this name that returns this integer in all classes. The following is an example of its use:
#include <stdio.h> #define SHORT 31 #define LONG 32 cmodule getType (in0,in1,in2,in3,in4,in5,io0,io1,io2, io3,io4,io5,out0,out1,out2,out3,out4,out5) input reg [LONG:0] in0; input reg [SHORT:0] in1; input reg in2; input bit [LONG:0] in3; input bit [SHORT:0] in4;
input bit in5; inout reg [LONG:0] io0; inout reg [SHORT:0] io1; inout reg io2; inout bit [LONG:0] io3; inout bit [SHORT:0] io4; inout bit io5; output reg [LONG:0] out0; output reg [SHORT:0] out1; output reg out2; output bit [LONG:0] out3; output bit [SHORT:0] out4; output bit out5; { initial{ printf("\n input reg in2 type is %d\n", in2.getType()); printf("\n input reg [LONG:0] in0 type is %d\n", in0.getType()); printf("\n input reg [SHORT:0] in1 type is %d\n", in1.getType()); printf("\n input bit in5 type is %d\n", in5.getType()); printf("\n input bit [LONG:0] in3 type is %d\n", in3.getType()); printf("\n input bit [SHORT:0] in4 type is %d\n", in4.getType()); printf("\n output reg out2 type is %d\n", out2.getType()); printf("\n output reg [LONG:0] out0 type is %d\n", out0.getType()); printf("\n output reg [SHORT:0] out1 type is %d\n", out1.getType()); printf("\n output bit out5 type is %d\n", out5.getType()); printf("\n output bit [SHORT:0] out4 type is %d\n", out4.getType()); printf("\n output bit out5 type is %d\n", out5.getType()); printf("\n inout reg io2 type is %d\n", io2.getType()); printf("\n inout reg [LONG:0] io0 type is %d\n",
io0.getType()); printf("\n inout reg [SHORT:0] io1 io1.getType()); printf("\n inout bit io5 type is io5.getType()); printf("\n inout bit [LONG:0] io3 io3.getType()); printf("\n inout bit [SHORT:0] io4 io4.getType()); } }
inout reg [LONG:0] io0 type is 24 inout reg [SHORT:0] io1 type is 25 inout bit io5 type is 26 inout bit [LONG:0] io3 type is 27 inout bit [SHORT:0] io4 type is 28
int toInteger()
The toInteger() functions return an integer value for a scalar port or a short (32-bits or fewer) vector port. These toInteger() functions are in all classes of ports except those for vector ports wider that 32 bits. The following is an example of a cmodule that uses these access functions.
#include <stdio.h> cmodule toInteger (in1,in2,in3,in4,in5,in6,out1,out2,out3,out4,out5,out6) input reg in1; input reg [32:0] in2; input reg [31:0] in3; input bit in4; input bit [32:0] in5; input bit [31:0] in6; output reg out1; output reg [32:0] out2; output reg [31:0] out3; output bit out4; output bit [32:0] out5; output bit [31:0] out6; { always{ @(in1 or on2 or in3 or in4 or in5 or in6); out1 = in1.toInteger(); out2 = in2.toString(); out3 = in3.toInteger();
This simple example cmodule passes values from input ports to output ports. The corresponding ports are the same size and are both declared for two-state or four-state simulation. Notice that assignment statements are for assigning values to output or output ports but that access functions are needed to obtain port values. Also notice that toInteger() functions were used for scalar and short vector ports, but different functions named toString() were needed for the ports wider that 32 bits. These functions return a 0 for a Z value and a 1 for an X value.
char toChar()
The toChar() functions return 0, 1, X, or Z characters for the values of scalar ports. These functions are only in the classes for scalar ports. The following is an example of a cmodule that uses these access functions.
#include <stdio.h> cmodule toChar (in1,in2,in3,in4,in5,in6,out1,out2,out3,out4,out5,out6) input reg in1; input reg [32:0] in2; input reg [31:0] in3; input bit in4; input bit [32:0] in5; input bit [31:0] in6; output reg out1;
output reg [32:0] out2; output reg [31:0] out3; output bit out4; output bit [32:0] out5; output bit [31:0] out6; { always{ @(in1 or in2 or in3 or in4 or in5 or in6); out1 = in1.toChar(); out2 = in2.toString(); out3 = in3.toString(); out4 = in4.toChar(); out5 = in5.toString(); out6 = in6.toString(); } }
Notice that the toChar() functions are only used with scalar ports.
always{ @(in); int l = in.getLeftRange(); int r = in.getRightRange(); int i; for (i = l; i >= r; i--){ printf("\n working on bit [%d]\n\n",i); . . . } } }
int getWord()
Data for vector ports is stored in 32-bit chunks. These functions returns an integer values that is the number of 32-bit chunks needed for a class of vector ports. The getWord() functions are in all classes for vector ports. The following cmodule uses one of these functions:
#include <iostream.h> cmodule inputLvTest (a) input reg [131:0] a ; { always @(a) { //Array of return value unsigned int* aVal = a.toArray(); cout << " Size of a = " << a.getSize() << endl; cout << " No. of 32 bit chunks = " << a.getWord() << endl; } }
char* toString()
The toString() functions return a string of characters for the values of the bits in a vector port. These functions are in all classes for vector ports. The toString() functions for four-state simulation vector ports can return x and z values. The cmodule example for the toChar() functions also uses a toString() function.
int getSize()
The getSize() functions return the number of bits in a vector port. These functions are in all classes for vector ports. The following cmodule uses a getSize() function:
#include <stdio.h> cmodule getSize (in0,out0) input reg [32:0] in0; output reg [32:0] out0; { always{ @(in0); if (in0.getSize() == out0.getSize()) out0 = in0.toString(); } }
char* range(int,int)
The range() functions return a string of characters for a part-select of a vector port. You specify the part-select with the int arguments to the functions. There is a range() function in all classes for vector ports. The range() functions for four-state simulation vector ports can return x and z values for bit with these values. The following cmodule uses one of these functions:
#include <iostream.h> cmodule range (inport,outport) input reg [7:0] inport; output reg [7:0] outport; { always{ @(inport); cout <<" Middle 4 bits of inport are " << inport.range(5,2) << endl ;
} }
If the Verilog module that instantiate this cmodule passes a value to the input port:
module test; reg [7:0] a; wire [7:0] b; range r1 (a,b); initial a = 8b10xz10xz; endmodule