Changes between Version 2 and Version 3 of Writing Rules/Caba


Ignore:
Timestamp:
Mar 27, 2007, 2:10:38 PM (17 years ago)
Author:
Nicolas Pouillon
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Writing Rules/Caba

    v2 v3  
    11= Introduction =
    22
    3 This manual describes the modeling rules for writing "cycle-accurate / bit-accurate" SystemC simulation models. Those models can be used with the "standard" OSCI simulation engine (SystemC 2.0), but can be used also with others simulation engines, such as SystemCASS, which is optimized for models complying with those rules.
     3This manual describes the modeling rules for writing "cycle-accurate / bit-accurate" SystemC simulation models. Those models can be used with the "standard" OSCI simulation engine (SystemC 2.x), but can be used also with others simulation engines, such as [SystemCass SystemCASS], which is optimized for models complying with those rules.
    44
    55Those modeling rules are based on the "Synchronous Communicating Finite State Machines" theory. The idea is to force the "event driven" SystemC simulation engine to run as a cycle based simulator.
    66
    7 A given hardware architecture is obtained by direct instanciation of hardware modules, connected by signals. A given architecture can contain several instances of the same module. Each module is described as one (or several) synchronous FSM(s).
     7A given hardware architecture is obtained by direct instantiation of hardware modules, connected by signals. A given architecture can contain several instances of the same module. Each module is described as one (or several) synchronous FSM(s).
    88
    99If all internal register are clearly identified, any FSM can be described by three types of functions:
     
    2020== Component's module ==
    2121
    22 A SoCLib CABA component XXX is generally described as a class derived from the `soclib::caba ::BaseModule` class.
     22A SoCLib CABA component `XXX` is generally described as a class derived from the
     23[source:trunk/soclib/systemc/include/caba/util/base_module.h `soclib::caba ::BaseModule`] class.
     24
    2325At least two files are associated to each hardware component:
    24  * The `XXX.h` file describes the component interface, the internal registers, and the structural parameters.
    25  * The `XXX.cpp` file contains the code of the methods associated to this component.
     26 * `XXX.h` describes the component interface, internal registers, and structural parameters.
     27 * `XXX.cc` contains the code of the methods associated to this component.
     28
     29== Namespaces ==
     30
     31SystemC is built upon C++. We can benefit from C++ constructs. Namespaces allows us to create unambiguous names
     32while keeping them short and clean. SoCLib defines some namespaces:
     33 * `soclib`
     34 * `soclib::common`
     35 * `soclib::caba`
     36 * `soclib::tlmt`
    2637
    2738== Component indexation ==
    2839
    29 In a VCI-based architecture, all initiators must be indexed by an index. Targets are indexes by their assigned address space. For simplification purposes, we'll also assign an index to the targets. Index space for targets is different from index space for initiators.
    30 
    31 The target index is used in the routing table implemented by the interconnect components in order to choose destination port for command packet.
    32 The initiator index is used by the interconnect components to route the response packets.
    33 
    34 For the initiators, the index corresponds to the VCI SRCID value.
    35 
    36 This index can be a simple scalar index, in case of a ''flat'' interconnect, or it can be a composite index in case a hierarchical two level interconnect where each component is identified by two indexes : (cluster_index, local_index).
     40In a VCI-based architecture, all initiators must be indexed. Targets are identified by their assigned address space. However, for simplification purposes, we'll also give an index to the targets. Index space for targets is different from index space for initiators.
     41 * The target index is the connection port number on interconnects, this index will be decoded from target address in VCI cmd packet.
     42 * The initiator index is used by the interconnect components to route the response packets.
     43
     44For the initiators, the index is the VCI SRCID value.
     45
     46Indexes can be
     47 * a simple scalar index, in case of a ''flat'' interconnect
     48 * a composite index in case a hierarchical two level interconnect where each component is identified by two scalars: (cluster_index, local_index).
     49
     50[source:trunk/soclib/systemc/include/common/int_tab.h common/int_tab.h] defines an utility class storing list of indexes. All indexes are `IntTab`s.
    3751
    3852== VCI Interface ==
    3953
    40 In order to enforce interoperability between components, all SoCLib hardware components should respect the advanced VCI interface.
     54In order to enforce interoperability between components, all SoCLib hardware components should respect the advanced VCI standard.
    4155Any component having a VCI interface must include one of the the two following files
    42  * [source:trunk/soclib/systemc/include/caba/interface/vci_target.h]
    43  * [source:trunk/soclib/systemc/include/caba/interface/vci_initiator.h]
     56 * [source:trunk/soclib/systemc/include/caba/interface/vci_target.h caba/interface/vci_target.h]
     57 * [source:trunk/soclib/systemc/include/caba/interface/vci_initiator.h caba/interface/vci_initiator.h]
    4458
    4559The advanced VCI signals are defined in [source:trunk/soclib/systemc/include/caba/interface/vci_signals.h caba/interface/vci_signals.h].
    4660
    47 As several 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.
     61As several 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.
     62
     63A typical VCI component declaration is:
     64
     65{{{
     66#include "caba/util/base_module.h"
     67#include "caba/interface/vci_target.h"
     68
     69namespace soclib { namespace caba {
     70
     71template<typename vci_params>
     72class VciExampleModule
     73    : soclib::caba::BaseModule
     74{
     75
     76};
     77
     78}}}
     79
    4880
    4981== Address space segmentation ==
     
    6092   addressed by the address MSB bits.
    6193
    62 In order to simplify the memory map definition, and the hardware component configuration, a generic object, called ''mapping table'' has been defined in [source:trunk/soclib/systemc/include/common/mapping_table.h common/mapping_table.h]. This is an associative table of memory segments. Any segment must be allocated to one single VCI target. The segment object is defined in [source:trunk/soclib/systemc/include/common/segment.h common/segment.h], and contains five attributes:
     94In order to simplify the memory map definition, and the hardware component configuration, a
     95generic object, called ''mapping table'' has been defined in
     96[source:trunk/soclib/systemc/include/common/mapping_table.h common/mapping_table.h].
     97This is an associative table of memory segments. Any segment must be allocated to one single
     98VCI target. The segment object is defined in
     99[source:trunk/soclib/systemc/include/common/segment.h common/segment.h],
     100and contains five attributes:
    63101{{{
    64102const std::string   m_name;           // segment's name
     
    73111== Constructor arguments ==
    74112
    75 Any hardware component must have an instance name, and most SoCLib component must have a VCI index. Moreover, generic simulation models can have structural parameters, that must be defined as arguments in the constructor. A typical VCI component will have the following constructor arguments:
    76 
    77 {{{
    78 VciLocks(
     113Any hardware component must have an instance name, and most SoCLib component must have
     114a VCI index. Moreover, generic simulation models can have structural parameters defined as
     115arguments in the constructor. A typical VCI component will have the following constructor arguments:
     116
     117{{{
     118VciExampleModule(
    79119    sc_module_name                     insname,
    80120    const soclib::common::IntTab       &index,
     
    108148All internal registers must be defined with the type ''sc_signal''
    109149
    110 This point is a bit tricky: It allows the model designer to benefit from the delayed update mechanism associated by SystemC to the sc_signal type. When a single module contains several interacting FSMs, it helps to write the `Transition()`, as the registers values are not updated until the exit from the transition function. Conversely, any member variable declared with the `sc_signal` type is considered as a register.
    111 
    112 In order to improve the code readability, all internal registers should be prefixed with `_r`.
     150This point is a bit tricky: It allows the model designer to benefit from the delayed update
     151mechanism associated by SystemC to the sc_signal type. When a single module contains several
     152interacting FSMs, it helps to write the `Transition()`, as the registers values are not updated
     153until the exit from the transition function. Conversely, any member variable declared with the
     154`sc_signal` type is considered as a register.
     155
     156In order to improve the code readability, all internal registers should be prefixed with `r_`.
    113157
    114158A typical VCI target will contain the following registers :
    115 
    116159{{{
    117160sc_signal<int>                          r_vci_fsm;
     
    121164}}}
    122165
    123 ''typename vci_param::trdid_t and others are generically-defined VCI field types.''
     166''`typename vci_param::trdid_t` and others are generically-defined VCI field types''
    124167
    125168=== Structural parameters ===
    126169
    127170All structural parameters must be be defined as member variables. The values are generally defined by a constructor argument.
    128 A typical SoCLib component will contain an instance name, and a reference to the segment allocated to this component :
    129 
    130 {{{
    131 sc_module_name                  m_name;
     171Instance name is stored in [source:trunk/soclib/systemc/include/common/base_module.h soclib::common::BaseModule], inherited by
     172[source:trunk/soclib/systemc/include/caba/util/base_module.h soclib::caba::BaseModule].
     173
     174For a VCI target, assigned segment should be copied in order to check commands.
     175
     176{{{
    132177const soclib::common::Segment   m_segment;
    133178}}}
     
    135180== Constructor & descructor ==
    136181
    137 The first constructor argument must be the instance name. Other arguments can be identifiers (such as a target VCI index or initiator VCI index). The constructor must configure some hardware ressources, such as the address decoding ROM that exists in any VCI interconnect. An argument frequently used is a reference on the Soclib segment table, that defines the segmentation of the system address space.
    138 
    139 Moreover, the constructor must define the sensitivity list of the Transition,(), genMoore() and genMealy() methods, that are described below. The sensitivity list of the Transition () method contains only the clock rising edge. The sensitivity list of the genMoore()n method contains only the clock falling edge. The 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.
    140 
    141 Be careful : the constructor should NOT initialize the registers. The register initialisation must be an hardware mechanism explicitely described in the Transition function on reset condition.
    142 
    143 == Transition() method ==
    144 
    145 For each hardware component, there is only one `Transition()` method. It is called once per cycle, as the sensitivity list contains only the clock rising edge. This method computes the next values of the registers (variables that have the sc_signal type). No output port can be assigned in this method. Each register should be assigned only once.
     182The first constructor argument must be the instance name. Other arguments can be identifiers
     183(such as a target VCI index or initiator VCI index). The constructor must configure some hardware
     184ressources, such as the address decoding ROM that exists in any VCI interconnect. An argument frequently
     185used is a reference on the [source:trunk/soclib/systemc/include/common/mapping_table.h soclib::common::MappingTable],
     186that defines the segmentation of the system address space.
     187
     188Moreover, the constructor must define the sensitivity list of the Transition(), genMoore() and genMealy()
     189methods, that are described below.
     190 * sensitivity list of the transition() method contains only the clock rising edge.
     191 * sensitivity list of the genMoore() method contains only the clock falling edge.
     192 * 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.
     193
     194Be careful: the constructor should NOT initialize the registers.
     195The register initialization must be an hardware mechanism explicitly described in the Transition function on reset condition.
     196
     197== transition() method ==
     198
     199For each hardware component, there is only one `Transition()` method. It is called
     200once per cycle, as the sensitivity list contains only the clock rising edge. This method
     201computes the next values of the registers (variables that have the `sc_signal` type).
     202
     203No output port can be assigned in this method. Each register should be assigned only once.
    146204
    147205== genMoore() method ==
    148206
    149 For each hardware component, there is only one `genMoore()` method. It is called once per cycle, as the sensitivity list contains only the clock falling edge. This method computes the values of the Moore output ports. (variables that have the sc_out type)
    150 No register can be assigned in this method. Each output port can be assigned only once.  No input port can be read in this method
     207For each hardware component, there is only one `genMoore()` method. It is called once
     208per cycle, as the sensitivity list contains only the clock falling edge. This method computes
     209the values of the Moore output ports. (variables that have the `sc_out` type).
     210
     211No register can be assigned in this method. Each output port can be assigned only once.
     212No input port can be read in this method
    151213
    152214== genMealy() method ==
    153215
    154 For each hardware component, there is zero, one or several `genMealy()` methods (one 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.
    155 No register can be assigned in this method. Each output port can be assigned only once. This method can use automatic variables. It can be missing if there is no Mealy output.
    156 
     216For each hardware component, there is zero, one or several `genMealy()` methods (one method
     217for each output port). These methods can be called several times per cycle. The sensitivity
     218list can contain several input ports. This method computes the Mealy values of the ouput ports,
     219using only the register values and the input ports values.
     220
     221No register can be assigned in this method. Each output port can be assigned only once.
     222This method can use automatic variables. It can be missing if there is no Mealy output.
     223
     224= Complete example file =
     225
     226Let's take the [source:trunk/soclib/systemc/include/caba/target/vci_locks.h soclib::caba::VciLocks]
     227component definition and comment it.
     228
     229{{{
     230#include <systemc.h>
     231#include "caba/util/base_module.h"
     232#include "caba/interface/vci_target.h"
     233#include "common/mapping_table.h"
     234
     235// Have this component in the soclib::caba namespace
     236namespace soclib { namespace caba {
     237
     238// Here we pass the VCI parameters as a template argument. This is intended because VCI parameters
     239// change data type widths, therefore change some compile-time intrinsics
     240template<typename vci_param>
     241class VciLocks
     242    : public soclib::caba::BaseModule
     243{
     244
     245    // We have only one FSM in this component. It handles the
     246    // VCI target port. The states are:
     247    enum vci_target_fsm_state_e {
     248        IDLE,
     249        WRITE_RSP,
     250        READ_RSP,
     251        ERROR_RSP,
     252    };
     253
     254    // The register holding the FSM state:
     255    sc_signal<int> r_vci_fsm;
     256
     257    // While we are handling a command, we save the VCI out-of-order markers in order to
     258    // send them back in response
     259    sc_signal<typename vci_param::srcid_t> r_buf_srcid;
     260    sc_signal<typename vci_param::trdid_t> r_buf_trdid;
     261    sc_signal<typename vci_param::pktid_t> r_buf_pktid;
     262    sc_signal<typename vci_param::eop_t>   r_buf_eop;
     263
     264    // Do we have a command to handle ?
     265    sc_signal<bool>                        r_buf_value;
     266
     267    // Our assigned segment
     268    soclib::common::Segment m_segment;
     269
     270    // The actual data handled by this component: the locks
     271    bool *m_contents;
     272
     273protected:
     274    // Mandatory SystemC construct
     275    SC_HAS_PROCESS(VciLocks);
     276
     277public:
     278    // The ports
     279    sc_in<bool> p_resetn;
     280    sc_in<bool> p_clk;
     281    soclib::caba::VciTarget<vci_param> p_vci;
     282
     283    // Constructor & descructor, explained above
     284    VciLocks(
     285        sc_module_name insname,
     286        const soclib::common::IntTab &index,
     287        const soclib::common::MappingTable &mt);
     288    ~VciLocks();
     289
     290private:
     291    // The FSM functions
     292    void transition();
     293    void genMoore();
     294};
     295
     296// Namespace closing
     297}}
     298}}}
     299
     300And the [source:trunk/soclib/systemc/src/caba/target/vci_locks.cc implementation]:
     301
     302{{{
     303#include "caba/target/vci_locks.h"
     304
     305// Namespace, again
     306namespace soclib { namespace caba {
     307
     308// This macro is an helper function to factor out the template parameters
     309// This is useful in two ways:
     310// * It makes the syntax clearer
     311// * It makes template parameters changes easier (only one line to change them all)
     312// x is the method's return value
     313#define tmpl(x) template<typename vci_param> x VciLocks<vci_param>
     314
     315// The /**/ is a hack to pass no argument to a macro taking one. (constructor has no
     316// return value in C++)
     317tmpl(/**/)::VciLocks(
     318    sc_module_name insname,
     319    const soclib::common::IntTab &index,
     320    const soclib::common::MappingTable &mt)
     321// This is the C++ construct for parent construction and
     322// member variables initialization.
     323// We initialize the BaseModule with the component's name
     324    : soclib::caba::BaseModule(insname),
     325// and get the segment from the mapping table and our index
     326      m_segment(mt.getSegment(index))
     327{
     328
     329    // There is one lock every 32-bit word in memory. We
     330    // allocate an array for the locks, one every 4 addressable
     331    // byte.
     332    m_contents = new bool[m_segment.size()/4];
     333
     334    // Sensitivity list for transition() and genMoore(), no genMealy()
     335    // in this component
     336    SC_METHOD(transition);
     337    dont_initialize();
     338    sensitive << p_clk.pos();
     339       
     340    SC_METHOD(genMoore);
     341    dont_initialize();
     342    sensitive << p_clk.neg();
     343}
     344
     345tmpl(/**/)::~VciLocks()
     346{
     347    // Here we must delete every dynamically-allocated data...
     348    delete [] m_contents;
     349}
     350
     351tmpl(void)::transition()
     352{
     353    // On reset condition, we initialize everything in the component,
     354    // from FSMs to internal data.
     355    if (!p_resetn) {
     356        for (size_t i=0; i<m_segment.size()/4; ++i)
     357            m_contents[i] = false;
     358        r_vci_fsm = IDLE;
     359        return;
     360    }
     361
     362    // We are not on reset case.
     363
     364    // Take the address, transform it into an index in our locks table.
     365    typename vci_param::addr_t address = p_vci.address.read();
     366    uint32_t cell = (address-m_segment.baseAddress())/4;
     367
     368    // Implement the VCI target FSM
     369    switch (r_vci_fsm) {
     370    case IDLE:
     371        if ( ! p_vci.cmdval.read() )
     372            break;
     373        /*
     374         * We only accept 1-word request packets
     375         * and we check for segmentation violations
     376         */
     377        if ( ! p_vci.eop.read() ||
     378             ! m_segment.contains(address) )
     379            r_vci_fsm = ERROR_RSP;
     380        else {
     381            switch (p_vci.cmd.read()) {
     382            case VCI_CMD_READ:
     383                r_buf_value = m_contents[cell];
     384                m_contents[cell] = true;
     385                r_vci_fsm = READ_RSP;
     386                break;
     387            case VCI_CMD_WRITE:
     388                m_contents[cell] = false;
     389                r_vci_fsm = WRITE_RSP;
     390                break;
     391            default:
     392                r_vci_fsm = ERROR_RSP;
     393                break;
     394            }
     395        }
     396        r_buf_srcid = p_vci.srcid.read();
     397        r_buf_trdid = p_vci.trdid.read();
     398        r_buf_pktid = p_vci.pktid.read();
     399        r_buf_eop = p_vci.eop.read();
     400        break;
     401
     402    // In those states, we only wait for response to be accepted.
     403    case WRITE_RSP:
     404    case READ_RSP:
     405    case ERROR_RSP:
     406        if ( p_vci.rspack.read() )
     407            r_vci_fsm = IDLE;
     408        break;
     409    }
     410}
     411
     412tmpl(void)::genMoore()
     413{
     414    // This is an helper function defined in the VciTarget port definition
     415    p_vci.rspSetIds( r_buf_srcid.read(), r_buf_trdid.read(), r_buf_pktid.read() );
     416
     417    // Depending on the state, we give back the expected response.
     418    switch (r_vci_fsm) {
     419    case IDLE:
     420        p_vci.rspNop();
     421        break;
     422    case WRITE_RSP:
     423        p_vci.rspWrite( r_buf_eop.read() );
     424        break;
     425    case READ_RSP:
     426        p_vci.rspRead( r_buf_eop.read(), r_buf_value.read() );
     427        break;
     428    case ERROR_RSP:
     429        p_vci.rspError( r_buf_eop.read() );
     430        break;
     431    }   
     432}
     433
     434}}
     435}}}