System Level Design Languages:Communication between Modules
Communication between Modules
Communication between SystemC modules or concurrent processes is discussed here. First, ports and signals are described, channels and primitive channels will be discussed then, and at the end a new concept developed in SystemC 2.1, which is called sc_export, will be described.
Ports and Signals
For communicating with the outside world, a hardware module has to receive inputs and provide outputs. These inputs and outputs constitute the ports of that module. In SystemC, ports are generally defined with the sc_port class. sc_port syntax is shown in Figure 86.63.
As an example for a port, consider the partial code of Figure 86.64. In this figure, the sc_port shown is used in place of MemRead, MemWrite, and MemDataReady signals.
There are also other port definitions that are simpler than sc_port. Although sc_port is used for system level implementations, these definitions are closer to the RTL descriptions. These port definitions are sc_in<>, sc_out<>, and sc_inout<>. Examples of these port definitions, showing their syntax are shown in Figure 86.65.
sc_in, sc_out, and sc_inout have several predefined methods that are used by the processes in modules for event passing. A number of these methods are posedge_event, value_changed_event, and default_event methods belonging to the sc_in class. Note that posedge_event and negedge_event are used for special types of ports like bool. These functions are not valid for types like int or sc_int<n>.
Other classes developed for event passing in SystemC are sc_signal and sc_buffer. sc_signal is similar to SIGNAL in VHDL. sc_signals are used to connect module ports in a higher level of hierarchy in a system. They are also used when there is a need to pass events inside a module. Figure 86.66 shows an example of sc_signal declaration and usage.
There are predefined methods for sc_signal that deal with events. Examples of sc_signal methods are
posedge_event, negedge_event, and value_changed_event.
sc_buffer’s behavior is similar to sc_signal, but differs in the notification of the value_changed_event method. This method in sc_signal is notified only when a write occurs and the value of that signal differs from its previous value. In contrast, this method in sc_buffer is notified when a write occurs, even if the value of the signal remains unchanged.
Like signals in VHDL, if a value is written to a signal of type sc_signal or sc_buffer, the signal value will be changed in the next delta cycle (SC_ZERO_TIME).
Another signal type in SystemC is sc_resolved_signal. This type is used when more than one module wants to write to a signal. This type is similar to VHDL STD_LOGIC or other resolved types.
Channels
Modules in a design can communicate with each other through events. But with complex handshakings between modules, using these events becomes risky since a number of them may be missed. For this reason, designers use SystemC primitives or their custom channels for complex data communications.
SystemC primitive channels have several methods for safe use of shared data. The simplest primitive channels include the following classes:
• sc_mutex: Mutex is a shared resource to which several modules can access without any collisions. Each module must lock a mutex to use it. If at this time any other module wants to use the mutex, it must wait until the consumer module unlocks it. Modules can use SystemC mutexes (defined by sc_mutex) in two ways: blocking and nonblocking. If a module calls the blocking methods of a mutex, it will be suspended until the mutex is unlocked. In contrast, a nonblocking method will only return false if the mutex is locked. The methods for a mutex are lock for blocking lock of a mutex, try_lock for nonblocking lock, and unlock for unlocking a mutex. Figure 86.67 shows an arbiter example that uses a sc_mutex in blocking mode.
• sc_fifo: FIFO is a shared resource which simply works as a queue, meaning that the first data read from it is the first written to it. A FIFO is implemented in the sc_fifo class in SystemC. The default length of sc_fifo is 16. Like sc_mutex, there are blocking and nonblocking methods for accessing sc_fifo. Similarly, when a module wants to read from a FIFO, it will have to wait if the FIFO is empty. In the blocking mode, the module wanting to read the FIFO is suspended until it is no longer empty. In nonblocking mode the read function only returns false if the FIFO is empty and true if it is not empty. The methods implemented for sc_fifo include write for blocking write to a FIFO, nb_write for nonblocking write, read for blocking read from a FIFO, nb_read for nonblocking read, num_available for the number of written but not yet read elements, num_free for free elements (not yet written) of a FIFO, data_written_event for an event that shows the occurrence of a write operation, and data_read_event for an event that shows the occurrence of a read operation.
• sc_semaphore: Semaphore is a limited number of shared resources to which several modules can access. Semaphore is similar to mutex, but mutex has only one resource. In SystemC, semaphores are implemented in the sc_semaphore class. Several modules can access a semaphore, use one of its available resources, and free that resource. Similar to sc_fifo and sc_mutex, sc_semaphore has blocking and nonblocking modes. In the blocking mode if there are no free resources in a semaphore for a module, that module will be suspended until a resource in the semaphore becomes free. In the nonblocking mode, the function only returns false if all resources are busy. The functions of sc_semaphore are wait for blocking mode resource allocation, try_wait for nonblocking mode resource allocation, get_value for the number of available resources in a semaphore, and post to unlock a previously locked resource.
A new method of
sc_export
communication has been developed in SystemC 2.1, which is also named portless channel access. This kind of channel access is necessary when module m of Figure 86.68(a) wants to access a member function of a channel instance in module c of this figure. In this case, there must be a communication between the channel instance ch of module c with module m. This communication that is implemented with sc_export is shown in Figure 86.68(b).
In this example, the internal channel ch, in module c is connected to its communication means with other modules (c_ch) in the constructor of module c. In contrast, module m needs to use module c’s internal channel in its f function through its m_ch port. In sc_main, this communication is established by m_inst.m_ch (c_inst.c_ch); statement.
Another example of using sc_export is the memory communications in Sayeh CPU as shown in Figure 86.69. As we discussed, we can encapsulate the memory operations in a channel. When using sc_ports, we have to instantiate a channel in sc_main and use this channel instance in both memory and CPU (bind this instance to CPU and memory ports). If we instantiate our channel in the memory module, we need to use sc_export in the memory. By using this concept, the CPU can access the channel member functions directly (see Figure 86.69).
Another advantage of using sc_export is that it can be bound directly to another sc_export of a module in the hierarchy. Using sc_ports in contrast needs an explicit connection, while sc_exports do not require such connections.
Comments
Post a Comment