Changes between Initial Version and Version 1 of Writing Rules/Tlmt


Ignore:
Timestamp:
Dec 24, 2007, 7:05:52 PM (16 years ago)
Author:
alain
Comment:

Legend:

Unmodified
Added
Removed
Modified
  • Writing Rules/Tlmt

    v1 v1  
     1{{{
     2#!html
     3<h1>Writing efficient TLM-T SystemC simulation models for SoCLib</h1>
     4
     5Authors : Alain Greiner, François Pécheux, Emmanuel Viaud, Nicolas Pouillon
     6 
     7}}}
     8[[PageOutline]]
     9
     10= A) Introduction =
     11
     12This manual describes the modeling rules for writing TLM-T SystemC simulation models for SoCLib.
     13Those rules enforce the PDES (Parallel Discrete Event Simulation) principles. Each PDES process involved in the
     14simulation has is own, local time, and processes synchronize through timed messages.
     15Models complying with those rules can be used with the "standard" OSCI simulation engine (SystemC 2.x), but can be used also with others simulation engines, especially distributed, parallelized simulation engines.
     16
     17Besides you may also want to follow the [WritingRules/General general SoCLib rules].
     18
     19= B) VCI Communication between a single initiator and a single target =
     20
     21Figure 1 presents a minimal system containing one single initiator, and a single target. In the proposed example, the initiator module doesn't contains any parallelism, and can be modeled by a single SC_THREAD, describing a single PDES process. The activity of the '''my_initiator ''' module is described by the SC_THREAD '''execLoop()''', that contain an infinite loop. The variable '''m_time''' represents the PDES process local time.
     22
     23Contrary to the initiator, the target module has a purely reactive behaviour. There is no need to use a SC_THREAD to describe the target behaviour : A simple method is enough.
     24
     25The VCI communication channel is a point-to-point bi-directionnal channel, encapsulating two separated uni-directionnal channels : one to transmit the VCI command packet, one to transmit the VCI response packet.
     26
     27= C) Initiator Modeling =
     28
     29In the proposed example, the initiator module is modeled by the '''my_initiator''' class. This class inherit the '''Tlmt::BaseModule'''' class, that is the basis for all TLM-T modules. As there is only one thread in my_initiator, there is only one member variable '''time'' of type '''tlmt_time'''. This object can be accessed through the '''getTime()''', '''addTime()''' and '''setTime()''' methods. The '''execLoop()''' method, describing the initiator activity must be declared as a member function of the '''my_initiator''' class.
     30
     31== C.1) Sending a VCI command packet ==
     32
     33The class '''my_initiator''' must contain a member variable '''p_vci''', of type '''VciInitiatorPort'''. This object has a template parameter <'''vci_param'''> defining the widths of the VCI ADRESS & DATA fields.
     34
     35To send a VCI command packet, the '''execLoop()''' method must use the '''cmdSend()''' method, that is a member function of the '''p_vci''' port. The prototype is the following:
     36{{{
     37void cmdSend(   vci_cmd_t       *cmd,   // VCI command packet   
     38                                sc_time         time);  // initiator local time
     39}}}
     40
     41The informations transported by a VCI command packet are defined below:
     42{{{
     43class vci_cmd_t {
     44vci_command_t   cmd;            // VCI transaction type
     45vci_address_t   *address;       // pointer to an array of addresses on the target side
     46uint32_t                        *be;                    // pointer to an array of byte_enable signals
     47bool                    contig;         // contiguous addresses (when true)
     48vci_data_t             *buf;                // pointer to the local buffer on the initiator
     49uint32_t                      length;             // number of words in the packet
     50bool                  eop;                      // end of packet marker
     51uint32_t                     srcid;                    // SRCID VCI
     52uint32_t                     trdid;                    // TRDID VCI
     53uint32_t                     pktid;                    // PKTID VCI
     54}
     55}}}
     56
     57== C.2) Receiving a VCI response packet ==
     58
     59== C.3) Initiator Constructor ==
     60
     61== C.4) Lookahead parameter ==
     62
     63== C.5) TLM-T initiator example ==
     64
     65{{{
     66template <typename vci_param>
     67class my_initiator : Tlmt::BaseModule {
     68public:
     69                VciInitiatorPort <vci_param>            p_vci;
     70               
     71//////// constructor
     72                my_initiator (  sc_module_name  name,
     73uint32_t                initiatorIndex
     74uint32_t                lookahead) :
     75                        p_vci(“vci”, this, &my_initiator::rspReceived, &m_time) ,
     76                        BaseModule(name),
     77m_time(0),
     78{
     79m_index = InitiatorIndex;
     80m_lookahed = lookahead;
     81m_counter = 0;
     82SC_THREAD(execLoop);
     83} // end constructor
     84               
     85private:
     86tlmt_Time               m_time;         // local clock
     87                uint32_t                m_index;                // initiator index
     88                uint32_t                m_counter;      // iteration counter
     89                uint32_t                m_lookahed;     // lookahead value
     90                vci_param::data_t       m_data[8];      // local buffer
     91                vci_cmd_t       m_cmd;          // paquet VCI commande
     92
     93//////// thread
     94                void execLoop()
     95                {
     96                        while(1) {
     97                                …
     98                                m_cmd.cmd = VCI_CMD_READ;
     99p_vci.cmdSend(&m_cmd, m_time.get_time());       // lecture bloquante
     100                                p_vci.wait();
     101                                …
     102                                m_cmd.cmd = VCI_CMD_WRITE;
     103                                p_vci.send(VCI_CMD_WRITE,…);   
     104p_vci.cmdSend(&m_cmd, m_time.get_time());       // écriture non bloquante
     105                                ...
     106                                // lookahead management
     107m_counter++ ;
     108                                if (m_counter >= m_lookahead) {
     109                                        m_counter = 0 ;
     110                                        wait(SC_ZERO_TIME) ;
     111                                } // end if
     112                        m_time.addtime(1) ;
     113} // end while
     114                } // end execLoop()
     115
     116//////////////// call-back function
     117void  rspReceived(vci_cmd_t *cmd, sc_time rsp_time)
     118{
     119                if(cmd == VCI_CMD_READ) {
     120m_time.set_time(rsp_time + length);
     121p_vci.notify() ;
     122}
     123                        } // end rspReceived()
     124} // end class my_initiator
     125
     126}}}
     127
     128= D) Target Modeling =
     129
     130== D.1) Receiving a VCI command packet ==
     131
     132== D.2) Sending a VCI response packet ==
     133
     134== D.3) Target Constructor ==
     135
     136== D4) TLM-T target example
     137 {{{
     138Cible TLM-T
     139
     140template <typename vci_param>
     141class my_target : Tlmt::BaseModule {
     142public:
     143                VciTargetPort<vci_param>                p_vci;
     144
     145                ////////////// constructor
     146                my_target (     sc_module_name  name,
     147uint32_t                targetIndex,
     148sc_time         latency):
     149p_vci(“vci”,this, &my_target::cmdReceived),
     150BaseModule(name)
     151{
     152m_latency = latency;
     153m_index = targetIndex;
     154} // end constructor
     155       
     156private:
     157                vci_param::data_t       m_data[8];      // local buffer
     158                sc_time                 m_latency;      // target latency
     159                uint32_t                m_index;                // target index
     160vci_rsp_t               m_rsp;          // paquet VCI réponse
     161
     162/////////////// call-back function
     163sc_time  cmdReceived(   vci_cmd_t *cmd,
     164sc_time cmd_time)
     165{
     166
     167                if(cmd->cmd == VCI_CMD_WRITE) {
     168                        for(int i = 0 ; i < length ; i++) m_data[i] = cmd->buf[i];
     169}
     170if(cmd->cmd == VCI_CMD_READ) {
     171                        for(int i = 0 ; i < length ; i++) cmd->buf[i] = m_data[i];
     172}
     173m_rsp.srcid = cmd->srcid;
     174m_rsp.trdid  = cmd->trdid;
     175m_rsp.pktid = cmd>pktid;
     176m_rsp.length = cmd->length;
     177m_rsp.error = 0;
     178rsp_time = cmd_time + latency;
     179p_vci.rspSend(&m_rsp, rsp_time) ;
     180return (rsp_time + (sc_time)cmd->length);
     181                        } // end cmdReceived()
     182
     183} // end class my_target
     184
     185}}}
     186
     187
     188
     189
     190As VCI signals can have variable widths, all VCI components must be defined with templates. The [source:trunk/soclib/systemc/include/caba/interface/vci_param.h caba/interface/vci_param.h] file contains the definition of the VCI parameters object. This object must be passed as a template parameter to the component.
     191
     192A typical VCI component declaration is:
     193{{{
     194#include "caba/util/base_module.h"
     195#include "caba/interface/vci_target.h"
     196
     197namespace soclib { namespace caba {
     198
     199template<typename vci_params>
     200class VciExampleModule
     201    : soclib::caba::BaseModule
     202{
     203
     204};
     205}}
     206}}}
     207
     208The SystemC top cell defining the system architecture must include the following file, defining the advanced VCI signals :
     209 * [source:trunk/soclib/systemc/include/caba/interface/vci_signals.h caba/interface/vci_signals.h].
     210
     211A SoCLib hardware component that has no VCI interface should use a dedicated VCI  wrapper in order to be connected to the VCI interconnect.
     212
     213== B3) Address space segmentation ==
     214
     215In a shared memory architecture, the address space segmentation (or memory map) is a global characteristic of the system.
     216This memory map must be defined by the system designer, and is used by both software, and hardware components.
     217
     218Most hardware components use this memory map:
     219 * VCI interconnect components contain a ''routing table'' used to decode the VCI address MSB bits and
     220   route VCI commands to the proper targets.
     221 * VCI target components must be able to check for segmentation violation when receiving a
     222   command packet. Therefore, the base address and size of the segment allocated to a given
     223   VCI target must be ''known'' by this target.
     224 * A cache controller supporting uncached segments can contain a ''cacheability table''
     225   addressed by the VCI address MSB bits.
     226
     227In order to simplify the memory map definition, and the hardware component configuration, a
     228generic object, called ''mapping table'' has been defined in
     229[source:trunk/soclib/systemc/include/common/mapping_table.h common/mapping_table.h].
     230This is an associative table of memory segments. Any segment must be allocated to one single
     231VCI target. The segment object is defined in
     232[source:trunk/soclib/systemc/include/common/segment.h common/segment.h],
     233and contains five attributes:
     234{{{
     235const std::string   m_name;           // segment's name
     236addr_t              m_base_address;   // base address
     237size_t              m_size;           // size (bytes)
     238IntTab              m_target_index;   // VCI target index
     239bool                m_cacheability;   // cacheable
     240}}}
     241
     242Any hardware component using the memory map should have a constant reference to the mapping table as constructor argument.
     243
     244== B4) Component definition ==
     245
     246The component ''XXX.h'' file contains the following informations
     247
     248'''Interface definition'''
     249A typical VCI target component will contain the following ports:
     250{{{
     251sc_in<bool>                         p_resetn;
     252sc_in<bool>                         p_clk;
     253soclib::caba::VciTarget<vci_param>  p_vci;
     254}}}
     255
     256'''Internal registers definition'''
     257
     258All internal registers should be defined with the  ''sc_signal'' type.
     259
     260This point is a bit tricky: It allows the model designer to benefit from the delayed update
     261mechanism associated by SystemC to the sc_signal type. When a single module contains several
     262interacting FSMs, it helps to write the `Transition()`, as the registers values are not updated
     263until the exit from the transition function. Conversely, any member variable declared with the
     264`sc_signal` type is considered as a register.
     265
     266A typical VCI target will contain the following registers :
     267{{{
     268sc_signal<int>                          r_vci_fsm;
     269sc_signal<typename vci_param::trdid_t>  r_buf_trdid;
     270sc_signal<typename vci_param::pktid_t>  r_buf_srcid;
     271sc_signal<typename vci_param::srcid_t>  r_buf_srcid;
     272}}}
     273
     274''`typename vci_param::trdid_t` and others are generically-defined VCI field types''
     275
     276'''Structural parameters definition'''
     277
     278All structural parameters should be be defined as member variables. The values are generally defined by a constructor argument.
     279Instance name is stored in [source:trunk/soclib/systemc/include/common/base_module.h soclib::common::BaseModule], inherited by
     280[source:trunk/soclib/systemc/include/caba/util/base_module.h soclib::caba::BaseModule].
     281For example, a VCI target will contain a reference to the the assigned segment, in order to check
     282possible segmentation errors during execution.
     283
     284{{{
     285const soclib::common::Segment   m_segment;
     286}}}
     287
     288== B5) Constructor & destructor ==
     289
     290Any hardware component must have an instance name, and most SoCLib component must have
     291a VCI index. Moreover, generic simulation models can have structural parameters. The parameter
     292values must be defined as constructor arguments, and used by the constructor to configure the hardware ressources. A constructor  argument frequently
     293used is a reference on the [source:trunk/soclib/systemc/include/common/mapping_table.h soclib::common::MappingTable], that defines the segmentation of the system address space.
     294A typical VCI component will have the following constructor arguments:
     295
     296{{{
     297VciExampleModule(
     298    sc_module_name                     insname,
     299    const soclib::common::IntTab       &index,
     300    const soclib::common::MappingTable &mt);
     301}}}
     302
     303In this example, the first argument is the instance name, the second argument is the VCI target index, and the third argument is the mapping table.
     304
     305Moreover, the constructor must define the sensitivity list of the Transition(), genMoore() and genMealy()
     306methods, that are described below.
     307 * sensitivity list of the transition() method contains only the clock rising edge.
     308 * sensitivity list of the genMoore() method contains only the clock falling edge.
     309 * sensitivity list of the genMealy() method contains the clock falling edge, as well as all input ports thare in the support of the Mealy generation function.
     310
     311Be careful: the constructor should NOT initialize the registers.
     312The register initialization must be an hardware mechanism explicitly described in the Transition function on reset condition.
     313
     314== B6) member functions ==
     315
     316The component behaviour is described by simple member functions. The type ot those methods
     317(Transition, genMoore, or genMealy) is defined by the sensitivity lists, as specified in B5.
     318
     319'''transition() method'''
     320
     321For each hardware component, there is only one `Transition()` method. It is called
     322once per cycle, as the sensitivity list contains only the clock rising edge. This method
     323computes the next values of the registers (variables that have the `sc_signal` type).
     324No output port can be assigned in this method. Each register should be assigned only once.
     325
     326'''genMoore() method'''
     327
     328For each hardware component, there is only one `genMoore()` method. It is called once
     329per cycle, as the sensitivity list contains only the clock falling edge. This method computes
     330the values of the Moore output ports. (variables that have the `sc_out` type).
     331No register can be assigned in this method. Each output port can be assigned only once.
     332No input port can be read in this method
     333
     334'''genMealy() methods'''
     335
     336For each hardware component, there is zero, one or several `genMealy()` methods (it can be useful
     337to have one separated `gemealy()` method for each output port). These methods can be called several times per cycle. The sensitivity list can contain several input ports. This method computes the Mealy values of the ouput ports, using only the register values and the input ports values.
     338No register can be assigned in this method. Each output port can be assigned only once.
     339
     340= C) Complete example =
     341
     342== C1) Component definition ==
     343
     344Let's take the [source:trunk/soclib/systemc/include/caba/target/vci_locks.h soclib::caba::VciLocks]
     345component definition and comment it.
     346
     347{{{
     348#include <systemc.h>
     349#include "caba/util/base_module.h"
     350#include "caba/interface/vci_target.h"
     351#include "common/mapping_table.h"
     352
     353// Have this component in the soclib::caba namespace
     354namespace soclib { namespace caba {
     355
     356// Here we pass the VCI parameters as a template argument. This is intended because VCI parameters
     357// change data type widths, therefore change some compile-time intrinsics
     358template<typename vci_param>
     359class VciLocks
     360    : public soclib::caba::BaseModule
     361{
     362
     363    // We have only one FSM in this component. It handles the
     364    // VCI target port. The states are:
     365    enum vci_target_fsm_state_e {
     366        IDLE,
     367        WRITE_RSP,
     368        READ_RSP,
     369        ERROR_RSP,
     370    };
     371
     372    // The register holding the FSM state:
     373    sc_signal<int> r_vci_fsm;
     374
     375    // Some registers used to save useful data between command & response
     376    sc_signal<typename vci_param::srcid_t> r_buf_srcid;
     377    sc_signal<typename vci_param::trdid_t> r_buf_trdid;
     378    sc_signal<typename vci_param::pktid_t> r_buf_pktid;
     379    sc_signal<typename vci_param::eop_t>   r_buf_eop;
     380    sc_signal<bool>                        r_buf_value;
     381
     382    // Pointer on the table of locks (allocated in the constructor)
     383    sc_signal<bool>             *r_contents;
     384
     385    // The segment assigned to this peripheral
     386    soclib::common::Segment m_segment;
     387
     388protected:
     389    // Mandatory SystemC construct
     390    SC_HAS_PROCESS(VciLocks);
     391
     392public:
     393    // The ports
     394    sc_in<bool> p_resetn;
     395    sc_in<bool> p_clk;
     396    soclib::caba::VciTarget<vci_param> p_vci;
     397
     398    // Constructor & descructor, explained above
     399    VciLocks(
     400        sc_module_name insname,
     401        const soclib::common::IntTab &index,
     402        const soclib::common::MappingTable &mt);
     403    ~VciLocks();
     404
     405private:
     406    // The FSM functions
     407    void transition();
     408    void genMoore();
     409};
     410
     411// Namespace closing
     412}}
     413}}}
     414
     415== C2) Component implementation ==
     416
     417Here is the [source:trunk/soclib/systemc/src/caba/target/vci_locks.cc soclib::caba::VciLocks] component implementation:
     418
     419{{{
     420#include "caba/target/vci_locks.h"
     421
     422// Namespace, again
     423namespace soclib { namespace caba {
     424
     425// This macro is an helper function to factor out the template parameters
     426// This is useful in two ways:
     427// * It makes the syntax clearer
     428// * It makes template parameters changes easier (only one line to change them all)
     429// x is the method's return value
     430#define tmpl(x) template<typename vci_param> x VciLocks<vci_param>
     431
     432// The /**/ is a hack to pass no argument to a macro taking one. (constructor has no
     433// return value in C++)
     434tmpl(/**/)::VciLocks(
     435    sc_module_name insname,
     436    const soclib::common::IntTab &index,
     437    const soclib::common::MappingTable &mt)
     438// This is the C++ construct for parent construction and
     439// member variables initialization.
     440// We initialize the BaseModule with the component's name
     441    : soclib::caba::BaseModule(insname),
     442// and get the segment from the mapping table and our index
     443      m_segment(mt.getSegment(index))
     444{
     445
     446    // There is one lock every 32-bit word in memory. We
     447    // allocate an array of bool for the locks
     448    r_contents = new sc_signal<bool>[m_segment.size()/4];
     449
     450    // Sensitivity list for transition() and genMoore(), no genMealy()
     451    // in this component
     452    SC_METHOD(transition);
     453    dont_initialize();
     454    sensitive << p_clk.pos();
     455       
     456    SC_METHOD(genMoore);
     457    dont_initialize();
     458    sensitive << p_clk.neg();
     459}
     460
     461tmpl(/**/)::~VciLocks()
     462{
     463    // Here we must delete dynamically-allocated data...
     464    delete [] r_contents;
     465}
     466
     467tmpl(void)::transition()
     468{
     469    // On reset condition, we initialize the component,
     470    // from FSMs to internal data.
     471    if (!p_resetn) {
     472        for (size_t i=0; i<m_segment.size()/4; ++i)
     473            r_contents[i] = false;
     474        r_vci_fsm = IDLE;
     475        return;
     476    }
     477
     478    // We are not on reset case.
     479
     480    // Take the address, transform it into an index in our locks table.
     481    typename vci_param::addr_t address = p_vci.address.read();
     482    uint32_t cell = (address-m_segment.baseAddress())/4;
     483
     484    // Implement the VCI target FSM
     485    switch (r_vci_fsm) {
     486    case IDLE:
     487        if ( ! p_vci.cmdval.read() )
     488            break;
     489        /*
     490         * We only accept 1-word request packets
     491         * and we check for segmentation violations
     492         */
     493        if ( ! p_vci.eop.read() ||
     494             ! m_segment.contains(address) )
     495            r_vci_fsm = ERROR_RSP;
     496        else {
     497            switch (p_vci.cmd.read()) {
     498            case VCI_CMD_READ:
     499                r_buf_value = r_contents[cell];
     500                r_contents[cell] = true;
     501                r_vci_fsm = READ_RSP;
     502                break;
     503            case VCI_CMD_WRITE:
     504                r_contents[cell] = false;
     505                r_vci_fsm = WRITE_RSP;
     506                break;
     507            default:
     508                r_vci_fsm = ERROR_RSP;
     509                break;
     510            }
     511        }
     512        r_buf_srcid = p_vci.srcid.read();
     513        r_buf_trdid = p_vci.trdid.read();
     514        r_buf_pktid = p_vci.pktid.read();
     515        r_buf_eop = p_vci.eop.read();
     516        break;
     517
     518    // In those states, we only wait for response to be accepted.
     519    case WRITE_RSP:
     520    case READ_RSP:
     521    case ERROR_RSP:
     522        if ( p_vci.rspack.read() )
     523            r_vci_fsm = IDLE;
     524        break;
     525    }
     526}
     527
     528tmpl(void)::genMoore()
     529{
     530    // This is an helper function defined in the VciTarget port definition
     531    p_vci.rspSetIds( r_buf_srcid.read(), r_buf_trdid.read(), r_buf_pktid.read() );
     532
     533    // Depending on the state, we give back the expected response.
     534    switch (r_vci_fsm) {
     535    case IDLE:
     536        p_vci.rspNop();
     537        break;
     538    case WRITE_RSP:
     539        p_vci.rspWrite( r_buf_eop.read() );
     540        break;
     541    case READ_RSP:
     542        p_vci.rspRead( r_buf_eop.read(), r_buf_value.read() );
     543        break;
     544    case ERROR_RSP:
     545        p_vci.rspError( r_buf_eop.read() );
     546        break;
     547    }
     548
     549    // We only accept commands in Idle state
     550    p_vci.cmdack = (r_vci_fsm == IDLE);
     551}
     552}}
     553
     554}}}
     555
     556Component instanciation could be (`template_inst.cc`):
     557{{{
     558#include "caba/target/vci_locks.cc"
     559template class soclib::caba::VciLocks<soclib::caba::VciParams<4,1,32,1,1,1,8,1,1,1> >;
     560}}}
     561
     562Command line:
     563{{{
     564g++ -c -o obj.o -I/path/to/soclib/systemc/src -I/path/to/soclib/systemc/include template_inst.cc -I/path/to/systemc $(OTHER_CFLAGS)
     565}}}
     566