Files
gem5/src/mem/slicc
Nilay Vaish c82a8979a3 Change interface between coherence protocols and CacheMemory
The purpose of this patch is to change the way CacheMemory interfaces with
coherence protocols. Currently, whenever a cache controller (defined in the
protocol under consideration) needs to carry out any operation on a cache
block, it looks up the tag hash map and figures out whether or not the block
exists in the cache. In case it does exist, the operation is carried out
(which requires another lookup). As observed through profiling of different
protocols, multiple such lookups take place for a given cache block. It was
noted that the tag lookup takes anything from 10% to 20% of the simulation
time. In order to reduce this time, this patch is being posted.

I have to acknowledge that the many of the thoughts that went in to this
patch belong to Brad.

Changes to CacheMemory, TBETable and AbstractCacheEntry classes:
1. The lookup function belonging to CacheMemory class now returns a pointer
to a cache block entry, instead of a reference. The pointer is NULL in case
the block being looked up is not present in the cache. Similar change has
been carried out in the lookup function of the TBETable class.
2. Function for setting and getting access permission of a cache block have
been moved from CacheMemory class to AbstractCacheEntry class.
3. The allocate function in CacheMemory class now returns pointer to the
allocated cache entry.

Changes to SLICC:
1. Each action now has implicit variables - cache_entry and tbe. cache_entry,
if != NULL, must point to the cache entry for the address on which the action
is being carried out. Similarly, tbe should also point to the transaction
buffer entry of the address on which the action is being carried out.
2. If a cache entry or a transaction buffer entry is passed on as an
argument to a function, it is presumed that a pointer is being passed on.
3. The cache entry and the tbe pointers received __implicitly__ by the
actions, are passed __explicitly__ to the trigger function.
4. While performing an action, set/unset_cache_entry, set/unset_tbe are to
be used for setting / unsetting cache entry and tbe pointers respectively.
5. is_valid() and is_invalid() has been made available for testing whether
a given pointer 'is not NULL' and 'is NULL' respectively.
6. Local variables are now available, but they are assumed to be pointers
always.
7. It is now possible for an object of the derieved class to make calls to
a function defined in the interface.
8. An OOD token has been introduced in SLICC. It is same as the NULL token
used in C/C++. If you are wondering, OOD stands for Out Of Domain.
9. static_cast can now taken an optional parameter that asks for casting the
given variable to a pointer of the given type.
10. Functions can be annotated with 'return_by_pointer=yes' to return a
pointer.
11. StateMachine has two new variables, EntryType and TBEType. EntryType is
set to the type which inherits from 'AbstractCacheEntry'. There can only be
one such type in the machine. TBEType is set to the type for which 'TBE' is
used as the name.

All the protocols have been modified to conform with the new interface.
2011-01-17 18:46:16 -06:00
..
2009-05-11 10:38:43 -07:00
2009-05-11 10:38:43 -07:00

Overview
========
This is SLICC, a domain specific language to specify cache coherence protocol
we have developed in Multifacet group.

It is developed by Milo Martin <milo@cs.wisc.edu>
This document is prepared by Min Xu <mxu@cae.wisc.edu> while I am learning the
system.  With minor correctness updates by Brad Beckmann <beckmann@cs.wisc.edu>

It can be used to generate C++ code that works with RUBY cache simulator as
well as generate HTML and other document to describe the target protocol.

Some user document is available in doc directory.

Tech details
============
SLICC take a text input with similar syntax to C++ language and use the lexer
and parser in parser directory to construct a Abstract Syntax Tree (AST)
internally. After having done this first pass, the AST is traversed to fill
several interval table, such as symbol table, type table, etc. Finally the code
is generated by traversing the tree once again.

Note, by Milo's good coding habit, almost all C++ class define their private
copy/assignment constructor. This prevents accidentally copying/assigning an
object by its address.

The AST basically looks like a hierarchical representation of the text input.
At the highest level, it has the "Machine", each Machine has several "states"
and "events" and "actions" and "transistions".

Since the language is domain specific, many assumptions of the target system is
hardcoded in SLICC. For example, ruby would expect the generated code for each
system node, has the following components:
  processor(sequencer, not generated?)
  cache
  directory (with memory block value, only when compiled with tester)
  network interface (NI)

