source: branches/MESI/modules/vci_mem_cache/caba/source/include/mem_cache_directory.h @ 670

Last change on this file since 670 was 670, checked in by haoliu, 10 years ago

TSAR branches -- Adding the first version of protocol MESI in TSAR architecture:

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