SLICC - Version 0.3 Design Document - January 17, 1999 Milo Martin and Dan Sorin Question: Rethinking of support for profiling the transactions Question: How do we deal with functions/methods and resources Comment: We need to discuss the sequencer interface so it can work now and for the speculative version buffer. Overview -------- We are in the process of designing and implementing SLICC v0.3, an evolution of SLICC v0.2. The new design includes capabilities for design of multilevel cache hierarchies including the specification of multiple protocol state machines (PSMs) and the queues which connect these PSMs and the network. We actually believe that most of the network and network topology, including the ordered network, can be expressed using the new hierarchical extensions to the language. In addition, many implicit aspects of the language will be eliminated in favor of explicit declarations. For example functions, queues, and objects declarations such as "cacheMemory" and "TBETable" will be explicitly declared. This will allow for full static type checking and easier extension for the language. Event triggering will be part of "in_port" declarations and not "event" declarations. Finally, many less fundamental, but important, features and internal code improvements will be enhanced. SLICC History ------------- v0.1 - Initially the language only handled the generation of the PSM transition table logic. All actions and event triggering were still coded in C++. At this point it was still called, "the language." v0.2 - Extended the language to include a simple C like syntax for specifying actions, event triggering, and manipulating queues and state elements. This version was the first version of the language known as SLICC (suggested by Amir) and was used for the Multifacet ISCA 2000 submission. v0.3 - Development effort started January 2000. Intended features and enhancements are described by this document. Specifying Hierarchical Designs ------------------------------- Right now all of our protocols have two tables, a processor/cache PSM and a directory PSM. In v0.2 this is a rigid requirement and the names are implicit. SLICC v0.3 will allow for an arbitrary number of different PSMs. The most significant improvement in v0.3 is the ability for the user to define an arbitrary set of interconnected PSMs. PSMs may include an L1 cache controller, L2 cache controller, directory controller, speculative version buffer, network interface, etc. There are a couple of "primitive PSMs" such as the sequencer. There will be a notion of a "node" of the system. In a node, each PSM will be instantiated and connected together with queues. For example, assume we define a PSMs and want to create a queue of RequestMessages to communicate between it and the network. machine(CacheController) { ... out_port(to_network, RequestMessage, "To the network", desc="..."); ... } CacheController cache, desc="..."; connect(cache.to_network, network.from_cache, ordered="yes", desc="..."); Explicit State Manipulation --------------------------- As before, PSMs have states, events, and transitions. New in v0.3 each PSM must have user defined methods for get_state(address) and set_state(address, state), and these methods are written explicitly, instead of being implicit functions of memory states (e.g., our current implementation which implicitly uses the TBE state if there is a TBE or uses the cache state). Functions have a return value, procedures do not. Function calls are expressions, procedure calls are statements. All function and procedure parameters are considered pass-by-value. procedure set_state(Address addr, State state) { ... } function State get_state(Address addr) { ... } Explicit Declaration -------------------- PSMs reference or declare structures, such as queues, ports, cache memories, main memory, TBEs, write buffers, etc. These primitive types and structures are written in C++, and their semantics are still specified by the C++ coder. Examples of these primitive types include "CacheMemory," "TBETable," as well as various types of queues. One major difference is that in v0.3 the interface for all of these primitive objects will be declared (but not defined) in the SLICC language. This also allows adding primitive structures by defining a C++ implementation and a SLICC interface specification. This will make the language much more extensible. Specifying the interface of these primitive types, structures, and queues in SLICC will eliminate much of the implicit semantics that is currently hiding in the controllers. The interface declaration might be in one file and shared between all protocols. The object instantiation would be internal to each PSM that requires a cache memory. The syntax for messages will also be enhanced by using this new syntax. Notice the support for default values. structure(CacheMemory, "Cache memory", desc="...") { void cache_change_state(Address addr, State state), desc="..."; Data dataBlk, default="", desc=""; bool cache_avail(Address addr), desc="..."; Address cache_probe(Address addr), desc="..."; void cache_allocate(Address addr), desc="..."; } CacheMemory L1cacheMemory, desc="..."; Structure specification is going to require the introduction of an object model in the language. The "." (dot) operator is going to be extended beyond the use as structure element access, but also allow for a object method call syntax similar to C++ and Java. L1cacheMemory.cache_allocate(addr); Polymorphism ------------ We are also going to want to allow for polymorphism for many of the structures. We already have a limited degree of polymorphism between different protocols by using the same cache memory structure with different "CacheEntry" types in each protocol. Now that we are going to have multiple levels of cache, each requiring slightly different state bits, we are going to want to specify cache memory structures which have different "CacheEntry" types in the same protocol. To do this right, this is going to require adding full polymorphism support to the language. Right now we imagine something like C++'s templates, since they are a more natural fit to hardware synthesis in the future. Type Checker ------------ All of the above substantially complicates our type system by requiring more types and scoping rules. As a step towards understanding the implications of the type system, a type checking system will be implemented. This is a hard requirement if we are ever to distribute the system since receiving compile time errors in the generated code is not acceptable. In order to ensure that we don't accidentally design a language that is not statically type checkable, it is important to add the type checker sooner rather than later. Event Triggering ---------------- In v0.2, PSM events were individually specified as sets of conditions. The following SLICC v0.2 code is a simplified example from the origin protocol. event(Dir_data_ack_0, "Data ack 0", desc="... ack count == 0") { if (queue_ready(responseNetwork)) { peek(responseNetwork, ResponseMsg) { if(in_msg.NumPendingAcks == 0) { trigger(in_msg.Address); } } } } event(Dir_data_ack_not_0, "Data ack not 0", desc="... ack count != 0") { if (queue_ready(responseNetwork)) { peek(responseNetwork, ResponseMsg) { if(in_msg.NumPendingAcks != 0) { trigger(in_msg.Address); } } } } The above code defines the exact conditions for the events to be triggered. This type of event specification led to redundant code and numerous bugs where conditions for different events were not completely orthogonal. In v0.3, events will be declared with no accompanying code (similar to how states are specified). Instead, the code that determines which event is triggered will be part of each incoming port's declaration. This approach should eliminate redundancy and bugs in trigger conditions. The v0.3 code for the above would look like: event(Dir_data_ack_0, "Data ack 0", desc="... ack count = 0"); event(Dir_data_ack_not_0, "Data ack not 0", desc="... ack count != 0"); in_port(responseNetwork, ResponseMsg, "Response Network", desc="...") { if(in_msg.NumPendingAcks == 0) { trigger(Dir_data_ack_0, in_msg.Address); } else { trigger(Dir_data_ack_not_0, in_msg.Address); } } Notice that one no longer needs to explicitly check if the queue is ready or to perform the peek operation. Also notice that the type of messages that arrives on the port is explicitly declared. All ports, incoming and outgoing, are now explicitly type channels. You will still be required to include the type of message when manipulating the queue. The type specified will be statically type checked and also acts as self-documenting code. Other Improvements ------------------ There will be a number of other improvements in v0.3 such as general performance tuning and clean up of the internals of the compiler. The compiler will be modified to operate on multiple files. In addition, the abstract syntax tree internal to the code will need to be extended to encompass more information, including information parsed in from multiple files. The affiliates talk and the document for the language should also be updated to reflect the changes in the new version. Looking Forward --------------- When designing v0.3 we are keeping future plans in mind. - When our designs of the multilevel cache hierarchy are complete, we expect to have a large amount of replication between the protocols and caches controllers within a protocol. For v0.4 we hope to look at the patterns that have evolved and look for ways in which the language can capture these patterns. Exploiting reuse will provide quicker protocol development and maintainability. - By keeping the specification structural, we are looking towards generating VHDL/Verilog from SLICC. The type system will help this, as will more explicit instantiation and declaration of types and structures. The structures now written in C++ (sequencer, network, cache arrays) will be ported to the HDL we select. The rest of the controllers will be generated by the compiler. At first the generated controller will not be optimized. I believe that with more effort we can automatically generate reasonably optimized, pipelined implementation of the controllers. Implementation Plan ------------------- - HTML generator - Extend internal parser AST nodes - Add get_state function and set_state procedure declarations - Move trigger logic from events to in_ports - Types - Change type declaration syntax - Declare primitive types and corresponding C++ types - Add default values to structures and types - Add object method call syntax - Write type checker - Documentation - Revise document - Update presentation Document History ---------------- $Id: SLICC_V03.txt,v 3.0 2000/09/12 20:27:59 sorin Exp $ $Log: SLICC_V03.txt,v $ Revision 3.0 2000/09/12 20:27:59 sorin Version 3.0 signifies a checkpoint of the source tree right after the final draft of the ASPLOS '00 paper. Revision 1.1.1.1 2000/03/09 10:18:38 milo Initial import Revision 2.0 2000/01/19 07:21:13 milo Version 2.0 Revision 1.5 2000/01/18 10:26:24 milo Changed the SLICC parser so that it generates a full AST. This is the first step in moving towards v0.3 Revision 1.4 2000/01/17 18:36:15 sorin *** empty log message *** Revision 1.3 2000/01/15 10:30:16 milo Added implementation list Revision 1.2 2000/01/15 08:11:44 milo Minor revisions Revision 1.1 2000/01/15 07:14:17 milo Converted Dan's first draft into a text file. Significant modifications were made.