Directory generator/ contains routines to generate HTML/MIF format output.
fileio.[Ch] has a routine to conditionally write a file only when the original
content of the file is different from what is going to be written, this avoid
re-make those file after regenerate the protocol.  html_gen.[Ch] contains the
symbol name munge and index page generation. mif_gen.[Ch] contains the entire
MIF output generation routine, mainly a table buildup.

Directory symbol/ contains classes to represent symbols in the slicc input
file. Base class is "Symbol". Derived subclasses are "Action Event Func State
StateMachine Transition Type Var". "Symbol" has knowledge about its locations
in the source file and short name, long name. "SymbolTable" is a list of
symbols and g_sym_table is the global SymbolTable of the slicc system.
One can query a SymbolTable by symbol's id. Also SymbolTable is responsible for
keeping track of Symbol's declaration in correct scope. The current
implementation uses a stack which dynamically determine the scope of symbol
lookups. Global scope is at bottom of the stack (vector[0]). SymbolTable is
also the main place to write out the generated C++/HTML/MIF files.
SymbolTable::writeNodeFiles() is one of the place to look for hardcoded C++
code for node.[Ch]. And Type.[Ch] is the place where generating enumeration and
Message/NetworkMessage declaration and implementation. Func.[Ch] is used to
generate function of the class Chip. StateMachine.[Ch] wrap the whole thing
up by putting States, Actions, Events together. It actually has a two dimension
table like the one represented in the HTML output. Actions are indexed with
the initial state and observed event. After the tabel being built, the
StateMachine class can write out Transitions/Controller/wakeup_logic into C++
outputs. Finally, in symbol directory, Var.[Ch] seem to incomplete?

Demystify all those "predefined" external types, like "Address". Where are
they defined? They are in ../protocol/RubySlicc-*.sm and
../protocol/RubySlicc_interfaces.slicc is include in the slicc invocation
command in ../ruby/Makefile.

Another myth: "trigger" method is hardcoded in ast/InPortDeclAST.C and
ast/FuncCallExprAST.C. The function is similar to inlined function in the
output generated code, so you cannot find any occurance of string "trigger" in
the generated code. "trigger" also increment a counter that is checked every
time a transition is done. In one ruby cycle, only TRANSITIONS_PER_RUBY_CYCLE
number of transitions can be done. ast/FuncCallExprAST.C also contains some
code for function "error" and "assert" and "DEBUG_EXPR", all in the same
manner. Ruby always issues transitions from the first port while there is any.
Stalled transition in Ruby does not consume a sub-cycle. This models the
hardware that probe all port in parallel, pick one transition from the highest
priority queue if the transistion was not stalled by any resources constraint.

Another note: scheduleEvent() call of ruby make sure a consumer is woken up at
specified cycle, and only once per cycle.

Action z_stall, where is it? It is hardcoded in symbols/StateMachine.C. In
function StateMachine::printCSwitch(), z_stall cause the generated code return
TransitionResult_ProtocolStall. Also the HTML output for z_stall has to be
consequently hardcoded. I am not sure that's really a good idea or not. :-)

Question: How comes there is no "for" loop statement in slicc?
Answer: Been there, done that. That is easy to add, first of all. But unbound
loop make slicc eventually un-synthesizable. We want to avoid that. If you want
to loop through a bounded array do something, make the action done in a
external interface in RubySlicc_Util.h. Inside, you just pass the vector as
parameter to the external interface to achieve the same effects.

Another bad thing of using loop statement like for is that we can not determine
how many buffer space to allocate before the transition. With a vector, if it
easy to understand we can always allocate the worst case number of hardware
resources.

Question: Wait! It seems statement check_allocate does nothing!
Answer: No, it does call areNSoltsAvailable() function of the object before any
statement is executed in one action. It does *NOT* generate code in its
original place in the code, instead, it scan the body of the action code and
determine how many slots are needed to allocated before hand. So the
transaction is all done or nothing done. I had tried to make all actions return
boolean values and the false return cause a transition to abort with
ResourceStall. But it is later on deemed to be too flexible in its semantics.
We should never introduce control flow inside the transitions, so that each
transition is either "all" or "nothing". Just that simple. BTW, if you call
check_allocate twice, areNSoltsAvailable(2) is generated, three times generates
areNSoltsAvailable(3), etc.