source: branches/ODCCP/modules/vci_mem_cache/caba/source/include/mem_cache_directory.h @ 452

Last change on this file since 452 was 452, checked in by devigne, 11 years ago

Introduction of ODCCP branch
New components :
-dspin_odccp_param (Cleanup Data)
-generic_tlb_odccp (flag CC)
-generic_cache_odccp (STATE : VALID_CC and VALID_NCC)
-vci_cc_vcache_wrapper (Hybrid cache Write-Through / Write-Back,

Cleanup Data, new pktid TYPE_READ_MISS_NO_COHERENT)

-vci_mem_cache (Support for Cleanup Data, Bit coherent for directory entry)

File size: 23.6 KB
Line 
1#ifndef SOCLIB_CABA_MEM_CACHE_DIRECTORY_H
2#define SOCLIB_CABA_MEM_CACHE_DIRECTORY_H
3
4#include <inttypes.h>
5#include <systemc>
6#include <cassert>
7#include "arithmetics.h"
8
9// !!!
10// The L1_MULTI_CACHE mechanism does no longer work with the new pktid encoding
11// of TSAR. Turning the define below to a non null value will cause the memcache
12// to behave in an unpredicted way.
13// TODO Either remove the mechanism from the mem cache or update its behaviour.
14#define L1_MULTI_CACHE 0
15
16//#define RANDOM_EVICTION
17
18namespace soclib { namespace caba {
19
20  using namespace sc_core;
21
22  ////////////////////////////////////////////////////////////////////////
23  //                    A LRU entry
24  ////////////////////////////////////////////////////////////////////////
25  class LruEntry {
26
27    public:
28
29      bool recent;           
30
31      void init()
32      {
33        recent=false;
34      }
35
36  }; // end class LruEntry
37
38  ////////////////////////////////////////////////////////////////////////
39  //                    An Owner
40  ////////////////////////////////////////////////////////////////////////
41  class Owner{
42   
43    public:
44    // Fields
45      bool      inst;       // Is the owner an ICache ?
46      size_t    srcid;      // The SRCID of the owner
47#if L1_MULTI_CACHE
48      size_t    cache_id;   // In multi_cache configuration
49#endif
50
51    ////////////////////////
52    // Constructors
53    ////////////////////////
54      Owner(bool   i_inst
55            ,size_t i_srcid
56#if L1_MULTI_CACHE
57            ,size_t i_cache_id
58#endif
59            ){
60        inst    = i_inst;
61        srcid   = i_srcid;
62#if L1_MULTI_CACHE
63        cache_id= i_cache_id;
64#endif
65      }
66
67      Owner(const Owner &a){
68        inst    = a.inst;
69        srcid   = a.srcid;
70#if L1_MULTI_CACHE
71        cache_id= a.cache_id;
72#endif
73      }
74
75      Owner(){
76        inst    = false;
77        srcid   = 0;
78#if L1_MULTI_CACHE
79        cache_id= 0;
80#endif
81      }
82      // end constructors
83
84  }; // end class Owner
85
86
87  ////////////////////////////////////////////////////////////////////////
88  //                    A directory entry                               
89  ////////////////////////////////////////////////////////////////////////
90  class DirectoryEntry {
91
92    typedef uint32_t tag_t;
93
94    public:
95
96    bool    valid;                  // entry valid
97    bool    is_cnt;                 // directory entry is in counter mode
98    bool    dirty;                  // entry dirty
99    bool    lock;                   // entry locked
100    bool    coherent;               // entry coherent or not
101    tag_t   tag;                    // tag of the entry
102    size_t  count;                  // number of copies
103    Owner   owner;                  // an owner of the line
104    size_t  ptr;                    // pointer to the next owner
105
106    DirectoryEntry()
107    {
108      valid         = false;
109      is_cnt        = false;
110      dirty         = false;
111      lock          = false;
112      coherent      = true;
113      tag           = 0;
114      count         = 0;
115      owner.inst    = 0;
116      owner.srcid   = 0;
117#if L1_MULTI_CACHE
118      owner.cache_id= 0;
119#endif
120      ptr           = 0;
121    }
122
123    DirectoryEntry(const DirectoryEntry &source)
124    {
125      valid         = source.valid;
126      is_cnt        = source.is_cnt;
127      dirty         = source.dirty;
128      lock          = source.lock;
129      coherent      = source.coherent;
130      tag           = source.tag;
131      count         = source.count;
132      owner         = source.owner;
133      ptr           = source.ptr;
134    }         
135
136    /////////////////////////////////////////////////////////////////////
137    // The init() function initializes the entry
138    /////////////////////////////////////////////////////////////////////
139    void init()
140    {
141      valid     = false;
142      is_cnt    = false;
143      dirty     = false;
144      lock      = false;
145      coherent  = true;
146      count     = 0;
147    }
148
149    /////////////////////////////////////////////////////////////////////
150    // The copy() function copies an existing source entry to a target
151    /////////////////////////////////////////////////////////////////////
152    void copy(const DirectoryEntry &source)
153    {
154      valid         = source.valid;
155      is_cnt    = source.is_cnt;
156      dirty         = source.dirty;
157      lock          = source.lock;
158      coherent  = source.coherent;
159      tag           = source.tag;
160      count     = source.count;
161      owner     = source.owner;
162      ptr       = source.ptr;
163    }
164
165    ////////////////////////////////////////////////////////////////////
166    // The print() function prints the entry
167    ////////////////////////////////////////////////////////////////////
168    void print()
169    {
170      std::cout << "Valid = " << valid
171                << " ; IS COUNT = " << is_cnt
172                << " ; Dirty = " << dirty
173                << " ; Lock = " << lock
174                << " ; Tag = " << std::hex << tag << std::dec
175                << " ; Count = " << count
176                << " ; Owner = " << owner.srcid
177#if L1_MULTI_CACHE
178                << "." << owner.cache_id
179#endif
180                << " " << owner.inst
181                << " ; Pointer = " << ptr << std::endl;
182    }
183
184  }; // end class DirectoryEntry
185
186  ////////////////////////////////////////////////////////////////////////
187  //                       The directory 
188  ////////////////////////////////////////////////////////////////////////
189  class CacheDirectory {
190
191    typedef sc_dt::sc_uint<40> addr_t;
192    typedef uint32_t data_t;
193    typedef uint32_t tag_t;
194
195    private:
196
197    // Directory constants
198    size_t                                      m_ways;
199    size_t                                      m_sets;
200    size_t                                      m_words;
201    size_t                                      m_width;
202    uint32_t                lfsr;
203
204    // the directory & lru tables
205    DirectoryEntry                              **m_dir_tab;
206    LruEntry                                    **m_lru_tab;
207
208    public:
209
210    ////////////////////////
211    // Constructor
212    ////////////////////////
213    CacheDirectory( size_t ways, size_t sets, size_t words, size_t address_width)       
214    {
215      m_ways  = ways; 
216      m_sets  = sets;
217      m_words = words;
218      m_width = address_width;
219      lfsr = -1;
220
221      m_dir_tab = new DirectoryEntry*[sets];
222      for ( size_t i=0; i<sets; i++ ) {
223        m_dir_tab[i] = new DirectoryEntry[ways];
224        for ( size_t j=0 ; j<ways ; j++) m_dir_tab[i][j].init();
225      }
226      m_lru_tab = new LruEntry*[sets];
227      for ( size_t i=0; i<sets; i++ ) {
228        m_lru_tab[i] = new LruEntry[ways];
229        for ( size_t j=0 ; j<ways ; j++) m_lru_tab[i][j].init();
230      }
231    } // end constructor
232
233    /////////////////
234    // Destructor
235    /////////////////
236    ~CacheDirectory()
237    {
238      for(size_t i=0 ; i<m_sets ; i++){
239        delete [] m_dir_tab[i];
240        delete [] m_lru_tab[i];
241      }
242      delete [] m_dir_tab;
243      delete [] m_lru_tab;
244    } // end destructor
245
246    /////////////////////////////////////////////////////////////////////
247    // The read() function reads a directory entry. In case of hit, the
248    // LRU is updated.
249    // Arguments :
250    // - address : the address of the entry
251    // - way : (return argument) the way of the entry in case of hit
252    // The function returns a copy of a (valid or invalid) entry 
253    /////////////////////////////////////////////////////////////////////
254    DirectoryEntry read(const addr_t &address,size_t &way)
255    {
256
257#define L2 soclib::common::uint32_log2
258      const size_t set = (size_t)(address >> (L2(m_words) + 2)) & (m_sets - 1);
259      const tag_t  tag = (tag_t)(address >> (L2(m_sets) + L2(m_words) + 2));
260#undef L2
261
262      bool hit       = false;
263      for ( size_t i=0 ; i<m_ways ; i++ ) {
264        bool equal = ( m_dir_tab[set][i].tag == tag );
265        bool valid = m_dir_tab[set][i].valid;
266        hit = equal && valid;
267        if ( hit ) {                   
268          way = i;
269          break;
270        } 
271      }
272      if ( hit ) {
273        m_lru_tab[set][way].recent = true;
274        return DirectoryEntry(m_dir_tab[set][way]);
275      } else {
276        return DirectoryEntry();
277      }
278    } // end read()
279
280    /////////////////////////////////////////////////////////////////////
281    // The inval function invalidate an entry defined by the set and
282    // way arguments.
283    /////////////////////////////////////////////////////////////////////
284    void inval( const size_t &set, const size_t &way )
285    {
286        m_dir_tab[set][way].init();
287    }
288
289    /////////////////////////////////////////////////////////////////////
290    // The read_neutral() function reads a directory entry, without
291    // changing the LRU
292    // Arguments :
293    // - address : the address of the entry
294    // The function returns a copy of a (valid or invalid) entry 
295    /////////////////////////////////////////////////////////////////////
296    DirectoryEntry read_neutral(const addr_t &address)
297    {
298
299#define L2 soclib::common::uint32_log2
300      const size_t set = (size_t)(address >> (L2(m_words) + 2)) & (m_sets - 1);
301      const tag_t  tag = (tag_t)(address >> (L2(m_sets) + L2(m_words) + 2));
302#undef L2
303
304      bool hit       = false;
305      for ( size_t i=0 ; i<m_ways ; i++ ) {
306        bool equal = ( m_dir_tab[set][i].tag == tag );
307        bool valid = m_dir_tab[set][i].valid;
308        hit = equal && valid;
309        if ( hit ) {                   
310          return DirectoryEntry(m_dir_tab[set][i]);
311        } 
312      }
313      return DirectoryEntry();
314    } // end read_neutral()
315
316    /////////////////////////////////////////////////////////////////////
317    // The write function writes a new entry,
318    // and updates the LRU bits if necessary.
319    // Arguments :
320    // - set : the set of the entry
321    // - way : the way of the entry
322    // - entry : the entry value
323    /////////////////////////////////////////////////////////////////////
324    void write(const size_t &set, const size_t &way, const DirectoryEntry &entry)
325    {
326      assert( (set<m_sets) 
327          && "Cache Directory write : The set index is invalid");
328      assert( (way<m_ways) 
329          && "Cache Directory write : The way index is invalid");
330
331      // update Directory
332      m_dir_tab[set][way].copy(entry);
333
334      // update LRU bits
335      bool all_recent = true;
336      for ( size_t i=0 ; i<m_ways ; i++ ) 
337      {
338          if ( i != way ) all_recent = m_lru_tab[set][i].recent && all_recent;
339      }
340      if ( all_recent ) 
341      {
342          for( size_t i=0 ; i<m_ways ; i++ ) m_lru_tab[set][i].recent = false;
343      } 
344      else 
345      {
346          m_lru_tab[set][way].recent = true;
347      }
348    } // end write()
349
350    /////////////////////////////////////////////////////////////////////
351    // The print() function prints a selected directory entry
352    // Arguments :
353    // - set : the set of the entry to print
354    // - way : the way of the entry to print
355    /////////////////////////////////////////////////////////////////////
356    void print(const size_t &set, const size_t &way)
357    {
358      std::cout << std::dec << " set : " << set << " ; way : " << way << " ; " ;
359      m_dir_tab[set][way].print();
360    } // end print()
361
362    /////////////////////////////////////////////////////////////////////
363    // The select() function selects a directory entry to evince.
364    // Arguments :
365    // - set   : (input argument) the set to modify
366    // - way   : (return argument) the way to evince
367    /////////////////////////////////////////////////////////////////////
368    DirectoryEntry select(const size_t &set, size_t &way)
369    {
370      assert( (set < m_sets) 
371          && "Cache Directory : (select) The set index is invalid");
372
373      for(size_t i=0; i<m_ways; i++){
374        if(!m_dir_tab[set][i].valid){
375          way=i;
376          return DirectoryEntry(m_dir_tab[set][way]);
377        }
378      }
379
380#ifdef RANDOM_EVICTION
381      lfsr = (lfsr >> 1) ^ ((-(lfsr & 1)) & 0xd0000001);
382      way = lfsr % m_ways;
383      return DirectoryEntry(m_dir_tab[set][way]);
384#endif
385
386      for(size_t i=0; i<m_ways; i++){
387        if(!(m_lru_tab[set][i].recent) && !(m_dir_tab[set][i].lock)){
388          way=i;
389          return DirectoryEntry(m_dir_tab[set][way]);
390        }
391      }
392      for(size_t i=0; i<m_ways; i++){
393        if( !(m_lru_tab[set][i].recent) && (m_dir_tab[set][i].lock)){
394          way=i;
395          return DirectoryEntry(m_dir_tab[set][way]);
396        }
397      }
398      for(size_t i=0; i<m_ways; i++){
399        if( (m_lru_tab[set][i].recent) && !(m_dir_tab[set][i].lock)){
400          way=i;
401          return DirectoryEntry(m_dir_tab[set][way]);
402        }
403      }
404      way = 0;
405      return DirectoryEntry(m_dir_tab[set][0]);
406    } // end select()
407
408    /////////////////////////////////////////////////////////////////////
409    //          Global initialisation function
410    /////////////////////////////////////////////////////////////////////
411    void init()
412    {
413      for ( size_t set=0 ; set<m_sets ; set++ ) {
414        for ( size_t way=0 ; way<m_ways ; way++ ) {
415          m_dir_tab[set][way].init();
416          m_lru_tab[set][way].init();
417        }
418      }
419    } // end init()
420
421  }; // end class CacheDirectory
422
423  ///////////////////////////////////////////////////////////////////////
424  //                    A Heap Entry
425  ///////////////////////////////////////////////////////////////////////
426  class HeapEntry{
427
428    public:
429    // Fields of the entry
430      Owner     owner;
431      size_t    next;
432
433    ////////////////////////
434    // Constructor
435    ////////////////////////
436      HeapEntry()
437      :owner(false,0
438#if L1_MULTI_CACHE
439             ,0
440#endif
441             )
442      {
443        next = 0;
444      } // end constructor
445
446    ////////////////////////
447    // Constructor
448    ////////////////////////
449      HeapEntry(const HeapEntry &entry){
450        owner.inst  = entry.owner.inst;
451        owner.srcid = entry.owner.srcid;
452#if L1_MULTI_CACHE
453        owner.cache_id = entry.owner.cache_id;
454#endif       
455        next           = entry.next;
456      } // end constructor
457
458    /////////////////////////////////////////////////////////////////////
459    // The copy() function copies an existing source entry to a target
460    /////////////////////////////////////////////////////////////////////
461      void copy(const HeapEntry &entry){
462        owner.inst     = entry.owner.inst;
463        owner.srcid    = entry.owner.srcid;
464#if L1_MULTI_CACHE
465        owner.cache_id = entry.owner.cache_id;
466#endif
467        next           = entry.next;
468      } // end copy()
469
470    ////////////////////////////////////////////////////////////////////
471    // The print() function prints the entry
472    ////////////////////////////////////////////////////////////////////
473      void print(){
474        std::cout
475        << " -- owner.inst     : " << std::dec << owner.inst << std::endl
476        << " -- owner.srcid    : " << std::dec << owner.srcid << std::endl
477#if L1_MULTI_CACHE
478        << " -- owner.cache_id : " << std::dec << owner.cache_id << std::endl
479#endif
480        << " -- next           : " << std::dec << next << std::endl;
481
482      } // end print()
483
484  }; // end class HeapEntry
485
486  ////////////////////////////////////////////////////////////////////////
487  //                        The Heap
488  ////////////////////////////////////////////////////////////////////////
489  class HeapDirectory{
490   
491    private:
492    // Registers and the heap
493      size_t    ptr_free;
494      bool      full;
495      HeapEntry *m_heap_tab;
496
497    // Constants for debugging purpose
498      size_t    tab_size;
499
500    public:
501    ////////////////////////
502    // Constructor
503    ////////////////////////
504      HeapDirectory(uint32_t size){
505        assert(size>0 && "Memory Cache, HeapDirectory constructor : invalid size");
506        ptr_free    = 0;
507        full        = false;
508        m_heap_tab  = new HeapEntry[size];
509        tab_size    = size;
510      } // end constructor
511
512    /////////////////
513    // Destructor
514    /////////////////
515      ~HeapDirectory(){
516        delete [] m_heap_tab;
517      } // end destructor
518
519    /////////////////////////////////////////////////////////////////////
520    //          Global initialisation function
521    /////////////////////////////////////////////////////////////////////
522      void init(){
523        ptr_free=0;
524        full=false;
525        for(size_t i=0; i< tab_size-1;i++){
526          m_heap_tab[i].next = i+1;
527        }
528        m_heap_tab[tab_size-1].next = tab_size-1;
529        return;
530      }
531
532    /////////////////////////////////////////////////////////////////////
533    // The print() function prints a selected directory entry
534    // Arguments :
535    // - ptr : the pointer to the entry to print
536    /////////////////////////////////////////////////////////////////////
537      void print(const size_t &ptr){
538        std::cout << "Heap, printing the entry : " << std::dec << ptr << std::endl;
539        m_heap_tab[ptr].print();
540      } // end print()
541
542    /////////////////////////////////////////////////////////////////////
543    // The print_list() function prints a list from selected directory entry
544    // Arguments :
545    // - ptr : the pointer to the first entry to print
546    /////////////////////////////////////////////////////////////////////
547      void print_list(const size_t &ptr){
548        bool end = false;
549        size_t ptr_temp = ptr;
550        std::cout << "Heap, printing the list from : " << std::dec << ptr << std::endl;
551        while(!end){
552            m_heap_tab[ptr_temp].print();
553            if(ptr_temp == m_heap_tab[ptr_temp].next) end = true;
554            ptr_temp = m_heap_tab[ptr_temp].next;
555        } 
556      } // end print_list()
557
558    /////////////////////////////////////////////////////////////////////
559    // The is_full() function return true if the heap is full.
560    /////////////////////////////////////////////////////////////////////
561      bool is_full(){
562        return full;
563      } // end is_full()
564
565    /////////////////////////////////////////////////////////////////////
566    // The next_free_ptr() function returns the pointer
567    // to the next free entry.
568    /////////////////////////////////////////////////////////////////////
569      size_t next_free_ptr(){
570        return ptr_free;
571      } // end next_free_ptr()
572
573    /////////////////////////////////////////////////////////////////////
574    // The next_free_entry() function returns
575    // a copy of the next free entry.
576    /////////////////////////////////////////////////////////////////////
577      HeapEntry next_free_entry(){
578        return HeapEntry(m_heap_tab[ptr_free]);
579      } // end next_free_entry()
580   
581    /////////////////////////////////////////////////////////////////////
582    // The write_free_entry() function modify the next free entry.
583    // Arguments :
584    // - entry : the entry to write
585    /////////////////////////////////////////////////////////////////////
586      void write_free_entry(const HeapEntry &entry){
587        m_heap_tab[ptr_free].copy(entry);
588      } // end write_free_entry()
589
590    /////////////////////////////////////////////////////////////////////
591    // The write_free_ptr() function writes the pointer
592    // to the next free entry
593    /////////////////////////////////////////////////////////////////////
594      void write_free_ptr(const size_t &ptr){
595        assert( (ptr<tab_size) && "HeapDirectory error : try to write a wrong free pointer");
596        ptr_free = ptr;
597      } // end write_free_ptr()
598
599    /////////////////////////////////////////////////////////////////////
600    // The set_full() function sets the full bit (to true).
601    /////////////////////////////////////////////////////////////////////
602      void set_full(){
603        full = true;
604      } // end set_full()
605
606    /////////////////////////////////////////////////////////////////////
607    // The unset_full() function unsets the full bit (to false).
608    /////////////////////////////////////////////////////////////////////
609      void unset_full(){
610        full = false;
611      } // end unset_full()
612
613    /////////////////////////////////////////////////////////////////////
614    // The read() function returns a copy of
615    // the entry pointed by the argument
616    // Arguments :
617    //  - ptr : the pointer to the entry to read
618    /////////////////////////////////////////////////////////////////////
619      HeapEntry read(const size_t &ptr){
620        assert( (ptr<tab_size) && "HeapDirectory error : try to write a wrong free pointer");
621        return HeapEntry(m_heap_tab[ptr]);
622      } // end read()
623
624    /////////////////////////////////////////////////////////////////////
625    // The write() function writes an entry in the heap
626    // Arguments :
627    //  - ptr : the pointer to the entry to replace
628    //  - entry : the entry to write
629    /////////////////////////////////////////////////////////////////////
630      void write(const size_t &ptr, const HeapEntry &entry){
631        assert( (ptr<tab_size) && "HeapDirectory error : try to write a wrong free pointer");
632        m_heap_tab[ptr].copy(entry);
633      } // end write()
634
635  }; // end class HeapDirectory
636
637  ////////////////////////////////////////////////////////////////////////
638  //                        Cache Data
639  ////////////////////////////////////////////////////////////////////////
640  class CacheData {
641    private:
642      const uint32_t m_sets;
643      const uint32_t m_ways;
644      const uint32_t m_words;
645
646      uint32_t *** m_cache_data;
647
648    public:
649
650      CacheData(uint32_t ways, uint32_t sets, uint32_t words)
651        : m_sets(sets), m_ways(ways), m_words(words) {
652
653          m_cache_data = new uint32_t ** [ways];
654          for ( size_t i=0 ; i < ways ; i++ ) {
655            m_cache_data[i] = new uint32_t * [sets];
656          }
657          for ( size_t i=0; i<ways; i++ ) {
658            for ( size_t j=0; j<sets; j++ ) {
659              m_cache_data[i][j] = new uint32_t [words];
660            }
661          }
662        }
663
664      ~CacheData() {
665          for(size_t i=0; i<m_ways ; i++){
666              for(size_t j=0; j<m_sets ; j++){
667                  delete [] m_cache_data[i][j];
668              }
669          }
670          for(size_t i=0; i<m_ways ; i++){
671              delete [] m_cache_data[i];
672          }
673          delete [] m_cache_data;
674      }
675
676      uint32_t read (
677          const uint32_t &way,
678          const uint32_t &set,
679          const uint32_t &word) const {
680
681        assert((set  < m_sets ) && "Cache data error: Trying to read a wrong set" );
682        assert((way  < m_ways ) && "Cache data error: Trying to read a wrong way" );
683        assert((word < m_words) && "Cache data error: Trying to read a wrong word");
684
685        return m_cache_data[way][set][word];
686      }
687
688      void read_line(
689          const uint32_t &way,
690          const uint32_t &set,
691          sc_core::sc_signal<uint32_t> * cache_line)
692      {
693        assert((set < m_sets ) && "Cache data error: Trying to read a wrong set" );
694        assert((way < m_ways ) && "Cache data error: Trying to read a wrong way" );
695     
696        for (uint32_t word=0; word<m_words; word++)
697          cache_line[word].write(m_cache_data[way][set][word]);
698      }
699
700      void write (
701          const uint32_t &way,
702          const uint32_t &set,
703          const uint32_t &word,
704          const uint32_t &data,
705          const uint32_t &be = 0xF) {
706
707        assert((set  < m_sets ) && "Cache data error: Trying to write a wrong set" );
708        assert((way  < m_ways ) && "Cache data error: Trying to write a wrong way" );
709        assert((word < m_words) && "Cache data error: Trying to write a wrong word");
710        assert((be  <= 0xF    ) && "Cache data error: Trying to write a wrong word cell");
711
712        if (be == 0x0) return;
713
714        if (be == 0xF) {
715            m_cache_data[way][set][word] = data; 
716            return;
717        }
718
719        uint32_t mask = 0;
720        if  (be & 0x1) mask = mask | 0x000000FF;
721        if  (be & 0x2) mask = mask | 0x0000FF00;
722        if  (be & 0x4) mask = mask | 0x00FF0000;
723        if  (be & 0x8) mask = mask | 0xFF000000;
724
725        m_cache_data[way][set][word] = 
726          (data & mask) | (m_cache_data[way][set][word] & ~mask);
727      }
728  }; // end class CacheData
729
730}} // end namespaces
731
732#endif
733
734// Local Variables:
735// tab-width: 4
736// c-basic-offset: 4
737// c-file-offsets:((innamespace . 0)(inline-open . 0))
738// indent-tabs-mode: nil
739// End:
740
741// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
742
Note: See TracBrowser for help on using the repository browser.